Merge pull request #706 from Denton-L/withdrawal-pattern

Add Withdrawal Pattern Example
This commit is contained in:
chriseth
2016-08-20 02:57:07 +02:00
committed by GitHub
6 changed files with 113 additions and 5 deletions
+104
View File
@@ -2,6 +2,110 @@
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
******************