Skip to content
This repository has been archived by the owner on Apr 16, 2024. It is now read-only.

Commit

Permalink
feat: vanilla implementation (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
gitferry authored Nov 15, 2022
1 parent 8081b6a commit 871da1d
Show file tree
Hide file tree
Showing 18 changed files with 2,332 additions and 0 deletions.
44 changes: 44 additions & 0 deletions .circleci/config.yml
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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@

# Dependency directories (remove the comment below to include it)
# vendor/
.vscode/
.idea/
.testnet/
.DS_Store
main
tmp/
11 changes: 11 additions & 0 deletions Makefile
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:
go test ./...

mock-gen:
mkdir -p $(MOCKS_DIR)
$(MOCKGEN_CMD) -source=client/interface.go -package mocks -destination $(MOCKS_DIR)/client.go
55 changes: 55 additions & 0 deletions client/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()
}
}
45 changes: 45 additions & 0 deletions client/interface.go
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)
}
19 changes: 19 additions & 0 deletions client/keys.go
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
}
60 changes: 60 additions & 0 deletions client/keys_test.go
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)
})
}
30 changes: 30 additions & 0 deletions client/modified_lensclient.go
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
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
}
38 changes: 38 additions & 0 deletions client/query_btc_checkpoint.go
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
}
73 changes: 73 additions & 0 deletions client/query_btc_lightclient.go
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
}
Loading

0 comments on commit 871da1d

Please sign in to comment.