相關(guān):
https://cryptozombies.io
http://wiki.jikexueyuan.com/project/solidity-zh/
第一章: 課程概述
第二章: 合約
從最基本的開始入手:
Solidity 的代碼都包裹在合約里面. 一份合約就是以太應(yīng)幣應(yīng)用的基本模塊, 所有的變量和函數(shù)都屬于一份合約, 它是你所有應(yīng)用的起點(diǎn).
一份名為 HelloWorld 的空合約如下:
contract HelloWorld {
}
版本指令
所有的 Solidity 源碼都必須冠以 "version pragma" — 標(biāo)明 Solidity 編譯器的版本. 以避免將來新的編譯器可能破壞你的代碼。
例如: pragma solidity ^0.4.19; (當(dāng)前 Solidity 的最新版本是 0.4.19).
綜上所述, 下面就是一個(gè)最基本的合約 — 每次建立一個(gè)新的項(xiàng)目時(shí)的第一段代碼:
pragma solidity ^0.4.19;
contract HelloWorld {
}
第三章: 狀態(tài)變量和整數(shù)
下面學(xué)習(xí) Solidity 中如何使用變量。
狀態(tài)變量是被永久地保存在合約中。也就是說它們被寫入以太幣區(qū)塊鏈中. 想象成寫入一個(gè)數(shù)據(jù)庫。
例子:
contract Example {
// 這個(gè)無符號整數(shù)將會永久的被保存在區(qū)塊鏈中
uint myUnsignedInteger = 100;
}
在上面的例子中,定義 myUnsignedInteger 為 uint 類型,并賦值100。
無符號整數(shù): uint
uint 無符號數(shù)據(jù)類型, 指其值不能是負(fù)數(shù),對于有符號的整數(shù)存在名為 int 的數(shù)據(jù)類型。
注: Solidity中, uint 實(shí)際上是 uint256代名詞, 一個(gè)256位的無符號整數(shù)。你也可以定義位數(shù)少的uints — uint8, uint16, uint32, 等…… 但一般來講你愿意使用簡單的 uint, 除非在某些特殊情況下,這我們后面會講。
第四章
在 Solidity 中,數(shù)學(xué)運(yùn)算很直觀明了,與其它程序設(shè)計(jì)語言相同:
加法: x + y
減法: x - y,
乘法: x * y
除法: x / y
取模 / 求余: x % y (例如, 13 % 5 余 3, 因?yàn)?3除以5,余3)
Solidity 還支持 乘方操作 (如:x 的 y次方) // 例如: 5 ** 2 = 25
uint x = 5 ** 2; // equal to 5^2 = 25
第五章
有時(shí)你需要更復(fù)雜的數(shù)據(jù)類型,Solidity 提供了 結(jié)構(gòu)體:
struct Person {
uint age;
string name;
}
結(jié)構(gòu)體允許你生成一個(gè)更復(fù)雜的數(shù)據(jù)類型,它有多個(gè)屬性。
注:我們剛剛引進(jìn)了一個(gè)新類型, string。 字符串用于保存任意長度的 UTF-8 編碼數(shù)據(jù)。 如:
string greeting = "Hello world!"。
第六章: 數(shù)組
如果你想建立一個(gè)集合,可以用 數(shù)組這樣的數(shù)據(jù)類型. Solidity 支持兩種數(shù)組: 靜態(tài) 數(shù)組和動態(tài) 數(shù)組:
// 固定長度為2的靜態(tài)數(shù)組:
uint[2] fixedArray;
// 固定長度為5的string類型的靜態(tài)數(shù)組:
string[5] stringArray;
// 動態(tài)數(shù)組,長度不固定,可以動態(tài)添加元素:
uint[] dynamicArray;
你也可以建立一個(gè) 結(jié)構(gòu)體類型的數(shù)組 例如,上一章提到的 Person:
Person[] people; // dynamic Array, we can keep adding to it
記住:狀態(tài)變量被永久保存在區(qū)塊鏈中。所以在你的合約中創(chuàng)建動態(tài)數(shù)組來保存成結(jié)構(gòu)的數(shù)據(jù)是非常有意義的。
公共數(shù)組
你可以定義 public 數(shù)組, Solidity 會自動創(chuàng)建 getter 方法. 語法如下:
Person[] public people;
其它的合約可以從這個(gè)數(shù)組讀取數(shù)據(jù)(但不能寫入數(shù)據(jù)),所以這在合約中是一個(gè)有用的保存公共數(shù)據(jù)的模式。
第七章: 定義函數(shù)
在 Solidity 中函數(shù)定義的句法如下:
function eatHamburgers(string _name, uint _amount) {
}
這是一個(gè)名為 eatHamburgers 的函數(shù),它接受兩個(gè)參數(shù):一個(gè) string類型的 和 一個(gè) uint類型的?,F(xiàn)在函數(shù)內(nèi)部還是空的。
注:: 習(xí)慣上函數(shù)里的變量都是以(_)開頭 (但不是硬性規(guī)定) 以區(qū)別全局變量。我們整個(gè)教程都會沿用這個(gè)習(xí)慣。
我們的函數(shù)定義如下:
eatHamburgers("vitalik", 100);
第八章: 使用結(jié)構(gòu)體和數(shù)組
創(chuàng)建新的結(jié)構(gòu)體
還記得上個(gè)例子中的 Person 結(jié)構(gòu)嗎?
struct Person {
uint age;
string name;
}
Person[] public people;
現(xiàn)在我們學(xué)習(xí)創(chuàng)建新的 Person 結(jié)構(gòu),然后把它加入到名為 people 的數(shù)組中.
// 創(chuàng)建一個(gè)新的Person:
Person satoshi = Person(172, "Satoshi");
// 將新創(chuàng)建的satoshi添加進(jìn)people數(shù)組:
people.push(satoshi);
你也可以兩步并一步,用一行代碼更簡潔:
people.push(Person(16, "Vitalik"));
注:array.push() 在數(shù)組的 尾部 加入新元素 ,所以元素在數(shù)組中的順序就是我們添加的順序, 如:
uint[] numbers;
numbers.push(5);
numbers.push(10);
numbers.push(15);
// numbers is now equal to [5, 10, 15]
第九章: 私有 / 公共函數(shù)
Solidity 定義的函數(shù)的屬性默認(rèn)為公共。 這就意味著任何一方 (或其它合約) 都可以調(diào)用你合約里的函數(shù)。
顯然,不是什么時(shí)候都需要這樣,而且這樣的合約易于受到攻擊。
所以將自己的函數(shù)定義為私有是一個(gè)好的編程習(xí)慣,只有當(dāng)你需要外部世界調(diào)用它時(shí)才將它設(shè)置為公共。
如何定義一個(gè)私有的函數(shù)呢?
uint[] numbers;
function _addToArray(uint _number) private {
numbers.push(_number);
}
這意味著只有我們合約中的其它函數(shù)才能夠調(diào)用這個(gè)函數(shù),給 numbers 數(shù)組添加新成員。
可以看到,在函數(shù)名字后面使用關(guān)鍵字 private 即可。和函數(shù)的參數(shù)類似,私有函數(shù)的名字用(_)起始。
第十章: 函數(shù)的更多屬性
本章中我們將學(xué)習(xí)函數(shù)的返回值和修飾符。
返回值
要想函數(shù)返回一個(gè)數(shù)值,按如下定義:
string greeting = "What's up dog";
function sayHello() public returns (string) {
return greeting;
}
Solidity 里,函數(shù)的定義里可包含返回值的數(shù)據(jù)類型(如本例中 string)。
函數(shù)的修飾符
上面的函數(shù)實(shí)際上沒有改變 Solidity 里的狀態(tài),即,它沒有改變?nèi)魏沃祷蛘邔懭魏螙|西。
這種情況下我們可以把函數(shù)定義為 view, 意味著它只能讀取數(shù)據(jù)不能更改數(shù)據(jù):
function sayHello() public view returns (string) {
}
Solidity 還支持 pure 函數(shù), 表明這個(gè)函數(shù)甚至都不訪問應(yīng)用里的數(shù)據(jù),例如:
function _multiply(uint a, uint b) private pure returns (uint) {
return a * b;
}
這個(gè)函數(shù)甚至都不讀取應(yīng)用里的狀態(tài) — 它的返回值完全取決于它的輸入?yún)?shù),在這種情況下我們把函數(shù)定義為 pure.
注:可能很難記住何時(shí)把函數(shù)標(biāo)記為 pure/view。 幸運(yùn)的是, Solidity 編輯器會給出提示,提醒你使用這些修飾符。
第十一章: Keccak256 和 類型轉(zhuǎn)換
如何讓 _generateRandomDna 函數(shù)返回一個(gè)全(半) 隨機(jī)的 uint?
Ethereum 內(nèi)部有一個(gè)散列函數(shù)keccak256,它用了SHA3版本。一個(gè)散列函數(shù)基本上就是把一個(gè)字符串轉(zhuǎn)換為一個(gè)256位的16進(jìn)制數(shù)字。字符串的一個(gè)微小變化會引起散列數(shù)據(jù)極大變化。
這在 Ethereum 中有很多應(yīng)用,但是現(xiàn)在我們只是用它造一個(gè)偽隨機(jī)數(shù)。
例子:
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");
顯而易見,輸入字符串只改變了一個(gè)字母,輸出就已經(jīng)天壤之別了。
注: 在區(qū)塊鏈中安全地產(chǎn)生一個(gè)隨機(jī)數(shù)是一個(gè)很難的問題, 本例的方法不安全,但是在我們的Zombie DNA算法里不是那么重要,已經(jīng)很好地滿足我們的需要了。
類型轉(zhuǎn)換
有時(shí)你需要變換數(shù)據(jù)類型。例如:
uint8 a = 5;
uint b = 6;
// 將會拋出錯(cuò)誤,因?yàn)?a * b 返回 uint, 而不是 uint8:
uint8 c = a * b;
// 我們需要將 b 轉(zhuǎn)換為 uint8:
uint8 c = a * uint8(b);
上面, a * b 返回類型是 uint, 但是當(dāng)我們嘗試用 uint8 類型接收時(shí), 就會造成潛在的錯(cuò)誤。如果把它的數(shù)據(jù)類型轉(zhuǎn)換為 uint8, 就可以了,編譯器也不會出錯(cuò)。
第十三章: 事件
事件 是合約和區(qū)塊鏈通訊的一種機(jī)制。你的前端應(yīng)用“監(jiān)聽”某些事件,并做出反應(yīng)。
例子:
// 這里建立事件
event IntegersAdded(uint x, uint y, uint result);
function add(uint _x, uint _y) public {
uint result = _x + _y;
//觸發(fā)事件,通知app
IntegersAdded(_x, _y, result);
return result;
}
你的 app 前端可以監(jiān)聽這個(gè)事件。JavaScript 實(shí)現(xiàn)如下:
YourContract.IntegersAdded(function(error, result) {
// 干些事
}