aboutsummaryrefslogtreecommitdiffstats
path: root/docs/examples/blind-auction.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/examples/blind-auction.rst')
-rw-r--r--docs/examples/blind-auction.rst339
1 files changed, 339 insertions, 0 deletions
diff --git a/docs/examples/blind-auction.rst b/docs/examples/blind-auction.rst
new file mode 100644
index 00000000..70d95ad6
--- /dev/null
+++ b/docs/examples/blind-auction.rst
@@ -0,0 +1,339 @@
+.. 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);
+ }
+ } \ No newline at end of file