Breaking Ethereum π
Lessons learnt from broken contracts
10 September 2016
Thomson Reuters Hackethon
PΓ©ter SzilΓ‘gyi
Ethereum Core Developer
PΓ©ter SzilΓ‘gyi
Ethereum Core Developer
contract Faucet { uint amount = 0.01 ether; uint freq = 5760; mapping (address => uint) prev; function request() { if (address(this).balance < amount) { return; } if(block.number < prev[msg.sender] + freq) { return; } msg.sender.send(amount); prev[msg.sender] = block.number; } }
Give away 0.01 Ether to anyone, once per 24 hours... what could go wrong? π
Payout (0.01 Ether) is a nice amount
Faucet security β Account uniqueness
Sizeable implementation of a roulette game
contract Roulette { uint seed = 1; function rand() private returns (uint) { seed = ((seed*3 + 1)/2 % 10**9); return (seed + block.number + block.difficulty + block.timestamp + block.gaslimit) % 37; } [...] }
Uses an onchain random number generator... what could go wrong? π
Miners make the chain
Transactions are aware of the chain
Fairly involved dice game
contract Dice { function evaluate(bytes32 seed) { // [...] verify the seed for (uint i = 0; i < bets.length; i++) { // [...] evaluate bets and pay winners } } [...] }
Iterate over all accumulated bets in one go... what could go wrong? π
Blocks have limited gas allowances
Etherdice iterated all bets when closing a round
Lesson: Operations above O(1) will eventually exceed the gas limit!*
Twisted Ponzi scheme with smart contracts
contract GovernMental { address[] creditors; uint[] credited; function invest() { if (block.timestamp - lastInvested < TWELVE_HOURS) { ... } else { creditors = new address[](0); credited = new uint[](0); } } [...] }
Casually reset the contract at round end... what could go wrong? π
Contract storage in EVM is a single hash map
Freeing up a field β zeroing out a storage entry
Lesson: Understand and avoid magical constructs!
Game of Thrones pyramid contract
contract KingOfTheEtherThrone { address monarch; function claim() { // [...] calculate the ruler's compensation monarch.send(compensation); monarch = msg.sender; } [...] }
Send blindly to compensate the previous ruler... what could go wrong? π
Sending funds is an external CALL operation
But what if the ether transfer failed?
Lesson: Anything that can go wrong, will go wrong!
Contracts and frameworks for an on-chain crypto exchange
contract MakerEthToken { function withdraw(uint amount) { if (balances[msg.sender] >= amount) { if (msg.sender.call.value(amount)()) { balances[msg.sender] -= amount; } } } [...] }
Send funds with full gas allowance... what could go wrong? π
Calling another contract relinquishes execution
Recipient may have enough gas to call further
Lesson: External calls will eventually loop back in!
Wallet contract requiring multiple authorizations
contract MultiOwned { mapping(uint => uint) owners; modifier onlyowner { if (owners[tx.origin] > 0) { _ } } [...] }
Simply use tx.origin for authentication... what could go wrong? π
Pre-homestead, libraries used CALLCODE
Internal transactions retain the same tx.origin
Lesson: Authorization forwarding is exceptionally risky!
Lesson: Don't! Just don't!
Untappable beer contract
contract BeerKeg { bytes20 prev; // Nickname of the previous tap attempt function tap(bytes20 nickname) { prev = nickname; if (prev != nickname) { msg.sender.send(this.balance); } } }
Lesson: You tell me! π
Ethereum Core Developer