diff options
Diffstat (limited to 'docs/solidity-by-example.rst')
-rw-r--r-- | docs/solidity-by-example.rst | 342 |
1 files changed, 1 insertions, 341 deletions
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 0e7d507d..ddead4d5 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -3,347 +3,7 @@ Solidity by Example ################### .. include:: examples/voting.rst - -.. index:: auction;blind, auction;open, blind auction, open auction - -************* -Blind Auction -************* - -In this section, we will show how easy it is to create a -completely blind auction contract on Ethereum. -We will start with an open auction where everyone -can see the bids that are made and then extend this -contract into a blind auction where it is not -possible to see the actual bid until the bidding -period ends. - -.. _simple_auction: - -Simple Open Auction -=================== - -The general idea of the following simple auction contract -is that everyone can send their bids during -a bidding period. The bids already include sending -money / ether in order to bind the bidders to their -bid. If the highest bid is raised, the previously -highest bidder gets her money back. -After the end of the bidding period, the -contract has to be called manually for the -beneficiary to receive their money - contracts cannot -activate themselves. - -:: - - pragma solidity >=0.4.22 <0.6.0; - - contract SimpleAuction { - // Parameters of the auction. Times are either - // absolute unix timestamps (seconds since 1970-01-01) - // or time periods in seconds. - address payable public beneficiary; - uint public auctionEndTime; - - // Current state of the auction. - address public highestBidder; - uint public highestBid; - - // Allowed withdrawals of previous bids - mapping(address => uint) pendingReturns; - - // Set to true at the end, disallows any change. - // By default initialized to `false`. - bool ended; - - // Events that will be emitted on changes. - event HighestBidIncreased(address bidder, uint amount); - event AuctionEnded(address winner, uint amount); - - // The following is a so-called natspec comment, - // recognizable by the three slashes. - // It will be shown when the user is asked to - // confirm a transaction. - - /// Create a simple auction with `_biddingTime` - /// seconds bidding time on behalf of the - /// beneficiary address `_beneficiary`. - constructor( - uint _biddingTime, - address payable _beneficiary - ) public { - beneficiary = _beneficiary; - auctionEndTime = now + _biddingTime; - } - - /// Bid on the auction with the value sent - /// together with this transaction. - /// The value will only be refunded if the - /// auction is not won. - function bid() public payable { - // No arguments are necessary, all - // information is already part of - // the transaction. The keyword payable - // is required for the function to - // be able to receive Ether. - - // Revert the call if the bidding - // period is over. - require( - now <= auctionEndTime, - "Auction already ended." - ); - - // If the bid is not higher, send the - // money back. - require( - msg.value > highestBid, - "There already is a higher bid." - ); - - if (highestBid != 0) { - // Sending back the money by simply using - // highestBidder.send(highestBid) is a security risk - // because it could execute an untrusted contract. - // It is always safer to let the recipients - // withdraw their money themselves. - pendingReturns[highestBidder] += highestBid; - } - highestBidder = msg.sender; - highestBid = msg.value; - emit HighestBidIncreased(msg.sender, msg.value); - } - - /// Withdraw a bid that was overbid. - function withdraw() public returns (bool) { - uint amount = pendingReturns[msg.sender]; - if (amount > 0) { - // It is important to set this to zero because the recipient - // can call this function again as part of the receiving call - // before `send` returns. - pendingReturns[msg.sender] = 0; - - if (!msg.sender.send(amount)) { - // No need to call throw here, just reset the amount owing - pendingReturns[msg.sender] = amount; - return false; - } - } - return true; - } - - /// End the auction and send the highest bid - /// to the beneficiary. - function auctionEnd() public { - // It is a good guideline to structure functions that interact - // with other contracts (i.e. they call functions or send Ether) - // into three phases: - // 1. checking conditions - // 2. performing actions (potentially changing conditions) - // 3. interacting with other contracts - // If these phases are mixed up, the other contract could call - // back into the current contract and modify the state or cause - // effects (ether payout) to be performed multiple times. - // If functions called internally include interaction with external - // contracts, they also have to be considered interaction with - // external contracts. - - // 1. Conditions - require(now >= auctionEndTime, "Auction not yet ended."); - require(!ended, "auctionEnd has already been called."); - - // 2. Effects - ended = true; - emit AuctionEnded(highestBidder, highestBid); - - // 3. Interaction - beneficiary.transfer(highestBid); - } - } - -Blind Auction -============= - -The previous open auction is extended to a blind auction -in the following. The advantage of a blind auction is -that there is no time pressure towards the end of -the bidding period. Creating a blind auction on a -transparent computing platform might sound like a -contradiction, but cryptography comes to the rescue. - -During the **bidding period**, a bidder does not -actually send her bid, but only a hashed version of it. -Since it is currently considered practically impossible -to find two (sufficiently long) values whose hash -values are equal, the bidder commits to the bid by that. -After the end of the bidding period, the bidders have -to reveal their bids: They send their values -unencrypted and the contract checks that the hash value -is the same as the one provided during the bidding period. - -Another challenge is how to make the auction -**binding and blind** at the same time: The only way to -prevent the bidder from just not sending the money -after they won the auction is to make her send it -together with the bid. Since value transfers cannot -be blinded in Ethereum, anyone can see the value. - -The following contract solves this problem by -accepting any value that is larger than the highest -bid. Since this can of course only be checked during -the reveal phase, some bids might be **invalid**, and -this is on purpose (it even provides an explicit -flag to place invalid bids with high value transfers): -Bidders can confuse competition by placing several -high or low invalid bids. - - -:: - - pragma solidity >0.4.23 <0.6.0; - - contract BlindAuction { - struct Bid { - bytes32 blindedBid; - uint deposit; - } - - address payable public beneficiary; - uint public biddingEnd; - uint public revealEnd; - bool public ended; - - mapping(address => Bid[]) public bids; - - address public highestBidder; - uint public highestBid; - - // Allowed withdrawals of previous bids - mapping(address => uint) pendingReturns; - - event AuctionEnded(address winner, uint highestBid); - - /// Modifiers are a convenient way to validate inputs to - /// functions. `onlyBefore` is applied to `bid` below: - /// The new function body is the modifier's body where - /// `_` is replaced by the old function body. - modifier onlyBefore(uint _time) { require(now < _time); _; } - modifier onlyAfter(uint _time) { require(now > _time); _; } - - constructor( - uint _biddingTime, - uint _revealTime, - address payable _beneficiary - ) public { - beneficiary = _beneficiary; - biddingEnd = now + _biddingTime; - revealEnd = biddingEnd + _revealTime; - } - - /// Place a blinded bid with `_blindedBid` = - /// keccak256(abi.encodePacked(value, fake, secret)). - /// The sent ether is only refunded if the bid is correctly - /// revealed in the revealing phase. The bid is valid if the - /// ether sent together with the bid is at least "value" and - /// "fake" is not true. Setting "fake" to true and sending - /// not the exact amount are ways to hide the real bid but - /// still make the required deposit. The same address can - /// place multiple bids. - function bid(bytes32 _blindedBid) - public - payable - onlyBefore(biddingEnd) - { - bids[msg.sender].push(Bid({ - blindedBid: _blindedBid, - deposit: msg.value - })); - } - - /// Reveal your blinded bids. You will get a refund for all - /// correctly blinded invalid bids and for all bids except for - /// the totally highest. - function reveal( - uint[] memory _values, - bool[] memory _fake, - bytes32[] memory _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++) { - Bid storage bidToCheck = bids[msg.sender][i]; - (uint value, bool fake, bytes32 secret) = - (_values[i], _fake[i], _secret[i]); - if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) { - // Bid was not actually revealed. - // Do not refund deposit. - continue; - } - refund += bidToCheck.deposit; - if (!fake && bidToCheck.deposit >= value) { - if (placeBid(msg.sender, value)) - refund -= value; - } - // Make it impossible for the sender to re-claim - // the same deposit. - bidToCheck.blindedBid = bytes32(0); - } - msg.sender.transfer(refund); - } - - // This is an "internal" function which means that it - // can only be called from the contract itself (or from - // derived contracts). - function placeBid(address bidder, uint value) internal - returns (bool success) - { - if (value <= highestBid) { - return false; - } - if (highestBidder != address(0)) { - // Refund the previously highest bidder. - pendingReturns[highestBidder] += highestBid; - } - highestBid = value; - highestBidder = bidder; - return true; - } - - /// Withdraw a bid that was overbid. - function withdraw() public { - uint amount = pendingReturns[msg.sender]; - if (amount > 0) { - // It is important to set this to zero because the recipient - // can call this function again as part of the receiving call - // before `transfer` returns (see the remark above about - // conditions -> effects -> interaction). - pendingReturns[msg.sender] = 0; - - msg.sender.transfer(amount); - } - } - - /// End the auction and send the highest bid - /// to the beneficiary. - function auctionEnd() - public - onlyAfter(revealEnd) - { - require(!ended); - emit AuctionEnded(highestBidder, highestBid); - ended = true; - beneficiary.transfer(highestBid); - } - } - +.. include:: examples/blind-auction.rst .. index:: purchase, remote purchase, escrow |