如何在以太坊上搭建一個(gè)Dapp?對(duì)于開發(fā)人員來說,最好的學(xué)習(xí)辦法就是親自動(dòng)手做一個(gè)小項(xiàng)目。所以,接下來將會(huì)以一個(gè)投票程序?yàn)槔?,帶著你在以太坊平臺(tái)上搭建一個(gè)dapp,并且通過借助這樣一個(gè)例子介紹Dapp的編譯、部署及交互過程。
這個(gè)程序的功能很簡(jiǎn)單,只是設(shè)定一組候選項(xiàng),讓所有人都可以給這些候選項(xiàng)投票,以及顯示每個(gè)候選項(xiàng)收到的總票數(shù)。
事先說明,因?yàn)樗衐app框架都會(huì)隱藏掉一些底層細(xì)節(jié),對(duì)初學(xué)者來說,貿(mào)然使用框架可能會(huì)形成對(duì)系統(tǒng)認(rèn)識(shí)上的障礙,所以本文不會(huì)介紹如何借助框架搭建dapp。這樣等將來需要甄選框架時(shí),你也能清楚地看到框架到底幫你做了什么。
首先,準(zhǔn)備開發(fā)環(huán)境,學(xué)習(xí)在開發(fā)環(huán)境中的合約編寫、編譯和部署流程,通過node.js控制臺(tái)與區(qū)塊鏈上的合約交互,通過一個(gè)簡(jiǎn)單的網(wǎng)頁與合約交互,在頁面上提供投票功能并顯示候選項(xiàng)及相應(yīng)的票數(shù)。
整個(gè)程序的開發(fā)都是在一臺(tái)干凈的ubuntu 16.04 xenial上完成的。除此之外,我還在一臺(tái)macos上重復(fù)了一遍搭建和測(cè)試過程。
準(zhǔn)備開發(fā)環(huán)境
按web開發(fā)的說法,真實(shí)區(qū)塊鏈(live blockchain)相當(dāng)于生產(chǎn)環(huán)境,我們自然不應(yīng)該在生產(chǎn)環(huán)境上做開發(fā),因此本文用了一個(gè)名為ganache的內(nèi)存區(qū)塊鏈(相當(dāng)于區(qū)塊鏈模擬器)。本教程的第二篇文章才會(huì)跟真正的區(qū)塊鏈交互。
下面是在linux操作系統(tǒng)上安裝ganache和web3js,以及啟動(dòng)測(cè)試區(qū)塊鏈的步驟。在macos上可以用同樣的命令。windows系統(tǒng)可以參照這里的命令。
注意:ganache-cli會(huì)創(chuàng)建10個(gè)自動(dòng)參與交易的測(cè)試賬號(hào),每個(gè)賬號(hào)里都預(yù)存了100個(gè)以太幣(當(dāng)然,只能用于測(cè)試),區(qū)塊鏈DAPP項(xiàng)目開發(fā),DAPP系統(tǒng)開發(fā)模式源碼,DAPP錢包系統(tǒng)搭建技術(shù)。
簡(jiǎn)單的投票合約
接下來我們要用Solidity編程語言編寫合約。如果你熟悉面向?qū)ο缶幊?,就?huì)覺得這個(gè)學(xué)起來很輕松。
我們要編寫一個(gè)名為Voting的合約(相當(dāng)于OOP語言中的類)。這個(gè)合約中會(huì)有個(gè)構(gòu)造器,負(fù)責(zé)初始化一個(gè)包含候選項(xiàng)的數(shù)組;還會(huì)有兩個(gè)方法,一個(gè)用于返回指定候選項(xiàng)的總票數(shù),另一個(gè)給候選項(xiàng)的得票數(shù)加一。
注意:在將合約部署到區(qū)塊鏈上時(shí),構(gòu)造器會(huì)執(zhí)行,并且只會(huì)執(zhí)行這一次。在做web應(yīng)用時(shí),每次重新部署都會(huì)覆蓋掉原來的代碼,但部署到區(qū)塊鏈上的代碼是不可變的。也就是說,即便你更新了合約,又重新部署了一次,之前的合約仍然會(huì)原封不動(dòng)地留在區(qū)塊鏈上,并且其中存儲(chǔ)的數(shù)據(jù)也不會(huì)受到絲毫影響,新部署的代碼會(huì)創(chuàng)建一個(gè)全新的合約實(shí)例。
下面是帶有注釋的投票合約代碼:
pragma solidity^0.4.18;
//必須指明編譯這段代碼的編譯器版本
contract Voting{
/*下面這個(gè)mapping域相當(dāng)于一個(gè)關(guān)聯(lián)數(shù)組或哈希。
mapping的鍵是候選項(xiàng)的名字,類型為bytes32;
值的類型是無符號(hào)整型,用于存儲(chǔ)得票數(shù)。
*/
mapping(bytes32=>uint8)public votesReceived;
/*Solidity(還)不允許給構(gòu)造器傳入字符串?dāng)?shù)組。
所以我們用bytes32數(shù)組存儲(chǔ)候選項(xiàng)
*/
bytes32[]public candidateList;
/*這就是把合約部署到區(qū)塊鏈上時(shí)會(huì)執(zhí)行一次的構(gòu)造器。
在部署合約時(shí),我們會(huì)傳入一個(gè)包含候選項(xiàng)的數(shù)組。
*/
function Voting(bytes32[]candidateNames)public{
candidateList=candidateNames;
}
//這個(gè)函數(shù)用于返回指定候選項(xiàng)的總票數(shù),其參數(shù)即為指定候選項(xiàng)
function totalVotesFor(bytes32 candidate)view public returns(uint8){
require(validCandidate(candidate));
return votesReceived[candidate];
}
//這個(gè)函數(shù)用于將指定候選項(xiàng)的票數(shù)加一
//這相當(dāng)于實(shí)現(xiàn)了投票功能
function voteForCandidate(bytes32 candidate)public{
require(validCandidate(candidate));
votesReceived[candidate]+=1;
}
function validCandidate(bytes32 candidate)view public returns(bool){
for(uint i=0;i
if(candidateList==candidate){
return true;
}
}
return false;
}
}
部署區(qū)塊鏈
將上面的代碼保存到Voting.sol文件中,放在hello_world_voting目錄下。接下來我們要編譯這段代碼,并將它部署到ganache區(qū)塊鏈上。
在編譯Solidity代碼之前,需要先安裝npm模塊solc。我們會(huì)在node控制臺(tái)中用這個(gè)庫編譯合約。
首先,在終端中運(yùn)行node命令進(jìn)入node控制臺(tái),初始化solc和web3對(duì)象。下面是需要在node控制臺(tái)中輸入的代碼:
mahesh projectblockchain:~/hello_world_voting$node
>Web3=require('web3')
>web3=new Web3(new Web3.providers.HttpProvider
為了確保web3對(duì)象初始化成功,可以跟區(qū)塊鏈通訊,我們可以查詢一下區(qū)塊鏈上的所有賬號(hào)。
為了編譯合約,需要先加載文件Voting.sol中的代碼,并將其賦值給一個(gè)字符串變量,然后再編譯這個(gè)字符串。
>code=fs.readFileSync('Voting.sol').toString()
>solc=require('solc')
>compiledCode=solc.compile(code)
代碼編譯成功后,可以在node終端中輸入compiledCode命令查看contract對(duì)象,有兩個(gè)域非常重要,一定要搞明白:compiledCode.contracts[‘:Voting’].bytecode:這是Voting.sol中的代碼編譯而成的字節(jié)碼,也是要部署到區(qū)塊鏈上的代碼。compiledCode.contracts[‘:Voting’].interface:這是合約的接口或者說模板(稱為abi),告訴合約的用戶有哪些方法可用。將來不管什么時(shí)候要跟合約交互,都需要這個(gè)abi定義。這里有關(guān)于ABI的詳細(xì)介紹。
部署合約
先創(chuàng)建一個(gè)在區(qū)塊鏈中部署和初始化合約的合約對(duì)象(即下面的VotingContract)。
>abiDefinition=JSON.parse(compiledCode.contracts[':Voting'].interface)
>VotingContract=web3.eth.contract(abiDefinition)
>byteCode=compiledCode.contracts[':Voting'].bytecode
>deployedContract=VotingContract.new(['Rama','Nick','Jose'],{data:byteCode,from:web3.eth.accounts[0],gas:
4700000})
>deployedContract.address
>contractInstance=VotingContract.at(deployedContract.address)
上面代碼中的VotingContract.new將合約部署到區(qū)塊鏈上。它的第一個(gè)參數(shù)是包含候選項(xiàng)的數(shù)組,一看就能明白。第二個(gè)參數(shù)中各數(shù)據(jù)項(xiàng)的含義分別為:data:這是已編譯好要部署到區(qū)塊鏈上的字節(jié)碼。from:區(qū)塊鏈必須追蹤是誰部署的合約。在這個(gè)例子中,我們只是調(diào)用了web3.eth.accounts,然后將返回結(jié)果的第一個(gè)賬號(hào)作為這個(gè)合約的所有者(即將合約部署到區(qū)塊鏈上的賬號(hào))。
記住,web3.eth.accounts返回的是ganche在啟動(dòng)測(cè)試區(qū)塊鏈時(shí)創(chuàng)建的10個(gè)測(cè)試賬號(hào)組成的數(shù)組。然而在真實(shí)的區(qū)塊鏈中,不能隨便指定一個(gè)賬號(hào)。那必須是你擁有的賬號(hào),并且在交易之前要解鎖那個(gè)賬號(hào)。在創(chuàng)建賬號(hào)時(shí),系統(tǒng)會(huì)要求你提供一個(gè)口令,這個(gè)口令就是用來證明你對(duì)賬號(hào)的所有權(quán)的。為了用起來方便,Ganache默認(rèn)把10個(gè)賬號(hào)全解鎖了。
gas:跟區(qū)塊鏈交互是要花錢的。為了把你的代碼放到區(qū)塊鏈上,是需要讓礦機(jī)干活的,這筆錢就是給那些付出計(jì)算力的礦機(jī)的。
你必須明確愿意為此支付多少錢,即給‘gas’一個(gè)值。購買燃料的以太幣是從你的from賬號(hào)中出的。燃料的價(jià)格是由網(wǎng)絡(luò)設(shè)定的。合約部署好之后,我們就可以跟合約的實(shí)例(即上面的變量contractInstance)交互了。區(qū)塊鏈上有成百上千個(gè)合約,怎么確定哪個(gè)是你的呢?答案是用deployedContract.address。在你必須跟合約交互時(shí),需要這個(gè)部署地址和之前說過的那個(gè)abi定義。
審核編輯 黃昊宇
-
開發(fā)
+關(guān)注
關(guān)注
0文章
370瀏覽量
40847 -
區(qū)塊鏈智能合約
+關(guān)注
關(guān)注
4文章
426瀏覽量
11266
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論