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

feat: submitter/send sealed checkpoints to BTC #37

Merged
merged 12 commits into from
Sep 15, 2022
2 changes: 2 additions & 0 deletions .gitconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[url "ssh://[email protected]/"]
insteadOf = https://github.com/
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@
# vendor/

.vscode/
main
.idea/
.testnet/
.DS_Store
main
54 changes: 44 additions & 10 deletions btcclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,36 +30,70 @@ type Client struct {
IndexedBlockChan chan *types.IndexedBlock
}

// New creates a new BTC client
// NewWallet creates a new BTC wallet
// used by vigilant submitter
func New(cfg *config.BTCConfig) (*Client, error) {
// a wallet is essentially a BTC client
// that connects to the btcWallet daemon
func NewWallet(cfg *config.BTCConfig) (*Client, error) {
if err := cfg.Validate(); err != nil {
return nil, err
}

params := netparams.GetBTCParams(cfg.NetParams)
client := &Client{}
client.Cfg = cfg
client.Params = params
wallet := &Client{}
wallet.Cfg = cfg
wallet.Params = params

connCfg := &rpcclient.ConnConfig{
Host: cfg.Endpoint,
Host: cfg.WalletEndpoint,
Endpoint: "ws", // websocket
User: cfg.Username,
Pass: cfg.Password,
DisableTLS: cfg.DisableClientTLS,
Params: cfg.NetParams,
Certificates: readCAFile(cfg),
Certificates: readWalletCAFile(cfg),
}

rpcClient, err := rpcclient.New(connCfg, nil) // TODO: subscribe to wallet stuff?
if err != nil {
return nil, err
}
log.Info("Successfully created the BTC client and connected to the BTC server")
log.Info("Successfully connected to the BTC wallet server")

client.Client = rpcClient
return client, nil
wallet.Client = rpcClient

// load wallet from config
err = wallet.loadWallet(cfg.WalletName)
if err != nil {
return nil, err
}

return wallet, nil
}

func (cli *Client) loadWallet(name string) error {
backend, err := cli.BackendVersion()
if err != nil {
return err
}
// if the backend is btcd, no need to load wallet
if backend == rpcclient.Btcd {
log.Infof("BTC backend is btcd")
return nil
}

log.Infof("BTC backend is bitcoind")

// this is for bitcoind
res, err := cli.Client.LoadWallet(name)
if err != nil {
return err
}
log.Infof("Successfully loaded wallet %v", res.Name)
if res.Warning != "" {
log.Infof("Warning: %v", res.Warning)
}
return nil
}

// NewWithBlockNotificationHandlers creates a new BTC client that subscribes to newly connected/disconnected blocks
Expand Down
27 changes: 22 additions & 5 deletions btcclient/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,36 @@ import (

func readCAFile(cfg *config.BTCConfig) []byte {
// Read certificate file if TLS is not disabled.
var certs []byte
if !cfg.DisableClientTLS {
var err error
certs, err = ioutil.ReadFile(cfg.CAFile)
certs, err := ioutil.ReadFile(cfg.CAFile)
if err != nil {
log.Errorf("Cannot open CA file: %v", err)
// If there's an error reading the CA file, continue
// with nil certs and without the client connection.
certs = nil
return nil
}
return certs
} else {
log.Infof("Chain server RPC TLS is disabled")
}

return certs
return nil
}

func readWalletCAFile(cfg *config.BTCConfig) []byte {
// Read certificate file if TLS is not disabled.
if !cfg.DisableClientTLS {
certs, err := ioutil.ReadFile(cfg.WalletCAFile)
if err != nil {
log.Errorf("Cannot open wallet CA file in %v: %v", cfg.WalletCAFile, err)
// If there's an error reading the CA file, continue
// with nil certs and without the client connection.
return nil
}
return certs
} else {
log.Infof("Chain server RPC TLS is disabled")
}

return nil
}
2 changes: 2 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"github.com/babylonchain/babylon/app/params"
"os"

"github.com/spf13/cobra"
Expand All @@ -13,6 +14,7 @@ import (
// TODO: init log

func main() {
params.SetAddressPrefixes()
rootCmd := &cobra.Command{
Use: "vigilante",
Short: "Babylon vigilante",
Expand Down
9 changes: 8 additions & 1 deletion cmd/submitter/submitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,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"
vlog "github.com/babylonchain/vigilante/log"
Expand Down Expand Up @@ -38,13 +39,19 @@ func cmdFunc(cmd *cobra.Command, args []string) {
if err != nil {
panic(err)
}

// create BTC wallet and connect to BTC server
btcWallet, err := btcclient.NewWallet(&cfg.BTC)
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
vigilantSubmitter, err := submitter.New(&cfg.Submitter, babylonClient)
vigilantSubmitter, err := submitter.New(&cfg.Submitter, btcWallet, babylonClient)
if err != nil {
panic(err)
}
Expand Down
54 changes: 28 additions & 26 deletions config/babylon.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,22 @@ var ModuleBasics = append(
// BabylonConfig defines configuration for the Babylon client
// adapted from https://github.com/strangelove-ventures/lens/blob/v0.5.1/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:"-"`
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"`
SubmitterAddress string `mapstructure:"submitter-address"`
Modules []module.AppModuleBasic `mapstructure:"-"`
}

func (cfg *BabylonConfig) Validate() error {
Expand Down Expand Up @@ -83,17 +84,18 @@ func DefaultBabylonConfig() BabylonConfig {
// 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: "bbn",
KeyringBackend: "test",
GasAdjustment: 1.2,
GasPrices: "0.01ubbn",
KeyDirectory: defaultBabylonHome(),
Debug: true,
Timeout: "20s",
OutputFormat: "json",
SignModeStr: "direct",
Modules: ModuleBasics,
GRPCAddr: "https://localhost:9090",
AccountPrefix: "bbn",
KeyringBackend: "test",
GasAdjustment: 1.2,
GasPrices: "0.01ubbn",
KeyDirectory: defaultBabylonHome(),
Debug: true,
Timeout: "20s",
OutputFormat: "json",
SignModeStr: "direct",
SubmitterAddress: "bbn1v6k7k9s8md3k29cu9runasstq5zaa0lpznk27w", // this is currently a placeholder, will not recognized by Babylon
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case maybe the DefaultBabylonConfig could have this as a parameter, to make sure it's not forgotten. The example could be on the CLI arg or in the config file as a docstring. Or the Babylon devnet/local testnet config could give some funds to this account in its genesis file.

But at least the comment helps warn about it 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the advice! I marked it as issue #49 as a reminder.

Modules: client.ModuleBasics,
}
}

Expand Down
39 changes: 25 additions & 14 deletions config/bitcoin.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
package config

import "errors"
import (
"errors"
"github.com/btcsuite/btcd/btcutil"
)

// BTCConfig defines configuration for the Bitcoin client
type BTCConfig struct {
DisableClientTLS bool `mapstructure:"no-client-tls"`
CAFile string `mapstructure:"ca-file"`
Endpoint string `mapstructure:"endpoint"`
WalletEndpoint string `mapstructure:"wallet-endpoint"`
WalletPassword string `mapstructure:"wallet-password"`
WalletName string `mapstructure:"wallet-name"`
WalletCAFile string `mapstructure:"wallet-ca-file"`
NetParams string `mapstructure:"net-params"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
ReconnectAttempts int `mapstructure:"reconnect-attempts"`
DisableClientTLS bool `mapstructure:"no-client-tls"`
CAFile string `mapstructure:"ca-file"`
Endpoint string `mapstructure:"endpoint"`
WalletEndpoint string `mapstructure:"wallet-endpoint"`
WalletPassword string `mapstructure:"wallet-password"`
WalletName string `mapstructure:"wallet-name"`
WalletCAFile string `mapstructure:"wallet-ca-file"`
WalletLockTime int64 `mapstructure:"wallet-lock-time"` // time duration in which the wallet remains unlocked, in seconds
TxFee btcutil.Amount `mapstructure:"tx-fee"` // BTC tx fee, in BTC
NetParams string `mapstructure:"net-params"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
ReconnectAttempts int `mapstructure:"reconnect-attempts"`
}

func (cfg *BTCConfig) Validate() error {
Expand All @@ -25,6 +30,10 @@ func (cfg *BTCConfig) Validate() error {
}

func DefaultBTCConfig() BTCConfig {
feeAmount, err := btcutil.NewAmount(0.00001)
if err != nil {
panic(err)
}
return BTCConfig{
DisableClientTLS: false,
CAFile: defaultBtcCAFile,
Expand All @@ -33,9 +42,11 @@ func DefaultBTCConfig() BTCConfig {
WalletPassword: "walletpass",
WalletName: "default",
WalletCAFile: defaultBtcWalletCAFile,
WalletLockTime: 10,
TxFee: feeAmount,
NetParams: "simnet",
Username: "user",
Password: "pass",
Username: "rpcuser",
Password: "rpcpass",
ReconnectAttempts: 3,
}
}
26 changes: 24 additions & 2 deletions config/submitter.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,38 @@
package config

import (
"github.com/babylonchain/babylon/btctxformatter"
"github.com/babylonchain/vigilante/netparams"
)

const (
DefaultCheckpointCacheMaxEntries = 100
DefaultPollingFrequency = 60 // in seconds
)

// SubmitterConfig defines configuration for the gRPC-web server.
type SubmitterConfig struct {
NetParams string `mapstructure:"netparams"` // should be mainnet|testnet|simnet
NetParams string `mapstructure:"netparams"` // should be mainnet|testnet|simnet
BufferSize uint `mapstructure:"buffer-size"` // buffer for raw checkpoints
PollingFrequency uint `mapstructure:"polling-frequency"`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"frequency" is probably not the right word here. The unit of frequency is normally 1/s, so a default of 60 would mean 60 times per second.

Maybe "polling-interval", and if it's numeric, unlike the Timeout in the Babylon Config which is a string like "20s", then it should also mention what unit it's in, like "polling-interval-seconds".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Thanks. Will change this in the next PR.

}

func (cfg *SubmitterConfig) Validate() error {
return nil
}

func (cfg *SubmitterConfig) GetTag() btctxformatter.BabylonTag {
return netparams.GetBabylonParams(cfg.NetParams).Tag
}

func (cfg *SubmitterConfig) GetVersion() btctxformatter.FormatVersion {
return netparams.GetBabylonParams(cfg.NetParams).Version
}

func DefaultSubmitterConfig() SubmitterConfig {
return SubmitterConfig{
NetParams: "simnet",
NetParams: "simnet",
BufferSize: DefaultCheckpointCacheMaxEntries,
PollingFrequency: DefaultPollingFrequency,
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/spf13/cobra v1.5.0
github.com/spf13/viper v1.12.0
github.com/strangelove-ventures/lens v0.5.1
github.com/stretchr/testify v1.7.2
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b
google.golang.org/grpc v1.48.0
google.golang.org/protobuf v1.28.1
Expand Down Expand Up @@ -101,7 +102,6 @@ require (
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.7.2 // indirect
github.com/subosito/gotenv v1.3.0 // indirect
github.com/supranational/blst v0.3.8 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
Expand Down
5 changes: 5 additions & 0 deletions sample-vigilante.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ btc:
no-client-tls: false
ca-file: $TESTNET_PATH/bitcoin/rpc.cert
endpoint: localhost:18556
txfee: 0.00001
wallet-endpoint: localhost:18554
wallet-password: walletpass
wallet-name: default
wallet-lock-time: 10
wallet-ca-file: $TESTNET_PATH/bitcoin/rpc-wallet.cert
net-params: simnet
username: rpcuser
Expand All @@ -26,6 +28,7 @@ babylon:
timeout: 20s
block-timeout: ""
output-format: json
submitter-address: bbn1v6k7k9s8md3k29cu9runasstq5zaa0lpznk27w
vitsalis marked this conversation as resolved.
Show resolved Hide resolved
sign-mode: direct
grpc:
onetime-tls-key: true
Expand All @@ -37,5 +40,7 @@ grpcweb:
placeholder: grpcwebconfig
submitter:
netparams: simnet
buffer-size: 100
polling-frequency: 60
reporter:
netparams: simnet
Loading