Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vanilla Babylon client implementation #2

Merged
merged 14 commits into from
Aug 15, 2022
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,44 @@
# vigilante

Vigilante program for Babylon

## Requirements

- Go 1.18

## Development requirements

- Go 1.18

## Building

```bash
$ go build ./cmd/main.go
```

## Running the vigilante

1. Launch a Bitcoin node

```bash
$ btcd --simnet --rpclisten 127.0.0.1:18554 --rpcuser user --rpcpass pass
```

2. Launch a Babylon node following Babylon's documentation

```bash
$ babylond testnet \
--v 1 \
--output-dir ./.testnet \
--starting-ip-address 192.168.10.2 \
--keyring-backend test \
--chain-id chain-test
$ babylond start --home ./.testnet/node0/babylond
```

1. Launch a vigilante

```bash
$ go run ./cmd/main.go reporter # vigilant reporter
$ go run ./cmd/main.go submitter # vigilant submitter
```
52 changes: 52 additions & 0 deletions babylonclient/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package babylonclient

import (
"github.com/babylonchain/vigilante/config"
lensclient "github.com/strangelove-ventures/lens/client"
"go.uber.org/zap"
)

type Client struct {
*lensclient.ChainClient
Cfg *config.BabylonConfig
}

func New(cfg *config.BabylonConfig) (*Client, error) {
if err := cfg.Validate(); err != nil {
return nil, err
}

// init Zap logger, which is required by ChainClient
logger, _ := zap.NewProduction()
defer logger.Sync()
// create chainClient
cc, err := lensclient.NewChainClient(
logger,
cfg.Unwrap(),
cfg.KeyDirectory,
nil, // TODO: figure out this field
nil, // TODO: figure out this field
)
if err != nil {
return nil, err
}

// TODO: is context necessary here?
// ctx := client.Context{}.
// WithClient(cc.RPCClient).
// WithInterfaceRegistry(cc.Codec.InterfaceRegistry).
// WithChainID(cc.Config.ChainID).
// WithCodec(cc.Codec.Marshaler)

// wrap to our type
client := &Client{cc, cfg}
log.Infof("Successfully created the Babylon client")

return client, nil
}

func (c *Client) Stop() {
if c.RPCClient.IsRunning() {
<-c.RPCClient.Quit()
}
}
7 changes: 7 additions & 0 deletions babylonclient/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package babylonclient

import (
vlog "github.com/babylonchain/vigilante/log"
)

var log = vlog.Logger.WithField("module", "babylonclient")
37 changes: 37 additions & 0 deletions babylonclient/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package babylonclient

import (
epochingtypes "github.com/babylonchain/babylon/x/epoching/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/strangelove-ventures/lens/client/query"
)

// QueryStakingParams queries staking module's parameters via ChainClient
// code is adapted from https://github.com/strangelove-ventures/lens/blob/main/cmd/staking.go#L128-L149
func (c *Client) QueryStakingParams() (stakingtypes.Params, error) {
query := query.Query{Client: c.ChainClient, Options: query.DefaultOptions()} // TODO: what's the impact of DefaultOptions()?
resp, err := query.Staking_Params()
if err != nil {
return stakingtypes.Params{}, err
}

return resp.Params, nil
}

// QueryEpochingParams queries epoching module's parameters via ChainClient
// code is adapted from https://github.com/strangelove-ventures/lens/blob/main/client/query/staking.go#L7-L18
func (c *Client) QueryEpochingParams() (epochingtypes.Params, error) {
query := query.Query{Client: c.ChainClient, Options: query.DefaultOptions()} // TODO: what's the impact of DefaultOptions()?
ctx, cancel := query.GetQueryContext()
defer cancel()

queryClient := epochingtypes.NewQueryClient(c.ChainClient)
req := &epochingtypes.QueryParamsRequest{}
resp, err := queryClient.Params(ctx, req)
if err != nil {
return epochingtypes.Params{}, err
}
return resp.Params, nil
}

// TODO: implement necessary queries here
3 changes: 3 additions & 0 deletions babylonclient/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package babylonclient

// TODO: implement necessary message invocations here
13 changes: 7 additions & 6 deletions btcclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@
package btcclient

import (
"errors"

"github.com/babylonchain/vigilante/config"
"github.com/babylonchain/vigilante/netparams"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcwallet/chain"
)

var _ chain.Interface = &Client{}
// TODO: recover the below after we can bump to the latest version of btcd
// var _ chain.Interface = &Client{}

// Client represents a persistent client connection to a bitcoin RPC server
// for information regarding the current best block chain.
Expand All @@ -31,8 +30,8 @@ type Client struct {
// operate on the same bitcoin network as described by the passed chain
// parameters, the connection will be disconnected.
func New(cfg *config.BTCConfig) (*Client, error) {
if cfg.ReconnectAttempts < 0 {
return nil, errors.New("reconnectAttempts must be positive")
if err := cfg.Validate(); err != nil {
return nil, err
}

certs := readCAFile(cfg)
Expand All @@ -43,6 +42,7 @@ func New(cfg *config.BTCConfig) (*Client, error) {
return nil, err
}
client := &Client{rpcClient, params, cfg}
log.Infof("Successfully created the BTC client")
return client, err
}

Expand All @@ -51,8 +51,9 @@ func (c *Client) ConnectLoop() {
log.Infof("Start connecting to the BTC node %v", c.Cfg.Endpoint)
if err := c.Start(); err != nil {
log.Errorf("Unable to connect to the BTC node: %v", err)
} else {
log.Info("Successfully connected to the BTC node")
}
log.Info("Successfully connected to the BTC node")
c.WaitForShutdown()
}()
}
20 changes: 19 additions & 1 deletion cmd/reporter/reporter.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package reporter

import (
"github.com/babylonchain/vigilante/babylonclient"
"github.com/babylonchain/vigilante/btcclient"
"github.com/babylonchain/vigilante/cmd/utils"
"github.com/babylonchain/vigilante/config"
Expand Down Expand Up @@ -50,8 +51,13 @@ func cmdFunc(cmd *cobra.Command, args []string) {
if err != nil {
panic(err)
}
// create Babylon client. Note that requests from Babylon client are ad hoc
babylonClient, err := babylonclient.New(&cfg.Babylon)
if err != nil {
panic(err)
}
// create reporter
reporter, err := vigilante.NewReporter(&cfg.Reporter, btcClient)
reporter, err := vigilante.NewReporter(&cfg.Reporter, btcClient, babylonClient)
if err != nil {
panic(err)
}
Expand All @@ -70,6 +76,13 @@ func cmdFunc(cmd *cobra.Command, args []string) {
server.Start()
// start Prometheus metrics server
metrics.Start()
// TODO: replace the below with more suitable queries (e.g., version, node status, etc..)
params, err := babylonClient.QueryEpochingParams()
if err != nil {
log.Errorf("testing babylon client: %v", err)
} else {
log.Infof("epoching params: %v", params)
}

// SIGINT handling stuff
utils.AddInterruptHandler(func() {
Expand All @@ -88,6 +101,11 @@ func cmdFunc(cmd *cobra.Command, args []string) {
btcClient.Stop()
log.Info("BTC client shutdown")
})
utils.AddInterruptHandler(func() {
log.Info("Stopping Babylon client...")
babylonClient.Stop()
log.Info("Babylon client shutdown")
})

<-utils.InterruptHandlersDone
log.Info("Shutdown complete")
Expand Down
20 changes: 19 additions & 1 deletion cmd/submitter/submitter.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package submitter

import (
"github.com/babylonchain/vigilante/babylonclient"
"github.com/babylonchain/vigilante/btcclient"
"github.com/babylonchain/vigilante/cmd/utils"
"github.com/babylonchain/vigilante/config"
Expand Down Expand Up @@ -50,8 +51,13 @@ func cmdFunc(cmd *cobra.Command, args []string) {
if err != nil {
panic(err)
}
// create Babylon client. Note that requests from Babylon client are ad hoc
babylonClient, err := babylonclient.New(&cfg.Babylon)
if err != nil {
panic(err)
}
// create submitter
submitter, err := vigilante.NewSubmitter(&cfg.Submitter, btcClient)
submitter, err := vigilante.NewSubmitter(&cfg.Submitter, btcClient, babylonClient)
if err != nil {
panic(err)
}
Expand All @@ -70,6 +76,13 @@ func cmdFunc(cmd *cobra.Command, args []string) {
server.Start()
// start Prometheus metrics server
metrics.Start()
// TODO: replace the below with more suitable queries (e.g., version, node status, etc..)
params, err := babylonClient.QueryEpochingParams()
if err != nil {
log.Errorf("testing babylon client: %v", err)
} else {
log.Infof("epoching params: %v", params)
}

// SIGINT handling stuff
utils.AddInterruptHandler(func() {
Expand All @@ -88,6 +101,11 @@ func cmdFunc(cmd *cobra.Command, args []string) {
btcClient.Stop()
log.Info("BTC client shutdown")
})
utils.AddInterruptHandler(func() {
log.Info("Stopping Babylon client...")
babylonClient.Stop()
log.Info("Babylon client shutdown")
})

<-utils.InterruptHandlersDone
log.Info("Shutdown complete")
Expand Down
93 changes: 93 additions & 0 deletions config/babylon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package config

import (
"os"
"path/filepath"
"time"

"github.com/cosmos/cosmos-sdk/types/module"
"github.com/strangelove-ventures/lens/client"
)

// BabylonConfig defines configuration for the Babylon client
// adapted from https://github.com/strangelove-ventures/lens/blob/main/client/config.go
type BabylonConfig struct {
Key string `mapstructure:"key"`
ChainID string `mapstructure:"chain-id"`
RPCAddr string `mapstructure:"rpc-addr"`
GRPCAddr string `mapstructure:"grpc-addr"`
AccountPrefix string `mapstructure:"account-prefix"`
KeyringBackend string `mapstructure:"keyring-backend"`
GasAdjustment float64 `mapstructure:"gas-adjustment"`
GasPrices string `mapstructure:"gas-prices"`
KeyDirectory string `mapstructure:"key-directory"`
Debug bool `mapstructure:"debug"`
Timeout string `mapstructure:"timeout"`
BlockTimeout string `mapstructure:"block-timeout"`
OutputFormat string `mapstructure:"output-format"`
SignModeStr string `mapstructure:"sign-mode"`
Modules []module.AppModuleBasic `mapstructure:"-"`
}

func (cfg *BabylonConfig) Validate() error {
if _, err := time.ParseDuration(cfg.Timeout); err != nil {
return err
}
if cfg.BlockTimeout != "" {
if _, err := time.ParseDuration(cfg.BlockTimeout); err != nil {
return err
}
}
return nil
}

func (cfg *BabylonConfig) Unwrap() *client.ChainClientConfig {
return &client.ChainClientConfig{
Key: cfg.Key,
ChainID: cfg.ChainID,
RPCAddr: cfg.RPCAddr,
GRPCAddr: cfg.GRPCAddr,
AccountPrefix: cfg.AccountPrefix,
KeyringBackend: cfg.KeyringBackend,
GasAdjustment: cfg.GasAdjustment,
GasPrices: cfg.GasPrices,
KeyDirectory: cfg.KeyDirectory,
Debug: cfg.Debug,
Timeout: cfg.Timeout,
OutputFormat: cfg.OutputFormat,
SignModeStr: cfg.SignModeStr,
}
}

func DefaultBabylonConfig() BabylonConfig {
return BabylonConfig{
Key: "default",
ChainID: "chain-test",
// see https://docs.cosmos.network/master/core/grpc_rest.html for default ports
// TODO: configure HTTPS for Babylon's RPC server
// TODO: how to use Cosmos SDK's RPC server (port 1317) rather than Tendermint's RPC server (port 26657)?
RPCAddr: "http://localhost:26657",
// TODO: how to support GRPC in the Babylon client?
GRPCAddr: "https://localhost:9090",
AccountPrefix: "babylon",
KeyringBackend: "test",
GasAdjustment: 1.2,
GasPrices: "0.01ubbn",
KeyDirectory: defaultBabylonHome(),
Debug: true,
Timeout: "20s",
OutputFormat: "json",
SignModeStr: "direct",
}
}

// defaultBabylonHome returns the default Babylon node directory, which is $HOME/.babylond
// copied from https://github.com/babylonchain/babylon/blob/main/app/app.go#L205-L210
func defaultBabylonHome() string {
userHomeDir, err := os.UserHomeDir()
if err != nil {
panic(err)
}

return filepath.Join(userHomeDir, ".babylond")
}
Loading