diff --git a/core/chains/cosmos/chain.go b/core/chains/cosmos/chain.go deleted file mode 100644 index e11f95d356e..00000000000 --- a/core/chains/cosmos/chain.go +++ /dev/null @@ -1,317 +0,0 @@ -package cosmos - -import ( - "context" - "crypto/rand" - "fmt" - "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" - - 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/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. -// 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 - -var ( - // ErrChainIDEmpty is returned when chain is required but was empty. - ErrChainIDEmpty = errors.New("chain id empty") - // ErrChainIDInvalid is returned when a chain id does not match any configured chains. - ErrChainIDInvalid = errors.New("chain id does not match any local chains") -) - -// Chain is a wrap for easy use in other places in the core node -type Chain = adapters.Chain - -// ChainOpts holds options for configuring a Chain. -type ChainOpts struct { - QueryConfig pg.QConfig - Logger logger.Logger - DB *sqlx.DB - KeyStore loop.Keystore - EventBroadcaster pg.EventBroadcaster -} - -func (o *ChainOpts) Validate() (err error) { - required := func(s string) error { - return fmt.Errorf("%s is required", s) - } - if o.QueryConfig == nil { - err = multierr.Append(err, required("Config")) - } - if o.Logger == nil { - err = multierr.Append(err, required("Logger'")) - } - if o.DB == nil { - err = multierr.Append(err, required("DB")) - } - if o.KeyStore == nil { - err = multierr.Append(err, required("KeyStore")) - } - if o.EventBroadcaster == nil { - err = multierr.Append(err, required("EventBroadcaster")) - } - return -} - -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) - } - c, err := newChain(*cfg.ChainID, cfg, opts.DB, opts.KeyStore, opts.QueryConfig, opts.EventBroadcaster, opts.Logger) - if err != nil { - return nil, err - } - return c, nil -} - -var _ adapters.Chain = (*chain)(nil) - -type chain struct { - services.StateMachine - id string - cfg *coscfg.TOMLConfig - txm *cosmostxm.Txm - lggr logger.Logger -} - -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() (client.ReaderWriter, error) { - return ch.getClient("") - } - 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 - }), - }, lggr) - ch.txm = cosmostxm.NewTxm(db, tc, *gpe, ch.id, cfg, ks, lggr, logCfg, eb) - - return &ch, nil -} - -func (c *chain) Name() string { - return c.lggr.Name() -} - -func (c *chain) ID() string { - return c.id -} - -func (c *chain) ChainID() string { - return c.id -} - -func (c *chain) Config() coscfg.Config { - return c.cfg -} - -func (c *chain) TxManager() adapters.TxManager { - return c.txm -} - -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) (client.ReaderWriter, error) { - var node db.Node - if name == "" { // Any node - nodes, err := c.cfg.ListNodes() - if err != nil { - return nil, fmt.Errorf("failed to list nodes: %w", err) - } - if len(nodes) == 0 { - return nil, errors.New("no nodes available") - } - nodeIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(nodes)))) - if err != nil { - return nil, fmt.Errorf("could not generate a random node index: %w", err) - } - node = nodes[nodeIndex.Int64()] - } else { // Named node - var err error - node, err = c.cfg.GetNode(name) - if err != nil { - return nil, fmt.Errorf("failed to get node named %s: %w", name, err) - } - if node.CosmosChainID != c.id { - 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 := 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) - } - c.lggr.Debugw("Created client", "name", node.Name, "tendermint-url", node.TendermintURL) - return client, nil -} - -// Start starts cosmos chain. -func (c *chain) Start(ctx context.Context) error { - return c.StartOnce("Chain", func() error { - c.lggr.Debug("Starting") - return c.txm.Start(ctx) - }) -} - -func (c *chain) Close() error { - return c.StopOnce("Chain", func() error { - c.lggr.Debug("Stopping") - return c.txm.Close() - }) -} - -func (c *chain) Ready() error { - return multierr.Combine( - c.StateMachine.Ready(), - c.txm.Ready(), - ) -} - -func (c *chain) HealthReport() map[string]error { - m := map[string]error{c.Name(): c.Healthy()} - services.CopyHealth(m, c.txm.HealthReport()) - return m -} - -// ChainService interface -func (c *chain) GetChainStatus(ctx context.Context) (relaytypes.ChainStatus, error) { - toml, err := c.cfg.TOMLString() - if err != nil { - return relaytypes.ChainStatus{}, err - } - return relaytypes.ChainStatus{ - ID: c.id, - Enabled: *c.cfg.Enabled, - Config: toml, - }, nil -} -func (c *chain) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken string) (stats []relaytypes.NodeStatus, nextPageToken string, total int, err error) { - return relaychains.ListNodeStatuses(int(pageSize), pageToken, c.listNodeStatuses) -} - -func (c *chain) Transact(ctx context.Context, from, to string, amount *big.Int, balanceCheck bool) error { - 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 -func (c *chain) listNodeStatuses(start, end int) ([]relaytypes.NodeStatus, int, error) { - stats := make([]relaytypes.NodeStatus, 0) - total := len(c.cfg.Nodes) - if start >= total { - return stats, total, relaychains.ErrOutOfRange - } - if end > total { - end = total - } - nodes := c.cfg.Nodes[start:end] - for _, node := range nodes { - stat, err := nodeStatus(node, c.ChainID()) - if err != nil { - return stats, total, err - } - stats = append(stats, stat) - } - 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 -} diff --git a/core/chains/cosmos/cosmostxm/helpers_test.go b/core/chains/cosmos/cosmostxm/helpers_test.go deleted file mode 100644 index a2dfbbeed84..00000000000 --- a/core/chains/cosmos/cosmostxm/helpers_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package cosmostxm - -import "golang.org/x/exp/maps" - -func (ka *keystoreAdapter) Accounts() ([]string, error) { - ka.mutex.Lock() - defer ka.mutex.Unlock() - err := ka.updateMappingLocked() - if err != nil { - return nil, err - } - addresses := maps.Keys(ka.addressToPubKey) - - return addresses, nil -} diff --git a/core/chains/cosmos/cosmostxm/key_wrapper.go b/core/chains/cosmos/cosmostxm/key_wrapper.go deleted file mode 100644 index e03dfd89b89..00000000000 --- a/core/chains/cosmos/cosmostxm/key_wrapper.go +++ /dev/null @@ -1,62 +0,0 @@ -package cosmostxm - -import ( - "bytes" - "context" - - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" -) - -// KeyWrapper uses a keystoreAdapter to implement the cosmos-sdk PrivKey interface for a specific key. -type KeyWrapper struct { - adapter *keystoreAdapter - account string -} - -var _ cryptotypes.PrivKey = &KeyWrapper{} - -func NewKeyWrapper(adapter *keystoreAdapter, account string) *KeyWrapper { - return &KeyWrapper{ - adapter: adapter, - account: account, - } -} - -func (a *KeyWrapper) Bytes() []byte { - // don't expose the private key. - return nil -} - -func (a *KeyWrapper) Sign(msg []byte) ([]byte, error) { - return a.adapter.Sign(context.Background(), a.account, msg) -} - -func (a *KeyWrapper) PubKey() cryptotypes.PubKey { - pubKey, err := a.adapter.PubKey(a.account) - if err != nil { - // return an empty pubkey if it's not found. - return &secp256k1.PubKey{Key: []byte{}} - } - return pubKey -} - -func (a *KeyWrapper) Equals(other cryptotypes.LedgerPrivKey) bool { - return bytes.Equal(a.PubKey().Bytes(), other.PubKey().Bytes()) -} - -func (a *KeyWrapper) Type() string { - return "secp256k1" -} - -func (a *KeyWrapper) Reset() { - // no-op -} - -func (a *KeyWrapper) String() string { - return "" -} - -func (a *KeyWrapper) ProtoMessage() { - // no-op -} diff --git a/core/chains/cosmos/cosmostxm/keystore_adapter.go b/core/chains/cosmos/cosmostxm/keystore_adapter.go deleted file mode 100644 index 6b360dde98c..00000000000 --- a/core/chains/cosmos/cosmostxm/keystore_adapter.go +++ /dev/null @@ -1,129 +0,0 @@ -package cosmostxm - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "sync" - - "github.com/cometbft/cometbft/crypto" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/types/bech32" - "github.com/pkg/errors" - "golang.org/x/crypto/ripemd160" //nolint: staticcheck - - "github.com/smartcontractkit/chainlink-relay/pkg/loop" -) - -type accountInfo struct { - Account string - PubKey *secp256k1.PubKey -} - -// keystoreAdapter adapts a Cosmos loop.Keystore to translate public keys into bech32-prefixed account addresses. -type keystoreAdapter struct { - keystore loop.Keystore - accountPrefix string - mutex sync.RWMutex - addressToPubKey map[string]*accountInfo -} - -func newKeystoreAdapter(keystore loop.Keystore, accountPrefix string) *keystoreAdapter { - return &keystoreAdapter{ - keystore: keystore, - accountPrefix: accountPrefix, - addressToPubKey: make(map[string]*accountInfo), - } -} - -func (ka *keystoreAdapter) updateMappingLocked() error { - accounts, err := ka.keystore.Accounts(context.Background()) - if err != nil { - return err - } - - // similar to cosmos-sdk, cache and re-use calculated bech32 addresses to prevent duplicated work. - // ref: https://github.com/cosmos/cosmos-sdk/blob/3b509c187e1643757f5ef8a0b5ae3decca0c7719/types/address.go#L705 - - type cacheEntry struct { - bech32Addr string - accountInfo *accountInfo - } - accountCache := make(map[string]cacheEntry, len(ka.addressToPubKey)) - for bech32Addr, accountInfo := range ka.addressToPubKey { - accountCache[accountInfo.Account] = cacheEntry{bech32Addr: bech32Addr, accountInfo: accountInfo} - } - - addressToPubKey := make(map[string]*accountInfo, len(accounts)) - for _, account := range accounts { - if prevEntry, ok := accountCache[account]; ok { - addressToPubKey[prevEntry.bech32Addr] = prevEntry.accountInfo - continue - } - pubKeyBytes, err := hex.DecodeString(account) - if err != nil { - return err - } - - if len(pubKeyBytes) != secp256k1.PubKeySize { - return errors.New("length of pubkey is incorrect") - } - - sha := sha256.Sum256(pubKeyBytes) - hasherRIPEMD160 := ripemd160.New() - _, _ = hasherRIPEMD160.Write(sha[:]) - address := crypto.Address(hasherRIPEMD160.Sum(nil)) - - bech32Addr, err := bech32.ConvertAndEncode(ka.accountPrefix, address) - if err != nil { - return err - } - - addressToPubKey[bech32Addr] = &accountInfo{ - Account: account, - PubKey: &secp256k1.PubKey{Key: pubKeyBytes}, - } - } - - ka.addressToPubKey = addressToPubKey - return nil -} - -func (ka *keystoreAdapter) lookup(id string) (*accountInfo, error) { - ka.mutex.RLock() - ai, ok := ka.addressToPubKey[id] - ka.mutex.RUnlock() - if !ok { - // try updating the mapping once, incase there was an update on the keystore. - ka.mutex.Lock() - err := ka.updateMappingLocked() - if err != nil { - ka.mutex.Unlock() - return nil, err - } - ai, ok = ka.addressToPubKey[id] - ka.mutex.Unlock() - if !ok { - return nil, errors.New("No such id") - } - } - return ai, nil -} - -func (ka *keystoreAdapter) Sign(ctx context.Context, id string, hash []byte) ([]byte, error) { - accountInfo, err := ka.lookup(id) - if err != nil { - return nil, err - } - return ka.keystore.Sign(ctx, accountInfo.Account, hash) -} - -// Returns the cosmos PubKey associated with the prefixed address. -func (ka *keystoreAdapter) PubKey(address string) (cryptotypes.PubKey, error) { - accountInfo, err := ka.lookup(address) - if err != nil { - return nil, err - } - return accountInfo.PubKey, nil -} diff --git a/core/chains/cosmos/cosmostxm/main_test.go b/core/chains/cosmos/cosmostxm/main_test.go deleted file mode 100644 index bc340afa430..00000000000 --- a/core/chains/cosmos/cosmostxm/main_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package cosmostxm - -import ( - "os" - "testing" - - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/params" -) - -func TestMain(m *testing.M) { - params.InitCosmosSdk( - /* bech32Prefix= */ "wasm", - /* token= */ "cosm", - ) - code := m.Run() - os.Exit(code) -} diff --git a/core/chains/cosmos/cosmostxm/orm.go b/core/chains/cosmos/cosmostxm/orm.go deleted file mode 100644 index cc9b179cce5..00000000000 --- a/core/chains/cosmos/cosmostxm/orm.go +++ /dev/null @@ -1,104 +0,0 @@ -package cosmostxm - -import ( - "database/sql" - - "github.com/pkg/errors" - - "github.com/smartcontractkit/sqlx" - - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - - "github.com/smartcontractkit/chainlink/v2/core/services/pg" -) - -// ORM manages the data model for cosmos tx management. -type ORM struct { - chainID string - q pg.Q -} - -// NewORM creates an ORM scoped to chainID. -func NewORM(chainID string, db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig) *ORM { - namedLogger := logger.Named(lggr, "Configs") - q := pg.NewQ(db, namedLogger, cfg) - return &ORM{ - chainID: chainID, - q: q, - } -} - -// InsertMsg inserts a cosmos msg, assumed to be a serialized cosmos ExecuteContractMsg. -func (o *ORM) InsertMsg(contractID, typeURL string, msg []byte, qopts ...pg.QOpt) (int64, error) { - var tm adapters.Msg - q := o.q.WithOpts(qopts...) - err := q.Get(&tm, `INSERT INTO cosmos_msgs (contract_id, type, raw, state, cosmos_chain_id, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, NOW(), NOW()) RETURNING *`, contractID, typeURL, msg, db.Unstarted, o.chainID) - if err != nil { - return 0, err - } - return tm.ID, nil -} - -// UpdateMsgsContract updates messages for the given contract. -func (o *ORM) UpdateMsgsContract(contractID string, from, to db.State, qopts ...pg.QOpt) error { - q := o.q.WithOpts(qopts...) - _, err := q.Exec(`UPDATE cosmos_msgs SET state = $1, updated_at = NOW() - WHERE cosmos_chain_id = $2 AND contract_id = $3 AND state = $4`, to, o.chainID, contractID, from) - if err != nil { - return err - } - return nil -} - -// GetMsgsState returns the oldest messages with a given state up to limit. -func (o *ORM) GetMsgsState(state db.State, limit int64, qopts ...pg.QOpt) (adapters.Msgs, error) { - if limit < 1 { - return adapters.Msgs{}, errors.New("limit must be greater than 0") - } - q := o.q.WithOpts(qopts...) - var msgs adapters.Msgs - if err := q.Select(&msgs, `SELECT * FROM cosmos_msgs WHERE state = $1 AND cosmos_chain_id = $2 ORDER BY id ASC LIMIT $3`, state, o.chainID, limit); err != nil { - return nil, err - } - return msgs, nil -} - -// GetMsgs returns any messages matching ids. -func (o *ORM) GetMsgs(ids ...int64) (adapters.Msgs, error) { - var msgs adapters.Msgs - if err := o.q.Select(&msgs, `SELECT * FROM cosmos_msgs WHERE id = ANY($1)`, ids); err != nil { - return nil, err - } - return msgs, nil -} - -// UpdateMsgs updates msgs with the given ids. -// Note state transitions are validated at the db level. -func (o *ORM) UpdateMsgs(ids []int64, state db.State, txHash *string, qopts ...pg.QOpt) error { - if state == db.Broadcasted && txHash == nil { - return errors.New("txHash is required when updating to broadcasted") - } - q := o.q.WithOpts(qopts...) - var res sql.Result - var err error - if state == db.Broadcasted { - res, err = q.Exec(`UPDATE cosmos_msgs SET state = $1, updated_at = NOW(), tx_hash = $2 WHERE id = ANY($3)`, state, *txHash, ids) - } else { - res, err = q.Exec(`UPDATE cosmos_msgs SET state = $1, updated_at = NOW() WHERE id = ANY($2)`, state, ids) - } - if err != nil { - return err - } - count, err := res.RowsAffected() - if err != nil { - return err - } - if int(count) != len(ids) { - return errors.Errorf("expected %d records updated, got %d", len(ids), count) - } - return nil -} diff --git a/core/chains/cosmos/cosmostxm/orm_test.go b/core/chains/cosmos/cosmostxm/orm_test.go deleted file mode 100644 index 3cee25bac12..00000000000 --- a/core/chains/cosmos/cosmostxm/orm_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package cosmostxm - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - cosmosdb "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" -) - -func TestORM(t *testing.T) { - db := pgtest.NewSqlxDB(t) - lggr := logger.TestLogger(t) - logCfg := pgtest.NewQConfig(true) - chainID := cosmostest.RandomChainID() - o := NewORM(chainID, db, lggr, logCfg) - - // Create - mid, err := o.InsertMsg("0x123", "", []byte("hello")) - require.NoError(t, err) - assert.NotEqual(t, 0, int(mid)) - - // Read - unstarted, err := o.GetMsgsState(cosmosdb.Unstarted, 5) - require.NoError(t, err) - require.Equal(t, 1, len(unstarted)) - assert.Equal(t, "hello", string(unstarted[0].Raw)) - assert.Equal(t, chainID, unstarted[0].ChainID) - t.Log(unstarted[0].UpdatedAt, unstarted[0].CreatedAt) - - // Limit - unstarted, err = o.GetMsgsState(cosmosdb.Unstarted, 0) - assert.Error(t, err) - assert.Empty(t, unstarted) - unstarted, err = o.GetMsgsState(cosmosdb.Unstarted, -1) - assert.Error(t, err) - assert.Empty(t, unstarted) - mid2, err := o.InsertMsg("0xabc", "", []byte("test")) - require.NoError(t, err) - assert.NotEqual(t, 0, int(mid2)) - unstarted, err = o.GetMsgsState(cosmosdb.Unstarted, 1) - require.NoError(t, err) - require.Equal(t, 1, len(unstarted)) - assert.Equal(t, "hello", string(unstarted[0].Raw)) - assert.Equal(t, chainID, unstarted[0].ChainID) - unstarted, err = o.GetMsgsState(cosmosdb.Unstarted, 2) - require.NoError(t, err) - require.Equal(t, 2, len(unstarted)) - assert.Equal(t, "test", string(unstarted[1].Raw)) - assert.Equal(t, chainID, unstarted[1].ChainID) - - // Update - txHash := "123" - err = o.UpdateMsgs([]int64{mid}, cosmosdb.Started, &txHash) - require.NoError(t, err) - err = o.UpdateMsgs([]int64{mid}, cosmosdb.Broadcasted, &txHash) - require.NoError(t, err) - broadcasted, err := o.GetMsgsState(cosmosdb.Broadcasted, 5) - require.NoError(t, err) - require.Equal(t, 1, len(broadcasted)) - assert.Equal(t, broadcasted[0].Raw, unstarted[0].Raw) - require.NotNil(t, broadcasted[0].TxHash) - assert.Equal(t, *broadcasted[0].TxHash, txHash) - assert.Equal(t, chainID, broadcasted[0].ChainID) - - err = o.UpdateMsgs([]int64{mid}, cosmosdb.Confirmed, nil) - require.NoError(t, err) - confirmed, err := o.GetMsgsState(cosmosdb.Confirmed, 5) - require.NoError(t, err) - require.Equal(t, 1, len(confirmed)) -} diff --git a/core/chains/cosmos/cosmostxm/txm.go b/core/chains/cosmos/cosmostxm/txm.go deleted file mode 100644 index 712e1b8fc73..00000000000 --- a/core/chains/cosmos/cosmostxm/txm.go +++ /dev/null @@ -1,542 +0,0 @@ -package cosmostxm - -import ( - "cmp" - "context" - "encoding/hex" - "fmt" - "slices" - "strings" - "time" - - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" - - "github.com/smartcontractkit/sqlx" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - "github.com/cometbft/cometbft/crypto/tmhash" - sdk "github.com/cosmos/cosmos-sdk/types" - txtypes "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/cosmos/cosmos-sdk/x/bank/types" - - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" - "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" - - "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/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -var ( - _ services.Service = (*Txm)(nil) - _ adapters.TxManager = (*Txm)(nil) -) - -// Txm manages transactions for the cosmos blockchain. -type Txm struct { - services.StateMachine - eb pg.EventBroadcaster - sub pg.Subscription - orm *ORM - lggr logger.Logger - tc func() (cosmosclient.ReaderWriter, error) - keystoreAdapter *keystoreAdapter - stop, done chan struct{} - cfg coscfg.Config - gpe cosmosclient.ComposedGasPriceEstimator -} - -// NewTxm creates a txm. Uses simulation so should only be used to send txes to trusted contracts i.e. OCR. -func NewTxm(db *sqlx.DB, tc func() (cosmosclient.ReaderWriter, error), gpe cosmosclient.ComposedGasPriceEstimator, chainID string, cfg coscfg.Config, ks loop.Keystore, lggr logger.Logger, logCfg pg.QConfig, eb pg.EventBroadcaster) *Txm { - lggr = logger.Named(lggr, "Txm") - keystoreAdapter := newKeystoreAdapter(ks, cfg.Bech32Prefix()) - return &Txm{ - eb: eb, - orm: NewORM(chainID, db, lggr, logCfg), - lggr: lggr, - tc: tc, - keystoreAdapter: keystoreAdapter, - stop: make(chan struct{}), - done: make(chan struct{}), - cfg: cfg, - gpe: gpe, - } -} - -// Start subscribes to pg notifications about cosmos msg inserts and processes them. -func (txm *Txm) Start(context.Context) error { - return txm.StartOnce("Txm", func() error { - sub, err := txm.eb.Subscribe(pg.ChannelInsertOnCosmosMsg, "") - if err != nil { - return err - } - txm.sub = sub - go txm.run() - return nil - }) -} - -func (txm *Txm) confirmAnyUnconfirmed(ctx context.Context) { - // Confirm any broadcasted but not confirmed txes. - // This is an edge case if we crash after having broadcasted but before we confirm. - for { - broadcasted, err := txm.orm.GetMsgsState(db.Broadcasted, txm.cfg.MaxMsgsPerBatch()) - if err != nil { - // Should never happen but if so, theoretically can retry with a reboot - logger.Criticalw(txm.lggr, "unable to look for broadcasted but unconfirmed txes", "err", err) - return - } - if len(broadcasted) == 0 { - return - } - tc, err := txm.tc() - if err != nil { - logger.Criticalw(txm.lggr, "unable to get client for handling broadcasted but unconfirmed txes", "count", len(broadcasted), "err", err) - return - } - msgsByTxHash := make(map[string]adapters.Msgs) - for _, msg := range broadcasted { - msgsByTxHash[*msg.TxHash] = append(msgsByTxHash[*msg.TxHash], msg) - } - for txHash, msgs := range msgsByTxHash { - maxPolls, pollPeriod := txm.confirmPollConfig() - err := txm.confirmTx(ctx, tc, txHash, msgs.GetIDs(), maxPolls, pollPeriod) - if err != nil { - txm.lggr.Errorw("unable to confirm broadcasted but unconfirmed txes", "err", err, "txhash", txHash) - if ctx.Err() != nil { - return - } - } - } - } -} - -func (txm *Txm) run() { - defer close(txm.done) - ctx, cancel := utils.StopChan(txm.stop).NewCtx() - defer cancel() - txm.confirmAnyUnconfirmed(ctx) - // Jitter in case we have multiple cosmos chains each with their own client. - tick := time.After(utils.WithJitter(txm.cfg.BlockRate())) - for { - select { - case <-txm.sub.Events(): - txm.sendMsgBatch(ctx) - case <-tick: - txm.sendMsgBatch(ctx) - tick = time.After(utils.WithJitter(txm.cfg.BlockRate())) - case <-txm.stop: - return - } - } -} - -var ( - typeMsgSend = sdk.MsgTypeURL(&types.MsgSend{}) - typeMsgExecuteContract = sdk.MsgTypeURL(&wasmtypes.MsgExecuteContract{}) -) - -func unmarshalMsg(msgType string, raw []byte) (sdk.Msg, string, error) { - switch msgType { - case typeMsgSend: - var ms types.MsgSend - err := ms.Unmarshal(raw) - if err != nil { - return nil, "", err - } - return &ms, ms.FromAddress, nil - case typeMsgExecuteContract: - var ms wasmtypes.MsgExecuteContract - err := ms.Unmarshal(raw) - if err != nil { - return nil, "", err - } - return &ms, ms.Sender, nil - } - return nil, "", errors.Errorf("unrecognized message type: %s", msgType) -} - -type msgValidator struct { - cutoff time.Time - expired, valid adapters.Msgs -} - -func (e *msgValidator) add(msg adapters.Msg) { - if msg.CreatedAt.Before(e.cutoff) { - e.expired = append(e.expired, msg) - } else { - e.valid = append(e.valid, msg) - } -} - -func (e *msgValidator) sortValid() { - slices.SortFunc(e.valid, func(a, b adapters.Msg) int { - ac, bc := a.CreatedAt, b.CreatedAt - if ac.Equal(bc) { - return cmp.Compare(a.ID, b.ID) - } - if ac.After(bc) { - return 1 - } - return -1 // ac.Before(bc) - }) -} - -func (txm *Txm) sendMsgBatch(ctx context.Context) { - msgs := msgValidator{cutoff: time.Now().Add(-txm.cfg.TxMsgTimeout())} - err := txm.orm.q.Transaction(func(tx pg.Queryer) error { - // There may be leftover Started messages after a crash or failed send attempt. - started, err := txm.orm.GetMsgsState(db.Started, txm.cfg.MaxMsgsPerBatch(), pg.WithQueryer(tx)) - if err != nil { - txm.lggr.Errorw("unable to read unstarted msgs", "err", err) - return err - } - if limit := txm.cfg.MaxMsgsPerBatch() - int64(len(started)); limit > 0 { - // Use the remaining batch budget for Unstarted - unstarted, err := txm.orm.GetMsgsState(db.Unstarted, limit, pg.WithQueryer(tx)) //nolint - if err != nil { - txm.lggr.Errorw("unable to read unstarted msgs", "err", err) - return err - } - for _, msg := range unstarted { - msgs.add(msg) - } - // Update valid, Unstarted messages to Started - err = txm.orm.UpdateMsgs(msgs.valid.GetIDs(), db.Started, nil, pg.WithQueryer(tx)) - if err != nil { - // Assume transient db error retry - txm.lggr.Errorw("unable to mark unstarted txes as started", "err", err) - return err - } - } - for _, msg := range started { - msgs.add(msg) - } - // Update expired messages (Unstarted or Started) to Errored - err = txm.orm.UpdateMsgs(msgs.expired.GetIDs(), db.Errored, nil, pg.WithQueryer(tx)) - if err != nil { - // Assume transient db error retry - txm.lggr.Errorw("unable to mark expired txes as errored", "err", err) - return err - } - return nil - }) - if err != nil { - return - } - if len(msgs.valid) == 0 { - return - } - msgs.sortValid() - txm.lggr.Debugw("building a batch", "not expired", msgs.valid, "marked expired", msgs.expired) - var msgsByFrom = make(map[string]adapters.Msgs) - for _, m := range msgs.valid { - msg, sender, err2 := unmarshalMsg(m.Type, m.Raw) - if err2 != nil { - // Should be impossible given the check in Enqueue - logger.Criticalw(txm.lggr, "Failed to unmarshal msg, skipping", "err", err2, "msg", m) - continue - } - m.DecodedMsg = msg - _, err2 = sdk.AccAddressFromBech32(sender) - if err2 != nil { - // Should never happen, we parse sender on Enqueue - logger.Criticalw(txm.lggr, "Unable to parse sender", "err", err2, "sender", sender) - continue - } - msgsByFrom[sender] = append(msgsByFrom[sender], m) - } - - txm.lggr.Debugw("msgsByFrom", "msgsByFrom", msgsByFrom) - gasPrice, err := txm.GasPrice() - if err != nil { - // Should be impossible - logger.Criticalw(txm.lggr, "Failed to get gas price", "err", err) - return - } - for s, msgs := range msgsByFrom { - sender, _ := sdk.AccAddressFromBech32(s) // Already checked validity above - err := txm.sendMsgBatchFromAddress(ctx, gasPrice, sender, msgs) - if err != nil { - txm.lggr.Errorw("Could not send message batch", "err", err, "from", sender.String()) - continue - } - if ctx.Err() != nil { - return - } - } - -} - -func (txm *Txm) sendMsgBatchFromAddress(ctx context.Context, gasPrice sdk.DecCoin, sender sdk.AccAddress, msgs adapters.Msgs) error { - tc, err := txm.tc() - if err != nil { - logger.Criticalw(txm.lggr, "unable to get client", "err", err) - return err - } - an, sn, err := tc.Account(sender) - if err != nil { - txm.lggr.Warnw("unable to read account", "err", err, "from", sender.String()) - // If we can't read the account, assume transient api issues and leave msgs unstarted - // to retry on next poll. - return err - } - - txm.lggr.Debugw("simulating batch", "from", sender, "msgs", msgs, "seqnum", sn) - simResults, err := tc.BatchSimulateUnsigned(msgs.GetSimMsgs(), sn) - if err != nil { - txm.lggr.Warnw("unable to simulate", "err", err, "from", sender.String()) - // If we can't simulate assume transient api issue and retry on next poll. - // Note one rare scenario in which this can happen: the cosmos node misbehaves - // in that it confirms a txhash is present but still gives an old seq num. - // This is benign as the next retry will succeeds. - return err - } - txm.lggr.Debugw("simulation results", "from", sender, "succeeded", simResults.Succeeded, "failed", simResults.Failed) - err = txm.orm.UpdateMsgs(simResults.Failed.GetSimMsgsIDs(), db.Errored, nil) - if err != nil { - txm.lggr.Errorw("unable to mark failed sim txes as errored", "err", err, "from", sender.String()) - // If we can't mark them as failed retry on next poll. Presumably same ones will fail. - return err - } - - // Continue if there are no successful txes - if len(simResults.Succeeded) == 0 { - txm.lggr.Warnw("all sim msgs errored, not sending tx", "from", sender.String()) - return errors.New("all sim msgs errored") - } - // Get the gas limit for the successful batch - s, err := tc.SimulateUnsigned(simResults.Succeeded.GetMsgs(), sn) - if err != nil { - // In the OCR context this should only happen upon stale report - txm.lggr.Warnw("unexpected failure after successful simulation", "err", err) - return err - } - gasLimit := s.GasInfo.GasUsed - - lb, err := tc.LatestBlock() - if err != nil { - txm.lggr.Warnw("unable to get latest block", "err", err, "from", sender.String()) - // Assume transient api issue and retry. - return err - } - header, timeout := lb.SdkBlock.Header.Height, txm.cfg.BlocksUntilTxTimeout() - if header < 0 { - return fmt.Errorf("invalid negative header height: %d", header) - } else if timeout < 0 { - return fmt.Errorf("invalid negative blocks until tx timeout: %d", timeout) - } - timeoutHeight := uint64(header) + uint64(timeout) - signedTx, err := tc.CreateAndSign(simResults.Succeeded.GetMsgs(), an, sn, gasLimit, txm.cfg.GasLimitMultiplier(), - gasPrice, NewKeyWrapper(txm.keystoreAdapter, sender.String()), timeoutHeight) - if err != nil { - txm.lggr.Errorw("unable to sign tx", "err", err, "from", sender.String()) - return err - } - - // We need to ensure that we either broadcast successfully and mark the tx as - // broadcasted OR we do not broadcast successfully and we do not mark it as broadcasted. - // We do this by first marking it broadcasted then rolling back if the broadcast api call fails. - // There is still a small chance of network failure or node/db crash after broadcasting but before committing the tx, - // in which case the msgs would be picked up again and re-broadcast, ensuring at-least once delivery. - var resp *txtypes.BroadcastTxResponse - err = txm.orm.q.Transaction(func(tx pg.Queryer) error { - txHash := strings.ToUpper(hex.EncodeToString(tmhash.Sum(signedTx))) - err = txm.orm.UpdateMsgs(simResults.Succeeded.GetSimMsgsIDs(), db.Broadcasted, &txHash, pg.WithQueryer(tx)) - if err != nil { - return err - } - - txm.lggr.Infow("broadcasting tx", "from", sender, "msgs", simResults.Succeeded, "gasLimit", gasLimit, "gasPrice", gasPrice.String(), "timeoutHeight", timeoutHeight, "hash", txHash) - resp, err = tc.Broadcast(signedTx, txtypes.BroadcastMode_BROADCAST_MODE_SYNC) - if err != nil { - // Rollback marking as broadcasted - // Note can happen if the node's mempool is full, where we expect errCode 20. - return err - } - if resp.TxResponse == nil { - // Rollback marking as broadcasted - return errors.New("unexpected nil tx response") - } - if resp.TxResponse.TxHash != txHash { - // Should never happen - logger.Criticalw(txm.lggr, "txhash mismatch", "got", resp.TxResponse.TxHash, "want", txHash) - } - return nil - }) - if err != nil { - txm.lggr.Errorw("error broadcasting tx", "err", err, "from", sender.String()) - // Was unable to broadcast, retry on next poll - return err - } - - maxPolls, pollPeriod := txm.confirmPollConfig() - if err := txm.confirmTx(ctx, tc, resp.TxResponse.TxHash, simResults.Succeeded.GetSimMsgsIDs(), maxPolls, pollPeriod); err != nil { - txm.lggr.Errorw("error confirming tx", "err", err, "hash", resp.TxResponse.TxHash) - return err - } - - return nil -} - -func (txm *Txm) confirmPollConfig() (maxPolls int, pollPeriod time.Duration) { - blocks := txm.cfg.BlocksUntilTxTimeout() - blockPeriod := txm.cfg.BlockRate() - pollPeriod = txm.cfg.ConfirmPollPeriod() - if pollPeriod == 0 { - // don't divide by zero - maxPolls = 1 - } else { - maxPolls = int((time.Duration(blocks) * blockPeriod) / pollPeriod) - } - return -} - -func (txm *Txm) confirmTx(ctx context.Context, tc cosmosclient.Reader, txHash string, broadcasted []int64, maxPolls int, pollPeriod time.Duration) error { - // We either mark these broadcasted txes as confirmed or errored. - // Confirmed: we see the txhash onchain. There are no reorgs in cosmos chains. - // Errored: we do not see the txhash onchain after waiting for N blocks worth - // of time (plus a small buffer to account for block time variance) where N - // is TimeoutHeight - HeightAtBroadcast. In other words, if we wait for that long - // and the tx is not confirmed, we know it has timed out. - for tries := 0; tries < maxPolls; tries++ { - // Jitter in-case we're confirming multiple txes in parallel for different keys - select { - case <-ctx.Done(): - return ctx.Err() - case <-time.After(utils.WithJitter(pollPeriod)): - } - // Confirm that this tx is onchain, ensuring the sequence number has incremented - // so we can build a new batch - tx, err := tc.Tx(txHash) - if err != nil { - if strings.Contains(err.Error(), "not found") { - txm.lggr.Infow("txhash not found yet, still confirming", "hash", txHash) - } else { - txm.lggr.Errorw("error looking for hash of tx", "err", err, "hash", txHash) - } - continue - } - // Sanity check - if tx.TxResponse == nil || tx.TxResponse.TxHash != txHash { - txm.lggr.Errorw("error looking for hash of tx, unexpected response", "tx", tx, "hash", txHash) - continue - } - - txm.lggr.Infow("successfully sent batch", "hash", txHash, "msgs", broadcasted) - // If confirmed mark these as completed. - err = txm.orm.UpdateMsgs(broadcasted, db.Confirmed, nil) - if err != nil { - return err - } - return nil - } - txm.lggr.Errorw("unable to confirm tx after timeout period, marking errored", "hash", txHash) - // If we are unable to confirm the tx after the timeout period - // mark these msgs as errored - err := txm.orm.UpdateMsgs(broadcasted, db.Errored, nil) - if err != nil { - txm.lggr.Errorw("unable to mark timed out txes as errored", "err", err, "txes", broadcasted, "num", len(broadcasted)) - return err - } - return nil -} - -// Enqueue enqueue a msg destined for the cosmos chain. -func (txm *Txm) Enqueue(contractID string, msg sdk.Msg) (int64, error) { - typeURL, raw, err := txm.marshalMsg(msg) - if err != nil { - return 0, err - } - - // We could consider simulating here too, but that would - // introduce another network call and essentially double - // the enqueue time. Enqueue is used in the context of OCRs Transmit - // and must be fast, so we do the minimum. - - var id int64 - err = txm.orm.q.Transaction(func(tx pg.Queryer) (err error) { - // cancel any unstarted msgs (normally just one) - err = txm.orm.UpdateMsgsContract(contractID, db.Unstarted, db.Errored, pg.WithQueryer(tx)) - if err != nil { - return err - } - id, err = txm.orm.InsertMsg(contractID, typeURL, raw, pg.WithQueryer(tx)) - return err - }) - return id, err -} - -func (txm *Txm) marshalMsg(msg sdk.Msg) (string, []byte, error) { - switch ms := msg.(type) { - case *wasmtypes.MsgExecuteContract: - _, err := sdk.AccAddressFromBech32(ms.Sender) - if err != nil { - txm.lggr.Errorw("failed to parse sender, skipping", "err", err, "sender", ms.Sender) - return "", nil, err - } - - case *types.MsgSend: - _, err := sdk.AccAddressFromBech32(ms.FromAddress) - if err != nil { - txm.lggr.Errorw("failed to parse sender, skipping", "err", err, "sender", ms.FromAddress) - return "", nil, err - } - - default: - return "", nil, &cosmos.ErrMsgUnsupported{Msg: msg} - } - typeURL := sdk.MsgTypeURL(msg) - raw, err := proto.Marshal(msg) - if err != nil { - txm.lggr.Errorw("failed to marshal msg, skipping", "err", err, "msg", msg) - return "", nil, err - } - return typeURL, raw, nil -} - -// GetMsgs returns any messages matching ids. -func (txm *Txm) GetMsgs(ids ...int64) (adapters.Msgs, error) { - return txm.orm.GetMsgs(ids...) -} - -// GasPrice returns the gas price from the estimator in the configured fee token. -func (txm *Txm) GasPrice() (sdk.DecCoin, error) { - prices := txm.gpe.GasPrices() - gasPrice, ok := prices[txm.cfg.GasToken()] - if !ok { - return sdk.DecCoin{}, errors.New("unexpected empty gas price") - } - return gasPrice, nil -} - -// Close close service -func (txm *Txm) Close() error { - return txm.StopOnce("Txm", func() error { - txm.sub.Close() - close(txm.stop) - <-txm.done - return nil - }) -} - -func (txm *Txm) Name() string { return txm.lggr.Name() } - -// Healthy service is healthy -func (txm *Txm) Healthy() error { - return nil -} - -// Ready service is ready -func (txm *Txm) Ready() error { - return nil -} - -func (txm *Txm) HealthReport() map[string]error { return map[string]error{txm.Name(): txm.Healthy()} } diff --git a/core/chains/cosmos/cosmostxm/txm_internal_test.go b/core/chains/cosmos/cosmostxm/txm_internal_test.go deleted file mode 100644 index f29f130cae4..00000000000 --- a/core/chains/cosmos/cosmostxm/txm_internal_test.go +++ /dev/null @@ -1,426 +0,0 @@ -package cosmostxm - -import ( - "fmt" - "testing" - "time" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - tmservicetypes "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" - cosmostypes "github.com/cosmos/cosmos-sdk/types" - txtypes "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - - cosmosclient "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" - tcmocks "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client/mocks" - coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - cosmosdb "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - relayutils "github.com/smartcontractkit/chainlink-relay/pkg/utils" - - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -func generateExecuteMsg(msg []byte, from, to cosmostypes.AccAddress) cosmostypes.Msg { - return &wasmtypes.MsgExecuteContract{ - Sender: from.String(), - Contract: to.String(), - Msg: msg, - Funds: cosmostypes.Coins{}, - } -} - -func newReaderWriterMock(t *testing.T) *tcmocks.ReaderWriter { - tc := new(tcmocks.ReaderWriter) - tc.Test(t) - t.Cleanup(func() { tc.AssertExpectations(t) }) - return tc -} - -func TestTxm(t *testing.T) { - db := pgtest.NewSqlxDB(t) - lggr := testutils.LoggerAssertMaxLevel(t, zapcore.ErrorLevel) - ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, pgtest.NewQConfig(true)) - require.NoError(t, ks.Unlock("blah")) - - for i := 0; i < 4; i++ { - _, err := ks.Cosmos().Create() - require.NoError(t, err) - } - - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - adapter := newKeystoreAdapter(loopKs, "wasm") - accounts, err := adapter.Accounts() - require.NoError(t, err) - require.Equal(t, len(accounts), 4) - - sender1, err := cosmostypes.AccAddressFromBech32(accounts[0]) - require.NoError(t, err) - sender2, err := cosmostypes.AccAddressFromBech32(accounts[1]) - require.NoError(t, err) - contract, err := cosmostypes.AccAddressFromBech32(accounts[2]) - require.NoError(t, err) - contract2, err := cosmostypes.AccAddressFromBech32(accounts[3]) - require.NoError(t, err) - - logCfg := pgtest.NewQConfig(true) - chainID := cosmostest.RandomChainID() - two := int64(2) - gasToken := "ucosm" - cfg := &coscfg.TOMLConfig{Chain: coscfg.Chain{ - MaxMsgsPerBatch: &two, - GasToken: &gasToken, - }} - cfg.SetDefaults() - gpe := cosmosclient.NewMustGasPriceEstimator([]cosmosclient.GasPricesEstimator{ - cosmosclient.NewFixedGasPriceEstimator(map[string]cosmostypes.DecCoin{ - cfg.GasToken(): cosmostypes.NewDecCoinFromDec(cfg.GasToken(), cosmostypes.MustNewDecFromStr("0.01")), - }, - lggr.(logger.SugaredLogger), - ), - }, lggr) - - t.Run("single msg", func(t *testing.T) { - tc := newReaderWriterMock(t) - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, logCfg, nil) - - // Enqueue a single msg, then send it in a batch - id1, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`1`), sender1, contract)) - require.NoError(t, err) - tc.On("Account", mock.Anything).Return(uint64(0), uint64(0), nil) - tc.On("BatchSimulateUnsigned", mock.Anything, mock.Anything).Return(&cosmosclient.BatchSimResults{ - Failed: nil, - Succeeded: cosmosclient.SimMsgs{{ID: id1, Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender1.String(), - Msg: []byte(`1`), - }}}, - }, nil) - tc.On("SimulateUnsigned", mock.Anything, mock.Anything).Return(&txtypes.SimulateResponse{GasInfo: &cosmostypes.GasInfo{ - GasUsed: 1_000_000, - }}, nil) - tc.On("LatestBlock").Return(&tmservicetypes.GetLatestBlockResponse{SdkBlock: &tmservicetypes.Block{ - Header: tmservicetypes.Header{Height: 1}, - }}, nil) - tc.On("CreateAndSign", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{0x01}, nil) - - txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} - tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil) - tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil) - txm.sendMsgBatch(testutils.Context(t)) - - // Should be in completed state - completed, err := txm.orm.GetMsgs(id1) - require.NoError(t, err) - require.Equal(t, 1, len(completed)) - assert.Equal(t, completed[0].State, cosmosdb.Confirmed) - }) - - t.Run("two msgs different accounts", func(t *testing.T) { - tc := newReaderWriterMock(t) - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - - id1, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`0`), sender1, contract)) - require.NoError(t, err) - id2, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`1`), sender2, contract)) - require.NoError(t, err) - - tc.On("Account", mock.Anything).Return(uint64(0), uint64(0), nil).Once() - // Note this must be arg dependent, we don't know which order - // the procesing will happen in (map iteration by from address). - tc.On("BatchSimulateUnsigned", cosmosclient.SimMsgs{ - { - ID: id2, - Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender2.String(), - Msg: []byte(`1`), - Contract: contract.String(), - }, - }, - }, mock.Anything).Return(&cosmosclient.BatchSimResults{ - Failed: nil, - Succeeded: cosmosclient.SimMsgs{ - { - ID: id2, - Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender2.String(), - Msg: []byte(`1`), - Contract: contract.String(), - }, - }, - }, - }, nil).Once() - tc.On("SimulateUnsigned", mock.Anything, mock.Anything).Return(&txtypes.SimulateResponse{GasInfo: &cosmostypes.GasInfo{ - GasUsed: 1_000_000, - }}, nil).Once() - tc.On("LatestBlock").Return(&tmservicetypes.GetLatestBlockResponse{SdkBlock: &tmservicetypes.Block{ - Header: tmservicetypes.Header{Height: 1}, - }}, nil).Once() - tc.On("CreateAndSign", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{0x01}, nil).Once() - txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} - tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil).Once() - tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil).Once() - txm.sendMsgBatch(testutils.Context(t)) - - // Should be in completed state - completed, err := txm.orm.GetMsgs(id1, id2) - require.NoError(t, err) - require.Equal(t, 2, len(completed)) - assert.Equal(t, cosmosdb.Errored, completed[0].State) // cancelled - assert.Equal(t, cosmosdb.Confirmed, completed[1].State) - }) - - t.Run("two msgs different contracts", func(t *testing.T) { - tc := newReaderWriterMock(t) - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - - id1, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`0`), sender1, contract)) - require.NoError(t, err) - id2, err := txm.Enqueue(contract2.String(), generateExecuteMsg([]byte(`1`), sender2, contract2)) - require.NoError(t, err) - ids := []int64{id1, id2} - senders := []string{sender1.String(), sender2.String()} - contracts := []string{contract.String(), contract2.String()} - for i := 0; i < 2; i++ { - tc.On("Account", mock.Anything).Return(uint64(0), uint64(0), nil).Once() - // Note this must be arg dependent, we don't know which order - // the procesing will happen in (map iteration by from address). - tc.On("BatchSimulateUnsigned", cosmosclient.SimMsgs{ - { - ID: ids[i], - Msg: &wasmtypes.MsgExecuteContract{ - Sender: senders[i], - Msg: []byte(fmt.Sprintf(`%d`, i)), - Contract: contracts[i], - }, - }, - }, mock.Anything).Return(&cosmosclient.BatchSimResults{ - Failed: nil, - Succeeded: cosmosclient.SimMsgs{ - { - ID: ids[i], - Msg: &wasmtypes.MsgExecuteContract{ - Sender: senders[i], - Msg: []byte(fmt.Sprintf(`%d`, i)), - Contract: contracts[i], - }, - }, - }, - }, nil).Once() - tc.On("SimulateUnsigned", mock.Anything, mock.Anything).Return(&txtypes.SimulateResponse{GasInfo: &cosmostypes.GasInfo{ - GasUsed: 1_000_000, - }}, nil).Once() - tc.On("LatestBlock").Return(&tmservicetypes.GetLatestBlockResponse{SdkBlock: &tmservicetypes.Block{ - Header: tmservicetypes.Header{Height: 1}, - }}, nil).Once() - tc.On("CreateAndSign", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{0x01}, nil).Once() - } - txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} - tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil).Twice() - tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil).Twice() - txm.sendMsgBatch(testutils.Context(t)) - - // Should be in completed state - completed, err := txm.orm.GetMsgs(id1, id2) - require.NoError(t, err) - require.Equal(t, 2, len(completed)) - assert.Equal(t, cosmosdb.Confirmed, completed[0].State) - assert.Equal(t, cosmosdb.Confirmed, completed[1].State) - }) - - t.Run("failed to confirm", func(t *testing.T) { - tc := newReaderWriterMock(t) - tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{ - Tx: &txtypes.Tx{}, - TxResponse: &cosmostypes.TxResponse{TxHash: "0x123"}, - }, errors.New("not found")).Twice() - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - i, err := txm.orm.InsertMsg("blah", "", []byte{0x01}) - require.NoError(t, err) - txh := "0x123" - require.NoError(t, txm.orm.UpdateMsgs([]int64{i}, cosmosdb.Started, &txh)) - require.NoError(t, txm.orm.UpdateMsgs([]int64{i}, cosmosdb.Broadcasted, &txh)) - err = txm.confirmTx(testutils.Context(t), tc, txh, []int64{i}, 2, 1*time.Millisecond) - require.NoError(t, err) - m, err := txm.orm.GetMsgs(i) - require.NoError(t, err) - require.Equal(t, 1, len(m)) - assert.Equal(t, cosmosdb.Errored, m[0].State) - }) - - t.Run("confirm any unconfirmed", func(t *testing.T) { - require.Equal(t, int64(2), cfg.MaxMsgsPerBatch()) - txHash1 := "0x1234" - txHash2 := "0x1235" - txHash3 := "0xabcd" - tc := newReaderWriterMock(t) - tc.On("Tx", txHash1).Return(&txtypes.GetTxResponse{ - TxResponse: &cosmostypes.TxResponse{TxHash: txHash1}, - }, nil).Once() - tc.On("Tx", txHash2).Return(&txtypes.GetTxResponse{ - TxResponse: &cosmostypes.TxResponse{TxHash: txHash2}, - }, nil).Once() - tc.On("Tx", txHash3).Return(&txtypes.GetTxResponse{ - TxResponse: &cosmostypes.TxResponse{TxHash: txHash3}, - }, nil).Once() - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - - // Insert and broadcast 3 msgs with different txhashes. - id1, err := txm.orm.InsertMsg("blah", "", []byte{0x01}) - require.NoError(t, err) - id2, err := txm.orm.InsertMsg("blah", "", []byte{0x02}) - require.NoError(t, err) - id3, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id1}, cosmosdb.Started, &txHash1) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id2}, cosmosdb.Started, &txHash2) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id3}, cosmosdb.Started, &txHash3) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id1}, cosmosdb.Broadcasted, &txHash1) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id2}, cosmosdb.Broadcasted, &txHash2) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id3}, cosmosdb.Broadcasted, &txHash3) - require.NoError(t, err) - - // Confirm them as in a restart while confirming scenario - txm.confirmAnyUnconfirmed(testutils.Context(t)) - msgs, err := txm.orm.GetMsgs(id1, id2, id3) - require.NoError(t, err) - require.Equal(t, 3, len(msgs)) - assert.Equal(t, cosmosdb.Confirmed, msgs[0].State) - assert.Equal(t, cosmosdb.Confirmed, msgs[1].State) - assert.Equal(t, cosmosdb.Confirmed, msgs[2].State) - }) - - t.Run("expired msgs", func(t *testing.T) { - tc := new(tcmocks.ReaderWriter) - timeout, err := relayutils.NewDuration(1 * time.Millisecond) - require.NoError(t, err) - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - two := int64(2) - cfgShortExpiry := &coscfg.TOMLConfig{Chain: coscfg.Chain{ - MaxMsgsPerBatch: &two, - TxMsgTimeout: &timeout, - }} - cfgShortExpiry.SetDefaults() - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfgShortExpiry, loopKs, lggr, pgtest.NewQConfig(true), nil) - - // Send a single one expired - id1, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) - require.NoError(t, err) - time.Sleep(1 * time.Millisecond) - txm.sendMsgBatch(testutils.Context(t)) - // Should be marked errored - m, err := txm.orm.GetMsgs(id1) - require.NoError(t, err) - assert.Equal(t, cosmosdb.Errored, m[0].State) - - // Send a batch which is all expired - id2, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) - require.NoError(t, err) - id3, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) - require.NoError(t, err) - time.Sleep(1 * time.Millisecond) - txm.sendMsgBatch(testutils.Context(t)) - require.NoError(t, err) - ms, err := txm.orm.GetMsgs(id2, id3) - require.NoError(t, err) - assert.Equal(t, cosmosdb.Errored, ms[0].State) - assert.Equal(t, cosmosdb.Errored, ms[1].State) - }) - - t.Run("started msgs", func(t *testing.T) { - tc := new(tcmocks.ReaderWriter) - tc.On("Account", mock.Anything).Return(uint64(0), uint64(0), nil) - tc.On("SimulateUnsigned", mock.Anything, mock.Anything).Return(&txtypes.SimulateResponse{GasInfo: &cosmostypes.GasInfo{ - GasUsed: 1_000_000, - }}, nil) - tc.On("LatestBlock").Return(&tmservicetypes.GetLatestBlockResponse{SdkBlock: &tmservicetypes.Block{ - Header: tmservicetypes.Header{Height: 1}, - }}, nil) - tc.On("CreateAndSign", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{0x01}, nil) - txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} - tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil) - tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil) - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - two := int64(2) - cfgMaxMsgs := &coscfg.TOMLConfig{Chain: coscfg.Chain{ - MaxMsgsPerBatch: &two, - }} - cfgMaxMsgs.SetDefaults() - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfgMaxMsgs, loopKs, lggr, pgtest.NewQConfig(true), nil) - - // Leftover started is processed - msg1 := generateExecuteMsg([]byte{0x03}, sender1, contract) - id1 := mustInsertMsg(t, txm, contract.String(), msg1) - require.NoError(t, txm.orm.UpdateMsgs([]int64{id1}, cosmosdb.Started, nil)) - msgs := cosmosclient.SimMsgs{{ID: id1, Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender1.String(), - Msg: []byte{0x03}, - Contract: contract.String(), - }}} - tc.On("BatchSimulateUnsigned", msgs, mock.Anything). - Return(&cosmosclient.BatchSimResults{Failed: nil, Succeeded: msgs}, nil).Once() - time.Sleep(1 * time.Millisecond) - txm.sendMsgBatch(testutils.Context(t)) - m, err := txm.orm.GetMsgs(id1) - require.NoError(t, err) - assert.Equal(t, cosmosdb.Confirmed, m[0].State) - - // Leftover started is not cancelled - msg2 := generateExecuteMsg([]byte{0x04}, sender1, contract) - msg3 := generateExecuteMsg([]byte{0x05}, sender1, contract) - id2 := mustInsertMsg(t, txm, contract.String(), msg2) - require.NoError(t, txm.orm.UpdateMsgs([]int64{id2}, cosmosdb.Started, nil)) - time.Sleep(time.Millisecond) // ensure != CreatedAt - id3 := mustInsertMsg(t, txm, contract.String(), msg3) - msgs = cosmosclient.SimMsgs{{ID: id2, Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender1.String(), - Msg: []byte{0x04}, - Contract: contract.String(), - }}, {ID: id3, Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender1.String(), - Msg: []byte{0x05}, - Contract: contract.String(), - }}} - tc.On("BatchSimulateUnsigned", msgs, mock.Anything). - Return(&cosmosclient.BatchSimResults{Failed: nil, Succeeded: msgs}, nil).Once() - time.Sleep(1 * time.Millisecond) - txm.sendMsgBatch(testutils.Context(t)) - require.NoError(t, err) - ms, err := txm.orm.GetMsgs(id2, id3) - require.NoError(t, err) - assert.Equal(t, cosmosdb.Confirmed, ms[0].State) - assert.Equal(t, cosmosdb.Confirmed, ms[1].State) - }) -} - -func mustInsertMsg(t *testing.T, txm *Txm, contractID string, msg cosmostypes.Msg) int64 { - typeURL, raw, err := txm.marshalMsg(msg) - require.NoError(t, err) - id, err := txm.orm.InsertMsg(contractID, typeURL, raw) - require.NoError(t, err) - return id -} diff --git a/core/chains/cosmos/cosmostxm/txm_test.go b/core/chains/cosmos/cosmostxm/txm_test.go deleted file mode 100644 index 25ac9e8d9ec..00000000000 --- a/core/chains/cosmos/cosmostxm/txm_test.go +++ /dev/null @@ -1,121 +0,0 @@ -//go:build integration - -package cosmostxm_test - -// TestTxm_Integration is disabled in order to be moved to chainlink-cosmos before DB testing is available -//func TestTxm_Integration(t *testing.T) { -// chainID := cosmostest.RandomChainID() -// cosmosChain := coscfg.Chain{} -// cosmosChain.SetDefaults() -// fallbackGasPrice := sdk.NewDecCoinFromDec(*cosmosChain.GasToken, sdk.MustNewDecFromStr("0.01")) -// chainConfig := cosmos.CosmosConfig{ChainID: &chainID, Enabled: ptr(true), Chain: cosmosChain} -// cfg, db := heavyweight.FullTestDBNoFixturesV2(t, "cosmos_txm", func(c *chainlink.Config, s *chainlink.Secrets) { -// c.Cosmos = cosmos.CosmosConfigs{&chainConfig} -// }) -// lggr := logger.TestLogger(t) -// logCfg := pgtest.NewQConfig(true) -// gpe := cosmosclient.NewMustGasPriceEstimator([]cosmosclient.GasPricesEstimator{ -// cosmosclient.NewFixedGasPriceEstimator(map[string]sdk.DecCoin{ -// *cosmosChain.GasToken: fallbackGasPrice, -// }, -// lggr.(logger.SugaredLogger), -// ), -// }, lggr) -// orm := cosmostxm.NewORM(chainID, db, lggr, logCfg) -// eb := pg.NewEventBroadcaster(cfg.Database().URL(), 0, 0, lggr, uuid.New()) -// require.NoError(t, eb.Start(testutils.Context(t))) -// t.Cleanup(func() { require.NoError(t, eb.Close()) }) -// ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, pgtest.NewQConfig(true)) -// zeConfig := sdk.GetConfig() -// fmt.Println(zeConfig) -// accounts, testdir, tendermintURL := cosmosclient.SetupLocalCosmosNode(t, chainID, *cosmosChain.GasToken) -// tc, err := cosmosclient.NewClient(chainID, tendermintURL, 0, lggr) -// require.NoError(t, err) -// -// loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} -// keystoreAdapter := cosmostxm.NewKeystoreAdapter(loopKs, *cosmosChain.Bech32Prefix) -// -// // First create a transmitter key and fund it with 1k native tokens -// require.NoError(t, ks.Unlock("blah")) -// err = ks.Cosmos().EnsureKey() -// require.NoError(t, err) -// ksAccounts, err := keystoreAdapter.Accounts() -// require.NoError(t, err) -// transmitterAddress := ksAccounts[0] -// transmitterID, err := sdk.AccAddressFromBech32(transmitterAddress) -// require.NoError(t, err) -// an, sn, err := tc.Account(accounts[0].Address) -// require.NoError(t, err) -// resp, err := tc.SignAndBroadcast([]sdk.Msg{banktypes.NewMsgSend(accounts[0].Address, transmitterID, sdk.NewCoins(sdk.NewInt64Coin(*cosmosChain.GasToken, 100000)))}, -// an, sn, gpe.GasPrices()[*cosmosChain.GasToken], accounts[0].PrivateKey, txtypes.BroadcastMode_BROADCAST_MODE_SYNC) -// tx, success := cosmosclient.AwaitTxCommitted(t, tc, resp.TxResponse.TxHash) -// require.True(t, success) -// require.Equal(t, types.CodeTypeOK, tx.TxResponse.Code) -// require.NoError(t, err) -// -// // TODO: find a way to pull this test artifact from -// // the chainlink-cosmos repo instead of copying it to cores testdata -// contractID := cosmosclient.DeployTestContract(t, tendermintURL, chainID, *cosmosChain.GasToken, accounts[0], cosmosclient.Account{ -// Name: "transmitter", -// PrivateKey: cosmostxm.NewKeyWrapper(keystoreAdapter, transmitterAddress), -// Address: transmitterID, -// }, tc, testdir, "../../../testdata/cosmos/my_first_contract.wasm") -// -// tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } -// // Start txm -// txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, &chainConfig, loopKs, lggr, pgtest.NewQConfig(true), eb) -// require.NoError(t, txm.Start(testutils.Context(t))) -// -// // Change the contract state -// setMsg := &wasmtypes.MsgExecuteContract{ -// Sender: transmitterID.String(), -// Contract: contractID.String(), -// Msg: []byte(`{"reset":{"count":5}}`), -// Funds: sdk.Coins{}, -// } -// _, err = txm.Enqueue(contractID.String(), setMsg) -// require.NoError(t, err) -// -// // Observe the counter gets set eventually -// gomega.NewWithT(t).Eventually(func() bool { -// d, err := tc.ContractState(contractID, []byte(`{"get_count":{}}`)) -// require.NoError(t, err) -// t.Log("contract value", string(d)) -// return string(d) == `{"count":5}` -// }, 20*time.Second, time.Second).Should(gomega.BeTrue()) -// // Ensure messages are completed -// gomega.NewWithT(t).Eventually(func() bool { -// msgs, err := orm.GetMsgsState(Confirmed, 5) -// require.NoError(t, err) -// return 1 == len(msgs) -// }, 5*time.Second, time.Second).Should(gomega.BeTrue()) -// -// // Ensure invalid msgs are marked as errored -// invalidMsg := &wasmtypes.MsgExecuteContract{ -// Sender: transmitterID.String(), -// Contract: contractID.String(), -// Msg: []byte(`{"blah":{"blah":5}}`), -// Funds: sdk.Coins{}, -// } -// _, err = txm.Enqueue(contractID.String(), invalidMsg) -// require.NoError(t, err) -// _, err = txm.Enqueue(contractID.String(), invalidMsg) -// require.NoError(t, err) -// _, err = txm.Enqueue(contractID.String(), setMsg) -// require.NoError(t, err) -// -// // Ensure messages are completed -// gomega.NewWithT(t).Eventually(func() bool { -// succeeded, err := orm.GetMsgsState(Confirmed, 5) -// require.NoError(t, err) -// errored, err := orm.GetMsgsState(Errored, 5) -// require.NoError(t, err) -// t.Log("errored", len(errored), "succeeded", len(succeeded)) -// return 2 == len(succeeded) && 2 == len(errored) -// }, 20*time.Second, time.Second).Should(gomega.BeTrue()) -// -// // Observe the messages have been marked as completed -// require.NoError(t, txm.Close()) -//} -// -//func ptr[T any](t T) *T { return &t } diff --git a/core/chains/cosmos/relayer_adapter.go b/core/chains/cosmos/relayer_adapter.go deleted file mode 100644 index ace441c2bb5..00000000000 --- a/core/chains/cosmos/relayer_adapter.go +++ /dev/null @@ -1,50 +0,0 @@ -package cosmos - -import ( - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" - - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - - pkgcosmos "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" - "github.com/smartcontractkit/chainlink/v2/core/chains" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" -) - -// LegacyChainContainer is container interface for Cosmos chains -type LegacyChainContainer interface { - Get(id string) (adapters.Chain, error) - Len() int - List(ids ...string) ([]adapters.Chain, error) - Slice() []adapters.Chain -} - -type LegacyChains = chains.ChainsKV[adapters.Chain] - -var _ LegacyChainContainer = &LegacyChains{} - -func NewLegacyChains(m map[string]adapters.Chain) *LegacyChains { - return chains.NewChainsKV[adapters.Chain](m) -} - -type LoopRelayerChainer interface { - loop.Relayer - Chain() adapters.Chain -} - -type LoopRelayerChain struct { - loop.Relayer - chain adapters.Chain -} - -func NewLoopRelayerChain(r *pkgcosmos.Relayer, s adapters.Chain) *LoopRelayerChain { - ra := relay.NewServerAdapter(r, s) - return &LoopRelayerChain{ - Relayer: ra, - chain: s, - } -} -func (r *LoopRelayerChain) Chain() adapters.Chain { - return r.chain -} - -var _ LoopRelayerChainer = &LoopRelayerChain{} diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 1ef99992a66..308ebf8da8c 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -173,9 +173,8 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G if cfg.CosmosEnabled() { cosmosCfg := chainlink.CosmosFactoryConfig{ - Keystore: keyStore.Cosmos(), - TOMLConfigs: cfg.CosmosConfigs(), - EventBroadcaster: eventBroadcaster, + Keystore: keyStore.Cosmos(), + TOMLConfigs: cfg.CosmosConfigs(), } initOps = append(initOps, chainlink.InitCosmos(ctx, relayerFactory, cosmosCfg)) } diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index d70b06f5a98..89b8704f87b 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -302,8 +302,7 @@ func TestShell_RebroadcastTransactions_Txm(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) legacy := cltest.NewLegacyChainsWithMockChain(t, ethClient, config) - mockRelayerChainInteroperators := chainlinkmocks.NewRelayerChainInteroperators(t) - mockRelayerChainInteroperators.On("LegacyEVMChains").Return(legacy, nil) + mockRelayerChainInteroperators := &chainlinkmocks.FakeRelayerChainInteroperators{EVMChains: legacy} app.On("GetRelayers").Return(mockRelayerChainInteroperators).Maybe() ethClient.On("Dial", mock.Anything).Return(nil) @@ -385,8 +384,7 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { ethClient.On("Dial", mock.Anything).Return(nil) legacy := cltest.NewLegacyChainsWithMockChain(t, ethClient, config) - mockRelayerChainInteroperators := chainlinkmocks.NewRelayerChainInteroperators(t) - mockRelayerChainInteroperators.On("LegacyEVMChains").Return(legacy, nil) + mockRelayerChainInteroperators := &chainlinkmocks.FakeRelayerChainInteroperators{EVMChains: legacy} app.On("GetRelayers").Return(mockRelayerChainInteroperators).Maybe() client := cmd.Shell{ @@ -465,8 +463,7 @@ func TestShell_RebroadcastTransactions_AddressCheck(t *testing.T) { ethClient.On("Dial", mock.Anything).Return(nil) legacy := cltest.NewLegacyChainsWithMockChain(t, ethClient, config) - mockRelayerChainInteroperators := chainlinkmocks.NewRelayerChainInteroperators(t) - mockRelayerChainInteroperators.On("LegacyEVMChains").Return(legacy, nil) + mockRelayerChainInteroperators := &chainlinkmocks.FakeRelayerChainInteroperators{EVMChains: legacy} app.On("GetRelayers").Return(mockRelayerChainInteroperators).Maybe() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(clienttypes.Successful, nil) diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index d47e6243b82..4cb9808fe24 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -414,11 +414,10 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn if cfg.CosmosEnabled() { cosmosCfg := chainlink.CosmosFactoryConfig{ - Keystore: keyStore.Cosmos(), - TOMLConfigs: cfg.CosmosConfigs(), - EventBroadcaster: eventBroadcaster, - DB: db, - QConfig: cfg.Database(), + Keystore: keyStore.Cosmos(), + TOMLConfigs: cfg.CosmosConfigs(), + DB: db, + QConfig: cfg.Database(), } initOps = append(initOps, chainlink.InitCosmos(testCtx, relayerFactory, cosmosCfg)) } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 690a8d189cc..f2b1f9a4c94 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -301,7 +301,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 5cbdb37427d..683cc1ea06c 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1456,8 +1456,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= diff --git a/core/services/chainlink/mocks/relayer_chain_interoperators.go b/core/services/chainlink/mocks/relayer_chain_interoperators.go index 0a8758f6d4b..81f112f7663 100644 --- a/core/services/chainlink/mocks/relayer_chain_interoperators.go +++ b/core/services/chainlink/mocks/relayer_chain_interoperators.go @@ -1,248 +1,61 @@ -// Code generated by mockery v2.28.1. DO NOT EDIT. - package mocks import ( - context "context" - - chainlink "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - - cosmos "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" + "context" + "slices" - evm "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + services2 "github.com/smartcontractkit/chainlink/v2/core/services" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - // Manually edited. mockery generates the wrong dependency. edited to use `loop` rather than `loop/internal` - // seems to caused by incorrect alias resolution of the relayer dep - internal "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" - mock "github.com/stretchr/testify/mock" + "github.com/smartcontractkit/chainlink-relay/pkg/loop" - relay "github.com/smartcontractkit/chainlink/v2/core/services/relay" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" - services "github.com/smartcontractkit/chainlink/v2/core/services" - - types "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-relay/pkg/types" ) -// RelayerChainInteroperators is an autogenerated mock type for the RelayerChainInteroperators type -type RelayerChainInteroperators struct { - mock.Mock -} - -// ChainStatus provides a mock function with given fields: ctx, id -func (_m *RelayerChainInteroperators) ChainStatus(ctx context.Context, id relay.ID) (types.ChainStatus, error) { - ret := _m.Called(ctx, id) - - var r0 types.ChainStatus - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, relay.ID) (types.ChainStatus, error)); ok { - return rf(ctx, id) - } - if rf, ok := ret.Get(0).(func(context.Context, relay.ID) types.ChainStatus); ok { - r0 = rf(ctx, id) - } else { - r0 = ret.Get(0).(types.ChainStatus) - } - - if rf, ok := ret.Get(1).(func(context.Context, relay.ID) error); ok { - r1 = rf(ctx, id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ChainStatuses provides a mock function with given fields: ctx, offset, limit -func (_m *RelayerChainInteroperators) ChainStatuses(ctx context.Context, offset int, limit int) ([]types.ChainStatus, int, error) { - ret := _m.Called(ctx, offset, limit) - - var r0 []types.ChainStatus - var r1 int - var r2 error - if rf, ok := ret.Get(0).(func(context.Context, int, int) ([]types.ChainStatus, int, error)); ok { - return rf(ctx, offset, limit) - } - if rf, ok := ret.Get(0).(func(context.Context, int, int) []types.ChainStatus); ok { - r0 = rf(ctx, offset, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]types.ChainStatus) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int, int) int); ok { - r1 = rf(ctx, offset, limit) - } else { - r1 = ret.Get(1).(int) - } - - if rf, ok := ret.Get(2).(func(context.Context, int, int) error); ok { - r2 = rf(ctx, offset, limit) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 +// FakeRelayerChainInteroperators is a fake chainlink.RelayerChainInteroperators. +// This exists because mockery generation doesn't understand how to produce an alias instead of the underlying type (which is not exported in this case). +type FakeRelayerChainInteroperators struct { + EVMChains evm.LegacyChainContainer + Nodes []types.NodeStatus + NodesErr error } -// Get provides a mock function with given fields: id -func (_m *RelayerChainInteroperators) Get(id relay.ID) (internal.Relayer, error) { - ret := _m.Called(id) - - var r0 internal.Relayer - var r1 error - if rf, ok := ret.Get(0).(func(relay.ID) (internal.Relayer, error)); ok { - return rf(id) - } - if rf, ok := ret.Get(0).(func(relay.ID) internal.Relayer); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(internal.Relayer) - } - } - - if rf, ok := ret.Get(1).(func(relay.ID) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 +func (f *FakeRelayerChainInteroperators) LegacyEVMChains() evm.LegacyChainContainer { + return f.EVMChains } -// LegacyCosmosChains provides a mock function with given fields: -func (_m *RelayerChainInteroperators) LegacyCosmosChains() cosmos.LegacyChainContainer { - ret := _m.Called() - - var r0 cosmos.LegacyChainContainer - if rf, ok := ret.Get(0).(func() cosmos.LegacyChainContainer); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(cosmos.LegacyChainContainer) - } - } - - return r0 +func (f *FakeRelayerChainInteroperators) NodeStatuses(ctx context.Context, offset, limit int, relayIDs ...relay.ID) (nodes []types.NodeStatus, count int, err error) { + return slices.Clone(f.Nodes), len(f.Nodes), f.NodesErr } -// LegacyEVMChains provides a mock function with given fields: -func (_m *RelayerChainInteroperators) LegacyEVMChains() evm.LegacyChainContainer { - ret := _m.Called() - - var r0 evm.LegacyChainContainer - if rf, ok := ret.Get(0).(func() evm.LegacyChainContainer); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(evm.LegacyChainContainer) - } - } - - return r0 +func (f *FakeRelayerChainInteroperators) Services() []services2.ServiceCtx { + panic("unimplemented") } -// List provides a mock function with given fields: filter -func (_m *RelayerChainInteroperators) List(filter chainlink.FilterFn) chainlink.RelayerChainInteroperators { - ret := _m.Called(filter) - - var r0 chainlink.RelayerChainInteroperators - if rf, ok := ret.Get(0).(func(chainlink.FilterFn) chainlink.RelayerChainInteroperators); ok { - r0 = rf(filter) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(chainlink.RelayerChainInteroperators) - } - } - - return r0 +func (f *FakeRelayerChainInteroperators) List(filter chainlink.FilterFn) chainlink.RelayerChainInteroperators { + panic("unimplemented") } -// NodeStatuses provides a mock function with given fields: ctx, offset, limit, relayIDs -func (_m *RelayerChainInteroperators) NodeStatuses(ctx context.Context, offset int, limit int, relayIDs ...relay.ID) ([]types.NodeStatus, int, error) { - _va := make([]interface{}, len(relayIDs)) - for _i := range relayIDs { - _va[_i] = relayIDs[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, offset, limit) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 []types.NodeStatus - var r1 int - var r2 error - if rf, ok := ret.Get(0).(func(context.Context, int, int, ...relay.ID) ([]types.NodeStatus, int, error)); ok { - return rf(ctx, offset, limit, relayIDs...) - } - if rf, ok := ret.Get(0).(func(context.Context, int, int, ...relay.ID) []types.NodeStatus); ok { - r0 = rf(ctx, offset, limit, relayIDs...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]types.NodeStatus) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int, int, ...relay.ID) int); ok { - r1 = rf(ctx, offset, limit, relayIDs...) - } else { - r1 = ret.Get(1).(int) - } - - if rf, ok := ret.Get(2).(func(context.Context, int, int, ...relay.ID) error); ok { - r2 = rf(ctx, offset, limit, relayIDs...) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 +func (f *FakeRelayerChainInteroperators) Get(id relay.ID) (loop.Relayer, error) { + panic("unimplemented") } -// Services provides a mock function with given fields: -func (_m *RelayerChainInteroperators) Services() []services.ServiceCtx { - ret := _m.Called() - - var r0 []services.ServiceCtx - if rf, ok := ret.Get(0).(func() []services.ServiceCtx); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]services.ServiceCtx) - } - } - - return r0 +func (f *FakeRelayerChainInteroperators) Slice() []loop.Relayer { + panic("unimplemented") } -// Slice provides a mock function with given fields: -func (_m *RelayerChainInteroperators) Slice() []internal.Relayer { - ret := _m.Called() - - var r0 []internal.Relayer - if rf, ok := ret.Get(0).(func() []internal.Relayer); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]internal.Relayer) - } - } - - return r0 +func (f *FakeRelayerChainInteroperators) LegacyCosmosChains() chainlink.LegacyCosmosContainer { + panic("unimplemented") } -type mockConstructorTestingTNewRelayerChainInteroperators interface { - mock.TestingT - Cleanup(func()) +func (f *FakeRelayerChainInteroperators) ChainStatus(ctx context.Context, id relay.ID) (types.ChainStatus, error) { + panic("unimplemented") } -// NewRelayerChainInteroperators creates a new instance of RelayerChainInteroperators. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewRelayerChainInteroperators(t mockConstructorTestingTNewRelayerChainInteroperators) *RelayerChainInteroperators { - mock := &RelayerChainInteroperators{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock +func (f *FakeRelayerChainInteroperators) ChainStatuses(ctx context.Context, offset, limit int) ([]types.ChainStatus, int, error) { + panic("unimplemented") } diff --git a/core/services/chainlink/relayer_chain_interoperators.go b/core/services/chainlink/relayer_chain_interoperators.go index e039afbfc91..b2ec0822d44 100644 --- a/core/services/chainlink/relayer_chain_interoperators.go +++ b/core/services/chainlink/relayer_chain_interoperators.go @@ -7,11 +7,12 @@ import ( "sort" "sync" + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2" @@ -24,10 +25,6 @@ var ErrNoSuchRelayer = errors.New("relayer does not exist") // encapsulates relayers and chains and is the primary entry point for // the node to access relayers, get legacy chains associated to a relayer // and get status about the chains and nodes -// -// note the generated mockery code incorrectly resolves dependencies and needs to be manually edited -// therefore this interface is not auto-generated. for reference use and edit the result: -// `go:generate mockery --quiet --name RelayerChainInteroperators --output ./mocks/ --case=underscoreā€œ` type RelayerChainInteroperators interface { Services() []services.ServiceCtx @@ -50,7 +47,7 @@ type LoopRelayerStorer interface { // on the relayer interface. type LegacyChainer interface { LegacyEVMChains() evm.LegacyChainContainer - LegacyCosmosChains() cosmos.LegacyChainContainer + LegacyCosmosChains() LegacyCosmosContainer } type ChainStatuser interface { @@ -135,7 +132,7 @@ func InitCosmos(ctx context.Context, factory RelayerFactory, config CosmosFactor op.loopRelayers[id] = a legacyMap[id.ChainID] = a.Chain() } - op.legacyChains.CosmosChains = cosmos.NewLegacyChains(legacyMap) + op.legacyChains.CosmosChains = NewLegacyCosmos(legacyMap) return nil } @@ -196,7 +193,7 @@ func (rs *CoreRelayerChainInteroperators) LegacyEVMChains() evm.LegacyChainConta // LegacyCosmosChains returns a container with all the cosmos chains // TODO BCF-2511 -func (rs *CoreRelayerChainInteroperators) LegacyCosmosChains() cosmos.LegacyChainContainer { +func (rs *CoreRelayerChainInteroperators) LegacyCosmosChains() LegacyCosmosContainer { rs.mu.Lock() defer rs.mu.Unlock() return rs.legacyChains.CosmosChains @@ -355,5 +352,44 @@ func (rs *CoreRelayerChainInteroperators) Services() (s []services.ServiceCtx) { // deprecated when chain-specific logic is removed from products. type legacyChains struct { EVMChains evm.LegacyChainContainer - CosmosChains cosmos.LegacyChainContainer + CosmosChains LegacyCosmosContainer } + +// LegacyCosmosContainer is container interface for Cosmos chains +type LegacyCosmosContainer interface { + Get(id string) (adapters.Chain, error) + Len() int + List(ids ...string) ([]adapters.Chain, error) + Slice() []adapters.Chain +} + +type LegacyCosmos = chains.ChainsKV[adapters.Chain] + +var _ LegacyCosmosContainer = &LegacyCosmos{} + +func NewLegacyCosmos(m map[string]adapters.Chain) *LegacyCosmos { + return chains.NewChainsKV[adapters.Chain](m) +} + +type CosmosLoopRelayerChainer interface { + loop.Relayer + Chain() adapters.Chain +} + +type CosmosLoopRelayerChain struct { + loop.Relayer + chain adapters.Chain +} + +func NewCosmosLoopRelayerChain(r *cosmos.Relayer, s adapters.Chain) *CosmosLoopRelayerChain { + ra := relay.NewServerAdapter(r, s) + return &CosmosLoopRelayerChain{ + Relayer: ra, + chain: s, + } +} +func (r *CosmosLoopRelayerChain) Chain() adapters.Chain { + return r.chain +} + +var _ CosmosLoopRelayerChainer = &CosmosLoopRelayerChain{} diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index cfc7dbadc18..87293069646 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -257,11 +257,10 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { name: "2 cosmos chains with 2 nodes", initFuncs: []chainlink.CoreRelayerChainInitFunc{ chainlink.InitCosmos(testctx, factory, chainlink.CosmosFactoryConfig{ - Keystore: keyStore.Cosmos(), - TOMLConfigs: cfg.CosmosConfigs(), - EventBroadcaster: pg.NewNullEventBroadcaster(), - DB: db, - QConfig: cfg.Database()}), + Keystore: keyStore.Cosmos(), + TOMLConfigs: cfg.CosmosConfigs(), + DB: db, + QConfig: cfg.Database()}), }, expectedCosmosChainCnt: 2, expectedCosmosNodeCnt: 2, @@ -290,11 +289,10 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Keystore: keyStore.StarkNet(), TOMLConfigs: cfg.StarknetConfigs()}), chainlink.InitCosmos(testctx, factory, chainlink.CosmosFactoryConfig{ - Keystore: keyStore.Cosmos(), - TOMLConfigs: cfg.CosmosConfigs(), - EventBroadcaster: pg.NewNullEventBroadcaster(), - DB: db, - QConfig: cfg.Database(), + Keystore: keyStore.Cosmos(), + TOMLConfigs: cfg.CosmosConfigs(), + DB: db, + QConfig: cfg.Database(), }), }, expectedEVMChainCnt: 2, diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index a159ee7cd06..76bfcd16412 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/sqlx" - pkgcosmos "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-solana/pkg/solana" @@ -17,7 +17,7 @@ import ( pkgstarknet "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink" starkchain "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/chain" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -225,7 +225,6 @@ func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOML type CosmosFactoryConfig struct { Keystore keystore.Cosmos coscfg.TOMLConfigs - EventBroadcaster pg.EventBroadcaster *sqlx.DB pg.QConfig } @@ -238,9 +237,6 @@ func (c CosmosFactoryConfig) Validate() error { if len(c.TOMLConfigs) == 0 { err = errors.Join(err, fmt.Errorf("no CosmosConfigs provided")) } - if c.EventBroadcaster == nil { - err = errors.Join(err, fmt.Errorf("nil EventBroadcaster")) - } if c.DB == nil { err = errors.Join(err, fmt.Errorf("nil DB")) } @@ -254,12 +250,12 @@ func (c CosmosFactoryConfig) Validate() error { return err } -func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConfig) (map[relay.ID]cosmos.LoopRelayerChainer, error) { +func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConfig) (map[relay.ID]CosmosLoopRelayerChainer, error) { err := config.Validate() if err != nil { return nil, fmt.Errorf("cannot create Cosmos relayer: %w", err) } - relayers := make(map[relay.ID]cosmos.LoopRelayerChainer) + relayers := make(map[relay.ID]CosmosLoopRelayerChainer) var ( cosmosLggr = r.Logger.Named("Cosmos") @@ -273,11 +269,9 @@ func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConf lggr := cosmosLggr.Named(relayID.ChainID) opts := cosmos.ChainOpts{ - QueryConfig: config.QConfig, - Logger: lggr, - DB: config.DB, - KeyStore: loopKs, - EventBroadcaster: config.EventBroadcaster, + Logger: lggr, + DB: config.DB, + KeyStore: loopKs, } chain, err := cosmos.NewChain(chainCfg, opts) @@ -285,7 +279,7 @@ func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConf return nil, fmt.Errorf("failed to load Cosmos chain %q: %w", relayID, err) } - relayers[relayID] = cosmos.NewLoopRelayerChain(pkgcosmos.NewRelayer(lggr, chain), chain) + relayers[relayID] = NewCosmosLoopRelayerChain(cosmos.NewRelayer(lggr, chain), chain) } return relayers, nil diff --git a/core/services/pg/channels.go b/core/services/pg/channels.go index 1d67dabe523..aed132a7f2c 100644 --- a/core/services/pg/channels.go +++ b/core/services/pg/channels.go @@ -1,7 +1,4 @@ package pg // Postgres channel to listen for new evm.txes -const ( - ChannelInsertOnCosmosMsg = "insert_on_cosmos_msg" - ChannelInsertOnEVMLogs = "evm.insert_on_logs" -) +const ChannelInsertOnEVMLogs = "evm.insert_on_logs" diff --git a/core/store/migrate/migrations/0207_drop_insert_on_terra_msg.sql b/core/store/migrate/migrations/0207_drop_insert_on_terra_msg.sql new file mode 100644 index 00000000000..f4ae4b98e2c --- /dev/null +++ b/core/store/migrate/migrations/0207_drop_insert_on_terra_msg.sql @@ -0,0 +1,20 @@ +-- +goose Up + +-- +goose StatementBegin +DROP TRIGGER IF EXISTS insert_on_terra_msg ON PUBLIC.cosmos_msgs; +DROP FUNCTION IF EXISTS PUBLIC.notify_terra_msg_insert; +-- +goose StatementEnd + +-- +goose Down + +-- +goose StatementBegin +CREATE FUNCTION notify_terra_msg_insert() RETURNS trigger + LANGUAGE plpgsql +AS $$ +BEGIN + PERFORM pg_notify('insert_on_terra_msg'::text, NOW()::text); + RETURN NULL; +END +$$; +CREATE TRIGGER notify_terra_msg_insertion AFTER INSERT ON cosmos_msgs FOR EACH STATEMENT EXECUTE PROCEDURE notify_terra_msg_insert(); +-- +goose StatementEnd diff --git a/core/web/loader/loader_test.go b/core/web/loader/loader_test.go index 0dd45a1735d..984aa9f6189 100644 --- a/core/web/loader/loader_test.go +++ b/core/web/loader/loader_test.go @@ -26,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" jobORMMocks "github.com/smartcontractkit/chainlink/v2/core/services/job/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -69,9 +68,6 @@ func TestLoader_Nodes(t *testing.T) { ctx := InjectDataloader(testutils.Context(t), app) chainID1, chainID2, notAnID := big.NewInt(1), big.NewInt(2), big.NewInt(3) - relayID1 := relay.ID{Network: relay.EVM, ChainID: relay.ChainID(chainID1.String())} - relayID2 := relay.ID{Network: relay.EVM, ChainID: relay.ChainID(chainID2.String())} - notARelayID := relay.ID{Network: relay.EVM, ChainID: relay.ChainID(notAnID.String())} genNodeStat := func(id string) relaytypes.NodeStatus { return relaytypes.NodeStatus{ @@ -79,11 +75,9 @@ func TestLoader_Nodes(t *testing.T) { ChainID: id, } } - rcInterops := chainlinkmocks.NewRelayerChainInteroperators(t) - rcInterops.On("NodeStatuses", mock.Anything, 0, -1, - relayID2, relayID1, notARelayID).Return([]relaytypes.NodeStatus{ + rcInterops := &chainlinkmocks.FakeRelayerChainInteroperators{Nodes: []relaytypes.NodeStatus{ genNodeStat(chainID2.String()), genNodeStat(chainID1.String()), - }, 2, nil) + }} app.On("GetRelayers").Return(rcInterops) batcher := nodeBatcher{app} diff --git a/core/web/resolver/eth_key_test.go b/core/web/resolver/eth_key_test.go index 6cac2f4ac4f..a7f8ce56d9f 100644 --- a/core/web/resolver/eth_key_test.go +++ b/core/web/resolver/eth_key_test.go @@ -100,7 +100,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.balM.On("GetEthBalance", address).Return(assets.NewEth(1)) f.Mocks.chain.On("BalanceMonitor").Return(f.Mocks.balM) f.Mocks.chain.On("Config").Return(f.Mocks.scfg) - f.Mocks.relayerChainInterops.On("LegacyEVMChains").Return(legacyEVMChains) + f.Mocks.relayerChainInterops.EVMChains = legacyEVMChains f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) f.App.On("GetKeyStore").Return(f.Mocks.keystore) @@ -149,7 +149,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("GetStatesForKeys", keys).Return(states, nil) f.Mocks.ethKs.On("Get", keys[0].Address.Hex()).Return(keys[0], nil) f.Mocks.ethKs.On("GetAll").Return(keys, nil) - f.Mocks.relayerChainInterops.On("LegacyEVMChains").Return(f.Mocks.legacyEVMChains) + f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) f.App.On("GetKeyStore").Return(f.Mocks.keystore) @@ -268,7 +268,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("GetAll").Return(keys, nil) f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(f.Mocks.chain, gError) - f.Mocks.relayerChainInterops.On("LegacyEVMChains").Return(f.Mocks.legacyEVMChains) + f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.App.On("GetRelayers").Return(f.Mocks.relayerChainInterops) f.App.On("GetKeyStore").Return(f.Mocks.keystore) }, @@ -302,7 +302,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(assets.NewLinkFromJuels(12), gError) f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(f.Mocks.chain, nil) - f.Mocks.relayerChainInterops.On("LegacyEVMChains").Return(f.Mocks.legacyEVMChains) + f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.Mocks.chain.On("Client").Return(f.Mocks.ethClient) f.Mocks.balM.On("GetEthBalance", address).Return(assets.NewEth(1)) f.Mocks.chain.On("BalanceMonitor").Return(f.Mocks.balM) @@ -358,7 +358,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.chain.On("BalanceMonitor").Return(nil) f.Mocks.chain.On("Config").Return(f.Mocks.scfg) f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(f.Mocks.chain, nil) - f.Mocks.relayerChainInterops.On("LegacyEVMChains").Return(f.Mocks.legacyEVMChains) + f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) f.App.On("GetKeyStore").Return(f.Mocks.keystore) diff --git a/core/web/resolver/node_test.go b/core/web/resolver/node_test.go index e949a67a85b..9f34b274201 100644 --- a/core/web/resolver/node_test.go +++ b/core/web/resolver/node_test.go @@ -5,10 +5,10 @@ import ( gqlerrors "github.com/graph-gophers/graphql-go/errors" "github.com/pkg/errors" - "github.com/stretchr/testify/mock" "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -43,17 +43,16 @@ func TestResolver_Nodes(t *testing.T) { name: "success", authenticated: true, before: func(f *gqlTestFramework) { - f.App.On("GetRelayers").Return(f.Mocks.relayerChainInterops) - f.Mocks.relayerChainInterops.On("NodeStatuses", mock.Anything, PageDefaultOffset, PageDefaultLimit).Return([]types.NodeStatus{ + f.App.On("GetRelayers").Return(chainlink.RelayerChainInteroperators(f.Mocks.relayerChainInterops)) + f.Mocks.relayerChainInterops.Nodes = []types.NodeStatus{ { Name: "node-name", ChainID: chainID.String(), Config: `Name = 'node-name'`, }, - }, 1, nil) + } f.App.On("EVMORM").Return(f.Mocks.evmORM) f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) - }, query: query, result: ` @@ -76,7 +75,7 @@ func TestResolver_Nodes(t *testing.T) { name: "generic error", authenticated: true, before: func(f *gqlTestFramework) { - f.Mocks.relayerChainInterops.On("NodeStatuses", mock.Anything, PageDefaultOffset, PageDefaultLimit).Return([]types.NodeStatus{}, 0, gError) + f.Mocks.relayerChainInterops.NodesErr = gError f.App.On("GetRelayers").Return(f.Mocks.relayerChainInterops) }, query: query, diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index d0523d6b968..fa8471c5e2b 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -52,7 +52,7 @@ type mocks struct { solana *keystoreMocks.Solana chain *evmORMMocks.Chain legacyEVMChains *evmORMMocks.LegacyChainContainer - relayerChainInterops *chainlinkMocks.RelayerChainInteroperators + relayerChainInterops *chainlinkMocks.FakeRelayerChainInteroperators ethClient *evmClientMocks.Client eIMgr *webhookmocks.ExternalInitiatorManager balM *evmORMMocks.BalanceMonitor @@ -111,7 +111,7 @@ func setupFramework(t *testing.T) *gqlTestFramework { solana: keystoreMocks.NewSolana(t), chain: evmORMMocks.NewChain(t), legacyEVMChains: evmORMMocks.NewLegacyChainContainer(t), - relayerChainInterops: chainlinkMocks.NewRelayerChainInteroperators(t), + relayerChainInterops: &chainlinkMocks.FakeRelayerChainInteroperators{}, ethClient: evmClientMocks.NewClient(t), eIMgr: webhookmocks.NewExternalInitiatorManager(t), balM: evmORMMocks.NewBalanceMonitor(t), diff --git a/go.mod b/go.mod index ad3cb5f78ed..2df4b05c749 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/smartcontractkit/chainlink/v2 go 1.21 require ( - github.com/CosmWasm/wasmd v0.40.1 github.com/Depado/ginprom v1.7.11 github.com/Masterminds/semver/v3 v3.2.1 github.com/Masterminds/sprig/v3 v3.2.3 @@ -24,7 +23,6 @@ require ( github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4 github.com/gin-gonic/gin v1.9.1 github.com/go-webauthn/webauthn v0.8.2 - github.com/gogo/protobuf v1.3.3 github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 github.com/google/uuid v1.3.1 github.com/gorilla/securecookie v1.1.1 @@ -67,7 +65,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb @@ -116,6 +114,7 @@ require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect + github.com/CosmWasm/wasmd v0.40.1 // indirect github.com/CosmWasm/wasmvm v1.2.4 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -184,6 +183,7 @@ require ( github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.3.1+incompatible // indirect + github.com/gogo/protobuf v1.3.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/go.sum b/go.sum index f879f16272b..f13b00d6db7 100644 --- a/go.sum +++ b/go.sum @@ -1457,8 +1457,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index aa670da1c96..be9285c6d48 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -383,7 +383,7 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f7b55b259f2..7c5542bfa68 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2360,8 +2360,8 @@ github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ=