2. SECURITY ISSUES
❖ Race Condition
❖ Timestamp Dependence
❖ Integer Overflow and Underflow
❖ DoS with (Unexpected) revert
❖ DoS with Block Gas Limit
❖ Transfer vs Send
FROM BUSINESS REQUIREMENTS TO TECHNICAL
IMPLEMENTATION (CROWDSALE, TRANCHE SALE, ETC)
❖ Basic Functionality
❖ Basic Functionality for Token
❖ Basic Functionality for Crowdsale
Timeline
3. RACE CONDITION
public class Counter {
protected long count = 0;
public void add(long value){
this.count = this.count + value;
}
}
this.count = 0;
A: Reads this.count into a register (0)
B: Reads this.count into a register (0)
B: Adds value 2 to register
B: Writes register value (2) back to memory.
this.count now equals 2
A: Adds value 3 to register
A: Writes register value (3) back to memory.
this.count now equals 3
❖ Thread A wants to add 2 to this.count.
❖ Thread B wants to add 3 to this.count.
❖ Expected result => 5.
❖ Real results => 3.
4. REENTRANCY
mapping (address => uint) private userBalances;
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
require(msg.sender.call.value(amountToWithdraw)()); // At this point, the caller's code is executed, and can call
withdrawBalance again
userBalances[msg.sender] = 0;
}
5. CROSS-FUNCTION RACE CONDITIONS
t
mapping (address => uint) private userBalances;
function transfer(address to, uint amount) {
if (userBalances[msg.sender] >= amount) {
userBalances[to] += amount;
userBalances[msg.sender] -= amount;
}
}
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
require(msg.sender.call.value(amountToWithdraw)()); // At this point, the caller's code is executed, and can call transfer()
userBalances[msg.sender] = 0;
}
6. CHECKS-
EFFECTS-INTERACTIONS
1. Conditions
2. Effects (potentially changing
conditions)
3. Interaction
You need to not only avoid calling external functions
too soon, but also avoid calling functions which call
external functions.
7. MUTEX
t
function deposit() payable public returns (bool) {
require(!lockBalances);
lockBalances = true;
balances[msg.sender] += msg.value;
lockBalances = false;
return true;
}
8. MUTEX
t
contract StateHolder {
uint private n;
address private lockHolder;
function getLock() {
require(lockHolder == 0);
lockHolder = msg.sender;
}
function releaseLock() {
require(msg.sender == lockHolder);
lockHolder = 0;
}
function set(uint newState) {
require(msg.sender == lockHolder);
n = newState;
}
}
If you use mutexes to protect against race conditions, you will need to
carefully ensure that there are no ways for a lock to be claimed and
never released.
11. DOS WITH BLOCK GAS LIMIT
struct Payee {
address addr;
uint256 value;
}
Payee[] payees;
uint256 nextPayeeIndex;
function payOut() {
uint256 i = nextPayeeIndex;
while (i < payees.length && msg.gas > 200000) {
payees[i].addr.send(payees[i].value);
i++;
}
nextPayeeIndex = i;
}
Don't iterate. If you have, you
can divide it into multiple
transaction.
12. DOS WITH (UNEXPECTED) REVERT
contract Auction {
address currentLeader;
uint highestBid;
function bid() payable {
require(msg.value > highestBid);
require(currentLeader.send(
highestBid)); // Refund the old leader, if it fails then revert
currentLeader = msg.sender;
highestBid = msg.value;
}
}
address[] private refundAddresses;
mapping (address => uint) public refunds;
// bad
function refundAll() public {
for(uint x; x < refundAddresses.length; x++) { // arbitrary
length iteration based on how many addresses participated
require(refundAddresses[x].send(
refunds[refundAddresses[x]])) // doubly bad, now a single
failure on send will hold up all funds
}
}
Solution: pull over push payment: create a refund function.
13. Transfer vs Send
Transfer
address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
If x is a contract address, its code (more specifically: its fallback function, if
present) will be executed together with the transfer call (this is a feature of
the EVM and cannot be prevented). If that execution runs out of gas or fails
in any way, the Ether transfer will be reverted and the current contract will
stop with an exception.
Send
If the execution fails, the current contract will not stop with an exception,
but send will return false.
There are some dangers in using send: The transfer fails if the call stack
depth is at 1024 (this can always be forced by the caller) and it also fails if
the recipient runs out of gas. So in order to make safe Ether transfers,
always check the return value of send, use transfer or even better: use a
pattern where the recipient withdraws the money.
15. SAFE MATH OWNABLE HALTABLE
Safe unsigned safe
math.
Provides basic
authorization control.
Implement an
emergency stop
mechanism.
16. BURNABLE
TOKEN
RELEASABLE
TOKEN
UPGRADEABLE
TOKEN
Implement burn
functionality for token.
It means destroy, or
make the token
invalid.
Allow token to be
transfered after the
crowdsale. Still give
some special case can
be transfered
Content: Allow to
transfer token from a
contract to another
contract. This "another
contract" can have
upgraded functionality
for the token.
20. FINALIZE AGENT NULL
FINALIZE AGENT
DEFAULT
FINALIZE AGENT
Finalize agent defines
what happens at the
end of succeseful
crowdsale.
A finalize agent that
does nothing. Token
transfer must be
manually released by
the owner.
Unlock tokens.
21. BONUS
FINALIZE AGENT
EXTRA
FINALIZE AGENT
At the end of the
successful crowdsale
allocate % bonus of
tokens to the team.
Unlock tokens.
At the end of the
successful crowdsale
allocate % bonus of
tokens to the team.
Do not unlock the
tokens.
22. CROWDSALEBASE CROWDSALE UNCAPPEDCROWDSALE
Implements basic state
machine logic, but leaves
out all buy functions so
that subclasses can
implement their own
buying logic.
Abstract base contract
for token sales with the
default buy entry
points.
Handle
Does not Handle
Intended usage
- A short time window
- Flat price
- No cap
23. MINTEDETHCAPPE-
CROWDSALE
MINTEDTOKENCAPPED-
CROWDSALE
RELAUNCHED-
CROWDSALE
ICO crowdsale contract
that is capped by
amount of ETH.
Tokens are dynamically
created during the
crowdsale.
ICO crowdsale contract
that is capped by amout
of tokens.
Tokens are dynamically
created during the
crowdsale.
A crowdsale that retains
the previous token, but
changes some parameters.
Investor data can be
manually fed in.
Mostly useful as a hot fix