Lesson 1
《区块链技术进阶与实战》一书里提到了区块链编写的语言——Solidity,于是我决定去看一看。看到了一个对于初学者,包括刚接触编程的人都很友好的一个学习网站:https://cryptozombies.io/。
接下来我进行一些自己的总结:首先,和c++ java一样,需要引入头文件。Solidity引入头文件的方法就是加上一句pragma solidity ^0.4.19;
。Solidity一些地方和python很像,比如x的y次方,都会使用x ** y
来表示。但是Solidity的private、public关键字是声明在类型后面的:Zombie[] public zombies;
比如这样,声明的就是一个Zombie类型的动态数组。需要注意的是,动态数组使用.push()方法会返回一个值,就是加入的元素的索引。但是下标是从0开始的,所以使用x=array.push(tmp)-1
就可以获得刚刚插入的tmp元素的下标。如果是私有类型的方法,在方法名前面还得加上下划线_
:
function _createZombie(string _name, uint _dna) private {
zombies.push(Zombie(_name, _dna));
}
但是如果是public的方法,就不需要加上这个下划线。
最后附加上这个网站游戏的通关代码:
pragma solidity ^0.4.19;
contract ZombieFactory {
event NewZombie(uint zombieId,string name,uint dna);
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
function _createZombie(string _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna))-1;
NewZombie(id,_name,_dna);
}
function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(_str));
return rand % dnaModulus;
}
function createRandomZombie(string _name) public {
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
}
(其实在你每一关出错的时候会出现查看正确答案的按钮)
但是,到这里还没有结束。下面就是介绍以太坊的Web3.js库了,
下面是使用Web3.js的代码:
// Here's how we would access our contract:
var abi = /* abi generated by the compiler */
var ZombieFactoryContract = web3.eth.contract(abi)
var contractAddress = /* our contract address on Ethereum after deploying */
var ZombieFactory = ZombieFactoryContract.at(contractAddress)
// `ZombieFactory` has access to our contract's public functions and events
// some sort of event listener to take the text input:
$("#ourButton").click(function(e) {
var name = $("#nameInput").val()
// Call our contract's `createRandomZombie` function:
ZombieFactory.createRandomZombie(name)
})
// Listen for the `NewZombie` event, and update the UI
var event = ZombieFactory.NewZombie(function(error, result) {
if (error) return
generateZombie(result.zombieId, result.name, result.dna)
})
// take the Zombie dna, and update our image
function generateZombie(id, name, dna) {
let dnaStr = String(dna)
// pad DNA with leading zeroes if it's less than 16 characters
while (dnaStr.length < 16)
dnaStr = "0" + dnaStr
let zombieDetails = {
// first 2 digits make up the head. We have 7 possible heads, so % 7
// to get a number 0 - 6, then add 1 to make it 1 - 7. Then we have 7
// image files named "head1.png" through "head7.png" we load based on
// this number:
headChoice: dnaStr.substring(0, 2) % 7 + 1,
// 2nd 2 digits make up the eyes, 11 variations:
eyeChoice: dnaStr.substring(2, 4) % 11 + 1,
// 6 variations of shirts:
shirtChoice: dnaStr.substring(4, 6) % 6 + 1,
// last 6 digits control color. Updated using CSS filter: hue-rotate
// which has 360 degrees:
skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360),
eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360),
clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360),
zombieName: name,
zombieDescription: "A Level 1 CryptoZombie",
}
return zombieDetails
}
这些代码主要是获取刚才的Solidity代码生成的随机基因数,实际效果是你输入一个名字,它会生成名字相应的僵尸基因对应的样子。
以上就是完成该网站第一课的内容。
Lesson 2
在Solidity中,mapping默认是键值对方式存储数据。下面是mapping的写法:
//key是地址,value是一个uint类型的值(在Solidity中,address是一个类型)
mapping (address => uint) public accountBalance;
msg.sender:一个全局变量,可以被所有函数调用。指的是当前调用者(智能合约)的address。使用msg.sender来更新 mapping的例子:
mapping (address => uint) favoriteNumber;
function setMyNumber(uint _myNumber) public {
// 更新我们的 `favoriteNumber` 映射来将 `_myNumber`存储在 `msg.sender`名下
favoriteNumber[msg.sender] = _myNumber;
// 存储数据至映射的方法和将数据存储在数组相似
}
function whatIsMyNumber() public view returns (uint) {
// 拿到存储在调用者地址名下的值
// 若调用者还没调用 setMyNumber, 则值为 `0`
return favoriteNumber[msg.sender];
}
msg.sender拥有以太坊区块链的安全保障,所以安全性较高。
require:用该方法声明一个function,如果不满足条件就会返回一个error信息。例:
require(ownerZombieCount[msg.sender] == 0);
继承:
contract ZombieFeeding is ZombieFactory {
}
//合约ZombieFeeding 继承了ZombieFactory
在solidity中,你有两个位置能存储数据,一个是storage,一个是memory,相当于电脑的硬盘和RAM,storage是永久存储在区块链中的,而方法外是无法访问到方法内存储在memory中的变量的。
声明方式如下:
Sandwich storage mySandwich = sandwiches[_index];
Sandwich memory anotherSandwich = sandwiches[_index + 1];
Chapter 8 中设置了一个小错误,当你在一个.sol文件中引用另一个.sol文件合约内的private函数的时候,是无法访问到的,这个时候就需要用到internal和external概念了。external和public差不多,但是external是只有外部合约才能调用的,本合约内部无法调用。而internal类似于private,不同的是继承本合约的合约也能访问到。internal和external声明的位置和public,private相同。
当和区块链中其他的非自己拥有的合约互动的时候,需要用到接口——interface的概念。以下是接口的写法:
contract NumberInterface {
function getNum(address _myAddress) public view returns (uint);
}
对,没错,和你之前想的类似java的interface声明方法不同,根本就没有interface关键字!接口的声明和合约类似,不同的是只声明一个方法,而且没有其他状态变量,并且函数没有函数体。这其实就是一个合约的骨架。
下面是一个合约调用NumberInterface 中的getNum函数的例子:
contract MyContract {
address NumberInterfaceAddress = 0xab38...
// ^ The address of the FavoriteNumber contract on Ethereum
NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);
// Now `numberContract` is pointing to the other contract
function someFunction() public {
// Now we can call `getNum` from that contract:
uint num = numberContract.getNum(msg.sender);
// ...and do something with `num` here
}
}
这样,你就能调用任何别人的声明为public或者external的区块中的函数。
Lesson2的新增的zombiefeeding.sol文件内容如下:
pragma solidity ^0.4.19;
import "./zombiefactory.sol";
contract KittyInterface {
function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
);
}
contract ZombieFeeding is ZombieFactory {
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
KittyInterface kittyContract = KittyInterface(ckAddress);
// Modify function definition here:
function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
_targetDna = _targetDna % dnaModulus;
uint newDna = (myZombie.dna + _targetDna) / 2;
// Add an if statement here
if(keccak256(_species) == keccak256("kitty")){
newDna = newDna - newDna % 100 + 99;
//replace the last 2 digits of DNA with 99
}
_createZombie("NoName", newDna);
}
function feedOnKitty(uint _zombieId, uint _kittyId) public {
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
// And modify function call here:
feedAndMultiply(_zombieId, kittyDna,"kitty");
}
}
网友评论