Test Ethereum Smart-Contracts with Go: Tell JavaScript Goodbye

I want to thank my colleagues at Inn4Science Sergey Nemesh, Michael Popsuev, Evgeniy Babich and Ihor Tytarenko for guidance, reviews and testing. I also thank the PolySwarm team for developing the original version of Perigord.

Testing has always been mandatory but the least attractive part of software development. When it comes to smart-contracts, testing should be performed with exceptional attention to detail because errors cannot be corrected after deployment to the blockchain. Over the past years, Ethereum community came up with many tools for smart-contract development. Some of them did not manage to become popular like Vyper, Python dialect used to write smart-contracts, others became the recognized standard like Solidity. The most documented approach to smart-contract testing today is Truffle&Ganache combo. Both tools have good documentation and many issues have already been addressed on Stack Overflow and similar resources. However, there is one important drawback: you have to use Node.js to write test suites.

JavaScript traps

Even if you are not an adept of statically typed languages and love JavaScript, consider a case when you make a typo and start to compare function output that returns a string with a boolean value using the deprecated equal instead of strictEqual function.

If checkProposal returns strings like “yes” or “no”, you will always get them converted to true. Dynamic typing bears many similar traps and even experienced programmers can commit such mistakes when working on a big project or in a team with other developers who can make code changes without letting everyone know of them.

Static typing in Go eliminates this kind of mistakes. Besides, using Go instead of Node.js for testing is a dream of all Go developers who start building smart-contracts.

My team was developing a smart-contract based investment system, which had a very sophisticated architecture and over 2 000 lines of code. As the majority of the team are Go developers, testing in Go was more preferable than using Node.js.

First Go testing environment for smart-contracts

In 2017 PolySwarm developed Perigord: a tool similar to Truffle, which uses Go instead of JavaScript. Unfortunately, this project is not maintained anymore, has only one tutorial with very simple examples and does not support integration with Ganache, a personal blockchain for Ethereum development with a very user-friendly GUI. We enhanced Perigord by fixing bugs and implementing two new features: generating wallets from a mnemonic and using them for testing, and connecting to the Ganache blockchain. You will find the source code here.

The original Perigord tutorial includes only a simple example of calling the contract to change one value. However, in the real world, you also need to be able to make contract calls from different wallets, send and receive Ether, etc. Now you can do it all using enhanced Perigord and good old Ganache. Below you will find a detailed guide on Perigord&Ganache smart-contract development and testing.

Using enhanced Perigord: complete manual

To start using Perigord, you need Go 1.7+, solc, abigen and Ganache installed. Please refer to their documentation for your operating system.

Go get the repository and build Perigord

After this, you will be able to use perigord command:

We are going to build a simple smart-contract Market to show testing options.

To start a project, type in the terminal:

The project is initiated in the src/ folder in your GOPATH. Move the project to the other directory and update import paths accordingly if it is not the desired location. Let’s look at the contents of market/.

Looks very similar to a project generated by Truffle, right? But everything is in Go! Let’s look inside perigord.yaml configuration file.

You can test either using private geth testnet and wallet files or connect to Ganache. These options are mutually exclusive. We are going to use the default mnemonic, generate 10 accounts and connect to Ganache. Change perigord.yaml to:

HTTP://127.0.0.1:7545 is the default address of the Ganache RPC server. Pay attention that you can generate as many accounts as you want for testing but only accounts that are also generated in Ganache will have funds.

The contract we are going to write will be called Market.sol. It can hold a record of address pairs where one sends funds to the contract and another is entitled to receive these funds when the contract owner allows such transfer. It can be a model of a situation where two parties do not trust each other but trust contract owner who decides if a certain commitment is fulfilled. Only a couple of core functions are implemented here for demonstration purposes.

Let’s add it to the project:

Omit .sol, it will be added automatically. You can also add other contracts and get rid of the example contract Foo.sol and example test Foo.go. As long as you are working in GOPATH, you can use import contracts to create complex constructions. We will have three Solidity files: Market, which is the main contract, helper contracts Ownable and Migrations, and SafeMath library. You can find source code here.

Now the project has the following structure:

Generate EVM bytecode, ABI and Go bindings:

Add migrations for all added contracts that you are going to deploy. Since we are actually deploying only Market.sol, we need only one new migration:

Our contract does not have a constructor that takes parameters. If you need to pass parameters to the constructor, add them to Deploy{NewContract}Deployer function in the migrations file:

Add a test file for our contract:

To be able to use deterministic wallets we need to read the mnemonic from our configuration file:

The next helper function is used to get network address:

Next helper function that we are going to need is sendETH used to transfer Ether from one of the generated wallets (referred by index) to any Ethereum address:

The following two functions are used to change contract caller:

Test suite

To make calls, we create contractSessionActual for the actual contract. As the contract is ownable, we can get contract owner address and check that it corresponds to the default 0 Ganache account as follows (error handling is omitted to save space):

The next useful feature is making changes to the contract:

As one of the core features used in testing is changing who calls the contract, let’s make a deposit on behalf of the sender

You can find the whole test suite here.

Now open stub_test.go and check that all imports point to your current project. In this case we have:

Run tests:

You will get the similar output if everything was done correctly:

If you are having any issues, download source files from the repository and repeat steps from this manual.

Final thoughts

Perigord is a reliable testing tool written in your favorite programming language. It creates a project structure similar to Truffle project structure. Most of its commands are the same as Truffle commands so you do not have to learn them if you are used to Truffle. Static typing and clear function signatures allow you to develop and debug quickly and leave not typo in arguments unnoticed. It is both easy to migrate existing Truffle project to Perigord (you just have to copy&paste contract files to the appropriate folder and add tests) and to start a brand new project with test written in Go.

I hope that the work started by the PolySwarm and continued by Inn4Science will be useful for the Go community and spare many hours of testing and debugging with not Go-based tools.

Olena Stoliarova, Go and smart-contract developer at Inn4Science