mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Change withdrawal example
The example is now a "King of the Ether"-esque contract. This is actually relevant as they suffered an attack because of an almost identical issue. See the post-mortem here: https://www.kingoftheether.com/postmortem.html
This commit is contained in:
parent
666c46ac29
commit
4737100d00
@ -18,102 +18,67 @@ introduces a potential security risk. You may read
|
|||||||
more about this on the :ref:`security_considerations` page.
|
more about this on the :ref:`security_considerations` page.
|
||||||
|
|
||||||
This is an example of the withdrawal pattern in practice in
|
This is an example of the withdrawal pattern in practice in
|
||||||
an Ether "store" contract.
|
a contract where the goal is to send the most money to the
|
||||||
|
contract in order to become the "richest".
|
||||||
|
|
||||||
|
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 richest.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
contract WithdrawalStore {
|
contract WithdrawalContract {
|
||||||
|
address public richest;
|
||||||
|
uint public mostSent;
|
||||||
|
|
||||||
struct Item {
|
mapping (address => uint) pending;
|
||||||
uint price;
|
|
||||||
uint quantity;
|
function WithdrawalContract() {
|
||||||
|
richest = msg.sender;
|
||||||
|
mostSent = msg.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
modifier onlyOwner {
|
function becomeRichest() returns (bool) {
|
||||||
if (msg.sender == owner) {
|
if (msg.value > mostSent) {
|
||||||
_
|
richest = msg.sender;
|
||||||
}
|
mostSent = msg.value;
|
||||||
else {
|
pending[richest] = msg.value;
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
address owner;
|
|
||||||
mapping (string => Item) inventory;
|
|
||||||
|
|
||||||
function WithdrawalStore() {
|
|
||||||
owner = msg.sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateInventory(
|
|
||||||
string _item,
|
|
||||||
uint _price,
|
|
||||||
uint _quantity
|
|
||||||
) onlyOwner {
|
|
||||||
inventory[_item] = Item(_price, _quantity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notice that the owner withdraws their own Ether
|
|
||||||
function withdraw() onlyOwner {
|
|
||||||
owner.send(this.balance);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buy(string _item) returns (bool) {
|
|
||||||
if (
|
|
||||||
inventory[_item].quantity > 0 &&
|
|
||||||
inventory[_item].price <= msg.value
|
|
||||||
) {
|
|
||||||
inventory[_item].quantity--;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function withdraw() {
|
||||||
|
uint amount = pending[msg.sender];
|
||||||
|
// Remember to zero the pending refund before sending
|
||||||
|
// to prevent re-entrancy attacks
|
||||||
|
pending[msg.sender] = 0;
|
||||||
|
if (!msg.sender.send(amount)) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
This is as opposed to the more intuitive sending pattern.
|
This is as opposed to the more intuitive sending pattern.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
contract SendStore {
|
contract SendContract {
|
||||||
|
address public richest;
|
||||||
struct Item {
|
uint public mostSent;
|
||||||
uint price;
|
|
||||||
uint quantity;
|
function SendContract() {
|
||||||
|
richest = msg.sender;
|
||||||
|
mostSent = msg.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
modifier onlyOwner {
|
function becomeRichest() returns (bool) {
|
||||||
if (msg.sender == owner) {
|
if (msg.value > mostSent) {
|
||||||
_
|
richest = msg.sender;
|
||||||
}
|
mostSent = msg.value;
|
||||||
else {
|
richest.send(msg.value);
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
address owner;
|
|
||||||
mapping (string => Item) inventory;
|
|
||||||
|
|
||||||
function SendStore() {
|
|
||||||
owner = msg.sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateInventory(
|
|
||||||
string _item,
|
|
||||||
uint _price,
|
|
||||||
uint _quantity
|
|
||||||
) onlyOwner {
|
|
||||||
inventory[_item] = Item(_price, _quantity);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buy(string _item) returns (bool) {
|
|
||||||
if (
|
|
||||||
inventory[_item].quantity > 0 &&
|
|
||||||
inventory[_item].price <= msg.value
|
|
||||||
) {
|
|
||||||
inventory[_item].quantity--;
|
|
||||||
owner.send(msg.value);// WARNING - this send is unchecked!!
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -122,9 +87,9 @@ This is as opposed to the more intuitive sending pattern.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Notice that, in this example, an attacker could trap
|
Notice that, in this example, an attacker could trap the
|
||||||
the owner's funds in the contract by causing the
|
previous richest person's funds in the contract by causing
|
||||||
execution of `send` to fail through a callstack attack.
|
the execution of `send` to fail through a callstack attack.
|
||||||
|
|
||||||
.. index:: access;restricting
|
.. index:: access;restricting
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user