Skip to content

Commit

Permalink
core/chains/cosmos/cosmostxm: simplifications (#11101)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmank88 authored Oct 30, 2023
1 parent 67a79f1 commit e3caf76
Show file tree
Hide file tree
Showing 32 changed files with 377 additions and 821 deletions.
102 changes: 85 additions & 17 deletions core/chains/cosmos/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,38 @@ import (
"math/big"
"time"

"github.com/pelletier/go-toml/v2"
"github.com/pkg/errors"
"go.uber.org/multierr"

sdk "github.com/cosmos/cosmos-sdk/types"
bank "github.com/cosmos/cosmos-sdk/x/bank/types"

"github.com/smartcontractkit/sqlx"

"github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters"
cosmosclient "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client"
coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config"
"github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db"
relaychains "github.com/smartcontractkit/chainlink-relay/pkg/chains"
"github.com/smartcontractkit/chainlink-relay/pkg/logger"
"github.com/smartcontractkit/chainlink-relay/pkg/loop"
"github.com/smartcontractkit/chainlink-relay/pkg/services"

"github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters"
"github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client"
coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config"
"github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db"
relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types"

"github.com/smartcontractkit/chainlink/v2/core/chains"
"github.com/smartcontractkit/chainlink/v2/core/chains/cosmos/cosmostxm"
"github.com/smartcontractkit/chainlink/v2/core/services/pg"
"github.com/smartcontractkit/chainlink/v2/core/services/relay"
)

// DefaultRequestTimeout is the default Cosmos client timeout.
// defaultRequestTimeout is the default Cosmos client timeout.
// Note that while the cosmos node is processing a heavy block,
// requests can be delayed significantly (https://github.com/tendermint/tendermint/issues/6899),
// however there's nothing we can do but wait until the block is processed.
// So we set a fairly high timeout here.
// TODO(BCI-979): Remove this, or make this configurable with the updated client.
const DefaultRequestTimeout = 30 * time.Second
const defaultRequestTimeout = 30 * time.Second

var (
// ErrChainIDEmpty is returned when chain is required but was empty.
Expand Down Expand Up @@ -78,7 +81,7 @@ func (o *ChainOpts) Validate() (err error) {
return
}

func NewChain(cfg *CosmosConfig, opts ChainOpts) (adapters.Chain, error) {
func NewChain(cfg *coscfg.TOMLConfig, opts ChainOpts) (adapters.Chain, error) {
if !cfg.IsEnabled() {
return nil, fmt.Errorf("cannot create new chain with ID %s, the chain is disabled", *cfg.ChainID)
}
Expand All @@ -94,23 +97,23 @@ var _ adapters.Chain = (*chain)(nil)
type chain struct {
services.StateMachine
id string
cfg *CosmosConfig
cfg *coscfg.TOMLConfig
txm *cosmostxm.Txm
lggr logger.Logger
}

func newChain(id string, cfg *CosmosConfig, db *sqlx.DB, ks loop.Keystore, logCfg pg.QConfig, eb pg.EventBroadcaster, lggr logger.Logger) (*chain, error) {
func newChain(id string, cfg *coscfg.TOMLConfig, db *sqlx.DB, ks loop.Keystore, logCfg pg.QConfig, eb pg.EventBroadcaster, lggr logger.Logger) (*chain, error) {
lggr = logger.With(lggr, "cosmosChainID", id)
var ch = chain{
id: id,
cfg: cfg,
lggr: logger.Named(lggr, "Chain"),
}
tc := func() (cosmosclient.ReaderWriter, error) {
tc := func() (client.ReaderWriter, error) {
return ch.getClient("")
}
gpe := cosmosclient.NewMustGasPriceEstimator([]cosmosclient.GasPricesEstimator{
cosmosclient.NewClosureGasPriceEstimator(func() (map[string]sdk.DecCoin, error) {
gpe := client.NewMustGasPriceEstimator([]client.GasPricesEstimator{
client.NewClosureGasPriceEstimator(func() (map[string]sdk.DecCoin, error) {
return map[string]sdk.DecCoin{
cfg.GasToken(): sdk.NewDecCoinFromDec(cfg.GasToken(), cfg.FallbackGasPrice()),
}, nil
Expand Down Expand Up @@ -141,12 +144,12 @@ func (c *chain) TxManager() adapters.TxManager {
return c.txm
}

func (c *chain) Reader(name string) (cosmosclient.Reader, error) {
func (c *chain) Reader(name string) (client.Reader, error) {
return c.getClient(name)
}

// getClient returns a client, optionally requiring a specific node by name.
func (c *chain) getClient(name string) (cosmosclient.ReaderWriter, error) {
func (c *chain) getClient(name string) (client.ReaderWriter, error) {
var node db.Node
if name == "" { // Any node
nodes, err := c.cfg.ListNodes()
Expand All @@ -171,7 +174,7 @@ func (c *chain) getClient(name string) (cosmosclient.ReaderWriter, error) {
return nil, fmt.Errorf("failed to create client for chain %s with node %s: wrong chain id %s", c.id, name, node.CosmosChainID)
}
}
client, err := cosmosclient.NewClient(c.id, node.TendermintURL, DefaultRequestTimeout, logger.Named(c.lggr, "Client."+name))
client, err := client.NewClient(c.id, node.TendermintURL, defaultRequestTimeout, logger.Named(c.lggr, "Client."+name))
if err != nil {
return nil, fmt.Errorf("failed to create client: %w", err)
}
Expand Down Expand Up @@ -224,7 +227,41 @@ func (c *chain) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken
}

func (c *chain) Transact(ctx context.Context, from, to string, amount *big.Int, balanceCheck bool) error {
return chains.ErrLOOPPUnsupported
fromAcc, err := sdk.AccAddressFromBech32(from)
if err != nil {
return fmt.Errorf("failed to parse from account: %s", fromAcc)
}
toAcc, err := sdk.AccAddressFromBech32(to)
if err != nil {
return fmt.Errorf("failed to parse from account: %s", toAcc)
}
coin := sdk.Coin{Amount: sdk.NewIntFromBigInt(amount), Denom: c.Config().GasToken()}

txm := c.TxManager()

if balanceCheck {
var reader client.Reader
reader, err = c.Reader("")
if err != nil {
return fmt.Errorf("chain unreachable: %v", err)
}
gasPrice, err2 := txm.GasPrice()
if err2 != nil {
return fmt.Errorf("gas price unavailable: %v", err2)
}

err = validateBalance(reader, gasPrice, fromAcc, coin)
if err != nil {
return fmt.Errorf("failed to validate balance: %v", err)
}
}

sendMsg := bank.NewMsgSend(fromAcc, toAcc, sdk.Coins{coin})
_, err = txm.Enqueue("", sendMsg)
if err != nil {
return fmt.Errorf("failed to enqueue tx: %w", err)
}
return nil
}

// TODO BCF-2602 statuses are static for non-evm chain and should be dynamic
Expand All @@ -247,3 +284,34 @@ func (c *chain) listNodeStatuses(start, end int) ([]relaytypes.NodeStatus, int,
}
return stats, total, nil
}

func nodeStatus(n *coscfg.Node, id relay.ChainID) (relaytypes.NodeStatus, error) {
var s relaytypes.NodeStatus
s.ChainID = id
s.Name = *n.Name
b, err := toml.Marshal(n)
if err != nil {
return relaytypes.NodeStatus{}, err
}
s.Config = string(b)
return s, nil
}

// maxGasUsedTransfer is an upper bound on how much gas we expect a MsgSend for a single coin to use.
const maxGasUsedTransfer = 100_000

// validateBalance validates that fromAddr's balance can cover coin, including fees at gasPrice.
func validateBalance(reader client.Reader, gasPrice sdk.DecCoin, fromAddr sdk.AccAddress, coin sdk.Coin) error {
balance, err := reader.Balance(fromAddr, coin.GetDenom())
if err != nil {
return err
}

fee := gasPrice.Amount.MulInt64(maxGasUsedTransfer).RoundInt()
need := coin.Amount.Add(fee)

if balance.Amount.LT(need) {
return errors.Errorf("balance %q is too low for this transaction to be executed: need %s total, including %s fee", balance, need, fee)
}
return nil
}
Loading

0 comments on commit e3caf76

Please sign in to comment.