mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #706 from Denton-L/withdrawal-pattern
Add Withdrawal Pattern Example
This commit is contained in:
commit
0d736fde6d
@ -2,6 +2,110 @@
|
|||||||
Common Patterns
|
Common Patterns
|
||||||
###############
|
###############
|
||||||
|
|
||||||
|
.. index:: withdrawal
|
||||||
|
|
||||||
|
.. _withdrawal_pattern:
|
||||||
|
|
||||||
|
*************************
|
||||||
|
Withdrawal from Contracts
|
||||||
|
*************************
|
||||||
|
|
||||||
|
The recommended method of sending funds after an effect
|
||||||
|
is using the withdrawal pattern. Although the most intuitive
|
||||||
|
method of sending Ether, as a result of an effect, is a
|
||||||
|
direct ``send`` call, this is not recommended as it
|
||||||
|
introduces a potential security risk. You may read
|
||||||
|
more about this on the :ref:`security_considerations` page.
|
||||||
|
|
||||||
|
This is an example of the withdrawal pattern in practice in
|
||||||
|
a contract where the goal is to send the most money to the
|
||||||
|
contract in order to become the "richest", inspired by
|
||||||
|
`King of the Ether <https://www.kingoftheether.com/>`_.
|
||||||
|
|
||||||
|
In the following contract, if you are usurped as the richest,
|
||||||
|
you will recieve the funds of the person who has gone on to
|
||||||
|
become the new richest.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
contract WithdrawalContract {
|
||||||
|
address public richest;
|
||||||
|
uint public mostSent;
|
||||||
|
|
||||||
|
mapping (address => uint) pendingWithdrawals;
|
||||||
|
|
||||||
|
function WithdrawalContract() {
|
||||||
|
richest = msg.sender;
|
||||||
|
mostSent = msg.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function becomeRichest() returns (bool) {
|
||||||
|
if (msg.value > mostSent) {
|
||||||
|
pendingWithdrawals[richest] += msg.value;
|
||||||
|
richest = msg.sender;
|
||||||
|
mostSent = msg.value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function withdraw() returns (bool) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
This is as opposed to the more intuitive sending pattern.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
contract SendContract {
|
||||||
|
address public richest;
|
||||||
|
uint public mostSent;
|
||||||
|
|
||||||
|
function SendContract() {
|
||||||
|
richest = msg.sender;
|
||||||
|
mostSent = msg.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function becomeRichest() 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
|
||||||
|
if (!richest.send(msg.value)) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
richest = msg.sender;
|
||||||
|
mostSent = msg.value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
.. index:: access;restricting
|
.. index:: access;restricting
|
||||||
|
|
||||||
******************
|
******************
|
||||||
|
@ -70,7 +70,7 @@ Solidity Tools
|
|||||||
|
|
||||||
* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_
|
* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_
|
||||||
Try Solidity instantly with a command-line Solidity console.
|
Try Solidity instantly with a command-line Solidity console.
|
||||||
|
|
||||||
* `solgraph <https://github.com/raineorshine/solgraph>`_
|
* `solgraph <https://github.com/raineorshine/solgraph>`_
|
||||||
Visualize Solidity control flow and highlight potential security vulnerabilities.
|
Visualize Solidity control flow and highlight potential security vulnerabilities.
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ installed either by adding the Ethereum PPA (Option 1) or by backporting
|
|||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
sudo apt-get -y upgrade
|
sudo apt-get -y upgrade
|
||||||
sudo apt-get -y install libcryptopp-dev
|
sudo apt-get -y install libcryptopp-dev
|
||||||
|
|
||||||
## (Option 2) For those willing to backport libcrypto++:
|
## (Option 2) For those willing to backport libcrypto++:
|
||||||
#sudo apt-get -y install ubuntu-dev-tools
|
#sudo apt-get -y install ubuntu-dev-tools
|
||||||
#sudo pbuilder create
|
#sudo pbuilder create
|
||||||
|
@ -101,7 +101,7 @@ and then run the compiler as
|
|||||||
|
|
||||||
As a more complex example, suppose you rely on some module that uses a
|
As a more complex example, suppose you rely on some module that uses a
|
||||||
very old version of dapp-bin. That old version of dapp-bin is checked
|
very old version of dapp-bin. That old version of dapp-bin is checked
|
||||||
out at ``/usr/local/dapp-bin_old``, then you can use
|
out at ``/usr/local/dapp-bin_old``, then you can use
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
.. _security_considerations:
|
||||||
|
|
||||||
#######################
|
#######################
|
||||||
Security Considerations
|
Security Considerations
|
||||||
#######################
|
#######################
|
||||||
@ -124,7 +126,7 @@ Sending and Receiving Ether
|
|||||||
because the operation is just too expensive) - it "runs out of gas" (OOG).
|
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 the return value of ``send`` is checked, this might provide a
|
||||||
means for the recipient to block progress in the sending contract. Again, the best practice here is to use
|
means for the recipient to block progress in the sending contract. Again, the best practice here is to use
|
||||||
a "withdraw" pattern instead of a "send" pattern.
|
a :ref:`"withdraw" pattern instead of a "send" pattern <withdrawal_pattern>`.
|
||||||
|
|
||||||
Callstack Depth
|
Callstack Depth
|
||||||
===============
|
===============
|
||||||
|
@ -191,6 +191,8 @@ contract into a blind auction where it is not
|
|||||||
possible to see the actual bid until the bidding
|
possible to see the actual bid until the bidding
|
||||||
period ends.
|
period ends.
|
||||||
|
|
||||||
|
.. _simple_auction:
|
||||||
|
|
||||||
Simple Open Auction
|
Simple Open Auction
|
||||||
===================
|
===================
|
||||||
|
|
||||||
@ -269,7 +271,7 @@ activate themselves.
|
|||||||
// highestBidder.send(highestBid) is a security risk
|
// highestBidder.send(highestBid) is a security risk
|
||||||
// because it can be prevented by the caller by e.g.
|
// because it can be prevented by the caller by e.g.
|
||||||
// raising the call stack to 1023. It is always safer
|
// raising the call stack to 1023. It is always safer
|
||||||
// to let the recipient withdraw their money themselves.
|
// to let the recipient withdraw their money themselves.
|
||||||
pendingReturns[highestBidder] += highestBid;
|
pendingReturns[highestBidder] += highestBid;
|
||||||
}
|
}
|
||||||
highestBidder = msg.sender;
|
highestBidder = msg.sender;
|
||||||
|
Loading…
Reference in New Issue
Block a user