The year is 2023, Dave is an administrator who will soon lose his job because a SaaS Dapp is basically doing it now, and everyone’s in his floor. Still, as an honest worker, he is going to do his job well till the last day. Dave was asked by email to send a sum of ether, and an address to a smart contract’s function.
So our guy opens up his Ethereum wallet a.k.a. a word document. He fires myWallets.docx, and finds 4 entries: My secret key, the company’s secret key, my password, the company’s password.
He opens Mist, finds the Smart Contract he needs to send ether to, input 1000 ether from the company’s account to the function of the Smart Contract, go back to his emails, and copy the address he must send to the function. But, mistakenly, he doesn’t copy the whole address. He notices his mistake, and complete the address manually, but mistype the last character. Now, it’s a whole new address.
Dave presses the send button, copy-past the company’s password, confirm the transaction and the function is executed. Upon executing, that said function send ether to some predefined account addresses, and the rest of ether to the address that Dave typed in, through a self-destructing call, because the developer who wrote it thought it was a good idea to clean out the blockchain from a smart contract that is now useless (Spoiler: You can’t) after the execution of that function.
Coincidentally, the mistyped address is the address of a Wrestling contract from the first part of this tutorial, that someone created mistakenly on the mainnet following the second method of the second part of the tutorial.
Also, in the same timeline, someone found that Wrestling contract, registered as a second wrestler, put ether in using the Wrestle() function, but the first Wrestler never played his round, and the second wrestler’s ether is forever locked in that Wrestling contract.
So in this really improbable scenario(or is it?), ether was lost due to human error, and an incomplete contract.
About the Wrestling contract
Although we implemented the base of our game, we didn’t thought about the life-cycle of the contract. When it’s created, when it’s used, when it isn’t anymore.
What if a wrestler never plays after a few rounds? We should give the players the ability to withdraw their money after a certain time, if one of them never plays their round.
We also need to think about when a contract will not be used anymore (In our case the end of the wrestling). I said that, in order for a contract to receive ether, we should add the “payable” keyword to the function that will receive it. But a contract can still receive ether through two methods (and there is nothing you can do to refuse it), when a contract self-destruct(a special, pre-defined function within Solidity that will disable the contract and send all the ether it has to a designed address) to the address of another contract, or when ether is mined to that contract. So, you should always keep a way to take ether from a contract if it ever has more than it should.
In our case, since the winner will get all the ether off the contract anyway, we could let him do using an alternative withdraw function, like the following one:
It will let him withdraw whatever ether is on the contract, as many times as he wants.
When sending money in general, keep in mind that, for some reason, the process could fail, and you should prefer to let the contract’s users withdraw their money than send them directly (like we did with the Wrestling contract).
Also, you would think that it’s a good idea, for a contract to self-destruct itself, so you can clean the blockchain, but a contract that was destroyed will remain on the blockchain, and could always receive ether as outlined above.
You should also think about a “plan b”, if for some reason, your contract doesn’t behave as expected during production. Because you can’t modify a smart contract once it’s deployed, you may want to keep a sort of lock you can trigger, and it can either pause the transactions of the contract, or send the value it holds to another contract that’ll let the users of the contract withdraw their ether for example. That trigger diminish the decentralization of the contract by giving power to a third party, so it will depend on the use case of your contract, to whether or not use such a system.
Security on Solidity starts by following common development patterns, staying up to date with the development of the platform, making your contract bugs free(or at least trying to) through tests, knowing the limitations of the platform, keeping your contract code as simple as possible, and keeping in mind that Ethereum itself(The software behind the blockchain, Solidity compiler etc.) has bugs, and is changing everyday.
Tests are an important part of any serious development, and if your old methods included waiting for the bugs to show up before fixing them, you are going to have a hard time adapting.
Open the project of the last part, fire a couple command-line interfaces, and launch ganache-cli.
ganache-cli -p 7545
Create a new folder named “test”, and create a file named “TestExample.js” inside.
Paste the following content in it:
What it does is, retrieve the Wrestling contract, deploy it to our test network, and try to use the withdraw function. Because no one should be able to use the withdraw function before the game ends, it should return an error because of the “require” instruction we used inside the withdraw function in our contract.
Execute the test on the development console:
truffle test --network development
You should have a similar output to this:
Because executing contract functions is an asynchronous process, you would prefer to use async/await for a cleaner test code. But for the sake of simplicity, our example does the job.
An exercise for you would be to simulate a wrestling game, and make sure only the winner can withdraw the ether off the contract at the end (pretty much like we did with the truffle console in the last part).
Alternatively, there are some security tools made by the community that can help you when you audit your code. They are listed here.
Testing the functions inside the contract is good, but you should always step backward, and see the interaction between the functions inside your contract, and if you contract, as a whole, is doing what it should do (And nothing else).
Keeping your smart contract well-commented is the first step toward a clear, and well-written piece of code (note that the Wrestling.sol contract we saw in the first part is not). The second step would be to keep your contract as simple as possible, and only write in the smart contract the part of your application that needs to be decentralized. If your smart contract will be part of a Dapp (in simple words, a dapp is a web application that have a part of it decentralized* (i.e: A part of it is a smart contract, or it interacts with smart contracts)), make the difference between what needs to be on the blockchain, and what can be handled by the UI, or the backend of the web app.
Note*: The definition of a dapp is broader than that, and includes all applications that leverage peer-to-peer interactions. Mist, Bittorrent, Tor etc. are all application that can be called decentralized. See this article by Vitalik Buterin.
Know the platform
To really know what you are doing, you should read the docs, there is no way around it, and complete your knowledge by searching on aspects of Solidity development that are not covered in the docs.
For example, you should know that fixed-point variable (a.k.a. floats or double) are not fully implemented yet, and a division using the the type “uint”, like 7 / 3, will round down to the nearest integer, in this case 2. So you should not take for granted some things on a platform that is still under heavy development.
Because the blockchain is public, everyone could know the information that your variables hold, and you could only try to obfuscate the information within, and not conceal it completely. Same thing for generating a random number or string, anyone who knows how you’ll try to generate the number could potentially craft it. There are actually people trying to figure out the best method to generate random numbers, and you may want to join the fight if that interests you. As I said before, everything is still under development, and there is no industry standards for a lot of things.
Time dependent contracts are also a hot point, if your contract would need to run at certain times, you would need to rely on an external application (and keep in mind that it can go down or cease to function at a certain time), because a smart contract cannot trigger itself. If your contract rely on time to judge on certain points, remember that malicious miners could temper with the time a transaction will be executed.
Know also that your contract is public, everyone can read it, everyone can interact with it, and if your contract interact or make external calls to other contracts, you have to keep in mind that a malicious contract could temper with the execution of yours.
All of this become a bit James Bondy, but when there is a lot of money at stake, people get creative. If your smart contract is running behind a store that sells diapers for old people, you probably wouldn’t need to care that much.
Limitations of the platform
Know that the Ethereum platform is not meant for heavy computations, and your transactions are limited by gas. You should keep the logic as simple as possible, and beware of infinite loops, storage limits, value overflow, and all of these little details. Because you can’t remove or modify a contract once it’s on the blockchain, you should consider all these aspects before deploying it.
The compiler, and the software behind the Ethereum blockchain are still under development, and undergo continual change, so you should keep that in mind, and stay up to date.
There a lot of good actors trying to make the users and developers life easier, and let them access the Ethereum main blockchain without the need to download it locally. Such as MEW, that let you transfer ether and deploy smart contracts, or INFURA that let you access Ethereum blockchain through an API, which is, coupled with truffle, is a solid tool.
While there is no doubt about the good intentions of these services, and the skills of the developers behind them, it’s up to you to whether or not seek convenience over security. Platforms like that are always the target of hackers because of the number of transactions they handle, so they will be always a bigger target than a node you set on your machine. At the end, your choice will depend largely on how much money you are moving around and juggling with.
Some directions, and where to start
Before leaving you, here are some great materials where you can start your research:
- A great alternative introduction, in video, would be this one from @KonstantHacker.
- Common patterns on Solidity docs.
- Security considérantions on Solidity docs.
- A more exhaustive document regrouping more recommendations.
One thing to keep in mind is, no document can be complete when it comes to security, so you have to research on your own, and use well-thought out and good programming practices.
And most of all, start participating, there a lot of project that would need more eyes to find bugs in their code, and there a lot of bounties to grab.
And if you find a bug, or discover a better way to do something, don’t hesitate to share your knowledge. Ethereum and the blockchain world follow the open source principles, so the whole community benefit from whatever is discovered or made.
The code for this part is available on Github.
ethereum-walkthrough-3 - Repository for the third part of the tutorial series on Ethereum, "Ethereum development…github.com
As a conclusion, you really need to get that mindset of a smart contracts developer, and prepare to do a lot of research and tests before launching your contracts into the wild.
And as professor Moody says:
If you liked this third part, you can find me @dev_zl.
In the next part we will see tokens.