Skip to content

Commit

Permalink
feat: add thorchain support (#1219)
Browse files Browse the repository at this point in the history
* initial thorchain addition, configs setup, still need genesis update

* wip: thorchain genesis

* thorchain running

* thorchain and bifrost working together

* add savers, swaps, and saver eject example case for gaia

* arb wip, all other sim tests passing

* arb code in, needs testing with >1 pool

* add ethereum to thorchain test and start cleaning up

* clean up savers/arb features

* clean up swap feature

* cleanup of saver eject and ragnarok features

* fix: second+ saver eject now works

* Add back arbing and eth->gaia swap

* wip, add utxo support, SendFundsWithNote() still needed

* hardfork wip

* start with genesis contents

* progress further

* wip: removed non-BTC chains temporarily, BTC's SendFundsWithNote succeeds.

* thorchain<->btc dual lp working

* btc and bch looking good

* ltc working, clean up logging, fix coins funded on each chain, and fix utxo send amount

* utxo chains fully operational

* fix ether type

* add some protections around utxo node wallet usage

* send utxo change back to sender instead of a change address

* Run tests in parallel

* clean up utxo test

* fmt/lint

* More cleanup

* increase time for bifrost to initialize

* Set bifrost envs at runtime

* change wg to eg

* minor fmt

* add back mainnet-genesis.json

* remove mainnet-genesis.json

* lint

* fix: nil gRPC queries

---------

Co-authored-by: Andrew Gouin <andrew@gouin.io>
Co-authored-by: Reece Williams <reecepbcups@gmail.com>
  • Loading branch information
3 people authored Aug 22, 2024
1 parent 3ea6fbc commit 2f1634e
Show file tree
Hide file tree
Showing 109 changed files with 34,160 additions and 28 deletions.
7 changes: 5 additions & 2 deletions chain/cosmos/chain_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ type ChainNode struct {

func NewChainNode(log *zap.Logger, validator bool, chain *CosmosChain, dockerClient *dockerclient.Client, networkID string, testName string, image ibc.DockerImage, index int) *ChainNode {
tn := &ChainNode{
log: log,
log: log.With(
zap.Bool("validator", validator),
zap.Int("i", index),
),

Validator: validator,

Expand Down Expand Up @@ -144,7 +147,7 @@ func (tn *ChainNode) NewClient(addr string) error {

tn.Client = rpcClient

grpcConn, err := grpc.Dial(
grpcConn, err := grpc.NewClient(
tn.hostGRPCPort, grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
Expand Down
4 changes: 3 additions & 1 deletion chain/cosmos/cosmos_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ func (c *CosmosChain) WithPreStartNodes(preStartNodes func(*CosmosChain)) {
c.preStartNodes = preStartNodes
}


// GetCodec returns the codec for the chain.
func (c *CosmosChain) GetCodec() *codec.ProtoCodec {
return c.cdc
Expand Down Expand Up @@ -616,6 +615,9 @@ func (c *CosmosChain) UpgradeVersion(ctx context.Context, cli *client.Client, co

func (c *CosmosChain) pullImages(ctx context.Context, cli *client.Client) {
for _, image := range c.Config().Images {
if image.Version == "local" {
continue
}
rc, err := cli.ImagePull(
ctx,
image.Repository+":"+image.Version,
Expand Down
39 changes: 35 additions & 4 deletions chain/ethereum/ethererum_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ var _ ibc.Chain = &EthereumChain{}
const (
blockTime = 2 // seconds
rpcPort = "8545/tcp"
GWEI = 1_000_000_000
ETHER = 1_000_000_000 * GWEI
)

var (
GWEI = sdkmath.NewInt(1_000_000_000)
ETHER = GWEI.MulRaw(1_000_000_000)
)

var natPorts = nat.PortMap{
Expand Down Expand Up @@ -170,7 +173,8 @@ func (c *EthereumChain) pullImages(ctx context.Context, cli *dockerclient.Client

func (c *EthereumChain) Start(testName string, ctx context.Context, additionalGenesisWallets ...ibc.WalletAmount) error {
// TODO:
// * add support for different denom configuration, ether or wei, this will affect GetBalance, etc
// * add support for different denom configuration, ether, gwei or wei,
// this will affect SendFunds, SendFundsWithNote, GetBalance and anything other than wei will lose precision for GetBalance
// * add support for modifying genesis amount config, default is 10 ether
// * add support for ConfigFileOverrides
// * block time
Expand All @@ -186,6 +190,9 @@ func (c *EthereumChain) Start(testName string, ctx context.Context, additionalGe
"--block-time", "2", // 2 second block times
"--accounts", "10", // We current only use the first account for the faucet, but tests may expect the default
"--balance", "10000000", // Genesis accounts loaded with 10mil ether, change as needed
"--no-cors",
"--gas-price", "20000000000",
"--block-base-fee-per-gas", "0",
}

var mounts []mount.Mount
Expand Down Expand Up @@ -250,7 +257,11 @@ func (c *EthereumChain) HostName() string {
}

func (c *EthereumChain) Exec(ctx context.Context, cmd []string, env []string) (stdout, stderr []byte, err error) {
job := dockerutil.NewImage(c.logger(), c.DockerClient, c.NetworkID, c.testName, c.cfg.Images[0].Repository, c.cfg.Images[0].Version)
logger := zap.NewNop()
if cmd[1] != "block-number" { // too much logging, maybe switch to an rpc lib in the future
logger = c.logger()
}
job := dockerutil.NewImage(logger, c.DockerClient, c.NetworkID, c.testName, c.cfg.Images[0].Repository, c.cfg.Images[0].Version)
opts := dockerutil.ContainerOptions{
Env: env,
Binds: c.Bind(),
Expand Down Expand Up @@ -321,6 +332,26 @@ func (c *EthereumChain) CreateKey(ctx context.Context, keyName string) error {
return nil
}

// cast wallet import requires a password prompt which docker isn't properly handling. For now, we only use CreateKey().
// Can re-add/support with this commit: https://github.com/foundry-rs/foundry/pull/6671
func (c *EthereumChain) RecoverKey(ctx context.Context, keyName, mnemonic string) error {
err := c.MakeKeystoreDir(ctx) // Ensure keystore directory is created
if err != nil {
return err
}

cmd := []string{"cast", "wallet", "import", keyName, "--keystore-dir", c.KeystoreDir(), "--mnemonic", mnemonic, "--unsafe-password", ""}
_, _, err = c.Exec(ctx, cmd, nil)
if err != nil {
return err
}

// This is needed for CreateKey() since that keystore path does not use the keyname
c.keystoreMap[keyName] = path.Join(c.KeystoreDir(), keyName)

return nil
}

// Get address of account, cast to a string to use
func (c *EthereumChain) GetAddress(ctx context.Context, keyName string) ([]byte, error) {
keystore, ok := c.keystoreMap[keyName]
Expand Down
13 changes: 0 additions & 13 deletions chain/ethereum/unimplemented.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,6 @@ func (*EthereumChain) GetHostPeerAddress() string {
return ""
}

// cast wallet import requires a password prompt which docker isn't properly handling. For now, we only use CreateKey().
func (c *EthereumChain) RecoverKey(ctx context.Context, keyName, mnemonic string) error {
/*cmd := []string{"cast", "wallet", "import", keyName, "--mnemonic", mnemonic, "--password", ""}
stdout, stderr, err := c.Exec(ctx, cmd, nil)
fmt.Println("stdout: ", string(stdout))
fmt.Println("stderr: ", string(stderr))
if err != nil {
return err
}*/
PanicFunctionName()
return nil
}

func (c *EthereumChain) GetGasFeesInNativeDenom(gasPaid int64) int64 {
PanicFunctionName()
return 0
Expand Down
1 change: 0 additions & 1 deletion chain/penumbra/penumbra_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ func (c *PenumbraChain) SendFunds(ctx context.Context, keyName string, amount ib
return fn.PenumbraClientNodes[keyName].SendFunds(ctx, amount)
}


// SendFundsWithNote will initiate a local transfer from the account associated with the specified keyName,
// amount, token denom, and recipient are specified in the amount and attach a note/memo
func (c *PenumbraChain) SendFundsWithNote(ctx context.Context, keyName string, amount ibc.WalletAmount, note string) (string, error) {
Expand Down
2 changes: 1 addition & 1 deletion chain/penumbra/penumbra_client_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ func (p *PenumbraClientNode) StartContainer(ctx context.Context) error {

p.hostGRPCPort = hostPorts[0]

p.GRPCConn, err = grpc.Dial(p.hostGRPCPort, grpc.WithTransportCredentials(insecure.NewCredentials()))
p.GRPCConn, err = grpc.NewClient(p.hostGRPCPort, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return err
}
Expand Down
89 changes: 89 additions & 0 deletions chain/thorchain/account_retriever.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package thorchain

import (
"context"
"fmt"
"strconv"

authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

grpc "google.golang.org/grpc"
"google.golang.org/grpc/metadata"

"github.com/cosmos/cosmos-sdk/client"
sdk "github.com/cosmos/cosmos-sdk/types"
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
)

var (
_ client.Account = sdk.AccountI(nil)
_ client.AccountRetriever = AccountRetriever{}
)

// AccountRetriever defines the properties of a type that can be used to
// retrieve accounts.
type AccountRetriever struct {
chain *Thorchain
}

// GetAccount queries for an account given an address and a block height. An
// error is returned if the query or decoding fails.
func (ar AccountRetriever) GetAccount(clientCtx client.Context, addr sdk.AccAddress) (client.Account, error) {
account, _, err := ar.GetAccountWithHeight(clientCtx, addr)
return account, err
}

// GetAccountWithHeight queries for an account given an address. Returns the
// height of the query with the account. An error is returned if the query
// or decoding fails.
func (ar AccountRetriever) GetAccountWithHeight(clientCtx client.Context, addr sdk.AccAddress) (client.Account, int64, error) {
var header metadata.MD

bech32Address, err := ar.chain.AccAddressToBech32(addr)
if err != nil {
return nil, 0, err
}

queryClient := authtypes.NewQueryClient(clientCtx)
res, err := queryClient.Account(context.Background(), &authtypes.QueryAccountRequest{Address: bech32Address}, grpc.Header(&header))
if err != nil {
return nil, 0, err
}

blockHeight := header.Get(grpctypes.GRPCBlockHeightHeader)
if l := len(blockHeight); l != 1 {
return nil, 0, fmt.Errorf("unexpected '%s' header length; got %d, expected: %d", grpctypes.GRPCBlockHeightHeader, l, 1)
}

nBlockHeight, err := strconv.Atoi(blockHeight[0])
if err != nil {
return nil, 0, fmt.Errorf("failed to parse block height: %w", err)
}

var acc sdk.AccountI
if err := clientCtx.InterfaceRegistry.UnpackAny(res.Account, &acc); err != nil {
return nil, 0, err
}

return acc, int64(nBlockHeight), nil
}

// EnsureExists returns an error if no account exists for the given address else nil.
func (ar AccountRetriever) EnsureExists(clientCtx client.Context, addr sdk.AccAddress) error {
if _, err := ar.GetAccount(clientCtx, addr); err != nil {
return err
}

return nil
}

// GetAccountNumberSequence returns sequence and account number for the given address.
// It returns an error if the account couldn't be retrieved from the state.
func (ar AccountRetriever) GetAccountNumberSequence(clientCtx client.Context, addr sdk.AccAddress) (uint64, uint64, error) {
acc, err := ar.GetAccount(clientCtx, addr)
if err != nil {
return 0, 0, err
}

return acc.GetAccountNumber(), acc.GetSequence(), nil
}
34 changes: 34 additions & 0 deletions chain/thorchain/address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package thorchain

import (
"errors"
"strings"

"github.com/cosmos/cosmos-sdk/types/bech32"

sdk "github.com/cosmos/cosmos-sdk/types"
)

// AccAddressFromBech32 creates an AccAddress from a Bech32 string.
// https://github.com/cosmos/cosmos-sdk/blob/v0.50.2/types/address.go#L193-L212
func (c *Thorchain) AccAddressFromBech32(address string) (addr sdk.AccAddress, err error) {
if len(strings.TrimSpace(address)) == 0 {
return sdk.AccAddress{}, errors.New("empty address string is not allowed")
}

bz, err := sdk.GetFromBech32(address, c.Config().Bech32Prefix)
if err != nil {
return nil, err
}

err = sdk.VerifyAddressFormat(bz)
if err != nil {
return nil, err
}

return sdk.AccAddress(bz), nil
}

func (c *Thorchain) AccAddressToBech32(addr sdk.AccAddress) (string, error) {
return bech32.ConvertAndEncode(c.Config().Bech32Prefix, addr)
}
Loading

0 comments on commit 2f1634e

Please sign in to comment.