April 11, 2018
Acceptance Test Smart Contracts to Avoid Expensive Mistakes
Because crypto-currencies can be sold for fiat-currencies on exchanges – they represent real value.
Some crypto-currencies such as Ethereum, NEO and Cardano are code execution environments that can run smart contracts.
Ethereum in particular is a Turing complete virtual machine. You write source-code in a high level language like Solidity and this is compiled into EVM opcodes.
Because of this – it is possible to deploy a wide range of different applications to the Ethereum blockchain. Each smart-contract is able to receive money, execute code, make decisions and send money to other accounts.
It is with this power that comes great responsibility. All code will have errors and when you are writing code in a language that deals with financial assets – it is more important than ever to make sure that code is well tested in a realistic environment before it ever makes it to the live, production Ethereum blockchain.
Parity Technologies learned this the hard way in November 2017. A bug in code that was shared between many wallets containing real ether meant that a library was deleted and the wallets lost access to their money – forever. The Parity Multi-Sig Library Self-Destruct bug has locked away
513,774.16 ether forever, worth 278 million dollars at the time of writing (March 2018).
This highlights how important it is to acceptance test smart contracts!
How to acceptance test smart contracts against changing data
It is possible to write unit tests for smart contracts and use mocks of empty blockchain nodes to deploy and interact with your contract.
This is unit testing for your smart contract. It checks that individual parts of your code do what they should.
Each transaction sent to your smart contract will run a function. That function can make payments, change the internal state of the contract and call functions in external contracts. Those external contracts also have their own state that can change in turn.
The Parity bug happened because somebody changed the state of an external contract on the live network that permenantly disabled access to a function used by many other contracts.
We should be testing our contracts against all possible environments. Therefore, it’s important to test your smart contract against realistic data and not just have unit tests. We can look at this as acceptance testing for smart contracts.
However, this opens up a storage challenge because we want to:
- Save blockchain ledgers that are large and ever changing
- Make many copies of the entire ledger
- Snapshot versions of the data at certain points in time
- Run tests against those snapshots that will make writes to the snapshot
- Be able to do this without eating huge amounts of disk space
Being able to manage your underlying storage in a way that enables you to test your code against production type data would be an invaluable tool in our quest to acceptance test smart contracts and avoid expensive mistakes.
Using Portworx snapshots to acceptance test smart contracts
Portworx is a copy on write file system. This means it can take snapshots of data at a certain point in time and present the snapshot to another container whilst the original container continues to make writes to the original volume.
You can think of copy-on-write like panes of glass – you are looking for a file down each layer of glass – you can read all files (because glass is transparent) but the moment you write data it creates a new layer on the top meaning the next time you ask for the file it will read the most recently changed version.
Each time you snapshot a volume – it creates a new layer that uses all the layers beneath it. The new layer only consumes the amount of storage that has actually changed – this leads to very space efficient copies of large datasets.
Using Portworx – you can take a snapshot of a volume at any time thus creating a new layer in the copy-on-write system.
Using Portworx snapshots to acceptance test smart contracts
This is useful in the case you are running a mainnet Ethereum node and wanted to take a snapshot of the production blockchain in a certain state. You can then spin up containers that can run tests against the blockchain at that point in time. Because of copy-on-write – this will take no extra space if all you do is read from the snapshot volume. Any data you write to the volume will be saved only as a delta meaning you can run many tests in parallel with very little additional actual storage being consumed.
- Provide storage for a mainnet Ethereum node
- Take snapshots of the production volume so we have the current state of the mainnet
- Enable the mainnet Ethereum client to continue writing the production data to disk
- Present copy-on-write volumes to containers when we run tests
This means we can efficiently present many copies of the entire production blockchain which is useful when running many tests in parallel.
Snapshotting failed state
These tests might change the state of the blockchain and if they fail – it would be very useful to take a snapshot of the failed state for later inspection. This will lead to a quicker resolution of the problem because you will be able to fire up an Ethereum node that is running against the failed state and you can then interrogate the RPC api to see what went wrong.
Keeping golden states
Equally – starting with an empty volume and populating a golden state using fixture code before taking a snapshot enables you to manage a library of golden states to test against. You can keep these golden snapshots in a library and run containers against them at any time.
Using Portworx we can:
- Take a snapshot of a known golden state
- Present that volume to containers on demand
- Use copy-on-write to use minimal disk-space
This means we can test our contracts against a current and known, golden blockchain state before we deploy it to the mainnet.
Testing smart contracts with CI
Bringing this all together should be a CI server like Gitlab CI or Jenkins to be continuously integrating our code changes into master.
Fortunately – Portworx can be integrated into these tools easily which means that running automated tests of your smart contracts against realistic and production data becomes a realistic possibility.
Using different environments for production and testing
Another approach is to use a seperate environment for production and testing. Portworx can manage snapshots between the two environments and this means you are not consuming valuable resources for tests that otherwise would be used by your production stack.
If you are running any kind of mining operation, ensuring that every last bit of performance is focused on the mining operation can result in an increase in profits as we demonstrate in the blog post about improving mining pool performance
This post has demonstrated some strategies towards testing smart contracts and shown how Portworx can help with snapshots.
To summarise, to acceptance test smart contracts and avoid expensive mistakes:
- Test against production blockchain data
- Keep golden states and test against them
- Snapshot failed state and analyse further
- Test contracts with CI
- Use different environments for production and test data
Make sure you also read the other posts in this series:
- How to run production blockchain applications using Portworx
- Using Portworx to improve mining pool performance
- Deal with the growing ledger size using Portworx
Be sure to download and try Portworx today so you can see how it works and get a feel for the product – it’s free to try for 30 days!
Here are some links that you might find useful to find out more about our product and awesome team:
* Product introduction
* Our customers
Back to Blog