aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Ward <chris.ward@ethereum.org>2019-01-14 16:36:47 +0800
committerChris Ward <chris.ward@ethereum.org>2019-01-14 16:36:47 +0800
commit7bc1f1a4a3824156c3e315d420d5e6a2047b4822 (patch)
tree7a01bac7dc47bd886cd0522f517b960238bcecb3
parent051df31924e7d10351e9a3abd4becd3631e83391 (diff)
downloaddexon-solidity-7bc1f1a4a3824156c3e315d420d5e6a2047b4822.tar.gz
dexon-solidity-7bc1f1a4a3824156c3e315d420d5e6a2047b4822.tar.zst
dexon-solidity-7bc1f1a4a3824156c3e315d420d5e6a2047b4822.zip
Split blind auction example into seperate doc
-rw-r--r--docs/examples/blind-auction.rst339
-rw-r--r--docs/solidity-by-example.rst342
2 files changed, 340 insertions, 341 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
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