Solidity and Blocks — Ethernaut Level 3

Kevan Mordan
DraftKings Engineering
3 min readNov 4, 2022

All transactions in Ethereum are executed in blocks. Blocks can be thought of as a batch, or container, of multiple transactions. Transactions are committed as part of a batch to give network participants time to reach a consensus. Blocks are important in Ethereum and other blockchains because they are what is used to synchronize the state of the networks. A block contains all of the latest changes (transactions) since the last block was created. Someone can play back the entire history of the blockchain by following the blocks, similar to an event log.

What Is Part of a Block?

  • timestamp – the time when the block was mined.
  • blockNumber – the length of the blockchain in blocks.
  • baseFeePerGas - the minimum fee per gas required for a transaction to be included in the block.
  • difficulty – the effort required to mine the block.
  • mixHash – a unique identifier for that block.
  • parentHash – the unique identifier for the block that came before (this is how blocks are linked in a chain).
  • transactions – the transactions included in the block.
  • stateRoot – the entire state of the system: account balances, contract storage, contract code and account nonces are inside.
  • nonce – a hash that, when combined with the mixHash, proves that the block has gone through proof-of-work.

https://ethereum.org/en/developers/docs/blocks/

Blocks in Solidity

Transactions are executed in the current block being mined as part of the network and therefore can access information about it. This can be accessed at any part of the smart contract using the reserved keyword block.

One of the most common use cases to use the last block hash is to introduce a source of randomness, particularly for on-chain games. The blockchain is deterministic and public, making it very difficult to introduce randomness. Additionally, random number generation (RNG) is expensive and oftentimes it’s just not worth it to try and get more random.

The dApp game ETHERBOTS uses the last block hash to generate random numbers.

The Problem

https://ethernaut.openzeppelin.com/level/0x4dF32584890A0026e56f7535d0f2C6486753624f

The CoinFlip contract flip function takes an argument _guess which is compared against the result of a “flip”. The function uses last block hash to calculate the flip outcome as true or false and increments the unsigned integer consecutiveWins if guessed correctly. The level is won by guessing the coin flip result 10 times in a row and having the results validated by the consecutiveWins value.

The odds of guessing a coin flip 10 times in a row are 0.0009765625 so you should check to see if there is anything within the flip function that can be exploited.

The flip function gets the last block hash and divides it by a factor value, determining the outcome. The factor never changes though, and all flip transactions made within the current block will result in the same value!

You can take advantage of that by writing your own smart contract that will call it. Since your contract calls the flip function, the entire function chain will occur in the same block, meaning you can predict the flip outcome before you call the flip function.

All you need to do is copy the same flip logic into your own function, generate the flip result, and call the function with the result as your guess! SafeMath is no longer needed and has been omitted in the contract. In order to call the level instance’s flip function, you need to create an interface for the function by matching the function name, arguments, and return signature. Once your contract is deployed, you just need to call it 10 times to beat the level.

The next problem, Ethernaut Level 4, will focus on what composes a transaction and how messages are sent.

In Remix, simply deploy the contract and call flip to the instance address.

--

--