-
Notifications
You must be signed in to change notification settings - Fork 1
feat: vanilla implementation #1
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Use the latest 2.1 version of CircleCI pipeline process engine. | ||
# See: https://circleci.com/docs/2.0/configuration-reference | ||
version: 2.1 | ||
|
||
# Define a job to be invoked later in a workflow. | ||
# See: https://circleci.com/docs/2.0/configuration-reference/#jobs | ||
jobs: | ||
build: | ||
# Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub. | ||
# See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor | ||
machine: | ||
image: ubuntu-2204:2022.07.1 | ||
resource_class: large | ||
# Add steps to the job | ||
# See: https://circleci.com/docs/2.0/configuration-reference/#steps | ||
steps: | ||
- checkout | ||
- run: | ||
name: Print Go environment | ||
command: "go env" | ||
- restore_cache: # restores saved cache if no changes are detected since last run | ||
keys: | ||
- go-mod-v6-{{ checksum "go.sum" }} | ||
- run: | ||
name: Install Dependencies | ||
command: sudo apt-get install libzmq3-dev | ||
- run: | ||
name: Build vigilante | ||
command: make build | ||
- save_cache: | ||
key: go-mod-v6-{{ checksum "go.sum" }} | ||
paths: | ||
- "/home/circleci/.go_workspace/pkg/mod" | ||
- run: | ||
name: Run tests | ||
command: | | ||
make test | ||
|
||
# Invoke jobs via workflows | ||
# See: https://circleci.com/docs/2.0/configuration-reference/#workflows | ||
workflows: | ||
build-and-test: | ||
jobs: | ||
- build |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
MOCKS_DIR=$(CURDIR)/testutil/mocks/ | ||
MOCKGEN_REPO=github.com/golang/mock/mockgen | ||
MOCKGEN_VERSION=v1.6.0 | ||
MOCKGEN_CMD=go run ${MOCKGEN_REPO}@${MOCKGEN_VERSION} | ||
|
||
test: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this depend on the mocks getting generated by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you saying we should re-generate mock files every time before we run tests? Not sure about that. It seems we can manually generate mock files once and upload them in the repo so that others do not need to generate them again, just like the protobuf files. |
||
go test ./... | ||
|
||
mock-gen: | ||
mkdir -p $(MOCKS_DIR) | ||
$(MOCKGEN_CMD) -source=client/interface.go -package mocks -destination $(MOCKS_DIR)/client.go |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package client | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/babylonchain/rpc-client/config" | ||
lensclient "github.com/strangelove-ventures/lens/client" | ||
"go.uber.org/zap" | ||
) | ||
|
||
var _ BabylonClient = &Client{} | ||
|
||
type Client struct { | ||
*lensclient.ChainClient | ||
cfg *config.BabylonConfig | ||
|
||
log *zap.Logger | ||
|
||
// retry attributes | ||
retrySleepTime time.Duration | ||
maxRetrySleepTime time.Duration | ||
} | ||
|
||
func New(cfg *config.BabylonConfig, log *zap.Logger, retrySleepTime, maxRetrySleepTime time.Duration) (*Client, error) { | ||
// create a Tendermint/Cosmos client for Babylon | ||
cc, err := newLensClient(cfg.Unwrap()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// wrap to our type | ||
client := &Client{cc, cfg, log, retrySleepTime, maxRetrySleepTime} | ||
|
||
return client, nil | ||
} | ||
|
||
func (c *Client) GetConfig() *config.BabylonConfig { | ||
return c.cfg | ||
} | ||
|
||
func (c *Client) GetTagIdx() uint8 { | ||
tagIdxStr := c.cfg.TagIdx | ||
if len(tagIdxStr) != 1 { | ||
panic(fmt.Errorf("tag index should be one byte")) | ||
} | ||
// convert tagIdx from string to its ascii value | ||
return uint8(rune(tagIdxStr[0])) | ||
} | ||
|
||
func (c *Client) Stop() { | ||
if c.RPCClient != nil && c.RPCClient.IsRunning() { | ||
<-c.RPCClient.Quit() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package client | ||
|
||
import ( | ||
btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" | ||
btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" | ||
checkpointingtypes "github.com/babylonchain/babylon/x/checkpointing/types" | ||
epochingtypes "github.com/babylonchain/babylon/x/epoching/types" | ||
"github.com/babylonchain/rpc-client/config" | ||
"github.com/btcsuite/btcd/chaincfg/chainhash" | ||
"github.com/btcsuite/btcd/wire" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" | ||
) | ||
|
||
type BabylonClient interface { | ||
Stop() | ||
GetConfig() *config.BabylonConfig | ||
GetTagIdx() uint8 | ||
GetAddr() (sdk.AccAddress, error) | ||
MustGetAddr() sdk.AccAddress | ||
InsertBTCSpvProof(msg *btcctypes.MsgInsertBTCSpvProof) (*sdk.TxResponse, error) | ||
InsertHeader(msg *btclctypes.MsgInsertHeader) (*sdk.TxResponse, error) | ||
InsertHeaders(msgs []*btclctypes.MsgInsertHeader) (*sdk.TxResponse, error) | ||
MustInsertBTCSpvProof(msg *btcctypes.MsgInsertBTCSpvProof) *sdk.TxResponse | ||
|
||
// staking module related queries | ||
QueryStakingParams() (*stakingtypes.Params, error) | ||
|
||
// epoch module related queries | ||
QueryEpochingParams() (*epochingtypes.Params, error) | ||
|
||
// btclightclient module related queries | ||
QueryBTCLightclientParams() (*btclctypes.Params, error) | ||
QueryHeaderChainTip() (*chainhash.Hash, uint64, error) | ||
QueryBaseHeader() (*wire.BlockHeader, uint64, error) | ||
QueryContainsBlock(blockHash *chainhash.Hash) (bool, error) | ||
|
||
// btccheckpoint module related queries | ||
QueryBTCCheckpointParams() (*btcctypes.Params, error) | ||
MustQueryBTCCheckpointParams() *btcctypes.Params | ||
|
||
// checkpointing module related queries | ||
QueryRawCheckpoint(epochNumber uint64) (*checkpointingtypes.RawCheckpointWithMeta, error) | ||
QueryRawCheckpointList(status checkpointingtypes.CheckpointStatus) ([]*checkpointingtypes.RawCheckpointWithMeta, error) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package client | ||
|
||
import ( | ||
"fmt" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
func (c *Client) GetAddr() (sdk.AccAddress, error) { | ||
return c.ChainClient.GetKeyAddress() | ||
} | ||
|
||
func (c *Client) MustGetAddr() sdk.AccAddress { | ||
addr, err := c.ChainClient.GetKeyAddress() | ||
if err != nil { | ||
panic(fmt.Errorf("Failed to get signer: %v", err)) | ||
} | ||
return addr | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package client_test | ||
|
||
import ( | ||
"go.uber.org/zap/zaptest" | ||
"math/rand" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
bbn "github.com/babylonchain/babylon/app" | ||
"github.com/babylonchain/babylon/testutil/datagen" | ||
"github.com/babylonchain/rpc-client/client" | ||
"github.com/babylonchain/rpc-client/config" | ||
"github.com/cosmos/cosmos-sdk/crypto/hd" | ||
"github.com/cosmos/cosmos-sdk/crypto/keyring" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func FuzzKeys(f *testing.F) { | ||
datagen.AddRandomSeedsToFuzzer(f, 10) | ||
|
||
f.Fuzz(func(t *testing.T, seed int64) { | ||
rand.Seed(seed) | ||
|
||
// create a keyring | ||
keyringName := datagen.GenRandomHexStr(10) | ||
dir := t.TempDir() | ||
mockIn := strings.NewReader("") | ||
cdc := bbn.MakeTestEncodingConfig() | ||
kr, err := keyring.New(keyringName, "test", dir, mockIn, cdc.Marshaler) | ||
require.NoError(t, err) | ||
|
||
// create a random key pair in this keyring | ||
keyName := datagen.GenRandomHexStr(10) | ||
kr.NewMnemonic( | ||
keyName, | ||
keyring.English, | ||
hd.CreateHDPath(118, 0, 0).String(), | ||
keyring.DefaultBIP39Passphrase, | ||
hd.Secp256k1, | ||
) | ||
|
||
// create a Babylon client with this random keyring | ||
cfg := config.DefaultBabylonConfig() | ||
cfg.KeyDirectory = dir | ||
cfg.Key = keyName | ||
cl, err := client.New(&cfg, zaptest.NewLogger(t), 1*time.Minute, 5*time.Minute) | ||
require.NoError(t, err) | ||
|
||
// retrieve the key info from key ring | ||
keys, err := kr.List() | ||
require.NoError(t, err) | ||
require.Equal(t, 1, len(keys)) | ||
|
||
// test if the key is consistent in Babylon client and keyring | ||
bbnAddr := cl.MustGetAddr() | ||
addr, _ := keys[0].GetAddress() | ||
require.Equal(t, addr, bbnAddr) | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package client | ||
|
||
import ( | ||
"github.com/cosmos/cosmos-sdk/crypto/hd" | ||
"github.com/cosmos/cosmos-sdk/crypto/keyring" | ||
lensclient "github.com/strangelove-ventures/lens/client" | ||
) | ||
|
||
// adapted from https://github.com/strangelove-ventures/lens/blob/v0.5.1/client/chain_client.go#L48-L63 | ||
// The notable difference is parsing the key directory | ||
// TODO: key directory support for different types of keyring backend | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can add a Github issue for that for easier tracking. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @SebastianElvis Not sure about this description. Could you please revisit this TODO and create an issue for tracking if it is needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, created #2 |
||
func newLensClient(ccc *lensclient.ChainClientConfig, kro ...keyring.Option) (*lensclient.ChainClient, error) { | ||
// attach the supported algorithms to the keyring options | ||
keyringOptions := []keyring.Option{} | ||
keyringOptions = append(keyringOptions, func(options *keyring.Options) { | ||
options.SupportedAlgos = keyring.SigningAlgoList{hd.Secp256k1} | ||
options.SupportedAlgosLedger = keyring.SigningAlgoList{hd.Secp256k1} | ||
}) | ||
keyringOptions = append(keyringOptions, kro...) | ||
|
||
cc := &lensclient.ChainClient{ | ||
KeyringOptions: keyringOptions, | ||
Config: ccc, | ||
Codec: lensclient.MakeCodec(ccc.Modules), | ||
} | ||
if err := cc.Init(); err != nil { | ||
return nil, err | ||
} | ||
return cc, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package client | ||
|
||
import ( | ||
"github.com/babylonchain/babylon/types/retry" | ||
btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types" | ||
"github.com/strangelove-ventures/lens/client/query" | ||
) | ||
|
||
// QueryBTCCheckpointParams queries btccheckpoint module's parameters via ChainClient | ||
func (c *Client) QueryBTCCheckpointParams() (*btcctypes.Params, error) { | ||
query := query.Query{Client: c.ChainClient, Options: query.DefaultOptions()} | ||
ctx, cancel := query.GetQueryContext() | ||
defer cancel() | ||
|
||
queryClient := btcctypes.NewQueryClient(c.ChainClient) | ||
req := &btcctypes.QueryParamsRequest{} | ||
resp, err := queryClient.Params(ctx, req) | ||
if err != nil { | ||
return &btcctypes.Params{}, err | ||
} | ||
return &resp.Params, nil | ||
} | ||
|
||
func (c *Client) MustQueryBTCCheckpointParams() *btcctypes.Params { | ||
var ( | ||
params *btcctypes.Params | ||
err error | ||
) | ||
|
||
err = retry.Do(c.retrySleepTime, c.maxRetrySleepTime, func() error { | ||
params, err = c.QueryBTCCheckpointParams() | ||
return err | ||
}) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return params | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package client | ||
|
||
import ( | ||
btclctypes "github.com/babylonchain/babylon/x/btclightclient/types" | ||
"github.com/btcsuite/btcd/chaincfg/chainhash" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/strangelove-ventures/lens/client/query" | ||
) | ||
|
||
// QueryBTCLightclientParams queries btclightclient module's parameters via ChainClient | ||
func (c *Client) QueryBTCLightclientParams() (*btclctypes.Params, error) { | ||
query := query.Query{Client: c.ChainClient, Options: query.DefaultOptions()} | ||
ctx, cancel := query.GetQueryContext() | ||
defer cancel() | ||
|
||
queryClient := btclctypes.NewQueryClient(c.ChainClient) | ||
req := &btclctypes.QueryParamsRequest{} | ||
resp, err := queryClient.Params(ctx, req) | ||
if err != nil { | ||
return &btclctypes.Params{}, err | ||
} | ||
return &resp.Params, nil | ||
} | ||
|
||
// QueryHeaderChainTip queries hash/height of the latest BTC block in the btclightclient module | ||
func (c *Client) QueryHeaderChainTip() (*chainhash.Hash, uint64, error) { | ||
query := query.Query{Client: c.ChainClient, Options: query.DefaultOptions()} | ||
ctx, cancel := query.GetQueryContext() | ||
defer cancel() | ||
|
||
queryClient := btclctypes.NewQueryClient(c.ChainClient) | ||
req := &btclctypes.QueryTipRequest{} | ||
resp, err := queryClient.Tip(ctx, req) | ||
if err != nil { | ||
return nil, 0, err | ||
} | ||
|
||
return resp.Header.Hash.ToChainhash(), resp.Header.Height, nil | ||
} | ||
|
||
func (c *Client) QueryBaseHeader() (*wire.BlockHeader, uint64, error) { | ||
query := query.Query{Client: c.ChainClient, Options: query.DefaultOptions()} | ||
ctx, cancel := query.GetQueryContext() | ||
defer cancel() | ||
|
||
queryClient := btclctypes.NewQueryClient(c.ChainClient) | ||
|
||
req := &btclctypes.QueryBaseHeaderRequest{} | ||
resp, err := queryClient.BaseHeader(ctx, req) | ||
if err != nil { | ||
return nil, 0, err | ||
} | ||
|
||
header := resp.Header.Header.ToBlockHeader() | ||
height := resp.Header.Height | ||
|
||
return header, height, nil | ||
} | ||
|
||
func (c *Client) QueryContainsBlock(blockHash *chainhash.Hash) (bool, error) { | ||
query := query.Query{Client: c.ChainClient, Options: query.DefaultOptions()} | ||
ctx, cancel := query.GetQueryContext() | ||
defer cancel() | ||
|
||
queryClient := btclctypes.NewQueryClient(c.ChainClient) | ||
req := btclctypes.QueryContainsBytesRequest{Hash: blockHash.CloneBytes()} | ||
resp, err := queryClient.ContainsBytes(ctx, &req) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
return resp.Contains, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rules for building would also be useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, is there a usecase in which someone uses this client directly from this repository without anything else? Currently, I do not think we have such a usecase so a
build
rule might not be needed.In the future, I can envision this client also providing a CLI interface in which someone can interact with a remote Babylon node (although the same can be accomplished through
babylond
commands so this would be a wrapper).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. We should implement it as a command line tool just like lens did.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good! An issue for that would be appreciated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.