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
|
||||||
|
|
||||||
******************
|
******************
|
||||||
|
@ -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
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user