Smart contracts are often mistakenly thought of as improved digital legal documents of some sort because of that “contract” part in their name. This is, however, far from truth, because they have almost nothing in common.
That sounds great, but what do smart contracts actually do, or what can they do?
Well, they are Turing complete, which in theory means you could program one to complete any function. But they’re mostly used to manage and move funds, independently and free from human control. If they are properly coded, that is.
Ethereum and Solidity programming are both still very young and writing code that works as intended without any unexpected side effects, while being secure at the same time, is hard. Very hard.
It doesn’t help that the potential consequences of making a mistake when writing a smart contract can be huge, possibly even disastrous, because you deploy the smart contract code only once, without the option to patch any issues later if a mistake is found.
In order to write secure contracts that leave no backdoors open, besides following the best practices in the industry, you need to shift the paradigm of writing code. Smart contracts work differently to what we’re used to, as they run and execute on the blockchain, not on your computer or smartphone, and this requires a change in thinking while writing code.
Additionally, you should keep in mind that smart contracts are limited in memory and computing power. They are generally slow and code execution is only triggered by transactions, which means there’ll be a waiting time before it’s included in the blockchain. Only crucial logic is meant to be coded into a smart contract and they should be as tightly and as precisely put together as possible.
We can talk about a lot of different aspects of securing and optimizing a smart contract, but what I wanted to outline the most is how this differs from the usual programming and what the security implications of it are.
In the following sections we’ll discuss a few crucial tips on keeping your contract safe
Everything on Ethereum is public and that doesn’t include only your code, but all the data stored on the contract, too. This is something that most people don’t understand at first, that even though you can’t access some data directly (from a getter function), the contract’s storage is on the blockchain and everyone can read it. The confusion usually happens because in Solidity we have a ‘private’ keyword, but this private keyword doesn’t make the data private to other users but rather to other contracts. What all of this means in practise is that we must first encrypt any sensitive data that we want to keep, before storing it on a contract.
Proper access management is crucial for smart contracts. To put it simply, functions can be public or private, where public functions are callable from the outside world and private ones are called only from inside the contract itself. Always take care when determining which functions in a smart contract should be public, because those could be executed by anyone. There have been a lot of major contracts that got hacked due to now obvious oversights. Another important thing to mention is that users will usually have certain roles, like an administrator with special high-level privileges. Setup user roles carefully so that every function can only be accessed by users you approve.
Each transaction needs a certain amount of gas, and cannot be completed without it, making the computational power of a smart contract very limited. Although this is a big restraint in itself, you might wonder how would limited computation be a security issue? The problem that occurs is that some contracts can enter a deadlock state, when a certain function can’t be executed because of high gas cost. You can think of it as a DoS (Denial of Service) attack on the smart contract. That’s why you should be very careful when writing loops, as you should limit the amount of iterations the loop can do, if it affects the functioning of the contract.
Everything on Ethereum is deterministic and a certain log of code executions could be recreated time and time again, always having an exactly the same end result. While this is actually a feature of the Ethereum network, it means that getting any random numbers is very difficult, as there are no random factors on the blockchain. Contracts usually use the blockhash as a random factor, but that can be manipulated by the miners and in any transaction you can only use a previous, already known blockhash. As of now, the most secure way to get a random number for your contract is through an oracilize service, which fetches you a number from random.org
Lastly, even if we follow these security tips and combine them with other best security practices in Ethereum, we can still make mistakes. This has become painfully obvious even during the short history Ethereum has had so far, one example being the two major security hacks in contracts made by experts working at one of the largest companies in the Ethereum ecosystem (see Parity Hack 1 and Parity Hack 2).
This is a constant reminder that we all can and probably will make mistakes, but that’s one of the reasons why security audits are a must. And you know what’s even better than a security audit? Multiple security audits from independent auditors.
In the end, there are no silver bullets in Ethereum and Solidity programming, but if you follow the latest security advice, write clean code and have it audited, then you are on the write path to having a secure contract.