Tutorial: Build a personalized daemon¶
For the following tutorial, we will make references to https://github.com/Giulio2002/hello-tg-daemon.
We are going to build our daemon using golang and turbo-geth packages, so first of all we are going to create a file in which we are going to store our API methods and informations. (api.go).
our daemon will only contain one method: myNamespace_getBlockNumberByHash which will return the block number associated to certain hash.
package main
import (
"context"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/ethdb"
)
// API - implementation of ExampleApi
type API struct {
kv ethdb.KV
db ethdb.Getter
}
type ExampleAPI interface {
GetBlockNumberByHash(ctx context.Context, hash common.Hash) (uint64, error)
}
func NewAPI(kv ethdb.KV, db ethdb.Getter) *API {
return &API{kv: kv, db: db}
}
func (api *API) GetBlockNumberByHash(ctx context.Context, hash common.Hash) (uint64, error) {
return rawdb.ReadBlockByHash(api.db, hash).NumberU64(), nil
}
The type Api is the type that is going to contain the methods for our custom daemon. This type has two members: kv and db which are objects used to interact with the turbo-geth node remotely. they behave like normal db objects and can be used alongside with the rawdb package.
In our example we are making an rpcdaemon call that by receiving a certain block hash, it give the block number associated as an output. this is all done in GetBlockNumberByHash.
Now we are going to make our main.go where we are going to serve the api we made in api.go.
package main
import (
"context"
"os"
"github.com/ledgerwatch/turbo-geth/cmd/rpcdaemon/cli"
"github.com/ledgerwatch/turbo-geth/cmd/rpcdaemon/commands"
"github.com/ledgerwatch/turbo-geth/cmd/utils"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/rpc"
"github.com/spf13/cobra"
)
func main() {
cmd, cfg := cli.RootCommand()
cmd.RunE = func(cmd *cobra.Command, args []string) error {
db, backend, err := cli.OpenDB(*cfg)
if err != nil {
log.Error("Could not connect to remoteDb", "error", err)
return nil
}
apiList := APIList(db, backend, cfg)
return cli.StartRpcServer(cmd.Context(), *cfg, apiList)
}
if err := cmd.ExecuteContext(utils.RootContext()); err != nil {
log.Error(err.Error())
os.Exit(1)
}
}
func APIList(kv ethdb.KV, eth ethdb.Backend, cfg *cli.Flags) []rpc.API {
dbReader := ethdb.NewObjectDatabase(kv)
api := NewAPI(kv, dbReader)
customAPIList := []rpc.API{
{
Namespace: "myNamespace",
Public: true,
Service: ExampleAPI(api),
Version: "1.0",
},
}
// Add default TurboGeth api's
return commands.APIList(kv, eth, *cfg, customAPIList)
}
In the main we are just running our rpcdaemon as we defined it in APIList, in fact in APIList we are configuring our custom rpcdaemon to serve the ExampleAPI’s mathods on namespace myNamespace meaning that in order to call GetBlockNumberByHash via json rpc we have to call method myNamespace_getBlockNumberByHash.
Let’s now try it:
$ go build
$ ./hello-tg-daemon --http.api=myNamespace # the flag enables our namespace.
Note: Remember to run turbo-geth with –private.api.addr=localhost:9090
now it should be all set and we can test it with:
curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"myNamespace_getBlockNumberByHash","params":["ANYHASH"],"id":1}' localhost:8545
another example of custom daemon can be found at https://github.com/torquem-ch/project-1/blob/master/api.go.
Happy Building ~~~.