Building DAppChain clients in Golang
Overview
The go-loom
library contains everything you need to build Go apps & services that interact with Loom DAppChains, and to build the smart contracts that live on those DAppChains.
To get started, install go-loom
by running the following command:
go get github.com/loomnetwork/go-loom
In this section, you'll be introduced to the go-loom
API that you will use to write Go code that interacts with a Loom DAppChain. Writing smart contracts in Go will be covered in a later section.
In the go-loom
package you will find a number of examples. examples/cli
contains a CLI app that can be used to interact with the examples/plugins/helloworld
smart contract. We'll start by building and test driving the CLI app, then we'll introduce you to the go-loom
API that was used
to build it.
Let's start by generating the ./example-cli
executable:
make example-cli
Example CLI app
The helloworld smart contract has a public SetMsg
method that can be called to store an association between a key and a value:
./example-cli call set_msg -k 123 -v 456 --contract [contract_name] -p [priv_key]
The smart contract also has a public read-only GetMsg
method that can be called to look up an association between a key and a value:
./example-cli call get_msg -k 123 -p [priv_key]
You should see the following response:
{
Key: '123',
Value: '456'
}
And that concludes our demonstration of the functionality of the example CLI app. Now it's time to take a look at the parts of the go-loom
API that were used to implement it.
Connecting to a DAppChain
The client.Contract
type provides a convenient way to interact with a smart contract running on a Loom DAppChain. Let's write a function that creates a client.Contract
instance to interact with the sample helloworld smart contract from the Loom SDK:
package main
import (
"github.com/loomnetwork/go-loom/auth"
"github.com/loomnetwork/go-loom/client"
"github.com/loomnetwork/go-loom/examples/types"
"golang.org/x/crypto/ed25519"
)
// getContract creates a new `Contract` instance that can be used to interact with a smart contract.
func getContract(contractName string) (*client.Contract, error) {
rpcClient := client.NewDAppChainRPCClient(
"default",
"http://localhost:46658/rpc",
"http://localhost:46658/query",
)
contractAddr, err := rpcClient.Resolve(contractName)
if err != nil {
return nil, err
}
return client.NewContract(rpcClient, contractAddr.Local), nil
}
Writing data to a DAppChain
To mutate the state of a smart contract you need to call one of its public methods. To do so, a signed transaction must be sent to and validated by the DAppChain. Fortunately, the client.Contract
type takes care of most of this when you use the Contract.Call()
method.
The helloworld smart contract has a public SetMsg
method that can be called to store an association between a key and a value. Let's write a function that calls this method:
func store(contract *client.Contract, key, value string, signer auth.Signer) error {
params := types.MapEntry{
Key: key,
Value: value,
}
if _, err := contract.Call("SetMsg", ¶ms, signer, nil); err != nil {
return err
}
return nil
}
Reading data from a DAppChain
To read the state of a smart contract you need to call one of its public read-only methods, you can do so by using the Contract.StaticCall()
method.
The helloworld smart contract has a public GetMsg
method that can be called to look up an association between a key and a value. Let's write a function that calls this method:
func load(contract *client.Contract, key string) (string, error) {
params := types.MapEntry{
Key: key, // The smart contract will look up the value stored under this key.
}
var result types.MapEntry
if _, err := contract.StaticCall("GetMsg", ¶ms, contract.Address, &result); err != nil {
return "", err
}
return result.Value, nil
}
Putting it all together
Now that we have all the pieces in place make sure that you have the DAppChain running. Then, run the following code:
func main() {
_, privateKey, err := ed25519.GenerateKey(nil)
if err != nil {
panic(err)
}
signer := auth.NewEd25519Signer(privateKey)
contract, err := getContract("helloworld")
if err != nil {
panic(err)
}
store(contract, "123", "hello!", signer)
value, err := load(contract, "123")
if err != nil {
panic(err)
}
fmt.Printf("Value: %s\n", value)
}
You should see Value: hello!
printed to the console.