This is an experience report from teaching a smart contract programming course to undergraduates at the University of Maryland, back in the Fall of 2014. Of course that’s a very long time ago in the world of blockchains, and the paper shows its age in a few areas such as the use of the Serpent programming language rather than Solidity. Nonetheless, it still contains some good insights on the mindset change needed to start programming smart contracts vs. other kinds of development. From previous editions of The Morning Paper we know to be aware of mishandled exceptions, transaction-ordering dependencies, timestamp dependencies, and re-entrancy issues as well as general concurrent execution concerns. What else do Delmolino et al., have for us?
As we show below, surprisingly, even for a very simple smart contract it is difficult to create it correctly!
During the course students were divided into groups of four. In the first phase they created a smart contract program of their own choice. The contracts were then critiqued by the instructor, TAs, and fellow students, often revealing a number of flaws. In the second phase the students addressed the bugs found and amended their designs.
In contrast to traditional software development where bugs such as buffer overflows are typical, in our lab, we observed bugs and pitfalls that arise due to the unique nature of smart contract programs. Our lab experiences show that even for very simple smart contracts (e.g., a “Rock, Paper, Scissors” game), designing and implementing them correctly was highly non-trivial. This suggests that extra precautions and scrutiny are necessary when programming smart contracts.
In particular, contracts require an ‘economic thinking’ perspective to ensure fairness even when counterparties attempt to cheat in arbitrary ways to maximise their economic gains.
Using the rock, paper, scissors game, the authors illustrate three classes of mistake the students tended to make: leaking money, hiding in plain sight, and misaligned incentives.
Programming smart contracts typically involves encoding complex state machines. Logical errors in encoding state machines were commonly observed. The simplest type of logical error is a contract that leaks money in corner cases.
In the following extract, there are two ways money can be lost. On lines 19-20, if a third player tries to join and sends money, then the money becomes inaccessible. Note that miners control the ordering of transactions in a block, so a player attempting to join as the second player cannot always protect themselves from joining the party too late. Likewise on line 13, if a player sends an amount other than 1000 wei then the money will also be lost.
The following extract fixes these bugs:
Hiding in plain sight.
Another mistake is more subtle: players send their inputs in cleartext. Since transactions are broadcast across the entire cryptocurrency network, a cheating player may wait to see what his opponent chooses before providing his own input.
The solution to this is to use some form of cryptographic commitments – both players submit their commitment in one phase, and the inputs are revealed in a later phase. For example, players could create a random nonce and then send hash(input, nonce) as their commitment. You can see such a scheme at work in the following revised code:
What if one party waits for the other to open its commitment, and then seeing that it will lose, just abandons the games (does not send any further messages)? This saves the loser the gas cost of sending a transaction to open a commitment that won’t win, but also denies payment to the winner.
This generalizes to a broader question of how to ensure the incentive compatibility of a contract. Can any player profit by deviating from the intended behavior? Does the intended behavior have hidden costs?
To fix this issue, we need to make sure players are incented to do the right thing. For example: introducing a deadline before which the second player must reveal, otherwise the player who revealed first gets the reward. Additionally, players could make a security deposit in the first stage, which they forfeit unless they open their commitments in a timely manner.
The paper also introduces a number of Ethereum-specific gotchas, such as the call-stack limit. The ‘Security Considerations‘ section of the Ethereum docs does a good job today providing a high level overview of many of these: publicly visible state, re-entrancy, gas limits and loops, pitfalls when sending and receiving ether, callstack depth, and the use of tx.origin.
I also found the following ‘Ethereum Contract Security Techniques and Tips‘ document to be very useful. From the introduction:
Smart contract programming requires a different engineering mindset than you may be used to. The cost of failure can be high, and change can be difficult, making it in some ways more similar to hardware programming or financial services programming than web or mobile development. It is therefore not enough to defend against known vulnerabilities. Instead, you will need to learn a new philosophy of development…
If you want to get into smart contract development, it seems you’d better bring your A-game! Getting it right is going to require combinations of skills in verification, concurrency, cryptography, cybersecurity, and game theory as a starter!