NeoGo adds support for Neo Blockchain Toolkit

Neo SPCC
10 min readMay 29, 2020

--

Being a full Neo node implementation, NeoGo lets you compile/deploy/invoke smart contracts without any external dependencies, just using NeoGo alone. But it’s not always convenient (CLI tools only) and can even be quite painful if one is to try debugging these contracts. Luckily Neo community already has good tools for contract development and debugging and recently released version 0.75.0 of NeoGo added support for Neo Blockchain Toolkit, so now you can develop smart contracts in Go right from this standard environment using NeoGo compiler and SDK. In this article, we show how to write, compile and debug a simple smart contract written in Go using this toolkit.

Pre-requirements

  1. Visual Studio Code v1.40 or later
  2. .NET Core 3.1 SDK
  3. Neo Blockchain Toolkit extension for Visual Studio Code installation
  4. Go v1.12 or later

NeoGo SDK and compiler

Among other things, NeoGo project provides a compiler for smart contracts written in Go along with an SDK containing interop functions used by smart contracts. The compiler part is included in the single binary file whole NeoGo compiles down to (like many other software written in Go), so to be able to use it you just need this binary.

An SDK part is represented as a set of Go packages (see documentation on go.dev) that can be imported and used in a standard Go fashion. But unlike regular Go packages, these are just stubs that have no real implementation inside. Instead of using code from these packages Go smart contract compiler actually substitutes calls to functions with appropriate Neo system calls. Anyway, using them doesn’t differ much from using other Go packages and thus this part doesn’t really require any special care.

As with many other Neo projects NeoGo is currently on its way to Neo 3, so there are two main branches there — master, where all Neo 3 development is happening right now and master-2.x for stable Neo 2 implementation. Here we’ll use the master-2.x branch because Neo 3 is still in development and there might be some issues.

NeoGo compiler installation

Using pre-built binary

For Linux AMD64 only: just download pre-compiled binary of the latest stable release (0.75.0 or later, but before 0.90.0 which is reserved for Neo 3):

To check the installation, run the ./neo-go --version сommand. It should echo back neo-go version, like the following:

Build from source

Clone the repository and build the source code:

To check the installation, run the ./bin/neo-go --version command from the neo-go folder. It should echo back neo-go version, like the following:

Now you are ready to write, build, and debug Neo smart contracts in Go.

“2-storage” smart contract

In this part, we’ll write a smart contract in Go which uses storage, compile it with NeoGo compiler, debug it using Neo Smart Contract Debugger, deploy it to the Neo Express PrivateNet and invoke it via Neo Visual Dev Tracker. The contract itself is actually a part of our regular workshop, it’s just a bit more complex than usual “Hello world” to show some specific integration features.

Writing

You can grab the contract from our workshop repository, but to keep things clean and simple we’ll create a separate directory for the project like this:

It’ll create a directory and download the contract with appropriate metadata. Now you can run VS Code from this directory to start working with the project:

This will launch an instance of Visual Studio Code in the Storage folder. Inside 2-storage.go you can see the following code:

This smart contract is able to count the number of its own invocations by storing an integer value and incrementing it after each invocation. It uses the storage interop package to get value for its counter. It also uses the runtime interop package to send notifications to the network. Using runtime.Log would probably be more appropriate here, but notifications are a bit more interesting in how they’re handled by the system and the toolkit.

Contracts also need metadata, if you are familiar with writing Neo smart contracts in C#, you probably know about special assembly attributes such as [Features(...)], [ContractAuthor("...")], [ContractTitle("...")], etc. NeoGo uses a separate YAML file for these. Usually, it’s only needed for contract deployment with NeoGo (you can compile contract’s source code into AVM file without it), but it’s also very useful for Toolkit integration because it helps you create an abi.json file that is used as contract metadata there. That’s exactly the contents of 2-storage.yml:

Because this contract uses storage, we have to set hasstorage flag to true (and pay some additional GAS for it).

Building

Let’s now use the NeoGo compiler to build this smart contract. We recommend you to first init Go modules for this contract (depending on your Go version and setup it might be not strictly required) by the following command in the Storage directory:

To compile the contract do the following in the Storage directory (adjust for neo-go path if needed):

Where:

  • ../neo-go/bin/neo-go is the path to compiled neo-go
  • contract compile is the command for contract compilation
  • -i 2-storage.got is for input source code file
  • --config 2-storage.yml is for input contract manifest file
  • -o 2-storage.avm is for the target output file (compiled VM code)
  • --debug 2-storage.debug.json is for debug information output file (that will be used by Neo Smart Contract Debugger)
  • --abi 2-storage.abi.json is for target application binary interface information file (that will be used to deploy contract)

This is actually where integration kicks in because two out of three compilation results are made specifically for it. Note that using NAME.debug.json and NAME.abi.json file names is required for proper Neo Blockchain Toolkit compatibility.

Debugging

Now we can try debugging this smart contract in Neo Smart Contract Debugger using everything we’ve got so far. To be able to debug our AVM we need to feed the debugger with additional information from the compiled smart contract to map Neo Virtual Machine instructions back to source code. This is exactly what was generated at the previous step with the --debug compilation option and stored in the 2-storage.debug.json file. This file follows the open Neo Debug Info specification that is shared among all compilers that target Neo VM.

Before you can run the contract in the debugger, you need to create a launch configuration for Neo Contract Debugger using VS Code:

  1. From the top-level Runmenu, select “Add Configuration…
  2. From the Select Environment input box, select Neo Contract

This will create a new launch.json file in the .vscode directory of your workspace. The Neo Contract Debugger will automatically add a configuration to the launch.json file for every .avm file it locates in the workspace. As we have one generated from 2-storage.go it should be picked up already and your launch.json file should look something like this:

This launch configuration file allows you to specify a variety of values to control how your contract is executed. To make it more useful we can add one more configuration to be able to debug our contract using 2 different scenarios: with empty storage and with storage already containing counter value set to 1. To set context storage value put desired key-value pair into the storage field of configuration (using hex-encoded “test-storage-key” key here) so that the resulting launch.json would look like this:

Save this file and switch to Run tab using activity bar. There are 2 different debug configurations: Empty storage and Prefilled storage. Select Empty storage from the drop-down list and press Start Debugging button to debug the contract with empty storage:

From there, the following regular debugger functionality works for Go code:

  • stepping through the code (Continue, Step Into, Step Over and Step In)
  • setting breakpoints
  • inspecting the contents of emulated storage
  • inspecting the value of local parameters and variables.
  • adding local parameters and variables as well as emulated storage values to the watch window

If we’re to step through the code using this empty storage configuration (see Storage on the sidebar) we’ll see how itemValue initializes with 0 (see Locals on the sidebar) and a Value read from storage event (notification) is sent to the network (see Debug Console):

Because the counter value is zero, the contract sets it to one and puts this value into the storage. After this, we’re able to see the value at the Storage tab on the sidebar and two more notifications in the Debug Console:

Finally, we’ve got 1 as a return value.

To debug 2-storage.go contract with non-empty storage configuration, select Prefilled storage from the drop-down list of debug configurations, and press Start Debugging:

Now we have 0x1 value for hex-encoded test-storage-key key in the storage (which can be seen in the Storage section of the sidebar). We can see how it changes the behavior of the contract as a different notification is being sent and itemValue is getting incremented by one:

This value is then put back to the storage (and appropriate notification is generated). Now Storagesection in the sidebar contains 0x2 value for the hex-encoded test-storage-key key:

Deploying

First, follow the guide to create and run Neo Express private blockchain. To successfully deploy and invoke our smart contract, you also need to create a wallet, transfer NEO assets, and claim some GAS to be able to pay for various actions.

Deploying a contract written in Go doesn’t differ much from doing the same thing with C# contract. Select Deploy Contract from the Neo Express context menu and choose 2-storage contract from the drop-down list of all available compiled contracts in your workspace. Select test wallet to pay the deployment price from and press Deploy button:

After the deployment transaction have been sent, you can move to the transaction detail page using the transaction’s hash link:

There you can see transaction details, including its script and application (execution) log:

Invoking

Do you even remember that our deployed contract is written in Go? That’s the magic of integration, we can now invoke it exactly the same way any other contracts get invoked. Select Invoke Contract from the Neo Express context menu and choose 2-storage contract from the drop-down list of all available compiled contracts in your workspace. Select test wallet to sign the transaction and press Broadcast invocation transaction button:

After Transaction created window appears, click on the transaction hash to see transaction’s details:

If you’re to take a closer look at the application log of this transaction, you can notice that notifications array contains events generated by our contract with hexadecimal byte arrays that can be decoded into Value read from storage, Storage key not yet set. Setting to 1 and New value written into storage strings, as we expected. Also, we can see 1 as a return value here:

If you’re to make another invocation you’ll see three hex-encoded notification messages again, but this time they’ll be a bit different: Value read from storage, Storage key already set. Incrementing by 1 and New value written into storage, you’ll also get 2 as a return value:

Other Go smart contract examples

As you can see, it’s very easy to work with Go contracts using NeoGo and Neo Blockchain Toolkit together, so if you want to try more contracts written in Go visit our workshop page or examples directory of the neo-go repository.

Conclusion

The power of this integration shows the power of the Neo community working together. It is enabled by open standards joining together good products developed by great teams. We believe that this is the way community should work solving different parts of the puzzle by respective field experts and then joining these pieces together to create a great overall solution to the problem. As long as this work continues Neo smart contract developers can be sure to have the best tools available irrespective of the development language chosen.

--

--

No responses yet