diff options
Diffstat (limited to 'docs')
-rw-r--r-- | docs/common-patterns.rst | 29 | ||||
-rw-r--r-- | docs/contracts.rst | 2 | ||||
-rw-r--r-- | docs/security-considerations.rst | 21 | ||||
-rw-r--r-- | docs/solidity-by-example.rst | 28 |
4 files changed, 40 insertions, 40 deletions
diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 37973c62..acef13b7 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -52,17 +52,12 @@ become the new richest. } } - function withdraw() returns (bool) { + function withdraw() { uint amount = pendingWithdrawals[msg.sender]; // Remember to zero the pending refund before // sending to prevent re-entrancy attacks pendingWithdrawals[msg.sender] = 0; - if (msg.sender.send(amount)) { - return true; - } else { - pendingWithdrawals[msg.sender] = amount; - return false; - } + msg.sender.transfer(amount); } } @@ -83,9 +78,7 @@ This is as opposed to the more intuitive sending pattern: function becomeRichest() payable returns (bool) { if (msg.value > mostSent) { - // Check if call succeeds to prevent an attacker - // from trapping the previous person's funds in - // this contract through a callstack attack + // This line can cause problems (explained below). richest.transfer(msg.value); richest = msg.sender; mostSent = msg.value; @@ -98,12 +91,16 @@ This is as opposed to the more intuitive sending pattern: Notice that, in this example, an attacker could trap the contract into an unusable state by causing ``richest`` to be -the address of a contract that has a fallback function -which consumes more than the 2300 gas stipend. That way, -whenever ``send`` is called to deliver funds to the -"poisoned" contract, it will cause execution to always fail -because there will not be enough gas to finish the execution -of the fallback function. +the address of a contract that has a fallback function +which fails (e.g. by using ``revert()`` or by just +conssuming more than the 2300 gas stipend). That way, +whenever ``transfer`` is called to deliver funds to the +"poisoned" contract, it will fail and thus also ``becomeRichest`` +will fail, with the contract being stuck forever. + +In contrast, if you use the "withdraw" pattern from the first example, +the attacker can only cause his or her own withdraw to fail and not the +rest of the contract's workings. .. index:: access;restricting diff --git a/docs/contracts.rst b/docs/contracts.rst index a26d5a5e..8d7af2c1 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1034,7 +1034,7 @@ more advanced example to implement a set). // The library functions can be called without a // specific instance of the library, since the // "instance" will be the current contract. - assert(Set.insert(knownValues, value)); + require(Set.insert(knownValues, value)); } // In this contract, we can also directly access knownValues.flags, if we want. } diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index f9ceed5d..6479eeb8 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -88,7 +88,7 @@ outlined further below: function withdraw() { var share = shares[msg.sender]; shares[msg.sender] = 0; - require(msg.sender.send(share)); + msg.sender.transfer(share); } } @@ -123,22 +123,23 @@ Sending and Receiving Ether (for example in the "details" section in Remix). - There is a way to forward more gas to the receiving contract using - ``addr.call.value(x)()``. This is essentially the same as ``addr.send(x)``, + ``addr.call.value(x)()``. This is essentially the same as ``addr.transfer(x)``, only that it forwards all remaining gas and opens up the ability for the - recipient to perform more expensive actions. This might include calling back + recipient to perform more expensive actions (and it only returns a failure code + and does not automatically propagate the error). This might include calling back into the sending contract or other state changes you might not have thought of. So it allows for great flexibility for honest users but also for malicious actors. -- If you want to send Ether using ``address.send``, there are certain details to be aware of: +- If you want to send Ether using ``address.transfer``, there are certain details to be aware of: 1. If the recipient is a contract, it causes its fallback function to be executed which can, in turn, call back the sending contract. 2. Sending Ether can fail due to the call depth going above 1024. Since the caller is in total control of the call - depth, they can force the transfer to fail; make sure to always check the return value of ``send``. Better yet, + depth, they can force the transfer to fail; take this possibility into account or use ``send`` and make sure to always check its return value. Better yet, write your contract using a pattern where the recipient can withdraw Ether instead. 3. Sending Ether can also fail because the execution of the recipient contract - requires more than the allotted amount of gas (explicitly by using ``throw`` or + requires more than the allotted amount of gas (explicitly by using ``revert`` or because the operation is just too expensive) - it "runs out of gas" (OOG). - If the return value of ``send`` is checked, this might provide a + If you use ``transfer`` or ``send`` with a return value check, this might provide a means for the recipient to block progress in the sending contract. Again, the best practice here is to use a :ref:`"withdraw" pattern instead of a "send" pattern <withdrawal_pattern>`. @@ -171,9 +172,9 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like owner = msg.sender; } - function transfer(address dest, uint amount) { + function transferTo(address dest, uint amount) { require(tx.origin == owner); - require(dest.call.value(amount)()); + dest.transfer(amount); } } @@ -191,7 +192,7 @@ Now someone tricks you into sending ether to the address of this attack wallet: } function() { - TxUserWallet(msg.sender).transfer(owner, msg.sender.balance); + TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance); } } diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index a8e7eb2c..f6d45e0a 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -87,11 +87,13 @@ of votes. // Give `voter` the right to vote on this ballot. // May only be called by `chairperson`. function giveRightToVote(address voter) { - // `throw` terminates and reverts all changes to + // If the argument of `require` evaluates to `false`, + // it terminates and reverts all changes to // the state and to Ether balances. It is often // a good idea to use this if functions are // called incorrectly. But watch out, this - // will also consume all provided gas. + // will currently also consume all provided gas + // (this is planned to change in the future). require(msg.sender == chairperson || !voters[voter].voted); voters[voter].weight = 1; } @@ -267,7 +269,6 @@ activate themselves. // Revert the call if the bidding // period is over. require(now < auctionStart + biddingTime); - // If the bid is not higher, send the // money back. @@ -329,7 +330,7 @@ activate themselves. AuctionEnded(highestBidder, highestBid); // 3. Interaction - require(beneficiary.transfer(highestBid)); + beneficiary.transfer(highestBid); } } @@ -400,8 +401,8 @@ high or low invalid bids. /// 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); _; } + modifier onlyBefore(uint _time) { require(now < _time); _; } + modifier onlyAfter(uint _time) { require(now > _time); _; } function BlindAuction( uint _biddingTime, @@ -446,8 +447,8 @@ high or low invalid bids. { uint length = bids[msg.sender].length; require( - _values.length == length || - _fake.length == length || + _values.length == length && + _fake.length == length && _secret.length == length ); @@ -544,10 +545,10 @@ Safe Remote Purchase function Purchase() payable { seller = msg.sender; value = msg.value / 2; - require(!2 * value != msg.value); + require(2 * value == msg.value); } - modifier require(bool _condition) { + modifier condition(bool _condition) { require(_condition); _; } @@ -589,7 +590,7 @@ Safe Remote Purchase /// is called. function confirmPurchase() inState(State.Created) - require(msg.value == 2 * value) + condition(msg.value == 2 * value) payable { purchaseConfirmed(); @@ -609,8 +610,9 @@ Safe Remote Purchase // can call in again here. state = State.Inactive; // This actually allows both the buyer and the seller to - // block the refund. - require(buyer.transfer(value) || seller.transfer(this.balance)); + // block the refund - the withdraw pattern should be used. + buyer.transfer(value); + seller.transfer(this.balance)); } } |