聲明:本系列文章是本身在http://solidity-cn.readthedoc... 學習solidity時,由於英語水平不夠,被迫用谷歌粗略翻譯的。僅爲了方便學習,中間確定有不少錯誤的地方。請勘誤。html
本文原地址:http://solidity-cn.readthedoc...
如下是翻譯正文:數組
投票:
如下合同很是複雜,但展現了不少Solidity的功能。 它執行投票合同。 固然,電子投票的主要問題是如何爲正確的人員分配投票權,以及如何防止操縱。 咱們不會在這裏解決全部問題,但至少咱們會展現如何進行委派投票,以便計票自動且徹底透明。安全
這個想法是爲每一個選票建立一個合同,爲每一個選項提供一個簡稱。 而後,擔任主席的合同建立者將分別給予每一個地址的投票權。併發
而後,地址背後的人能夠選擇本身投票,或者將他們的投票委託給他們信任的人。app
在投票時間結束時,winningProposal()
將返回投票數最多的提案。函數
pragma solidity ^0.4.21 /// @title Voting with delegation. contract Ballot { //這將聲明一個新的複合類型 //稍後用於變量。 //它會表明一個voter。 struct Voter { uint weight; //weight is accumulated by delegation bool voted; // 若是true,已經voted address delegate; // 被受權的人 uint vote; // vote提案的索引 } // a type for a single proposal. struct Proposal { bytes32 name; uint voteCount; } address public chairperson; //聲明一個狀態變量,爲每一個可能的地址存儲一個`Voter`結構體。 mapping(address => Voter) public voters; // Proposal結構的動態大小的數組。 Proposal[] public proposals; ///建立一個新ballot來選擇`proposalNames`中的一個。 function Ballot(bytes32[] proposalNames) public { chairperson = msg.sender; voters[chairperson].weight = 1; //對於提供的每一個提議名稱,建立一個新的提議對象並將其添加到數組的末尾。 for (uint i = 0, i < proposalNames.length; i++) { //`Proposal({...})`建立一個臨時的Proposal對象, // `proposals.push(...)`將它附加到`proposals`的末尾 proposals.push(Proposal({ name:proposalNames[i]; voteCount:0 })); } } //讓`voter`有權對這張選票進行投票。 //只能由`chairperson`調用。 function giveRightToVote(address voter) public { //若是`require`的參數評估爲'false', //它會終止並恢復對狀態和以太平衡的全部更改。 若是函數被錯誤地調用, //一般使用它是一個好方法。 但要當心,這也將消耗全部提供的gas(這是計劃在將來改變)。 require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0)) voters[voter].weight = 1; } function delegate(address to) public { Voter storage sender = voters[msg.sender]; require(!sender.voted); require(to != msg.sender); // 通常來講,這樣的循環是很是危險的,由於若是它們運行時間太長, // 它們可能須要比塊中可用的更多的氣體。在這種狀況下,委託將不會被執行, // 但在其餘狀況下,這樣的循環可能致使合同 徹底「卡住」。 while (voters[to].delegate != address(0)) { to = voters[to].delegate; require(to != msg.sender); } //由於`sender`是一個引用,因此這會修改`voters[msg.sender].voted` 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; } } ///將您的投票(包括授予您的投票)提交給提案[proposal] .name`。 function vote(uint proposal) public { Voter storage sender = voters[msg.sender]; require(!sender.voted); sender.voted = true; sender.vote = proposal; //若是`proposal'超出了數組範圍,這將自動拋出並恢復全部更改。 proposals[proposal].voteCount += sender.weight; } /// @dev計算之前全部投票的獲勝建議。 function winningProposal() public view 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; } } } //調用winningProposal()函數獲取提議數組中包含的獲獎者的索引,而後返回獲勝者的名字 function winnerName() public view returns (bytes32 winnerName) { winnerName = proposals[winningProposal()].name; }
}
可能的改進
目前,須要許多交易來將投票權分配給全部參與者。 你能想出更好的方法嗎?學習
祕密競價(盲拍)
在本節中,咱們將展現在以太坊建立一個徹底失明的拍賣合同是多麼容易。 咱們將從公開拍賣開始,每一個人均可以看到所作的投標,而後將此合同擴展到盲目拍賣,在競標期結束以前沒法看到實際出價。ui
簡單的公開拍賣
如下簡單的拍賣合同的整體思路是每一個人均可以在投標期內發送他們的出價。 出價已經包括髮送金錢/以太幣以使投標人與他們的出價相結合。 若是提升最高出價,之前出價最高的出價人能夠拿回她的錢。 在投標期結束後,合同必須手動爲受益人接收他的錢, 合同不能激活本身。this
pragma solidity ^0.4.21 contract SimpleAuction { //拍賣的參數。 時間是絕對的unix時間戳 //(自1970-01-01以來的秒數)或以秒爲單位的時間段。 address public beneficiary; uint public auctionEnd; // 拍賣當前的狀態 address public highestBidder; uint public highestBid; //容許撤回以前的出價 mapping(address => uint) pendingReturns; // 設置爲true,禁止任何更改 bool ended; // 將在更改中觸發的事件。 event HighestBidIncreased(address bidder, uint amount); event AuctionEnded(address winner, uint amount); //如下是所謂的natspec評論,能夠經過三個斜槓來識別。 //當用戶被要求確認交易時將顯示。 ///表明受益人地址`_beneficiary`以`_biddingTime`秒的投標時間建立一個簡單的拍賣。 function SimpleAuction( uint _biddingTime, address _beneficiary ) public { beneficiary = _beneficiary; auctionEnd = now + _biddingTime; } ///使用與此交易一塊兒發送的價格拍賣拍賣品。 若是拍賣沒成功,價值只會被退還。 function bid() public payable { //不須要參數,全部信息已是交易的一部分。 爲了可以接收以太網,功能須要關鍵字。 //若是結束,請恢復通話。 require(now <= auctionEnd); // 若是出價不高,將錢退回。 require(msg.value > highestBid); if (highestBid != 0) { //經過使用highestBidder.send(highestBid)發送回款有安全風險, // 由於它能夠執行不可信的合同。 // 讓收款人本身收回錢會比較安全。 pendingReturns[highestBidder] += highestBid; } highestBidder = msg.sender; highestBid = msg.value; HighestBidIncreased(msg.sender, msg.value); } ///撤銷高出價的出價。 function withdraw() public returns (bool) { uint amount = pendingReturns[msg.sender]; if (amount > 0) { //將它設置爲零是很重要的,由於收件人能夠在`send`返回 // 以前再次調用此函數做爲接收調用的一部分。 pendingReturns[msg.sender] = 0; if (!msg.sender.send(amount)) { //不須要在這裏呼叫,只需從新設置欠款額 pendingReturns[msg.sender] = amount; return false; } } return true; } ///結束拍賣並將最高出價發送給受益人。 function auctionEnd() public { //將與其餘合約交互的函數(即它們調用函數或發送Ether)結構化爲三個階段是一個很好的指導: // 1.檢查條件 // 2.執行操做(潛在的變化條件) // 3.與其餘合同交互 //若是這些階段混淆在一塊兒,另外一個合約能夠回撥到當前合約中,並修改屢次執行的狀態或緣由效果(以太付款). //若是內部調用的函數包含與外部合同的交互,則必須將它們視爲與外部合同的交互。 // 1.條件 require(now >= auctionEnd); //拍賣尚未結束 require(!ended); //此功能已被調用 // 2.效果 ended = true; AuctionEnded(highestBidder, highestBid); // 3.交互 beneficiary.transfer(highestBid); }
}加密
盲拍
之前的公開拍賣會延伸到如下的盲拍。盲拍的優點在於投標期結束時沒有時間壓力。在一個透明的計算平臺上建立一個盲目拍賣可能聽起來像是一個矛盾,可是密碼學能夠解決這個問題。
在投標期間,投標人實際上並無發出她的投標,而只是一個散列版本。因爲目前認爲實際上不可能找到兩個(足夠長)的哈希值相等的值,所以投標人承諾經過該投標。投標結束後,投標人必須公開他們的投標:他們將他們的價值未加密而且合同檢查散列值與投標期間提供的散列值相同。
另外一個挑戰是如何在同一時間使拍賣具備約束力和盲目性:在贏得拍賣後,防止投標人不發送貨幣的惟一方法是讓她在拍賣中一併發送。因爲價值轉移不能在以太坊矇蔽,任何人均可以看到價值。
如下合同經過接受任何大於最高出價的值來解決此問題。由於這固然只能在披露階段進行檢查,因此有些出價多是無效的,這是有意的(它甚至提供了一個明確的標記,用高價值轉讓放置無效出價):投標人能夠經過放置幾個較高的低無效出價。
pragma solidity ^0.4.21 contract BlindAuction { struct Bid { bytes32 blindedBid; uint deposit; } address public beneficiary; 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 winnder, uint highestBid); ///修飾符是驗證函數輸入的便捷方式。 `onlyBefore`應用於下面的`bid`: // 新函數體是修飾符的主體,其中`_`被舊函數體替換。 modifier onlyBefore(uint _time) { require(now < _time); _; } modifier onlyAfter(uint _time) { require(now > _time); _; } function BlindAuction { uint _biddingTime, uint _revealTime, address _beneficiary } public { beneficiary = _beneficiary; biddingEnd = now + _biddingTime; revealEnd = biddingEnd + _revealTime; } ///用`_blindedBid` = keccak256(value,fake,secret)放置一個不知情的出價。 ///若是投標在披露階段正確顯示,則只會退還已發送的以太幣。 //若是與投標一塊兒發送的以太幣至少「value」和「fake」不是true,則投標有效。 //將「fake」設置爲true,併發送不確切的金額是隱藏實際出價但仍然須要存款的方法。 同一個地址能夠放置多個出價。 function bid(bytes32 _blindedBid) public payable onlyBefore(biddingEnd) { bids[msg.sender].push(Bid({ blindedBid: _blindedBid, deposit: msg.value })); } ///揭示你不知情的投標。 對於全部正確無視的無效出價以及除最高出價之外的全部出價,您都將得到退款。 function reveal ( uint[] _values, bool[] _fake, bytes32[] _secret ) public 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); } //這是一個「內部」功能,這意味着它只能從合同自己(或衍生合同)中調用。 function placeBid(address bidder, uint value) internal return (bool success) { if (value <= highestBid) { return false; } if (highestBidder != 0) { //退還先前出價最高的出價者。 pendingReturns[highestBidder] += highestBid; } highestBid = value; highestBidder = bidder; return true; } ///撤銷高出價的出價。 function withdraw() public { uint amount = pendingReturns[msg.sender]; if (amount > 0) { //將它設置爲零是很重要的,由於接收者能夠在`transfer`返回以前再次調用 //此函數做爲接收調用的一部分(請參閱上面關於條件 - >效果 - >交互的註釋)。 pendingReturns[msg.sender] = 0; msg.sender.transfer(amount); } } ///結束拍賣並將最高出價發送給受益人。 function auctionEnd () public onlyAfter(revealEnd) { require(!ended); AuctionEnded(highestBidder, highestBid); ended = true; beneficiary.transfer(highestBid); }
}
安全的遠程購買
pragma solidity ^0.4.21 contract Purchase { uint public value; address public seller; address public buyer; enum State { Created, Locked, Inactive} State public state; //確保`msg.value`是一個偶數。 //若是它是一個奇數,則它將被截斷。 //經過乘法檢查它不是奇數。 function Purchase() public payable { seller = msg.sender; value = msg.value / 2; require((2 * value) == msg.value); } modifier condition(bool _condition) { require(_condition); _; } modifier onlyBuyer() { require(msg.sender == buyer); _; } modifier onlySeller() { require(msg.sender == seller); _; } modifier inState(State _state) { require(state == _state); _; } event Aborted(); event PurchaseConfirmed(); event ItemReceived(); ///停止購買並回收以太。 ///只能在合同被鎖定以前由賣家調用。 function abort() public onlySeller inState(State.Created) { Aborted(); state = State.Inactive; seller.transfer(this.balance); } ///將購買確認爲買家。 /// Transaction必須包含`2 * value`以太。 ///以太會被鎖定,直到confirmReceived被調用。 function confirmPurchase() public inState(State.Created) condition(msg.value == (2 * value)) payable { PurchaseConfirmed(); buyer = msg.sender; state = State.Locked; } ///確認您(買家)收到該物品。 ///這將釋放鎖定的以太。 function confirmReceived() public onlyBuyer inState(State.Locked) { ItemReceived(); //先改變狀態很重要,不然使用下面的`send`調用的合約能夠在這裏再次調用。 state = State.Inactive; //NOTE:這實際上容許買家和賣家阻止退款 - 應該使用退款模式。 buyer.transfer(value); seller.transfer(this.balance); }
}
微支付通道To be written.