From 0ceb9551874620ff51e47a1b82b2a05b61ab7443 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Fri, 5 Aug 2016 14:35:00 -0300 Subject: [PATCH] Auction contracts --- BlindAuction.sol | 150 ++++++++++++++++++++++++++++++++++++++++++++++ SimpleAuction.sol | 122 +++++++++++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 BlindAuction.sol create mode 100644 SimpleAuction.sol diff --git a/BlindAuction.sol b/BlindAuction.sol new file mode 100644 index 000000000..473260acf --- /dev/null +++ b/BlindAuction.sol @@ -0,0 +1,150 @@ +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; + + // 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) { 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; + } + + /// Place a blinded bid with `_blindedBid` = sha3(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) + 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[] _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)) { + // Bid was not actually revealed. + // Do not refund deposit. + continue; + } + refund += bid.deposit; + if (!fake && bid.deposit >= value) { + if (placeBid(msg.sender, value)) + refund -= value; + } + // Make it impossible for the sender to re-claim + // the same deposit. + bid.blindedBid = 0; + } + if (!msg.sender.send(refund)) + throw; + } + + // 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 != 0) { + // Refund the previously highest bidder. + pendingReturns[highestBidder] += highestBid; + } + highestBid = value; + highestBidder = bidder; + return true; + } + + /// Withdraw a bid that was overbid. + function withdraw() { + var amount = pendingReturns[msg.sender]; + // 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 (see the remark above about + // conditions -> effects -> interaction). + pendingReturns[msg.sender] = 0; + if (!msg.sender.send(amount)) + throw; // If anything fails, this will revert the changes above + } + + /// End the auction and send the highest bid + /// to the beneficiary. + function auctionEnd() + onlyAfter(revealEnd) + { + if (ended) + throw; + AuctionEnded(highestBidder, highestBid); + ended = true; + // We send all the money we have, because some + // of the refunds might have failed. + if (!beneficiary.send(this.balance)) + throw; + } + + function () { + throw; + } +} diff --git a/SimpleAuction.sol b/SimpleAuction.sol new file mode 100644 index 000000000..c6f7b309a --- /dev/null +++ b/SimpleAuction.sol @@ -0,0 +1,122 @@ +contract SimpleAuction { + // Parameters of the auction. Times are either + // absolute unix timestamps (seconds since 1970-01-01) + // or time periods in seconds. + address public beneficiary; + uint public auctionStart; + uint public biddingTime; + + // 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 + bool ended; + + // Events that will be fired 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`. + function SimpleAuction( + uint _biddingTime, + address _beneficiary + ) { + beneficiary = _beneficiary; + auctionStart = now; + biddingTime = _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() { + // No arguments are necessary, all + // information is already part of + // the transaction. + if (now > auctionStart + biddingTime) { + // Revert the call if the bidding + // period is over. + throw; + } + if (msg.value <= highestBid) { + // If the bid is not higher, send the + // money back. + throw; + } + if (highestBidder != 0) { + // Sending back the money by simply using + // highestBidder.send(highestBid) is a security risk + // because it can be prevented by the caller by e.g. + // raising the call stack to 1023. It is always safer + // to let the recipient withdraw their money themselves. + pendingReturns[highestBidder] += highestBid; + } + highestBidder = msg.sender; + highestBid = msg.value; + HighestBidIncreased(msg.sender, msg.value); + } + + /// Withdraw a bid that was overbid. + function withdraw() { + var amount = pendingReturns[msg.sender]; + // 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)) + throw; // If anything fails, this will revert the changes above + } + + /// End the auction and send the highest bid + /// to the beneficiary. + function auctionEnd() { + // 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 perfromed multiple times. + // If functions called internally include interaction with external + // contracts, they also have to be considered interaction with + // external contracts. + + // 1. Conditions + if (now <= auctionStart + biddingTime) + throw; // auction did not yet end + if (ended) + throw; // this function has already been called + + // 2. Effects + ended = true; + AuctionEnded(highestBidder, highestBid); + + // 3. Interaction + if (!beneficiary.send(highestBid)) + throw; + } + + function () { + // This function gets executed if a + // transaction with invalid data is sent to + // the contract or just ether without data. + // We revert the send so that no-one + // accidentally loses money when using the + // contract. + throw; + } +}