Let’s Play — Capture the Ether : Lotteries (Part I)
This is the second installment of stories on my journey of Capture the Ether, a game where we hack Ethereum Solidity code for fun and learn about smart contract security. Read the introduction post for background, what to expect, and link to other sections.
What is Capture the Ether?
In Capture the Ether, we win points by completing challenges. Each challenge comes with a smart contract to deploy and objectives can be completed by exploiting the contract into a specific state. Challenges are grouped into categories that focus on specific areas of security vulnerabilities in smart contracts.
Spoiler Alert
We will cover Guess the number, Guess the secrete number, and Guess the random number challenges in this article. These challenges demonstrate it is difficult to store private information on a public blockchain or to source randomness from a deterministic programming model in EVM.
I recommend you read, think about, attempt the problem on your own first. For each problem, I will discuss the high level approach followed by detailed solution.
Guess the number
Approach
We simply want n == answer == 42
therefore we just need to call guess(42)
on the contract.
Details
You should already be a pro calling smart contraction function using transactions. The only difference this time is that we need to send ether along the function call because require(msg.value == 1 ether)
.
Web3.js
To send ether in web3.js, simply add value
in the transaction option:
Remix
To send transation with ether in Remix, use Value
option on the top right.
You can choose units from the drop down menu from the right.
Guess the secret number
Approach
We would like to find the preimage that hashes into a specific hash using keccak256
, the hash function used by Ethereum. Usually this would be very difficult because a good hash function like keccak256
has good preimage resistance.
In this case, it is trivial because n
has type uint8
and can only vary between 0 and 256 (2⁸). What remains is to iterate through all possible values by brute force.
Details
We can run through all possible values using either Solidity directly or via offline tools such as web3.js.
Solidity
We can easily replicate the keccak256
check in a for loop as shown in the GuessTheSecretNumberSolver
contract in the above fiddle. You can deploy the contract in EthFiddle which runs an in-browser test EVM that come with accounts loaded with Ether.
Note EthFiddle provides a subset of the functionalities by Remix, where you can also choose built-in EVM from the environment. From hereafter we demonstrate code using EthFiddle as they can be beautifully embedded on Medium.
Because we wrote the function with public view
modifiers, we can call it without gas. Shortly after, it should spit out the answer in the box below.
Pay attention that you are using the correct type, in this case, uint8
. Otherwise keccak256
would produce different result as shown in the keccak256Uint
function. This is because keccak256
hashes (tightly packed) arguments.
Web3.js
web3.sha3
computes keccak256
hash from a string (optionally in hex.) In our case, it is important to pad hex representation of uint8
to correct digits as shown in the last two lines.
Web3.js 1.x will have web3.utils.soliditySha3
that takes care of the type coercion for us.
Guess the random number
Approach
Two ways to solve this problem:
- Compute answer using
heccak256
based on block hash of the previous block of contract deploy (block.number — 1
) and timestamp of the contract deploy block. - Get
answer
value directly from EVM storage. Even thoughanswer
is a private field in the contract, because all transaction need to be validated by all nodes, all fields are in fact publicly readable from an Ethereum node, which can be queried using web3.js.
Details
Calculate Answer from Hash
We first need to find the transaction corresponding to contract creation. Unfortunately I cannot find straightforward API to convert contract address to transaction hash. You can look up this on Etherscan or from the result of your deployment transaction call in Metamask.
With that we can find the block and its timestamp as well as parent block hash. We compute the keccak256
hash as before and truncate the byte32
string to uint8
by taking the last two character and convert it back to integer.
Get Answer from EVM Storage
All persistent contract variables are stored in contract storage and they follow a specific layout as can be found in this documentation. I refer you to this excellent article on how to read contract storage using web3.js.
For the purpose of our exercise, there is only one field in the contract. Therefore we simply need to find the value at slot 0:
Conclusion
In this post, you have learnt to write Solidity code in EthFiddle to test various scenarios without spending gas, use web3.js to compute keccak256
hash and find block information from a transaction hash, and pry open EVM storage to peak private storage variable of a contract.
Next time, we will look at more challenges in the Lotteries section where we will discover more interesting security vulnerabilities in smart contract and how to indirectly interact with other smart contracts using a smart contract. We will also look at how to use Truffle framework to write and test smart contract in a local simulated blockchain.
Capture the Ether is brought to you by @smarx, who blogs about smart contract development at Program the Blockchain.
I am not affiliated with the game itself and the awesome company behind it. Views expressed are solely my own and do not express the views or opinions of any entity with which I have been, am now, and will be affiliated.