建立以太坊私有鏈(二) 編寫投票合約

區塊鏈

經過上一篇建立以太坊私有鏈(一) 節點部署,目前已經建立創世區塊並且成功讓兩個節點相識了

(在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,就代表還沒被打包至區塊上,放著等它一會兒再試試

 

下一篇,我們要來執行剛剛部署的投票合約

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *