經過上一篇建立以太坊私有鏈(一) 節點部署,目前已經建立創世區塊並且成功讓兩個節點相識了
(在terminal鍵入net.peerCount
應該會顯示1,如果還是0的檢查看看是不是前面漏了什麼步驟)
接下來要讓兩個節點開始挖礦,一起維護這條私有鏈
解鎖帳戶
挖礦之前先調用personal.unlockAccount()
解鎖帳戶
參數的eth.coinbase
會refer到剛剛創的帳戶
terminal1 $> personal.unlockAccount(eth.coinbase)
terminal2 $> personal.unlockAccount(eth.coinbase)
順便把剛剛創的帳戶設成預設帳戶
terminal1 $> web3.eth.defaultAccount = eth.coinbase
terminal2 $>web3.eth.defaultAccount = eth.coinbase
然後就可以開始挖礦
terminal1 $> miner.start()
terminal2 $> miner.start()
p.s. 如果往後在操作帳戶時跳出Exception,可能是又要再personal.unlockAccount()
解鎖帳戶一次
編寫智能合約
太坊私的智能合約編寫是用Solidity這個合約導向式語言
而網路上有個還不錯的可以寫Solidity的線上IDE叫Remix
所以我們下面的範例會在Remix上編寫
篇幅以及方便起見,我就先直接貼上範例的合約程式碼,然後註解一一說明
pragma solidity ^0.4.0; contract Vote { //投票可選的提案數據結構 struct Proposal { bytes32 name; uint voteCount; } //投票者的數據結構 struct Voter { uint weight; bool voted; uint votedProposal; } Proposal[] proposals; //提案列表 address chairperson; //發起投票的人(這個投票合約的擁有者) mapping(address => Voter) voters; //投票者地址與投票狀態的變數 //合約建構子 constructor(bytes32[] proposalNames) public { chairperson = msg.sender; //發起投票的人=呼叫建構子(創立這個合約)的人 voters[chairperson].weight = 1; //發起投票的人的投票權重(可以投1票) //把傳進的所有提案push進提案列表 for(uint i = 0; i < proposalNames.length; i++) { proposals.push(Proposal({ name: proposalNames[i], voteCount: 0 })); } } //發起投票的人指定誰有權利投票的function //預防有人惡意使用多個帳號影響投票公正性 function giveRightToVote(address voter) public { require(msg.sender == chairperson);//確認調用這個function的人是發起投票的人 require(voters[voter].voted == false);//確認要被授權投票權利的人是還沒有投票的人 require(voters[voter].weight == 0);//確認要被授權投票權利的人是沒有投票權利的人(投票權重0) voters[voter].weight = 1;//將要被授權投票權利的人的投票權重設為1 } //投票 function vote(uint proposal) public { Voter storage sender = voters[msg.sender];//用storage修飾這個變數讓這個投票者的資訊會被記錄在區塊鏈上 require(sender.voted ==false);//確認投票者是還沒有投過票的人 require(sender.weight > 0);//確認投票者是有權利投票的人(投票權重>0) sender.voted = true;//將投票者設為已投票 sender.votedProposal = proposal;//將投票者投的提案記錄在會被備份在區塊鏈的變數上 proposals[proposal].voteCount += sender.weight;//將提該案的人數加上投票者投的權重 } //開票 function showWinningProposal() public constant returns (uint winningProposal) { uint maxVote = 0; //將目前最多的得票數設為0 //迴圈找出所有提案得票數最多的 for(uint i = 0; i < proposals.length; i++) { if(proposals[i].voteCount > maxVote) { maxVote = proposals[i].voteCount; winningProposal = i; } } } }
其實這個代碼還是有bug
如果有兩個最高票的提案,showWinningProposal()
只會返回index順序比較前面的那個
不過沒關係,我們只是練習怎麼部署智能合約,所以就「假設」不會有兩個相同得票數最高的提案發生XD
編譯及部署智能合約
切到Compile分頁,選Detail
往下滑到WEB3DEPLOY處,把全部的結果複製起來
由於建構子在合約創立初期就會被執行
還記得我們的建構子有個proposalNames
的參數要傳
所以在第一行的等號後面我們把註解替換成要傳入的參數
var proposalNames = ["Proposal_1", "Proposal_2"];
這是編譯好並且參數也代入的樣子
var proposalNames = ["Proposal_1", "Proposal_2"]; var voteContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"proposal","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"showWinningProposal","outputs":[{"name":"winningProposal","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"voter","type":"address"}],"name":"giveRightToVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"proposalNames","type":"bytes32[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]); var vote = voteContract.new( proposalNames, { from: web3.eth.accounts[0], data: '0x608060405234801561001057600080fd5b50604051610353380380610353833981016040908152815160018054600160a060020a0319163317808255600160a060020a03166000908152600260205292832055909101905b81518110156100ba5760006040805190810160405280848481518110151561007b57fe5b60209081029091018101518252600091810182905283546001818101865594835291819020835160029093020191825591909101519082015501610057565b5050610288806100cb6000396000f3006080604052600436106100565763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630121b93f811461005b57806338351021146100755780639e7b8d611461009c575b600080fd5b34801561006757600080fd5b506100736004356100ca565b005b34801561008157600080fd5b5061008a61013e565b60408051918252519081900360200190f35b3480156100a857600080fd5b5061007373ffffffffffffffffffffffffffffffffffffffff600435166101a8565b336000908152600260205260409020600181015460ff16156100eb57600080fd5b80546000106100f957600080fd5b6001818101805460ff19169091179055600281018290558054600080548490811061012057fe5b60009182526020909120600160029092020101805490910190555050565b600080805b6000548110156101a3578160008281548110151561015d57fe5b906000526020600020906002020160010154111561019b57600080548290811061018357fe5b90600052602060002090600202016001015491508092505b600101610143565b505090565b60015473ffffffffffffffffffffffffffffffffffffffff1633146101cc57600080fd5b73ffffffffffffffffffffffffffffffffffffffff811660009081526002602052604090206001015460ff161561020257600080fd5b73ffffffffffffffffffffffffffffffffffffffff81166000908152600260205260409020541561023257600080fd5b73ffffffffffffffffffffffffffffffffffffffff166000908152600260205260409020600190555600a165627a7a7230582021b9649d3184a755a05749a30746e60836a349203f17a0a9a6a7ee31ca0e29690029', gas: '4700000' }, function (e, contract){ console.log(e, contract); if (typeof contract.address !== 'undefined') { console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); } })
把剛剛編譯好並且參數也填入的程式碼貼上terminal1 $
,讓它挖礦一會兒
當跳出類似 Contract mined!的訊息,代表合約已經打包至區塊上了!!!
後面接的address
是這個合約帳戶的地址
如果terminal1 $
的訊息太多也可以輸入合約變數名稱.address
取得合約地址
terminal1 $> vote.address
"0x33111237ea1c133fb27268700370109d8ec03e87"
如果出現undefined
,就代表還沒被打包至區塊上,放著等它一會兒再試試
下一篇,我們要來執行剛剛部署的投票合約