Introducing Perigord: Golang Tools for Ethereum DApp Development
Hey there! We’re building PolySwarm — the first decentralized threat intelligence market. PolySwarm is made possible by blockchain-based smart contracts that enable new forms of market design. Read more about why we’re building PolySwarm, or take a deep dive into the full details in our white paper. PolySwarm is fortunate to be advised by world-renowned information security experts.
Perigord
Do you like what Truffle has to offer but don’t particularly enjoy a Node.js development environment? Us too! There’s literally dozens of us!
Today we’re introducing Perigord: a Truffle-like framework for developing Ethereum DApps in Go that should make these instructions a little less painful.
The impatient can grab the source code here.
Pull requests welcome!
Exploring Perigord
Follow Perigord’s install instructions and make sure you have a working perigord
install:
$ perigord
A golang development environment for EthereumUsage:
perigord [command]...
Start a New Perigord DApp
This should be familiar to anyone who has used Truffle.
$ perigord init perigordexplore
$ pushd $GOPATH/src/perigordexplore
perigord init
started a DApp project for us:
$ tree .
.
├── contracts
│ ├── Foo.sol
│ └── Migrations.sol
├── generate.go
├── main.go
├── migrations
│ ├── 1_Migrations.go
│ └── 2_Foo.go
├── perigord.yaml
├── stub
│ ├── main.go
│ └── README.md
├── stub_test.go
└── tests
└── Foo.go
A sample contract (contract/Foo.sol
) is provided. This contract does nothing other than return 1337
. This behavior is tested in tests/Foo.go
.
See Truffle’s documentation for discussion on the migrations
folder and Migrations.sol
.
The bindings
directory is empty until we perigord build
our Solidity contracts and use their ABI to produce Go bindings. Let’s do that now.
Build Go Bindings
perigord
uses solc
to compile Solidity contracts into EVM bytecode and ABI specifications and then usesabigen
to generate Go bindings based on these ABIs.
Let’s build Foo.sol
's Go bindings:
$ perigord build
This caused Perigord to call out to solc
and compile our contracts (Foo.sol
& Migrations.sol
) into Ethereum EVM bytecode and an ABI specification (in JSON):
$ tree build/
build/
├── Foo.abi
├── Foo.bin
├── Migrations.abi
└── Migrations.bin
Next, Perigord fed the ABI into abigen
to produce .go
files that define the Go bindings for our Solidity contracts.
$ tree bindings/
bindings/
├── Foo.go
└── Migrations.go
These files define all the structs
and functions
from the Solidity contracts in a manner that can be easily imported into a Golang DApp. Neat!
Enough fun, let’s get down to business.
Build a Perigord DApp that Interfaces with a Token
In this section, we will build borrow the official Ethereum TokenERC20
contract, build bindings for it, and call into the contract using a standalone Golang DApp. We’ll do of all this with the help of Perigord.
Create a New DApp
$ perigord init tokendapp
$ pushd $GOPATH/src/tokendapp
Add the TokenERC20 Contract to our DApp
$ perigord add contract TokenERC20
Copy the contents of the TokenERC20
contract from the official Ethereum documentation into contracts/TokenERC20.sol
. Make sure to copy from THE CODE section, not MINIMUM VIABLE TOKEN section.
Build TokenERC20 Bindings
Invoke perigord
to build Go bindings for the TokenERC20
contract:
$ perigord build
Add a Migration
Let’s tell Perigord how to deploy the TokenERC20
contract:
$ perigord add migration TokenERC20
This will add a new file at migrations/3_TokenERC20.go
. We need to make a few simple modifications to this file.
Configure the Migration to exercise TokenERC20’s Constructor
The TokenERC20
contract has a constructor that takes several arguments, so we need to alter the migration such that it provides the correct parameters to the DeployTokenERC20
function.
Recall the TokenERC20
constructor:
/* Initializes contract with initial supply tokens to the creator of the contract */
function TokenERC20(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) {
...
}
Our token will have an initial supply of 1337
. We’re going to call our Token "FOO"
and its symbol will be "BAR"
.
In the Deploy
function of the generated TokenERC20Deployer
struct, change the first line from:
address, transaction, contract, err := bindings.DeployTokenERC20(auth, backend)
to:
address, transaction, contract, err := bindings.DeployTokenERC20(auth, backend, big.NewInt(1337), "FOO", "BAR")
Pretty simple, but we need to do one more thing: add math/big
to our list of imports. Once you’ve made these two changes, save 3_TokenERC20.go
.
Write Tests for Our Contract
It’s always good practice to verify the functionality of your contracts.
Add a new test with:
$ perigord add test TokenERC20
This will create a new file in tests/TokenERC20.go
.
Let’s test that the name of our token is properly set. Add the following test function to the file:
func (s *TokenERC20Suite) TestName(c *C) {
session := contract.Session("TokenERC20")
c.Assert(session, NotNil) token_session, ok := session.(*bindings.TokenERC20Session)
c.Assert(ok, Equals, true)
c.Assert(token_session, NotNil) ret, _ := token_session.Name()
c.Assert(ret, Equals, "FOO")
}
This test function will look up our deployed contract and call its Name()
function to ensure that the name we set in our migration’s DeployTokenERC20
function is correctly set.
Before each test is run, each contract is re-deployed onto our go-ethereum
private blockchain via the migration we configured in the previous step.
Let’s set up our private testnet. We include a script to set up a simple testnet in the scripts
directory in the Perigord repository. Run it in a separate terminal to start up our test network.
$ $GOPATH/src/github.com/polyswarm/perigord/scripts/launch_geth_testnet.sh
Now we can test our contract(s) with:
$ perigord test
Interacting with Our Contract
Our DApp code will go in main.go
. Let’s add some code that interacts with our new "FOO"
token:
package mainimport (
"context"
"fmt"
"log" "github.com/polyswarm/perigord/contract"
"github.com/polyswarm/perigord/migration"
"github.com/polyswarm/perigord/network" "tokendapp/bindings"
_ "tokendapp/migrations"
)func main() {
network.InitNetworks() nw, err := network.Dial("dev")
if err != nil {
log.Fatalln("could not connect to dev network: ", err)
} // Run our migrations
if err := migration.RunMigrations(context.Background(), nw); err != nil {
log.Fatalln("error running migrations: ", err)
} session, ok := contract.Session("TokenERC20").(*bindings.TokenERC20Session)
if !ok {
log.Fatalln("error retrieving session")
} name, _ := session.Name()
totalSupply, _ := session.TotalSupply()
symbol, _ := session.Symbol() fmt.Printf("Let's spend some %s\n", name)
fmt.Printf("There are %d %s in total\n", totalSupply, symbol)
}
If all went according to plan, we can now run our native DApp and see our results:
$ go build main.go
$ ./main
Running migration 1
Running migration 2
Running migration 3
Let's spend some FOO
There are 1337 BAR in total
Hooray! Our standalone, token-interacting, Golang-awesome binary works!
Cheat ;)
Feel free to check out the complete example here.
Connect with PolySwarm
That’s all for now; please stay in touch! Join the conversation on Telegram, connect with us on Twitter, send us an email or sign up for notifications concerning PolySwarm’s upcoming Nectar (NCT) token sale and open source release of PolySwarm’s contract code.
~Paul Makowski, PolySwarm CTO