With the rise of blockchain based crypto currencies, smart contracts have become increasingly more popular.
Crypto startups are utilizing smart contracts to create intelligent agreements that can be executed in a “trustless” environment. Smart contracts take out the middleman and execute on contract terms automatically, based on rules put in place. Although smart contracts harbor a lot of promise, we have seen a worrying history of hacked smart contracts, where hundreds of millions of dollars worth of crypto currencies have been lost or stolen.
My growing interest in crypto currencies and blockchain based technology has led me to research and investigate these hacks, to gain a better understanding of how they were hacked.
This article is part 1 of a 3 part series, where we will dive deep into the most infamous hacks. We will study how strokes of genius, or subtle mistakes caused earthquakes in the crypto world.
Aside: This article is based on the amazing lecture of Leonid Beder at Blockchain Academy — How to Not Destroy Millions. I highly recommend watching this talk, and all other materials on the Kin Ecosystem Foundation Youtube channel.
A smart contract is a set of promises, specified in digital form, including protocols within which the parties perform on these promises — Nick Szabo, 1996
Let’s break down this definition term by term.
Contract
An example of a common contract is a sales contract.
The seller promises to deliver the goods in exchange for the buyer.
The buyer promises to pay the desired price.
Digital form
Digital form means that the contract has to be programmed in machine readable code. We want the contract to be deterministic in nature, thus providing the same outcome based on the same input, every single time.
Protocols
For example, say the parties agree that the purchased good is to be paid in Bitcoin. The obvious protocol of choice would then be the Bitcoin protocol.
Smart
this just sounds cool.
Popular implementation of smart contract programming languages
Ethereum
Implements a nearly Turing-complete language on its blockchain, a prominent smart contract framework. We say nearly Turing-complete b/c in order to be completely Turing complete we need unlimited computational power.
Bitcoin
Implements a Turing-incomplete scripting language that allows the creation of limited smart contracts, such as:
Solidity is an OOP language for writing smart contracts (mostly?) on Ethereum. We’ll start with a simple example of a smart contract named Greeter, which will:
It is best practice at the moment to specify explicitly which version of solidity we are using, because we don’t know which bugs can be introduced in future releases.
Solidity has a similar syntax to JS. We are defining a contract named Greeter. Additionally, we have a public state variable of type string called greeting and two methods (constructor and setGreeting).
if (condition is true) {
// do something super simple which is very cheap in gas terms
} else {
// do something really complicated which will be very expensive!
}
Let’s upload the contract to the Remix IDE, create and deploy it.
Remix can work against main net and test nets. It’s really convenient to work against an Ethereum simulation. When using the simulation we can pretend to have millions of Ether, and not wait on Ethereum transactions to be processed which make development a lot faster. Running smart contracts via Remix is completely free, so I recommend you give it a try at home :)
Once the contract is deployed we see it in the Deployed Contracts section in the box (see the arrow!). Notice that we can easily change the constructor argument and redeploy to the network.
There are special variables and functions which always exist in the global namespace. Here are the ones we’ll discuss today
In Solidity, there are four types of visibilities:
For functions:
For state variables:
Modifiers can be used to modify the behavior of functions. This lets us add functionality before, after or even around the invocation of the function. (Logging for example)
Modifiers are very similar to before/after/around/hooks in other programming languages.
For example, let’s augment our Greeter smart contract to:
We’re going to see this pattern a lot, in the coming examples as well as in the wild. Additionally, let’s add a condition that says only the owner can change the greeting.
It is important to note that require is a predicate in Solidity. If its evaluation returns false, then an exception is thrown, and all changes done in contract are completely rolled back.
The underscore is equivalent to a yield. This basically means execute in this place the remaining code of the function.
A smart contract can have exactly one unnamed function.
This function is invoked when a call to the contract is made and none of the other functions match the given function identifier. A common use for fallback functions is when Ether is being transferred to the contract. This triggers the invocation of the fallback function. So, in order to set up an account to be able to receive Ether the bare minimum (in most cases) is setting this function up.
In order to receive Ether, every function must be marked as payable. This is to protect us from accidentally sending ether to a contract that doesn’t expect it.
If no such function exists, the contract cannot receive Ether through regular transactions.
This is a fancy way to do some logging in Ethereum.
Aside: We have a function called Donate that logs returns and event. This could be useful for the case of having an off-chain client that reads the loggings and manages all the donations.
We have a function called donate which is payable (we added the payable modifier). We also have a function called troll which is not payable, so sending Ether should fail in this case (payable modifier is missing).
Let’s deploy this contract to Remix so that we can verify the results of sending money via each of these methods.
In the gif above we can see that invoking the donate method results in a transaction where a value of 5 wei is transferred, and when we invoke the troll method no money is transferred.
We can see in Reward that we have a mapping between address and a uint. We use an owner concept here so our owner can call the setReward method, deciding that a specific address is supposed to get some amount of a magic token. Additionally, we have the claimReward method which is publicly accessible by everyone. This method gives you the reward that belongs to your address, and then emit an event for the reward.
Can anyone spot a problem with this?
Notice that when we declare the rewards mapping, it is a map from address to unsigned integer.
We are subtracting here without checking that the necessary funds are available! So any scenario where we reach a negative value in rewards will result in an underflow.
Aside: An underflow, will change what should be a negative value to an extremely large positive one. Check out the link, for a further explanation on how underflows work.
Consider the following example. A reward claim of 500 units is made. Now, our hacker will try and claim 1000. In theory, this shouldn’t work because we only have 500 left. After running this transaction the claimer will have an infinite amount of coins!
Mitigation #1: Test for Correctness
Mitigation #2: SafeMath
Can you spot an error in the Charity contract? Let’s break it down. We have a payable fallback function and a withdrawal function where the owner can withdraw his funds. You can imagine that at some point the owner may want to withdraw his funds so he can do something with them.
Now, let’s take a look at the setOwner method. It’s using the owner paradigm. It has no privacy. In Solidity functions are public by default! Anyone can change the owner, thus transferring the funds to himself / herself.
Hack Scenario:
Alice deposits 1000 wei for this example. Bob tries to withdraw with but fails because he is not the owner of the charity. Now, Bob calls into setOwner, changing the owner to himself. Bob takes all the funds. All this because someone forgot to define visibility!
Mitigation #3: Always Define Visibility!
This has been a primer on the Solidity language, and we have examined some of the simple, dangerous mistakes that can be made during smart contract development.
The next post in this series will dive into infamous, genius and nuanced hacks, that have so far drained hundreds of millions of dollars worth of crypto in the past several years.
If you’re looking for resources to ramp up on the blockchain world, I highly recommend (again) checking out the Kin Ecosystem Youtube channel, as it has a wonderful inventory of high quality, technical discussions and lectures. Huge thanks to Leonid Beder for building this lecture and teaching it at Blockchain Academy!