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

feat: vanilla implementation #1

Merged
merged 3 commits into from
Nov 15, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/
Copy link
Member

Choose a reason for hiding this comment

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

I would say this does not have a place in this repository. If someone wants to use this replacement rule, we can add instructions on how this can happen on the repository README.

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}

Copy link
Member

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.

Copy link
Member

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).

Copy link
Contributor Author

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.

Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

test:
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this depend on the mocks getting generated by mock-gen? Not entirely familiar with the mockgen package.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
62 changes: 62 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package client

import (
"fmt"
"time"

"github.com/babylonchain/vigilante/config"
Copy link
Member

Choose a reason for hiding this comment

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

If the vigilante depends on the babylon client and the client depends on the vigilante, this might introduce a cyclic dependency.

lensclient "github.com/strangelove-ventures/lens/client"
)

var _ BabylonClient = &Client{}

type Client struct {
*lensclient.ChainClient
cfg *config.BabylonConfig

// retry attributes
retrySleepTime time.Duration
maxRetrySleepTime time.Duration
}

func New(cfg *config.BabylonConfig, retrySleepTime, maxRetrySleepTime time.Duration) (*Client, error) {
Copy link
Member

Choose a reason for hiding this comment

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

I suggest that we do not depend on BabylonConfig, but instead the RPC client defines its own configuration file. Then, one can create a configuration file based on methods that the RPC client provides and provide them as arguments to this method.

Overall, since the RPC client is extracted from the vigilante, the vigilante should aim to have as little knowledge of the workings of the RPC client as possible. This includes configuration, which should be managed here.

// create a Tendermint/Cosmos client for Babylon
cc, err := newLensClient(cfg.Unwrap())
if err != nil {
return nil, err
}

// show addresses in the key ring
// TODO: specify multiple addresses in config
addrs, err := cc.ListAddresses()
if err != nil {
return nil, err
}
log.Debugf("Babylon key directory: %v", cfg.KeyDirectory)
log.Debugf("All Babylon addresses: %v", addrs)
Copy link
Member

Choose a reason for hiding this comment

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

Why do we retrieve the Babylon addresses? Is this some kind of sanity check? As they are not used anywhere else in this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems like a sanity check. Any thoughts @SebastianElvis?

Copy link
Member

Choose a reason for hiding this comment

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

Reporter uses this address to submit txs to Babylon. The logs and thus retrieving addresses can be omitted though.

Copy link
Member

Choose a reason for hiding this comment

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

Just making sure: Do we need to execute cc.ListAddresses() here? I don't see the addrs being used elsewhere other than logging here.

Copy link
Member

Choose a reason for hiding this comment

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

No need, the accounts are retrieved just for logging.


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

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()
}
}
35 changes: 35 additions & 0 deletions client/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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/vigilante/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
QueryStakingParams() (*stakingtypes.Params, error)
QueryEpochingParams() (*epochingtypes.Params, error)
QueryBTCLightclientParams() (*btclctypes.Params, error)
QueryBTCCheckpointParams() (*btcctypes.Params, error)
MustQueryBTCCheckpointParams() *btcctypes.Params
QueryHeaderChainTip() (*chainhash.Hash, uint64, error)
QueryRawCheckpoint(epochNumber uint64) (*checkpointingtypes.RawCheckpointWithMeta, error)
QueryRawCheckpointList(status checkpointingtypes.CheckpointStatus) ([]*checkpointingtypes.RawCheckpointWithMeta, error)
QueryBaseHeader() (*wire.BlockHeader, uint64, error)
QueryContainsBlock(blockHash *chainhash.Hash) (bool, error)
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
}
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
}
59 changes: 59 additions & 0 deletions client/keys_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package client_test

import (
"math/rand"
"strings"
"testing"
"time"

bbn "github.com/babylonchain/babylon/app"
"github.com/babylonchain/babylon/testutil/datagen"
"github.com/babylonchain/vigilante/babylonclient"
"github.com/babylonchain/vigilante/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 := babylonclient.New(&cfg, 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)
})
}
7 changes: 7 additions & 0 deletions client/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package client

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

var log = vlog.Logger.WithField("module", "babylonclient")
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
Copy link
Member

Choose a reason for hiding this comment

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

We can add a Github issue for that for easier tracking.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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?

Copy link
Member

Choose a reason for hiding this comment

The 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
}
Loading