準(zhǔn)備工作
- 安裝Nodejs環(huán)境,選擇對應(yīng)的平臺,并下載LTS版本。安裝完成后,在終端輸入下面命令查看,如果正常輸出版本號,則安裝成功
node -v
- 安裝truffle框架,輸入如下命令安裝
sudo npm install -g truffle
- 安裝Ganache,下載對應(yīng)平臺安裝包安裝即可。Ganache是一個圖形化的以太坊私有鏈程序,打開后便在本地建立起一個以太坊私有鏈,方便快捷。啟動Ganache后,會有10個默認(rèn)賬戶,并都有100Eth,當(dāng)然,這只是測試用的,因?yàn)樵谀愕乃接墟溕稀0惭b完成后請開啟Ganache,后面部署合約時會用到
image.png - 下載metamask以太坊錢包。介于網(wǎng)絡(luò)的原因,我們用firfox或者Opera版本的插件。當(dāng)然,你也可以直接下載瀏覽器。
創(chuàng)建目錄,編程初體驗(yàn)
-
首先,我們創(chuàng)建一個目錄。這就不需要多說了,我的目錄就叫SmartContart
image.png - 在當(dāng)前文件夾下輸入truffle命令,來unbox一個項目
truffle unbox pet-shop
-
用你的編輯器打開這個項目,我使用的是Atom,文件夾介紹在圖里。有一點(diǎn)需要說明下,migrations是部署合約的文件夾,通過js來部署(圖中migrations的解釋寫錯了,請忽略)。
image.png - 我們新建一個智能合約,叫Election.sol,并寫上下面的代碼:
pragma solidity ^0.4.2; //這句聲明了我們的solidity版本
//聲明一個合約 contract是關(guān)鍵字,Election是合約名字,合約名字和文件名要一致
contract Election {
/* 聲明一個public 的string類型的狀態(tài)變量(可以理解為成員變量) */
string public candidate;
/* constractor 函數(shù)名和合約名一樣的函數(shù)就是構(gòu)造函數(shù),與C++相似*/
/* 構(gòu)造函數(shù)是public的,因?yàn)樾枰獠縼碚{(diào)用 */
function Election() public{
candidate = "Candidate 1";
}
}
這個代碼聲明了一個合約,然后在構(gòu)造函數(shù)中給candicate這個狀態(tài)變量賦值,僅此而已。
- 在終端中打開項目目錄,輸入下面的命令部署合約
truffle migrate
- 部署后,輸入
truffle console
,打開truffle終端,輸入下面這行代碼
Election.deployed().then(function(instance) { app = instance})
你會得到undefined錯誤,原因我們暫時先放放
然后輸入app.address
,你會看到一個地址被打印出來,這就是合約地址,再輸入app.candicate()
,你會看到打印出來的值是我們在構(gòu)造函數(shù)中賦值給candicate狀態(tài)變量的值
當(dāng)我們聲明candicate狀態(tài)變量為public時,會自動生成同名的get函數(shù)來獲取這個狀態(tài)變量的值,我們通過創(chuàng)建的app對象訪問candicate,便得到了candicate的值。此時,打開你的Ganache,你會發(fā)現(xiàn)有個賬戶的以太幣減少了,這就是部署合約的開銷。
- 到此,我們已經(jīng)部署了一個很單純的合約,只有讀取變量的功能。接下來我們給Election.sol文件稍微增加些代碼。我們把Candidate換成了一個結(jié)構(gòu)體,添加了一個mapping及addCandidate方法。我們希望通過Candidate來保存某個Candidate的票數(shù),通過addCandidate來創(chuàng)建候選人
pragma solidity ^0.4.2; //這句聲明了我們的solidity版本
//聲明一個合約 contract是關(guān)鍵字,Election是合約名字,合約名字和文件名要一致
contract Election {
/* 聲明一個candicate結(jié)構(gòu) */
struct Candidate{
uint id;
string name;
uint voteCount;
}
/* 匹配到一個Candicate結(jié)構(gòu),通過一個mapping來做
mapping是key-value的,uint類型作為key,可以對應(yīng)Candicate
結(jié)構(gòu)里的id,而value則是一個Candicate對象
mapping操作會改變合約狀態(tài),將candicates放在區(qū)塊鏈上存儲*/
mapping (uint => Candidate) public candidates;
/* 我們需要手動記錄mapping的大小,因?yàn)閙apping不能被遍歷
mapping的key對應(yīng)的value如果不存在,則返回value類型的默認(rèn)值
比如unit類型返回的是0,Candidate類型就返回空Candidate對象 */
uint public candidatesCount;
/* constractor 函數(shù)名和合約名一樣的函數(shù)就是構(gòu)造函數(shù),與C++相似*/
/* 構(gòu)造函數(shù)是public的,因?yàn)樾枰獠縼碚{(diào)用 */
function Election() public{
addCandidate("candidate 1");
addCandidate("candidate 2");
}
/* 變量前加下劃線表明這個變量是局部變量,不是狀態(tài)變量 */
function addCandidate(string _name) private {
candidatesCount ++;
candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
}
}
修改完代碼后,在終端中輸入truffle migrate --reset
重新部署合約。加入--reset是因?yàn)橹暗暮霞s已經(jīng)記錄在區(qū)塊鏈上了,無法修改,只能重新部署一套合約,重新部署的合約會生成新的合約地址
部署成功后,進(jìn)入truffle終端,輸入下面的代碼創(chuàng)建對象
Election.deployed().then(function(instance) { app = instance})
然后輸入下面的代碼,創(chuàng)建candidate
app.candidates(1).then(function(c) { candidate = c;})
上圖中訪問
candidate
的成員id不能像C++等語言一樣,通過.操作符來訪問結(jié)構(gòu)體中的成員,EVM不認(rèn)識結(jié)構(gòu)體,結(jié)構(gòu)體只是Solidity里的一個語法糖,方便大家寫而已。我們需要通過下標(biāo)來訪問candidate里的成員。打印出來的id顯示為一個BigNumber,這是JavaScript的表示方法,c對應(yīng)的是值,也就是id為1,正如我們創(chuàng)建的那樣。
如果需要顯示正常些,可以用toNumber()
方法和toString()
方法來得到正常些的結(jié)果
調(diào)用以太坊的接口
上面的程序已經(jīng)可以記錄候選人的票數(shù),但是投票人怎么投票呢?在解決這個問題之前,我們先看看如何遠(yuǎn)程調(diào)用以太坊提供的方法(functions)
以太坊提供了一個web3.js庫來訪問以太坊內(nèi)部的方法或者命令。
我們可以在truffle終端中調(diào)用下web3的方法,在truffle終端中輸入web3.eth
,你會得到一堆輸出
這就是web3中eth提供的函數(shù),我們試試accounts,它會列出Ganache網(wǎng)絡(luò)內(nèi)的賬戶地址。這個命令與在自己搭建的私有鏈中查看accounts的調(diào)用方式不同之處在于前面加了個web3
合約測試
因?yàn)橹悄芎霞s一旦部署,不可更改,合約的Bug也會隨之在區(qū)塊鏈上永垂不朽。如果要修復(fù)Bug,必須重新部署合約,這會極大影響用戶體驗(yàn),所以奮不顧身地測試合約也是必要的。測試文件可以是Solidity編寫,也可以是JavaScript來寫。我們先看看JavaScript如何寫測試。
sudo npm install --global mocha
sudo npm install chai
- 寫測試腳本
在test文件夾中創(chuàng)建一個election.js的測試腳本
image.png
測試腳本的代碼如下,更多關(guān)于mocha測試框架和chai斷言庫的教程可以去網(wǎng)上搜搜
var Election = artifacts.require("./Election.sol");
// 聲明合約 第一個參數(shù)是段描述,描述你的測試,第二個參數(shù)是個函數(shù)
//函數(shù)中的it 是一個測試用例,一個contract可以包含多個it
contract("Election", function(accounts){
it("init with two candidates", function(accounts){
return Election.deployed().then(function (instance){
return instance.candidatesCount();
}).then(function(count){
assert.equal(count, 2);
});
});
});
寫好之后在終端中打開項目目錄,輸入truffle test
,如果看到下面的輸出,則測試通過
寫在后面
第一篇教程就暫時寫到這里,希望對大家有所幫助,后續(xù)教程即將來襲