Solidity編程開發實例

Solidity 編程開發實例

接下來的智能合約教程很是複雜,但展現了不少Solidity的編程開發特性。它實現了一個入門的投票合約。固然,電子選舉的主要問題是如何賦予投票權給準確的人,並防止操縱。咱們不能解決全部的問題,但至少咱們會展現如何委託投票能夠同時作到投票統計是自動和徹底透明。node

Voting 投票

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

地址後面的人們能夠選擇本身投票或者委託信任的表明人替他們投票。在投票結束後,winningProposal()將會返回得到票數最多的提案。編程

/// @title Voting with delegation.
 /// @title 受權投票
    contract Ballot
    {
       // 這裏聲明瞭複雜類型
	     // 將會在被後面的參數使用
		   // 表明一個獨立的投票人。
        struct Voter
        {
            uint weight; // 累積的權重。
            bool voted;  // 若是爲真,則表示該投票人已經投票。
            address delegate; // 委託的投票表明
            uint vote;   // 投票選擇的提案索引號
        }

       // 這是一個獨立提案的類型
        struct Proposal
        {
            bytes32 name;   // 短名稱(32字節)
            uint voteCount; // 累計得到的票數
        }
    address public chairperson;
  //這裏聲明一個狀態變量,保存每一個獨立地址的`Voter` 結構
    mapping(address => Voter) public voters;
    //一個存儲`Proposal`結構的動態數組
    Proposal[] public proposals;

    // 建立一個新的投票用於選出一個提案名`proposalNames`.
    function Ballot(bytes32[] proposalNames)
    {
        chairperson = msg.sender;
        voters[chairperson].weight = 1;
				
        //對提供的每個提案名稱,建立一個新的提案
        //對象添加到數組末尾
        for (uint i = 0; i < proposalNames.length; i++)
            //`Proposal({...})` 建立了一個臨時的提案對象,
            //`proposal.push(...)`添加到了提案數組`proposals`末尾。
            proposals.push(Proposal({
                name: proposalNames[i],
                voteCount: 0
            }));
    }

     //給投票人`voter`參加投票的投票權,
    //只能由投票主持人`chairperson`調用。
    function giveRightToVote(address voter)
    {
        if (msg.sender != chairperson || voters[voter].voted)
            //`throw`會終止和撤銷全部的狀態和以太改變。
           //若是函數調用無效,這一般是一個好的選擇。
           //可是須要注意,這會消耗提供的全部gas。
            throw;
        voters[voter].weight = 1;
    }

    // 委託你的投票權到一個投票表明 `to`。
    function delegate(address to)
    {
        // 指定引用
        Voter sender = voters[msg.sender];
        if (sender.voted)
            throw;
				
        //當投票表明`to`也委託給別人時,尋找到最終的投票表明
        while (voters[to].delegate != address(0) &&
               voters[to].delegate != msg.sender)
            to = voters[to].delegate;
        // 當最終投票表明等於調用者,是不被容許的。
        if (to == msg.sender)
            throw;
        //由於`sender`是一個引用,
        //這裏實際修改了`voters[msg.sender].voted`
        sender.voted = true;
        sender.delegate = to;
        Voter delegate = voters[to];
        if (delegate.voted)
            //若是委託的投票表明已經投票了,直接修改票數
            proposals[delegate.vote].voteCount += sender.weight;
        else
            //若是投票表明尚未投票,則修改其投票權重。
            delegate.weight += sender.weight;
    }

    ///投出你的選票(包括委託給你的選票)
    ///給 `proposals[proposal].name`。
    function vote(uint proposal)
    {
        Voter sender = voters[msg.sender];
        if (sender.voted) throw;
        sender.voted = true;
        sender.vote = proposal;
        //若是`proposal`索引超出了給定的提案數組範圍
        //將會自動拋出異常,並撤銷全部的改變。
        proposals[proposal].voteCount += sender.weight;
    }

   ///@dev 根據當前全部的投票計算出當前的勝出提案
    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;
            }
        }
    }
}

可能的改進

如今,指派投票權到全部的投票參加者須要許多的交易。你能想出更好的方法麼?數組

盲拍

這一節,咱們將展現在以太上建立一個完整的盲拍合約是多麼簡單。咱們從一個全部人都能看到出價的公開拍賣開始,接着擴展合約成爲一個在拍賣結束之前不能看到實際出價的盲拍。安全

簡單的公開拍賣

一般簡單的公開拍賣合約,是每一個人能夠在拍賣期間發送他們的競拍出價。爲了實現綁定競拍人的到他們的拍賣,競拍包括髮送金額/ether。若是產生了新的最高競拍價,前一個最高價競拍人將會拿回他的錢。在競拍階段結束後,受益人人須要手動調用合約收取他的錢 — — 合約不會激活本身。app

contract SimpleAuction {
    // 拍賣的參數。
   // 時間要麼爲unix絕對時間戳(自1970-01-01以來的秒數),
   // 或者是以秒爲單位的出塊時間
    address public beneficiary;
    uint public auctionStart;
    uint public biddingTime;

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

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

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

    //下面是一個叫作natspec的特殊註釋,
    //由3個連續的斜槓標記,當詢問用戶確認交易事務時將顯示。

    ///建立一個簡單的合約使用`_biddingTime`表示的競拍時間,
   /// 地址`_beneficiary`.表明實際的拍賣者
    function SimpleAuction(uint _biddingTime,
                           address _beneficiary) {
        beneficiary = _beneficiary;
        auctionStart = now;
        biddingTime = _biddingTime;
    }

    ///對拍賣的競拍保證金會隨着交易事務一塊兒發送,
   ///只有在競拍失敗的時候纔會退回
    function bid() {

       //不須要任何參數,全部的信息已是交易事務的一部分
        if (now > auctionStart + biddingTime)
           //當競拍結束時撤銷此調用
            throw;
        if (msg.value <= highestBid)
           //若是出價不是最高的,發回競拍保證金。
            throw;
        if (highestBidder != 0)
            highestBidder.send(highestBid);
        highestBidder = msg.sender;
        highestBid = msg.value;
        HighestBidIncreased(msg.sender, msg.value);
    }

   ///拍賣結束後發送最高的競價到拍賣人
    function auctionEnd() {
        if (now <= auctionStart + biddingTime)
            throw; 
            //拍賣尚未結束
        if (ended)
            throw; 
     //這個收款函數已經被調用了
        AuctionEnded(highestBidder, highestBid);
        //發送合約擁有全部的錢,由於有一些保證金可能退回失敗了。

        beneficiary.send(this.balance);
        ended = true;
    }

    function () {
        //這個函數將會在發送到合約的交易事務包含無效數據
        //或無數據的時執行,這裏撤銷全部的發送,
        //因此沒有人會在使用合約時由於意外而丟錢。
        throw;
    }
}

Blind Auction 盲拍

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

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

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;

    event AuctionEnded(address winner, uint highestBid);

   ///修飾器(Modifier)是一個簡便的途徑用來驗證函數輸入的有效性。
   ///`onlyBefore` 應用於下面的 `bid`函數,其舊的函數體替換修飾器主體中 `_`後就是其新的函數體
    modifier onlyBefore(uint _time) { if (now >= _time) throw; _ }
    modifier onlyAfter(uint _time) { if (now <= _time) throw; _ }

    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的時候纔是有效的競拍
   ///出價。設置 "fake"爲true或發送不合適的金額將會掩沒真正的競拍出
   ///價,可是仍然須要抵押保證金。同一個地址能夠放置多個競拍。
    function bid(bytes32 _blindedBid)
        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;
        if (_values.length != length || _fake.length != length ||
                    _secret.length != length)
            throw;
        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 != sha3(value, fake, secret))
                //出價未被正常揭拍,不能取回保證金。
                continue;
            refund += bid.deposit;
            if (!fake && bid.deposit >= value)
                if (placeBid(msg.sender, value))
                    refund -= value;
            //保證發送者毫不可能重複取回保證金
            bid.blindedBid = 0;
        }
        msg.sender.send(refund);
    }

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

   ///競拍結束後發送最高出價到競拍人
    function auctionEnd()
        onlyAfter(revealEnd)
    {
        if (ended) throw;
        AuctionEnded(highestBidder, highestBid);
        //發送合約擁有全部的錢,由於有一些保證金退回可能失敗了。
        beneficiary.send(this.balance);
        ended = true;
    }

    function () { throw; }
}
Safe Remote Purchase 安全的遠程購物

contract Purchase
{
    uint public value;
    address public seller;
    address public buyer;
    enum State { Created, Locked, Inactive }
    State public state;
    function Purchase()
    {
        seller = msg.sender;
        value = msg.value / 2;
        if (2 * value != msg.value) throw;
    }
    modifier require(bool _condition)
    {
        if (!_condition) throw;
        _
    }
    modifier onlyBuyer()
    {
        if (msg.sender != buyer) throw;
        _
    }
    modifier onlySeller()
    {
        if (msg.sender != seller) throw;
        _
    }
    modifier inState(State _state)
    {
        if (state != _state) throw;
        _
    }
    event aborted();
    event purchaseConfirmed();
    event itemReceived();

   ///終止購物並收回以太。僅僅能夠在合約未鎖定時被賣家調用。
    function abort()
        onlySeller
        inState(State.Created)
    {
        aborted();
        seller.send(this.balance);
        state = State.Inactive;
    }

   ///買家確認購買。交易包含兩倍價值的(`2 * value`)以太。
   ///這些以太會一直鎖定到收貨確認(confirmReceived)被調用。
    function confirmPurchase()
        inState(State.Created)
        require(msg.value == 2 * value)
    {
        purchaseConfirmed();
        buyer = msg.sender;
        state = State.Locked;
    }

    ///確認你(買家)收到了貨物,這將釋放鎖定的以太。
    function confirmReceived()
        onlyBuyer
        inState(State.Locked)
    {
        itemReceived();
        buyer.send(value);//咱們有意忽略了返回值。
        seller.send(this.balance);
        state = State.Inactive;
    }
    function() { throw; }
}

小額支付通道區塊鏈

待補ui

轉自: https://blog.csdn.net/mongo_node/article/details/80151986

若是你但願高效的學習以太坊DApp開發,能夠訪問匯智網提供的最熱門在線互動教程:

其餘更多內容也能夠訪問這個以太坊博客

相關文章
相關標籤/搜索