【智能合約】Solidity - 編程實例

Solidity - 編程實例


1. 投票

接下來的合約非常複雜,但展示了很多Solidity的特性。它實現了一個投票合約。當然,電子選舉的主要問題是如何賦予投票權給準確的人,並防止操縱。我們不能解決所有的問題,但至少我們會展示如何委託投票可以同時做到投票統計是自動和完全透明。

思路是爲每張選票創建一個合約,每個投票選項提供一個短名稱。合約創建者作爲會長將會給每個投票參與人各自的地址投票權。

地址後面的人們可以選擇自己投票或者委託信任的代表人替他們投票。在投票結束後,winningProposal()將會返回獲得票數最多的提案。
pragma solidity ^0.4.11;

contract Ballot {

    //獨立投票人
    struct Voter {
        uint weight; // 累積的權重
        bool voted;  // 判斷是否已投票
        address delegate; // 委託投票人地址
        uint vote;   // 投票選擇的提案索引號
    }

    //這是一個獨立提案的類型
    struct Proposal {
        bytes32 name;   // 提案名稱
        uint voteCount; // 票數
    }

    address public chairperson;  //合約創建人地址
    mapping(address => Voter) public voters;  //投票人數組
    Proposal[] public proposals;  //提案數組

    /// 初始化,傳入多個提案名稱
    function Ballot(bytes32[] proposalNames) {
        chairperson = msg.sender;  
        voters[chairperson].weight = 1; 

        //初始化提案,名稱 + 初始票數
        for (uint i = 0; i < proposalNames.length; i++) {
            proposals.push(Proposal({
                name: proposalNames[i],
                voteCount: 0
            }));
        }
    }

    // 給予投票人,投票的權利
    function giveRightToVote(address voter) {
        require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0));
        voters[voter].weight = 1;
    }

    // 將自己的投票權委託給別人-to
    function delegate(address to) {

        Voter storage sender = voters[msg.sender];
        require(!sender.voted);  //自己已投票,則無票委託給別人

        require(to != msg.sender);  //不能是自己

        //當投票代表`to`也委託給別人時,尋找到最終的投票代表
        while (voters[to].delegate != address(0)) {
            to = voters[to].delegate;
            require(to != msg.sender);
        }

        sender.voted = true;
        sender.delegate = to;
        Voter storage delegate = voters[to];
        if (delegate.voted) {
            //如果委託的投票代表已經投票了,直接修改票數
            proposals[delegate.vote].voteCount += sender.weight; 
        } else {
            //如果投票代表還沒有投票,則修改其投票權重。
            delegate.weight += sender.weight; 
        }
    }

    //投出你的選票(包括委託給你的選票)
    function vote(uint proposal) {
        Voter storage sender = voters[msg.sender];
        require(!sender.voted);
        sender.voted = true;
        sender.vote = proposal;
        proposals[proposal].voteCount += sender.weight;
    }

    //根據當前所有的投票計算出當前的勝出提案
    function winningProposal() constant
            returns (uint winningProposal)
    {
        uint winningVoteCount = 0;
        for (uint p = 0; p < proposals.length; p++) {
            if (proposals[p].voteCount > winningVoteCount) {
                winningVoteCount = proposals[p].voteCount;
                winningProposal = p;
            }
        }
    }

    //獲得勝出提案的名稱
    function winnerName() constant
            returns (bytes32 winnerName)
    {
        winnerName = proposals[winningProposal()].name;
    }
}

2. 簡單的公開拍賣

通常簡單的公開拍賣合約,是每個人可以在拍賣期間發送他們的競拍出價。爲了實現綁定競拍人的到他們的拍賣,競拍包括髮送金額ether。如果產生了新的最高競拍價,前一個最高價競拍人將會拿回他的錢。在競拍階段結束後,受益人人需要手動調用合約收取他的錢 — — 合約不會激活自己。
pragma solidity ^0.4.11;

contract SimpleAuction {

    address public beneficiary; //拍賣發起人
    uint public auctionStart;  //記錄開始時間,秒爲單位
    uint public biddingTime;  //拍賣時長

    //當前拍賣狀態
    address public highestBidder;
    uint public highestBid;

    mapping(address => uint) pendingReturns; //用戶存在該合約(需退回)的金額

    //在結束時設置爲true來拒絕任何改變
    bool ended;

    //當改變時將會觸發的Event
    event HighestBidIncreased(address bidder, uint amount);
    event AuctionEnded(address winner, uint amount);

    //初始化:發起人,開始時間,拍賣時長。
    function SimpleAuction(uint _biddingTime, address _beneficiary) {
        beneficiary = _beneficiary;
        auctionStart = now;
        biddingTime = _biddingTime;
    }

    function bid() payable {

        require(now <= (auctionStart + biddingTime));  //要求拍賣未結束
        require(msg.value > highestBid); // 如果給出的金額小於當前最高價,會退回錢

        //退回上一位最高價
        if (highestBidder != 0) {
            //上一位出最高價的人,需退回的金額加上他出的價。
            pendingReturns[highestBidder] += highestBid;  
        }
        //修改最高價
        highestBidder = msg.sender;
        highestBid = msg.value;
        HighestBidIncreased(msg.sender, msg.value);
    }

    //提現
    function withdraw() returns (bool) {
        uint amount = pendingReturns[msg.sender]; //用戶需退回的金額
        if (amount > 0) {
            pendingReturns[msg.sender] = 0;

            if (!msg.sender.send(amount)) {
                pendingReturns[msg.sender] = amount;
                return false;
            }
        }
        return true;
    }

    //拍賣結束,最高價的金額發送給拍賣者
    function auctionEnd() {
        //要求拍賣結束
        require(now >= (auctionStart + biddingTime)); 
        require(!ended);

        //修改拍賣參數,設置爲結束
        ended = true;
        AuctionEnded(highestBidder, highestBid);

        //發送金額給拍賣者
        beneficiary.transfer(highestBid);
    }
}

3.盲拍

接下來擴展前面的公開拍賣成爲一個盲拍。盲拍的特點是拍賣結束以前沒有時間壓力。在一個透明的計算平臺上創建盲拍系統聽起來可能有些矛盾,但是加密算法能讓你脫離困境。

在拍賣階段, 競拍人不需要發送實際的出價,僅僅只需要發送一個它的散列值。因爲目前幾乎不可能找到兩個值(足夠長)的散列值相等,競拍者提交他們的出價散列值。在拍賣結束後,競拍人重新發送未加密的競拍出價,合約將檢查其散列值是否和拍賣階段發送的一樣。 另一個挑戰是如何讓拍賣同時實現綁定和致盲 :防止競拍人競拍成功後不付錢的唯一的辦法是,在競拍出價的同時發送保證金。但是在Ethereum上發送保證金是無法致盲,所有人都能看到保證金。下面的合約通過接受任何儘量大的出價來解決這個問題。當然這可以在最後的揭拍階段進行復核,一些競拍出價可能是無效的,這樣做的目的是(它提供一個顯式的標誌指出是無效的競拍,同時包含高額保證金):競拍人可以通過放置幾個無效的高價和低價競拍來混淆競爭對手。
pragma solidity ^0.4.11;

contract BlindAuction {
    struct Bid {
        bytes32 blindedBid;
        uint deposit;
    }

    address public beneficiary;  //拍賣發起人
    uint public auctionStart;
    uint public biddingEnd;
    uint public revealEnd;
    bool public ended;

    mapping(address => Bid[]) public bids;

    address public highestBidder;
    uint public highestBid;

    mapping(address => uint) pendingReturns; 

    event AuctionEnded(address winner, uint highestBid);

    modifier onlyBefore(uint _time) { require(now < _time); _; }
    modifier onlyAfter(uint _time) { require(now > _time); _; }

    //初始化
    function BlindAuction(uint _biddingTime, uint _revealTime, address _beneficiary) {
        beneficiary = _beneficiary;
        auctionStart = now;
        biddingEnd = now + _biddingTime;
        revealEnd = biddingEnd + _revealTime;
    }

    /*放置一個盲拍出價使用`_blindedBid`=sha3(value,fake,secret).
      僅僅在競拍結束正常揭拍後退還發送的以太。
      當隨同發送的以太至少等於 "value"指定的保證金並且 "fake"不爲true的時候纔是有效的競拍出價。
      同一個地址可以放置多個競拍*/
    function bid(bytes32 _blindedBid) payable onlyBefore(biddingEnd){
        bids[msg.sender].push(Bid({
            blindedBid: _blindedBid,
            deposit: msg.value
        }));
    }

    //揭開你的盲拍競價。你將會拿回除了最高出價外的所有競拍保證金,以及正常的無效盲拍保證金。
    function reveal(uint[] _values, bool[] _fake, bytes32[] _secret)
        onlyAfter(biddingEnd) onlyBefore(revealEnd) {
        uint length = bids[msg.sender].length;
        require(_values.length == length);
        require(_fake.length == length);
        require(_secret.length == length);

        uint refund;
        for (uint i = 0; i < length; i++) {
            var bid = bids[msg.sender][i];
            var (value, fake, secret) = (_values[i], _fake[i], _secret[i]);
            if (bid.blindedBid != keccak256(value, fake, secret)) {
                continue;
            }
            refund += bid.deposit;
            if (!fake && bid.deposit >= value) {
                if (placeBid(msg.sender, value))
                    refund -= value;
            }
            bid.blindedBid = bytes32(0);
        }
        msg.sender.transfer(refund);
    }

    //這是一個內部 (internal)函數,意味着僅僅只有合約(或者從其繼承的合約)可以調用
    function placeBid(address bidder, uint value) internal
            returns (bool success)
    {
        if (value <= highestBid) {
            return false;
        }
        if (highestBidder != 0) {
            //退還前一個最高競拍出價
            pendingReturns[highestBidder] += highestBid;
        }
        highestBid = value;
        highestBidder = bidder;
        return true;
    }

    //提現多餘付出的金額
    function withdraw() {
        uint amount = pendingReturns[msg.sender];
        if (amount > 0) {
            pendingReturns[msg.sender] = 0;
            msg.sender.transfer(amount);
        }
    }

    //競拍結束後發送最高出價到競拍人
    function auctionEnd() onlyAfter(revealEnd)
    {
        require(!ended);
        AuctionEnded(highestBidder, highestBid);
        ended = true;
        beneficiary.transfer(this.balance);
    }
}

轉載自:here
參考原文:here

發佈了361 篇原創文章 · 獲贊 75 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章