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");
}
}