Testing: Still Important on the Blockchain

Everything New is Old Again

Cover Image

Whenever I am asked to review a smart contract there is one thing I look for immediately and it gives me a good idea of how the review is going to go: automated tests. If the contract has automated tests my job is much easier and I know that there aren’t nearly as many gremlins hiding in the code as there could be.

Of course this isn’t just limited to smart contracts, all software benefits from good tests. However, when you have software that is deploy once, has no update strategy, and can potentially handle large amounts of currency then it makes a lot of sense to invest in tests.

One example I like to use is a dApp that came out last year called Moon Cat Rescue. You use your browser to search (mine) for cats and then you can rescue them, name them, and sell them. Something happened on the way to release, though.

Are you getting paid for this?

Nope. We intended to collect ether from the sale of the genesis cats. As it turns out however, a fix we made during our QA process led to those funds being locked away forever. But that is okay.

If you destroyed your entire business model would you be “okay” with it? Looking at the contract source it’s pretty clear what the bug is, when a genesis cat is created the contract owner isn’t added to catOwners[catId]. When the cat is later adopted—transferCat(catId, catOwners[catId], msg.sender, offer.price);—the funds go to 0x0 which has so far racked up 5.4 ETH in the contract. An automated test that included adoption of a genesis cat and its subsequent withdrawal would have caught this.

Another example shows that even when you are up to no good a good test can help you out. Honeypot contracts are fun little Easter eggs hiding in the Ethereum network. Because many smart contracts have bugs people will go out looking for exploitable contracts. Honeypots are contracts set up to look exploitable but actually trap the “exploiter”‘s money and hold it for the creator. One such contract had a subtle little bug in it.

I won’t detail the inner workings too much except that it takes advantage of shadowing. The bug in question is on line 89: _addr.call.value(_wei); Can you spot the problem? It’s a confusing aspect of the Solidity syntax (but one familiar to Javascript programmers). The statement is missing the final parenthesis to actually make the call: _addr.call.value(_wei)(); Imagine the surprise when the contract creator attempted to withdraw his 1 ETH bait only to find a bug in the contract locked their funds forever.

My final—and favorite—example is this contract posted to Reddit. My analysis showed that the contract was vulnerable to an attack that could drain all of the funds from the contract. The core of the vulnerability lay in an extra equals sign: members[msg.sender].isPermitted == false; Sometimes a single character is all that stands between you and financial ruin.

Testing is an important component to maintainable software; but even when you don’t expect to maintain the software past initial release, automated testing can help you prevent last-minute bugs from creeping in.

Or don’t, I’m happy to keep collecting bug bounties.