From 7f768318e1bfbda87d4967bfdae3156d2c6aae10 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 10 Jan 2024 13:11:43 +0100 Subject: [PATCH 01/99] replace QueueBroadcastMsg calls with SyncBroadcastMsg --- orchestrator/cosmos/broadcast.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index d88a229e..510fb65f 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -160,8 +160,7 @@ func (s *peggyBroadcastClient) SendValsetConfirm( signature, err := s.ethPersonalSignFn(ethFrom, confirmHash.Bytes()) if err != nil { metrics.ReportFuncError(s.svcTags) - err = errors.New("failed to sign validator address") - return err + return errors.Wrap(err, "failed to sign validator address") } // MsgValsetConfirm // this is the message sent by the validators when they wish to submit their @@ -184,10 +183,10 @@ func (s *peggyBroadcastClient) SendValsetConfirm( Nonce: valset.Nonce, Signature: ethcmn.Bytes2Hex(signature), } - if err = s.broadcastClient.QueueBroadcastMsg(msg); err != nil { + + if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgValsetConfirm failed") - return err + return errors.Wrap(err, "failed to broadcast MsgValsetConfirm") } return nil @@ -226,10 +225,10 @@ func (s *peggyBroadcastClient) SendBatchConfirm( EthSigner: ethFrom.Hex(), TokenContract: batch.TokenContract, } - if err = s.broadcastClient.QueueBroadcastMsg(msg); err != nil { + + if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgConfirmBatch failed") - return err + return errors.Wrap(err, "broadcasting MsgConfirmBatch failed") } return nil @@ -554,10 +553,9 @@ func (s *peggyBroadcastClient) SendToEth( Amount: amount, BridgeFee: fee, // TODO: use exactly that fee for transaction } - if err := s.broadcastClient.QueueBroadcastMsg(msg); err != nil { + if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgSendToEth failed") - return err + return errors.Wrap(err, "broadcasting MsgSendToEth failed") } return nil @@ -584,10 +582,10 @@ func (s *peggyBroadcastClient) SendRequestBatch( Denom: denom, Orchestrator: s.AccFromAddress().String(), } - if err := s.broadcastClient.QueueBroadcastMsg(msg); err != nil { + + if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgRequestBatch failed") - return err + return errors.Wrap(err, "broadcasting MsgRequestBatch failed") } return nil From 655e3990a50294e4a0cd8f82398ce15bfa9f2b17 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 10 Jan 2024 13:18:42 +0100 Subject: [PATCH 02/99] sleep for 1.2s blocktime in between each broadcast to injective --- orchestrator/cosmos/broadcast.go | 66 ++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index 510fb65f..4c8b5054 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -2,7 +2,7 @@ package cosmos import ( "context" - "fmt" + "sync" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -100,13 +100,16 @@ func (s *peggyBroadcastClient) AccFromAddress() sdk.AccAddress { return s.broadcastClient.FromAddress() } +const injectiveBlockTime = 1200 * time.Millisecond + type peggyBroadcastClient struct { daemonQueryClient types.QueryClient broadcastClient chainclient.ChainClient ethSignerFn keystore.SignerFn ethPersonalSignFn keystore.PersonalSignFn - svcTags metrics.Tags + svcTags metrics.Tags + broadcastMux sync.Mutex } func (s *peggyBroadcastClient) UpdatePeggyOrchestratorAddresses( @@ -135,14 +138,16 @@ func (s *peggyBroadcastClient) UpdatePeggyOrchestratorAddresses( Orchestrator: orchestratorAddr.String(), } - res, err := s.broadcastClient.SyncBroadcastMsg(msg) - fmt.Println("Response of set eth address", "res", res) - if err != nil { + s.broadcastMux.Lock() + defer s.broadcastMux.Unlock() + + if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgSetOrchestratorAddresses failed") - return err + return errors.Wrap(err, "broadcasting MsgSetOrchestratorAddresses failed") } + time.Sleep(injectiveBlockTime) + return nil } @@ -184,11 +189,16 @@ func (s *peggyBroadcastClient) SendValsetConfirm( Signature: ethcmn.Bytes2Hex(signature), } + s.broadcastMux.Lock() + defer s.broadcastMux.Unlock() + if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) return errors.Wrap(err, "failed to broadcast MsgValsetConfirm") } + time.Sleep(injectiveBlockTime) + return nil } @@ -225,12 +235,16 @@ func (s *peggyBroadcastClient) SendBatchConfirm( EthSigner: ethFrom.Hex(), TokenContract: batch.TokenContract, } + s.broadcastMux.Lock() + defer s.broadcastMux.Unlock() if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) return errors.Wrap(err, "broadcasting MsgConfirmBatch failed") } + time.Sleep(injectiveBlockTime) + return nil } @@ -265,11 +279,16 @@ func (s *peggyBroadcastClient) sendOldDepositClaims( Data: "", } + s.broadcastMux.Lock() + defer s.broadcastMux.Unlock() + if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") return err } else { + time.Sleep(injectiveBlockTime) + log.WithFields(log.Fields{ "event_nonce": oldDeposit.EventNonce.String(), "tx_hash": txResponse.TxResponse.TxHash, @@ -311,11 +330,16 @@ func (s *peggyBroadcastClient) sendDepositClaims( Data: deposit.Data, } + s.broadcastMux.Lock() + defer s.broadcastMux.Unlock() + if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") return err } else { + time.Sleep(injectiveBlockTime) + log.WithFields(log.Fields{ "event_nonce": deposit.EventNonce.String(), "tx_hash": txResponse.TxResponse.TxHash, @@ -349,11 +373,16 @@ func (s *peggyBroadcastClient) sendWithdrawClaims( Orchestrator: s.AccFromAddress().String(), } + s.broadcastMux.Lock() + defer s.broadcastMux.Unlock() + if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) log.WithError(err).Errorln("broadcasting MsgWithdrawClaim failed") return err } else { + time.Sleep(injectiveBlockTime) + log.WithFields(log.Fields{ "event_nonce": withdraw.EventNonce.String(), "tx_hash": txResponse.TxResponse.TxHash, @@ -398,11 +427,16 @@ func (s *peggyBroadcastClient) sendValsetUpdateClaims( Orchestrator: s.AccFromAddress().String(), } + s.broadcastMux.Lock() + defer s.broadcastMux.Unlock() + if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) log.WithError(err).Errorln("broadcasting MsgValsetUpdatedClaim failed") return err } else { + time.Sleep(injectiveBlockTime) + log.WithFields(log.Fields{ "event_nonce": valsetUpdate.EventNonce.String(), "tx_hash": txResponse.TxResponse.TxHash, @@ -440,11 +474,16 @@ func (s *peggyBroadcastClient) sendErc20DeployedClaims( Orchestrator: s.AccFromAddress().String(), } + s.broadcastMux.Lock() + defer s.broadcastMux.Unlock() + if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) log.WithError(err).Errorln("broadcasting MsgERC20DeployedClaim failed") return err } else { + time.Sleep(injectiveBlockTime) + log.WithFields(log.Fields{ "event_nonce": erc20Deployed.EventNonce.String(), "tx_hash": txResponse.TxResponse.TxHash, @@ -521,7 +560,7 @@ func (s *peggyBroadcastClient) SendEthereumClaims( // Considering blockTime=1s on Injective chain, Adding Sleep to make sure new event is // sent only after previous event is executed successfully. // Otherwise it will through `non contiguous event nonce` failing CheckTx. - time.Sleep(1200 * time.Millisecond) + //time.Sleep(1200 * time.Millisecond) } return nil } @@ -553,11 +592,17 @@ func (s *peggyBroadcastClient) SendToEth( Amount: amount, BridgeFee: fee, // TODO: use exactly that fee for transaction } + + s.broadcastMux.Lock() + defer s.broadcastMux.Unlock() + if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) return errors.Wrap(err, "broadcasting MsgSendToEth failed") } + time.Sleep(injectiveBlockTime) + return nil } @@ -583,10 +628,15 @@ func (s *peggyBroadcastClient) SendRequestBatch( Orchestrator: s.AccFromAddress().String(), } + s.broadcastMux.Lock() + defer s.broadcastMux.Unlock() + if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) return errors.Wrap(err, "broadcasting MsgRequestBatch failed") } + time.Sleep(injectiveBlockTime) + return nil } From 728d83dd7c2a0101525fa2009d9317e96d9e9aac Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 11 Jan 2024 13:19:27 +0100 Subject: [PATCH 03/99] log eth tx opts --- orchestrator/ethereum/committer/eth_committer.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/orchestrator/ethereum/committer/eth_committer.go b/orchestrator/ethereum/committer/eth_committer.go index b231f1b8..874fa5ef 100644 --- a/orchestrator/ethereum/committer/eth_committer.go +++ b/orchestrator/ethereum/committer/eth_committer.go @@ -114,6 +114,11 @@ func (e *ethCommitter) SendTx( return common.Hash{}, errors.Errorf("Suggested gas price %v is greater than max gas price %v", opts.GasPrice.Int64(), maxGasPrice.Int64()) } + log.WithFields(log.Fields{ + "gas_price": opts.GasPrice.String(), + "gas_limit": opts.GasLimit, + }).Debugln("batch tx options") + resyncNonces := func(from common.Address) { e.nonceCache.Sync(from, func() (uint64, error) { nonce, err := e.evmProvider.PendingNonceAt(context.TODO(), from) From db8738c85db87c527a712abd04c55041ad491d76 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 11 Jan 2024 13:56:11 +0100 Subject: [PATCH 04/99] estimate gas limit for eth tx --- .../ethereum/committer/eth_committer.go | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/orchestrator/ethereum/committer/eth_committer.go b/orchestrator/ethereum/committer/eth_committer.go index 874fa5ef..1572f5ea 100644 --- a/orchestrator/ethereum/committer/eth_committer.go +++ b/orchestrator/ethereum/committer/eth_committer.go @@ -2,6 +2,7 @@ package committer import ( "context" + "github.com/ethereum/go-ethereum" "math/big" "strings" @@ -130,6 +131,34 @@ func (e *ethCommitter) SendTx( }) } + // estimate gas limit + contract := recipient + // Gas estimation cannot succeed without code for method invocations + code, err := e.evmProvider.PendingCodeAt(opts.Context, contract) + if err != nil { + return common.Hash{}, errors.Wrap(err, "failed to get code") + } + + if len(code) == 0 { + return common.Hash{}, bind.ErrNoCode + } + + // If the contract surely has code (or code is not needed), estimate the transaction + msg := ethereum.CallMsg{ + From: opts.From, + To: &contract, + GasPrice: gasPrice, + Value: new(big.Int), + Data: txData, + } + + gasLimit, err := e.evmProvider.EstimateGas(opts.Context, msg) + if err != nil { + return common.Hash{}, errors.Wrap(err, "failed to estimate gas") + } + + opts.GasLimit = gasLimit + if err := e.nonceCache.Serialize(e.fromAddress, func() (err error) { nonce, _ := e.nonceCache.Get(e.fromAddress) var resyncUsed bool From 2ef415ecffca9d4aa3c05923dfe8d3bb15f0d48a Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 11 Jan 2024 17:40:55 +0100 Subject: [PATCH 05/99] logs 3 --- orchestrator/batch_request.go | 2 +- orchestrator/oracle.go | 2 +- orchestrator/relayer.go | 6 +++--- orchestrator/signer.go | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 7ee1e01d..df3ad9bb 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -47,7 +47,7 @@ func (l *batchRequestLoop) requestBatches(ctx context.Context) error { } if len(fees) == 0 { - l.Logger().Debugln("no withdrawals to batch") + l.Logger().Infoln("no withdrawals to batch") return nil } diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index b6b6a108..6072f0c1 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -202,7 +202,7 @@ func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { if noEvents := len(legacyDeposits) == 0 && len(deposits) == 0 && len(withdrawals) == 0 && len(erc20Deployments) == 0 && len(valsetUpdates) == 0; noEvents { - l.Logger().Debugln("no new events on Ethereum") + l.Logger().Infoln("no new events on Ethereum") return nil } diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 17e5b9d2..1db8b052 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -56,7 +56,7 @@ func (l *relayerLoop) relayValsetsAndBatches(ctx context.Context) error { retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay valsets, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to relay valset, will retry (%d)", n) }), ) }) @@ -68,7 +68,7 @@ func (l *relayerLoop) relayValsetsAndBatches(ctx context.Context) error { retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay batches, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to relay batch, will retry (%d)", n) }), ) }) @@ -115,7 +115,7 @@ func (l *relayerLoop) relayValset(ctx context.Context) error { } if oldestConfirmedValset == nil { - l.Logger().Debugln("no valset update to relay") + l.Logger().Infoln("no valset to relay") return nil } diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 9b429f0d..37ef1209 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -88,7 +88,7 @@ func (l *ethSignerLoop) signNewValsetUpdates(ctx context.Context) error { } if len(oldestUnsignedValsets) == 0 { - l.Logger().Debugln("no valset updates to confirm") + l.Logger().Infoln("no valset updates to confirm") return nil } @@ -110,7 +110,7 @@ func (l *ethSignerLoop) signNewBatch(ctx context.Context) error { } if oldestUnsignedTransactionBatch == nil { - l.Logger().Debugln("no batch to confirm") + l.Logger().Infoln("no batch to confirm") return nil } From 2018269e5538f0dbc9bc16b4be5a32e024d5c22c Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 12 Jan 2024 12:40:55 +0100 Subject: [PATCH 06/99] change method signatures and update logs --- cmd/peggo/orchestrator.go | 4 ++-- orchestrator/cosmos/query.go | 26 +++++++++++++------------- orchestrator/injective.go | 3 ++- orchestrator/orchestrator.go | 14 ++++++++++---- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 3eac2716..b1553324 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -43,7 +43,7 @@ func orchestratorCmd(cmd *cli.Cmd) { }).Infoln("peggo - peggy binary for Ethereum bridge") if *cfg.cosmosUseLedger || *cfg.ethUseLedger { - log.Fatalln("cannot use Ledger for peggo, since signatures must be realtime") + log.Fatalln("cannot use Ledger for orchestrator, since signatures must be realtime") } valAddress, cosmosKeyring, err := initCosmosKeyring( @@ -71,7 +71,7 @@ func orchestratorCmd(cmd *cli.Cmd) { log.WithError(err).Fatalln("failed to initialize Ethereum account") } - log.WithFields(log.Fields{"inj_addr": valAddress.String(), "eth_addr": ethKeyFromAddress.String()}).Infoln("starting peggo service") + //log.WithFields(log.Fields{"inj_addr": valAddress.String(), "eth_addr": ethKeyFromAddress.String()}).Infoln("starting orchestrator service...") var ( injectiveNet orchestrator.InjectiveNetwork diff --git a/orchestrator/cosmos/query.go b/orchestrator/cosmos/query.go index f37b6879..b4bbbab0 100644 --- a/orchestrator/cosmos/query.go +++ b/orchestrator/cosmos/query.go @@ -25,7 +25,7 @@ type PeggyQueryClient interface { LastClaimEventByAddr(ctx context.Context, validatorAccountAddress sdk.AccAddress) (*types.LastClaimEvent, error) PeggyParams(ctx context.Context) (*types.Params, error) - HasRegisteredEthAddress(ctx context.Context, addr ethcmn.Address) (bool, error) + GetValidatorAddress(ctx context.Context, addr ethcmn.Address) (sdk.ValAddress, error) } func NewPeggyQueryClient(client types.QueryClient) PeggyQueryClient { @@ -44,29 +44,29 @@ type peggyQueryClient struct { var ErrNotFound = errors.New("not found") -func (s *peggyQueryClient) HasRegisteredEthAddress(ctx context.Context, addr ethcmn.Address) (bool, error) { +func (s *peggyQueryClient) GetValidatorAddress(ctx context.Context, addr ethcmn.Address) (sdk.ValAddress, error) { metrics.ReportFuncCall(s.svcTags) doneFn := metrics.ReportFuncTiming(s.svcTags) defer doneFn() - req := &types.QueryDelegateKeysByEthAddress{EthAddress: addr.Hex()} - resp, err := s.daemonQueryClient.GetDelegateKeyByEth(ctx, req) - - if errors.Is(err, types.ErrInvalid) { - // no record found - return false, nil - } + resp, err := s.daemonQueryClient.GetDelegateKeyByEth(ctx, &types.QueryDelegateKeysByEthAddress{ + EthAddress: addr.Hex(), + }) if err != nil { - metrics.ReportFuncError(s.svcTags) - return false, errors.Wrap(err, "failed to query GetDelegateKeyByEth from daemon") + return nil, err } if resp == nil { - return false, nil + return nil, ErrNotFound + } + + valAddr, err := sdk.ValAddressFromBech32(resp.ValidatorAddress) + if err != nil { + return nil, errors.Wrapf(err, "failed to decode validator address: %v", resp.ValidatorAddress) } - return true, nil + return valAddr, nil } func (s *peggyQueryClient) ValsetAt(ctx context.Context, nonce uint64) (*types.Valset, error) { diff --git a/orchestrator/injective.go b/orchestrator/injective.go index 6e6fb25d..255b9606 100644 --- a/orchestrator/injective.go +++ b/orchestrator/injective.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + sdk "github.com/cosmos/cosmos-sdk/types" "time" gethcommon "github.com/ethereum/go-ethereum/common" @@ -14,7 +15,7 @@ import ( type InjectiveNetwork interface { PeggyParams(ctx context.Context) (*peggytypes.Params, error) GetBlockCreationTime(ctx context.Context, height int64) (time.Time, error) - HasRegisteredEthAddress(ctx context.Context, addr gethcommon.Address) (bool, error) + GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (sdk.ValAddress, error) LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) SendEthereumClaims(ctx context.Context, diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 0b0582ae..61206c82 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -81,19 +81,25 @@ func NewPeggyOrchestrator( // Run starts all major loops required to make // up the Orchestrator, all of these are async loops. func (s *PeggyOrchestrator) Run(ctx context.Context) error { - if !s.hasRegisteredETHAddress(ctx) { + if !s.hasDelegateValidator(ctx) { return s.startRelayerMode(ctx) } return s.startValidatorMode(ctx) } -func (s *PeggyOrchestrator) hasRegisteredETHAddress(ctx context.Context) bool { +func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context) bool { subCtx, cancelFn := context.WithTimeout(ctx, 5*time.Second) defer cancelFn() - ok, _ := s.inj.HasRegisteredEthAddress(subCtx, s.eth.FromAddress()) - return ok + validator, err := s.inj.GetValidatorAddress(subCtx, s.eth.FromAddress()) + if err != nil { + return false + } + + s.logger.WithField("validator_addr", validator.String()).Debugln("found delegate validator address") + + return true } // startValidatorMode runs all orchestrator processes. This is called From cd212dc669e59b46356aa4467683366f59c52c93 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 12 Jan 2024 12:43:48 +0100 Subject: [PATCH 07/99] err log --- orchestrator/orchestrator.go | 1 + 1 file changed, 1 insertion(+) diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 61206c82..f42f184d 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -94,6 +94,7 @@ func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context) bool { validator, err := s.inj.GetValidatorAddress(subCtx, s.eth.FromAddress()) if err != nil { + s.logger.WithError(err).Warningln("failed to get validator address") return false } From 794fd54c485ab0ccd925f8e13b6d66bb86d2db25 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 12 Jan 2024 12:46:19 +0100 Subject: [PATCH 08/99] fix decoding val addr --- orchestrator/cosmos/query.go | 6 +++--- orchestrator/injective.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/orchestrator/cosmos/query.go b/orchestrator/cosmos/query.go index b4bbbab0..386814db 100644 --- a/orchestrator/cosmos/query.go +++ b/orchestrator/cosmos/query.go @@ -25,7 +25,7 @@ type PeggyQueryClient interface { LastClaimEventByAddr(ctx context.Context, validatorAccountAddress sdk.AccAddress) (*types.LastClaimEvent, error) PeggyParams(ctx context.Context) (*types.Params, error) - GetValidatorAddress(ctx context.Context, addr ethcmn.Address) (sdk.ValAddress, error) + GetValidatorAddress(ctx context.Context, addr ethcmn.Address) (sdk.AccAddress, error) } func NewPeggyQueryClient(client types.QueryClient) PeggyQueryClient { @@ -44,7 +44,7 @@ type peggyQueryClient struct { var ErrNotFound = errors.New("not found") -func (s *peggyQueryClient) GetValidatorAddress(ctx context.Context, addr ethcmn.Address) (sdk.ValAddress, error) { +func (s *peggyQueryClient) GetValidatorAddress(ctx context.Context, addr ethcmn.Address) (sdk.AccAddress, error) { metrics.ReportFuncCall(s.svcTags) doneFn := metrics.ReportFuncTiming(s.svcTags) defer doneFn() @@ -61,7 +61,7 @@ func (s *peggyQueryClient) GetValidatorAddress(ctx context.Context, addr ethcmn. return nil, ErrNotFound } - valAddr, err := sdk.ValAddressFromBech32(resp.ValidatorAddress) + valAddr, err := sdk.AccAddressFromBech32(resp.ValidatorAddress) if err != nil { return nil, errors.Wrapf(err, "failed to decode validator address: %v", resp.ValidatorAddress) } diff --git a/orchestrator/injective.go b/orchestrator/injective.go index 255b9606..89af15e2 100644 --- a/orchestrator/injective.go +++ b/orchestrator/injective.go @@ -15,7 +15,7 @@ import ( type InjectiveNetwork interface { PeggyParams(ctx context.Context) (*peggytypes.Params, error) GetBlockCreationTime(ctx context.Context, height int64) (time.Time, error) - GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (sdk.ValAddress, error) + GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (sdk.AccAddress, error) LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) SendEthereumClaims(ctx context.Context, From 20de03f979868b20a574aa8f4483a335415bd36d Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 12 Jan 2024 13:27:12 +0100 Subject: [PATCH 09/99] logs --- cmd/peggo/orchestrator.go | 6 ++++-- orchestrator/batch_request.go | 3 +++ orchestrator/oracle.go | 3 +++ orchestrator/orchestrator.go | 17 +++-------------- orchestrator/relayer.go | 7 +++++++ 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index b1553324..72c4edb1 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -71,8 +71,6 @@ func orchestratorCmd(cmd *cli.Cmd) { log.WithError(err).Fatalln("failed to initialize Ethereum account") } - //log.WithFields(log.Fields{"inj_addr": valAddress.String(), "eth_addr": ethKeyFromAddress.String()}).Infoln("starting orchestrator service...") - var ( injectiveNet orchestrator.InjectiveNetwork customEndpointRPCs = *cfg.cosmosGRPC != "" && *cfg.tendermintRPC != "" @@ -99,6 +97,8 @@ func orchestratorCmd(cmd *cli.Cmd) { ) } + log.WithField("inj_addr", valAddress.String()).Infoln("connected to Injective network") + orShutdown(err) ctx, cancelFn := context.WithCancel(context.Background()) @@ -129,6 +129,8 @@ func orchestratorCmd(cmd *cli.Cmd) { ) orShutdown(err) + log.WithField("eth_addr", ethKeyFromAddress.Hex()).Infoln("connected to Ethereum network") + coingeckoFeed := coingecko.NewCoingeckoPriceFeed(100, &coingecko.Config{BaseURL: *cfg.coingeckoApi}) // Create peggo and run it diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index df3ad9bb..b2a819f7 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -15,11 +15,14 @@ import ( ) func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context) (err error) { + loop := batchRequestLoop{ PeggyOrchestrator: s, loopDuration: defaultLoopDur, } + s.logger.WithField("loop_duration", loop.loopDuration.String()).Debugln("starting BatchRequester loop...") + return loop.Run(ctx) } diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 6072f0c1..921521fc 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -26,6 +26,7 @@ const ( // EthOracleMainLoop is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain // and ferried over to Cosmos where they will be used to issue tokens or process batches. func (s *PeggyOrchestrator) EthOracleMainLoop(ctx context.Context) error { + lastConfirmedEthHeight, err := s.getLastConfirmedEthHeightOnInjective(ctx) if err != nil { return err @@ -40,6 +41,8 @@ func (s *PeggyOrchestrator) EthOracleMainLoop(ctx context.Context) error { lastResyncWithInjective: time.Now(), } + s.logger.WithField("loop_duration", loop.loopDuration.String()).Debugln("starting EthOracle loop...") + return loop.Run(ctx) } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index f42f184d..810453c3 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -94,7 +94,7 @@ func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context) bool { validator, err := s.inj.GetValidatorAddress(subCtx, s.eth.FromAddress()) if err != nil { - s.logger.WithError(err).Warningln("failed to get validator address") + s.logger.WithError(err).Debugln("no delegate validator address found") return false } @@ -106,14 +106,7 @@ func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context) bool { // startValidatorMode runs all orchestrator processes. This is called // when peggo is run alongside a validator injective node. func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context) error { - log.WithFields(log.Fields{ - "batch_requesting": true, - "eth_event_tracking": true, - "batch_signing": true, - "valset_signing": true, - "valset_relaying": s.valsetRelayEnabled, - "batch_relaying": s.batchRelayEnabled, - }).Infoln("running in validator mode") + log.Infoln("running orchestrator in validator mode") var pg loops.ParanoidGroup @@ -129,11 +122,7 @@ func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context) error { // messages that do not require a validator's signature. This mode is run // alongside a non-validator injective node func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context) error { - log.WithFields(log.Fields{ - "batch_requesting": true, - "valset_relaying": s.valsetRelayEnabled, - "batch_relaying": s.batchRelayEnabled, - }).Infoln("running in relayer mode") + log.Infoln("running orchestrator in relayer mode") var pg loops.ParanoidGroup diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 1db8b052..84ab9f3b 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -29,6 +29,12 @@ func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context) (err error) { loopDuration: defaultRelayerLoopDur, } + s.logger.WithFields(log.Fields{ + "loop_duration": loop.loopDuration.String(), + "relay_batches": s.batchRelayEnabled, + "relay_valsets": s.valsetRelayEnabled, + }).Debugln("starting Relayer loop...") + return loop.Run(ctx) } @@ -42,6 +48,7 @@ func (l *relayerLoop) Logger() log.Logger { } func (l *relayerLoop) Run(ctx context.Context) error { + return loops.RunLoop(ctx, l.loopDuration, func() error { return l.relayValsetsAndBatches(ctx) }) From 0e6d3ea9e008c49221ed5d78430f9e5427edbc00 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 12 Jan 2024 14:01:17 +0100 Subject: [PATCH 10/99] logs 2 --- cmd/peggo/orchestrator.go | 4 ++-- orchestrator/batch_request_test.go | 6 ++++++ orchestrator/cosmos/custom_rpc.go | 3 +-- orchestrator/cosmos/load_balanced.go | 6 +++++- orchestrator/mocks_test.go | 7 ++++--- orchestrator/oracle.go | 9 +++------ orchestrator/relayer.go | 6 +++--- orchestrator/signer.go | 2 ++ orchestrator/signer_test.go | 10 +++++----- 9 files changed, 31 insertions(+), 22 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 72c4edb1..baeb07c5 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -97,7 +97,7 @@ func orchestratorCmd(cmd *cli.Cmd) { ) } - log.WithField("inj_addr", valAddress.String()).Infoln("connected to Injective network") + log.Infoln("using", valAddress.String(), "for Injective") orShutdown(err) @@ -129,7 +129,7 @@ func orchestratorCmd(cmd *cli.Cmd) { ) orShutdown(err) - log.WithField("eth_addr", ethKeyFromAddress.Hex()).Infoln("connected to Ethereum network") + log.Infoln("using", ethKeyFromAddress.Hex(), "for Ethereum") coingeckoFeed := coingecko.NewCoingeckoPriceFeed(100, &coingecko.Config{BaseURL: *cfg.coingeckoApi}) diff --git a/orchestrator/batch_request_test.go b/orchestrator/batch_request_test.go index 3ae46e36..e710915e 100644 --- a/orchestrator/batch_request_test.go +++ b/orchestrator/batch_request_test.go @@ -13,6 +13,12 @@ import ( peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) +func TestDusan(t *testing.T) { + logger := suplog.DefaultLogger + d := "dusan" + logger.Infoln("using", d, "for me") +} + func TestRequestBatches(t *testing.T) { t.Parallel() diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index 079a4d8e..38263539 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -82,10 +82,9 @@ func NewCustomRPCNetwork( log.WithFields(log.Fields{ "chain_id": chainID, - "conn": "custom", "injective": injectiveGRPC, "tendermint": tendermintRPC, - }).Infoln("connected to Injective network") + }).Infoln("connected to custom Injective endpoints") return n, nil } diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index 620da606..dd871d7e 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -80,7 +80,11 @@ func NewLoadBalancedNetwork( ExplorerClient: explorer, } - log.WithFields(log.Fields{"chain_id": chainID, "conn": "load_balanced"}).Infoln("connected to Injective network") + log.WithFields(log.Fields{ + "chain_id": chainID, + "injective": netCfg.ChainGrpcEndpoint, + "tendermint": netCfg.TmEndpoint, + }).Infoln("connected to Injective's load balanced endpoints") return n, nil } diff --git a/orchestrator/mocks_test.go b/orchestrator/mocks_test.go index 1011dcf4..170e920b 100644 --- a/orchestrator/mocks_test.go +++ b/orchestrator/mocks_test.go @@ -3,6 +3,7 @@ package orchestrator import ( "context" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + sdk "github.com/cosmos/cosmos-sdk/types" eth "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "math/big" @@ -54,11 +55,11 @@ type mockInjective struct { latestTransactionBatchesFn func(context.Context) ([]*peggytypes.OutgoingTxBatch, error) transactionBatchSignaturesFn func(context.Context, uint64, eth.Address) ([]*peggytypes.MsgConfirmBatch, error) - hasRegisteredEthAddress func(ctx context.Context, address eth.Address) (bool, error) + getValidatorAddressFn func(ctx context.Context, address eth.Address) (sdk.AccAddress, error) } -func (i *mockInjective) HasRegisteredEthAddress(ctx context.Context, addr eth.Address) (bool, error) { - return i.hasRegisteredEthAddress(ctx, addr) +func (i *mockInjective) GetValidatorAddress(ctx context.Context, addr eth.Address) (sdk.AccAddress, error) { + return i.getValidatorAddressFn(ctx, addr) } func (i *mockInjective) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 921521fc..9dc6c3d6 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -112,7 +112,7 @@ func (l *ethOracleLoop) observeEthEvents(ctx context.Context) error { return err } - l.Logger().WithFields(log.Fields{"block_start": l.lastCheckedEthHeight, "block_end": newHeight}).Debugln("scanned Ethereum blocks for events") + l.Logger().WithFields(log.Fields{"block_start": l.lastCheckedEthHeight, "block_end": newHeight}).Debugln("scanned Ethereum blocks") l.lastCheckedEthHeight = newHeight if time.Since(l.lastResyncWithInjective) >= 48*time.Hour { @@ -192,10 +192,7 @@ func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { return errors.Wrap(err, "failed to query last claim event from Injective") } - l.Logger().WithFields(log.Fields{ - "event_nonce": lastClaimEvent.EthereumEventNonce, - "event_height": lastClaimEvent.EthereumEventHeight, - }).Debugln("last Ethereum claim event on Injective") + l.Logger().WithFields(log.Fields{"event_nonce": lastClaimEvent.EthereumEventNonce, "event_height": lastClaimEvent.EthereumEventHeight}).Debugln("last observed Ethereum event") legacyDeposits = filterSendToCosmosEventsByNonce(legacyDeposits, lastClaimEvent.EthereumEventNonce) deposits = filterSendToInjectiveEventsByNonce(deposits, lastClaimEvent.EthereumEventNonce) @@ -226,7 +223,7 @@ func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { "withdrawals": len(withdrawals), "erc20_deployments": len(erc20Deployments), "valset_updates": len(valsetUpdates), - }).Infoln("sent new claims to Injective") + }).Infoln("sent new event claims to Injective") return nil } diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 84ab9f3b..739c08ba 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -147,7 +147,7 @@ func (l *relayerLoop) relayValset(ctx context.Context) error { return nil } - l.Logger().WithFields(log.Fields{"inj_nonce": oldestConfirmedValset.Nonce, "eth_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("latest valset updates") + l.Logger().WithFields(log.Fields{"inj_nonce": oldestConfirmedValset.Nonce, "eth_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("new valset update") // Check custom time delay offset blockTime, err := l.inj.GetBlockCreationTime(ctx, int64(oldestConfirmedValset.Height)) @@ -204,7 +204,7 @@ func (l *relayerLoop) relayBatch(ctx context.Context) error { } if oldestConfirmedBatch == nil { - l.Logger().Debugln("no batch to relay") + l.Logger().Infoln("no batch to relay") return nil } @@ -236,7 +236,7 @@ func (l *relayerLoop) relayBatch(ctx context.Context) error { return nil } - l.Logger().WithFields(log.Fields{"inj_nonce": oldestConfirmedBatch.BatchNonce, "eth_nonce": latestEthereumBatch.Uint64()}).Debugln("latest batch updates") + l.Logger().WithFields(log.Fields{"inj_nonce": oldestConfirmedBatch.BatchNonce, "eth_nonce": latestEthereumBatch.Uint64()}).Debugln("new batch update") // Check custom time delay offset blockTime, err := l.inj.GetBlockCreationTime(ctx, int64(oldestConfirmedBatch.Block)) diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 37ef1209..94c31553 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -30,6 +30,8 @@ func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context) error { ethFrom: s.eth.FromAddress(), } + s.logger.WithField("loop_duration", loop.loopDuration.String()).Debugln("starting EthSigner loop...") + return loop.Run(ctx) } diff --git a/orchestrator/signer_test.go b/orchestrator/signer_test.go index bf530ce6..73cd42cb 100644 --- a/orchestrator/signer_test.go +++ b/orchestrator/signer_test.go @@ -60,7 +60,7 @@ func TestEthSignerLoop(t *testing.T) { loopDuration: defaultLoopDur, } - assert.NoError(t, l.signBatchesAndValsets(context.TODO())) + assert.NoError(t, l.signNewValsetUpdates(context.TODO())) }) t.Run("failed to send valset confirm", func(t *testing.T) { @@ -99,7 +99,7 @@ func TestEthSignerLoop(t *testing.T) { loopDuration: defaultLoopDur, } - assert.Error(t, l.signBatchesAndValsets(context.TODO())) + assert.Error(t, l.signNewValsetUpdates(context.TODO())) }) t.Run("no transaction batch sign", func(t *testing.T) { @@ -123,7 +123,7 @@ func TestEthSignerLoop(t *testing.T) { loopDuration: defaultLoopDur, } - assert.NoError(t, l.signBatchesAndValsets(context.TODO())) + assert.NoError(t, l.signNewBatch(context.TODO())) }) t.Run("failed to send batch confirm", func(t *testing.T) { @@ -151,7 +151,7 @@ func TestEthSignerLoop(t *testing.T) { loopDuration: defaultLoopDur, } - assert.Error(t, l.signBatchesAndValsets(context.TODO())) + assert.Error(t, l.signNewBatch(context.TODO())) }) t.Run("valset update and transaction batch are confirmed", func(t *testing.T) { @@ -179,6 +179,6 @@ func TestEthSignerLoop(t *testing.T) { loopDuration: defaultLoopDur, } - assert.NoError(t, l.signBatchesAndValsets(context.TODO())) + assert.NoError(t, l.signNewBatch(context.TODO())) }) } From e967990002b9d2ede96c2d8d0b04d1f385fbb95d Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 17 Jan 2024 10:41:40 +0100 Subject: [PATCH 11/99] Revert "sleep for 1.2s blocktime in between each broadcast to injective" This reverts commit 655e3990a50294e4a0cd8f82398ce15bfa9f2b17. --- orchestrator/cosmos/broadcast.go | 66 ++++---------------------------- 1 file changed, 8 insertions(+), 58 deletions(-) diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index 4c8b5054..510fb65f 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -2,7 +2,7 @@ package cosmos import ( "context" - "sync" + "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -100,16 +100,13 @@ func (s *peggyBroadcastClient) AccFromAddress() sdk.AccAddress { return s.broadcastClient.FromAddress() } -const injectiveBlockTime = 1200 * time.Millisecond - type peggyBroadcastClient struct { daemonQueryClient types.QueryClient broadcastClient chainclient.ChainClient ethSignerFn keystore.SignerFn ethPersonalSignFn keystore.PersonalSignFn - svcTags metrics.Tags - broadcastMux sync.Mutex + svcTags metrics.Tags } func (s *peggyBroadcastClient) UpdatePeggyOrchestratorAddresses( @@ -138,16 +135,14 @@ func (s *peggyBroadcastClient) UpdatePeggyOrchestratorAddresses( Orchestrator: orchestratorAddr.String(), } - s.broadcastMux.Lock() - defer s.broadcastMux.Unlock() - - if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { + res, err := s.broadcastClient.SyncBroadcastMsg(msg) + fmt.Println("Response of set eth address", "res", res) + if err != nil { metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "broadcasting MsgSetOrchestratorAddresses failed") + err = errors.Wrap(err, "broadcasting MsgSetOrchestratorAddresses failed") + return err } - time.Sleep(injectiveBlockTime) - return nil } @@ -189,16 +184,11 @@ func (s *peggyBroadcastClient) SendValsetConfirm( Signature: ethcmn.Bytes2Hex(signature), } - s.broadcastMux.Lock() - defer s.broadcastMux.Unlock() - if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) return errors.Wrap(err, "failed to broadcast MsgValsetConfirm") } - time.Sleep(injectiveBlockTime) - return nil } @@ -235,16 +225,12 @@ func (s *peggyBroadcastClient) SendBatchConfirm( EthSigner: ethFrom.Hex(), TokenContract: batch.TokenContract, } - s.broadcastMux.Lock() - defer s.broadcastMux.Unlock() if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) return errors.Wrap(err, "broadcasting MsgConfirmBatch failed") } - time.Sleep(injectiveBlockTime) - return nil } @@ -279,16 +265,11 @@ func (s *peggyBroadcastClient) sendOldDepositClaims( Data: "", } - s.broadcastMux.Lock() - defer s.broadcastMux.Unlock() - if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") return err } else { - time.Sleep(injectiveBlockTime) - log.WithFields(log.Fields{ "event_nonce": oldDeposit.EventNonce.String(), "tx_hash": txResponse.TxResponse.TxHash, @@ -330,16 +311,11 @@ func (s *peggyBroadcastClient) sendDepositClaims( Data: deposit.Data, } - s.broadcastMux.Lock() - defer s.broadcastMux.Unlock() - if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") return err } else { - time.Sleep(injectiveBlockTime) - log.WithFields(log.Fields{ "event_nonce": deposit.EventNonce.String(), "tx_hash": txResponse.TxResponse.TxHash, @@ -373,16 +349,11 @@ func (s *peggyBroadcastClient) sendWithdrawClaims( Orchestrator: s.AccFromAddress().String(), } - s.broadcastMux.Lock() - defer s.broadcastMux.Unlock() - if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) log.WithError(err).Errorln("broadcasting MsgWithdrawClaim failed") return err } else { - time.Sleep(injectiveBlockTime) - log.WithFields(log.Fields{ "event_nonce": withdraw.EventNonce.String(), "tx_hash": txResponse.TxResponse.TxHash, @@ -427,16 +398,11 @@ func (s *peggyBroadcastClient) sendValsetUpdateClaims( Orchestrator: s.AccFromAddress().String(), } - s.broadcastMux.Lock() - defer s.broadcastMux.Unlock() - if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) log.WithError(err).Errorln("broadcasting MsgValsetUpdatedClaim failed") return err } else { - time.Sleep(injectiveBlockTime) - log.WithFields(log.Fields{ "event_nonce": valsetUpdate.EventNonce.String(), "tx_hash": txResponse.TxResponse.TxHash, @@ -474,16 +440,11 @@ func (s *peggyBroadcastClient) sendErc20DeployedClaims( Orchestrator: s.AccFromAddress().String(), } - s.broadcastMux.Lock() - defer s.broadcastMux.Unlock() - if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) log.WithError(err).Errorln("broadcasting MsgERC20DeployedClaim failed") return err } else { - time.Sleep(injectiveBlockTime) - log.WithFields(log.Fields{ "event_nonce": erc20Deployed.EventNonce.String(), "tx_hash": txResponse.TxResponse.TxHash, @@ -560,7 +521,7 @@ func (s *peggyBroadcastClient) SendEthereumClaims( // Considering blockTime=1s on Injective chain, Adding Sleep to make sure new event is // sent only after previous event is executed successfully. // Otherwise it will through `non contiguous event nonce` failing CheckTx. - //time.Sleep(1200 * time.Millisecond) + time.Sleep(1200 * time.Millisecond) } return nil } @@ -592,17 +553,11 @@ func (s *peggyBroadcastClient) SendToEth( Amount: amount, BridgeFee: fee, // TODO: use exactly that fee for transaction } - - s.broadcastMux.Lock() - defer s.broadcastMux.Unlock() - if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) return errors.Wrap(err, "broadcasting MsgSendToEth failed") } - time.Sleep(injectiveBlockTime) - return nil } @@ -628,15 +583,10 @@ func (s *peggyBroadcastClient) SendRequestBatch( Orchestrator: s.AccFromAddress().String(), } - s.broadcastMux.Lock() - defer s.broadcastMux.Unlock() - if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) return errors.Wrap(err, "broadcasting MsgRequestBatch failed") } - time.Sleep(injectiveBlockTime) - return nil } From 42cbeaac64de129d0a51f2e960c2f8f7251b4320 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 17 Jan 2024 10:41:57 +0100 Subject: [PATCH 12/99] Revert "replace QueueBroadcastMsg calls with SyncBroadcastMsg" This reverts commit 7f768318e1bfbda87d4967bfdae3156d2c6aae10. --- orchestrator/cosmos/broadcast.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index 510fb65f..d88a229e 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -160,7 +160,8 @@ func (s *peggyBroadcastClient) SendValsetConfirm( signature, err := s.ethPersonalSignFn(ethFrom, confirmHash.Bytes()) if err != nil { metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "failed to sign validator address") + err = errors.New("failed to sign validator address") + return err } // MsgValsetConfirm // this is the message sent by the validators when they wish to submit their @@ -183,10 +184,10 @@ func (s *peggyBroadcastClient) SendValsetConfirm( Nonce: valset.Nonce, Signature: ethcmn.Bytes2Hex(signature), } - - if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { + if err = s.broadcastClient.QueueBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "failed to broadcast MsgValsetConfirm") + err = errors.Wrap(err, "broadcasting MsgValsetConfirm failed") + return err } return nil @@ -225,10 +226,10 @@ func (s *peggyBroadcastClient) SendBatchConfirm( EthSigner: ethFrom.Hex(), TokenContract: batch.TokenContract, } - - if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { + if err = s.broadcastClient.QueueBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "broadcasting MsgConfirmBatch failed") + err = errors.Wrap(err, "broadcasting MsgConfirmBatch failed") + return err } return nil @@ -553,9 +554,10 @@ func (s *peggyBroadcastClient) SendToEth( Amount: amount, BridgeFee: fee, // TODO: use exactly that fee for transaction } - if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { + if err := s.broadcastClient.QueueBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "broadcasting MsgSendToEth failed") + err = errors.Wrap(err, "broadcasting MsgSendToEth failed") + return err } return nil @@ -582,10 +584,10 @@ func (s *peggyBroadcastClient) SendRequestBatch( Denom: denom, Orchestrator: s.AccFromAddress().String(), } - - if _, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { + if err := s.broadcastClient.QueueBroadcastMsg(msg); err != nil { metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "broadcasting MsgRequestBatch failed") + err = errors.Wrap(err, "broadcasting MsgRequestBatch failed") + return err } return nil From c24e667abcd9c86e49ea77f82613f570e7fc0436 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 17 Jan 2024 11:56:05 +0100 Subject: [PATCH 13/99] sort eth events in broadcast client --- orchestrator/cosmos/broadcast.go | 332 ++++++++++++++++++------------- 1 file changed, 194 insertions(+), 138 deletions(-) diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index d88a229e..d20f5217 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -235,6 +235,200 @@ func (s *peggyBroadcastClient) SendBatchConfirm( return nil } +func (s *peggyBroadcastClient) SendToEth( + ctx context.Context, + destination ethcmn.Address, + amount, fee sdk.Coin, +) error { + // MsgSendToEth + // This is the message that a user calls when they want to bridge an asset + // it will later be removed when it is included in a batch and successfully + // submitted tokens are removed from the users balance immediately + // ------------- + // AMOUNT: + // the coin to send across the bridge, note the restriction that this is a + // single coin not a set of coins that is normal in other Cosmos messages + // FEE: + // the fee paid for the bridge, distinct from the fee paid to the chain to + // actually send this message in the first place. So a successful send has + // two layers of fees for the user + metrics.ReportFuncCall(s.svcTags) + doneFn := metrics.ReportFuncTiming(s.svcTags) + defer doneFn() + + msg := &types.MsgSendToEth{ + Sender: s.AccFromAddress().String(), + EthDest: destination.Hex(), + Amount: amount, + BridgeFee: fee, // TODO: use exactly that fee for transaction + } + if err := s.broadcastClient.QueueBroadcastMsg(msg); err != nil { + metrics.ReportFuncError(s.svcTags) + err = errors.Wrap(err, "broadcasting MsgSendToEth failed") + return err + } + + return nil +} + +func (s *peggyBroadcastClient) SendRequestBatch( + ctx context.Context, + denom string, +) error { + // MsgRequestBatch + // this is a message anyone can send that requests a batch of transactions to + // send across the bridge be created for whatever block height this message is + // included in. This acts as a coordination point, the handler for this message + // looks at the AddToOutgoingPool tx's in the store and generates a batch, also + // available in the store tied to this message. The validators then grab this + // batch, sign it, submit the signatures with a MsgConfirmBatch before a relayer + // can finally submit the batch + // ------------- + metrics.ReportFuncCall(s.svcTags) + doneFn := metrics.ReportFuncTiming(s.svcTags) + defer doneFn() + + msg := &types.MsgRequestBatch{ + Denom: denom, + Orchestrator: s.AccFromAddress().String(), + } + if err := s.broadcastClient.QueueBroadcastMsg(msg); err != nil { + metrics.ReportFuncError(s.svcTags) + err = errors.Wrap(err, "broadcasting MsgRequestBatch failed") + return err + } + + return nil +} + +type event interface { + Nonce() uint64 +} + +type ( + eventSendToCosmos wrappers.PeggySendToCosmosEvent + eventSendToInjective wrappers.PeggySendToInjectiveEvent + eventTransactionBatchExecuted wrappers.PeggyTransactionBatchExecutedEvent + eventERC20Deployed wrappers.PeggyERC20DeployedEvent + eventValsetUpdated wrappers.PeggyValsetUpdatedEvent +) + +func (e eventSendToCosmos) Nonce() uint64 { + return e.EventNonce.Uint64() +} + +func (e eventSendToInjective) Nonce() uint64 { + return e.EventNonce.Uint64() +} + +func (e eventTransactionBatchExecuted) Nonce() uint64 { + return e.EventNonce.Uint64() +} + +func (e eventERC20Deployed) Nonce() uint64 { + return e.EventNonce.Uint64() +} + +func (e eventValsetUpdated) Nonce() uint64 { + return e.EventNonce.Uint64() +} + +func (s *peggyBroadcastClient) SendEthereumClaims( + ctx context.Context, + lastClaimEvent uint64, + oldDeposits []*wrappers.PeggySendToCosmosEvent, + deposits []*wrappers.PeggySendToInjectiveEvent, + withdraws []*wrappers.PeggyTransactionBatchExecutedEvent, + erc20Deployed []*wrappers.PeggyERC20DeployedEvent, + valsetUpdates []*wrappers.PeggyValsetUpdatedEvent, +) error { + metrics.ReportFuncCall(s.svcTags) + doneFn := metrics.ReportFuncTiming(s.svcTags) + defer doneFn() + + events := make([]event, 0) + + for _, deposit := range oldDeposits { + events = append(events, eventSendToCosmos(*deposit)) + } + + for _, deposit := range deposits { + events = append(events, eventSendToInjective(*deposit)) + } + + for _, withdrawal := range withdraws { + events = append(events, eventTransactionBatchExecuted(*withdrawal)) + } + + for _, deployment := range erc20Deployed { + events = append(events, eventERC20Deployed(*deployment)) + } + + for _, vs := range valsetUpdates { + events = append(events, eventValsetUpdated(*vs)) + } + + // todo: sort the events + + totalClaimEvents := len(oldDeposits) + len(deposits) + len(withdraws) + len(erc20Deployed) + len(valsetUpdates) + var count, h, i, j, k, l int + + // Individual arrays (oldDeposits, deposits, withdraws, valsetUpdates) are sorted. + // Broadcast claim events sequentially starting with eventNonce = lastClaimEvent + 1. + for count < totalClaimEvents { + if h < len(oldDeposits) && oldDeposits[h].EventNonce.Uint64() == lastClaimEvent+1 { + // send old deposit + if err := s.sendOldDepositClaims(ctx, oldDeposits[h]); err != nil { + metrics.ReportFuncError(s.svcTags) + log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") + return err + } + h++ + } + if i < len(deposits) && deposits[i].EventNonce.Uint64() == lastClaimEvent+1 { + // send deposit + if err := s.sendDepositClaims(ctx, deposits[i]); err != nil { + metrics.ReportFuncError(s.svcTags) + log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") + return err + } + i++ + } else if j < len(withdraws) && withdraws[j].EventNonce.Uint64() == lastClaimEvent+1 { + // send withdraw claim + if err := s.sendWithdrawClaims(ctx, withdraws[j]); err != nil { + metrics.ReportFuncError(s.svcTags) + log.WithError(err).Errorln("broadcasting MsgWithdrawClaim failed") + return err + } + j++ + } else if k < len(valsetUpdates) && valsetUpdates[k].EventNonce.Uint64() == lastClaimEvent+1 { + // send valset update claim + if err := s.sendValsetUpdateClaims(ctx, valsetUpdates[k]); err != nil { + metrics.ReportFuncError(s.svcTags) + log.WithError(err).Errorln("broadcasting MsgValsetUpdateClaim failed") + return err + } + k++ + } else if l < len(erc20Deployed) && erc20Deployed[l].EventNonce.Uint64() == lastClaimEvent+1 { + // send erc20 deployed claim + if err := s.sendErc20DeployedClaims(ctx, erc20Deployed[l]); err != nil { + metrics.ReportFuncError(s.svcTags) + log.WithError(err).Errorln("broadcasting MsgERC20DeployedClaim failed") + return err + } + l++ + } + count = count + 1 + lastClaimEvent = lastClaimEvent + 1 + + // Considering blockTime=1s on Injective chain, Adding Sleep to make sure new event is + // sent only after previous event is executed successfully. + // Otherwise it will through `non contiguous event nonce` failing CheckTx. + time.Sleep(1200 * time.Millisecond) + } + return nil +} + func (s *peggyBroadcastClient) sendOldDepositClaims( ctx context.Context, oldDeposit *wrappers.PeggySendToCosmosEvent, @@ -454,141 +648,3 @@ func (s *peggyBroadcastClient) sendErc20DeployedClaims( return nil } - -func (s *peggyBroadcastClient) SendEthereumClaims( - ctx context.Context, - lastClaimEvent uint64, - oldDeposits []*wrappers.PeggySendToCosmosEvent, - deposits []*wrappers.PeggySendToInjectiveEvent, - withdraws []*wrappers.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*wrappers.PeggyERC20DeployedEvent, - valsetUpdates []*wrappers.PeggyValsetUpdatedEvent, -) error { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - totalClaimEvents := len(oldDeposits) + len(deposits) + len(withdraws) + len(erc20Deployed) + len(valsetUpdates) - var count, h, i, j, k, l int - - // Individual arrays (oldDeposits, deposits, withdraws, valsetUpdates) are sorted. - // Broadcast claim events sequentially starting with eventNonce = lastClaimEvent + 1. - for count < totalClaimEvents { - if h < len(oldDeposits) && oldDeposits[h].EventNonce.Uint64() == lastClaimEvent+1 { - // send old deposit - if err := s.sendOldDepositClaims(ctx, oldDeposits[h]); err != nil { - metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") - return err - } - h++ - } - if i < len(deposits) && deposits[i].EventNonce.Uint64() == lastClaimEvent+1 { - // send deposit - if err := s.sendDepositClaims(ctx, deposits[i]); err != nil { - metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") - return err - } - i++ - } else if j < len(withdraws) && withdraws[j].EventNonce.Uint64() == lastClaimEvent+1 { - // send withdraw claim - if err := s.sendWithdrawClaims(ctx, withdraws[j]); err != nil { - metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgWithdrawClaim failed") - return err - } - j++ - } else if k < len(valsetUpdates) && valsetUpdates[k].EventNonce.Uint64() == lastClaimEvent+1 { - // send valset update claim - if err := s.sendValsetUpdateClaims(ctx, valsetUpdates[k]); err != nil { - metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgValsetUpdateClaim failed") - return err - } - k++ - } else if l < len(erc20Deployed) && erc20Deployed[l].EventNonce.Uint64() == lastClaimEvent+1 { - // send erc20 deployed claim - if err := s.sendErc20DeployedClaims(ctx, erc20Deployed[l]); err != nil { - metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgERC20DeployedClaim failed") - return err - } - l++ - } - count = count + 1 - lastClaimEvent = lastClaimEvent + 1 - - // Considering blockTime=1s on Injective chain, Adding Sleep to make sure new event is - // sent only after previous event is executed successfully. - // Otherwise it will through `non contiguous event nonce` failing CheckTx. - time.Sleep(1200 * time.Millisecond) - } - return nil -} - -func (s *peggyBroadcastClient) SendToEth( - ctx context.Context, - destination ethcmn.Address, - amount, fee sdk.Coin, -) error { - // MsgSendToEth - // This is the message that a user calls when they want to bridge an asset - // it will later be removed when it is included in a batch and successfully - // submitted tokens are removed from the users balance immediately - // ------------- - // AMOUNT: - // the coin to send across the bridge, note the restriction that this is a - // single coin not a set of coins that is normal in other Cosmos messages - // FEE: - // the fee paid for the bridge, distinct from the fee paid to the chain to - // actually send this message in the first place. So a successful send has - // two layers of fees for the user - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - msg := &types.MsgSendToEth{ - Sender: s.AccFromAddress().String(), - EthDest: destination.Hex(), - Amount: amount, - BridgeFee: fee, // TODO: use exactly that fee for transaction - } - if err := s.broadcastClient.QueueBroadcastMsg(msg); err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgSendToEth failed") - return err - } - - return nil -} - -func (s *peggyBroadcastClient) SendRequestBatch( - ctx context.Context, - denom string, -) error { - // MsgRequestBatch - // this is a message anyone can send that requests a batch of transactions to - // send across the bridge be created for whatever block height this message is - // included in. This acts as a coordination point, the handler for this message - // looks at the AddToOutgoingPool tx's in the store and generates a batch, also - // available in the store tied to this message. The validators then grab this - // batch, sign it, submit the signatures with a MsgConfirmBatch before a relayer - // can finally submit the batch - // ------------- - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - msg := &types.MsgRequestBatch{ - Denom: denom, - Orchestrator: s.AccFromAddress().String(), - } - if err := s.broadcastClient.QueueBroadcastMsg(msg); err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgRequestBatch failed") - return err - } - - return nil -} From c2267e28acc28fc722a40ad0730f7a21974be8bb Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 18 Jan 2024 17:04:48 +0100 Subject: [PATCH 14/99] simplify claim sending --- orchestrator/cosmos/broadcast.go | 243 ++++++++++++++++--------------- orchestrator/injective.go | 2 +- orchestrator/oracle.go | 18 +-- 3 files changed, 131 insertions(+), 132 deletions(-) diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index d20f5217..366a1790 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -3,6 +3,7 @@ package cosmos import ( "context" "fmt" + "sort" "time" sdk "github.com/cosmos/cosmos-sdk/types" @@ -10,15 +11,12 @@ import ( "github.com/pkg/errors" log "github.com/xlab/suplog" - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - chainclient "github.com/InjectiveLabs/sdk-go/client/chain" - "github.com/InjectiveLabs/metrics" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" - - wrappers "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + chainclient "github.com/InjectiveLabs/sdk-go/client/chain" ) type PeggyBroadcastClient interface { @@ -38,7 +36,7 @@ type PeggyBroadcastClient interface { ctx context.Context, ethFrom ethcmn.Address, peggyID ethcmn.Hash, - valset *types.Valset, + valset *peggytypes.Valset, ) error // SendBatchConfirm broadcasts in a confirmation for a specific transaction batch set for a specific block height @@ -47,18 +45,18 @@ type PeggyBroadcastClient interface { ctx context.Context, ethFrom ethcmn.Address, peggyID ethcmn.Hash, - batch *types.OutgoingTxBatch, + batch *peggytypes.OutgoingTxBatch, ) error SendEthereumClaims( ctx context.Context, lastClaimEvent uint64, - oldDeposits []*wrappers.PeggySendToCosmosEvent, - deposits []*wrappers.PeggySendToInjectiveEvent, - withdraws []*wrappers.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*wrappers.PeggyERC20DeployedEvent, - valsetUpdates []*wrappers.PeggyValsetUpdatedEvent, - ) error + oldDeposits []*peggyevents.PeggySendToCosmosEvent, + deposits []*peggyevents.PeggySendToInjectiveEvent, + withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, + erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, + valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, + ) (uint64, error) // SendToEth broadcasts a Tx that tokens from Cosmos to Ethereum. // These tokens will not be sent immediately. Instead, they will require @@ -77,7 +75,7 @@ type PeggyBroadcastClient interface { } func NewPeggyBroadcastClient( - queryClient types.QueryClient, + queryClient peggytypes.QueryClient, broadcastClient chainclient.ChainClient, ethPersonalSignFn keystore.PersonalSignFn, ) PeggyBroadcastClient { @@ -101,7 +99,7 @@ func (s *peggyBroadcastClient) AccFromAddress() sdk.AccAddress { } type peggyBroadcastClient struct { - daemonQueryClient types.QueryClient + daemonQueryClient peggytypes.QueryClient broadcastClient chainclient.ChainClient ethSignerFn keystore.SignerFn ethPersonalSignFn keystore.PersonalSignFn @@ -129,7 +127,7 @@ func (s *peggyBroadcastClient) UpdatePeggyOrchestratorAddresses( // sets submissions carry any weight. // ------------- - msg := &types.MsgSetOrchestratorAddresses{ + msg := &peggytypes.MsgSetOrchestratorAddresses{ Sender: s.AccFromAddress().String(), EthAddress: ethFrom.Hex(), Orchestrator: orchestratorAddr.String(), @@ -150,7 +148,7 @@ func (s *peggyBroadcastClient) SendValsetConfirm( ctx context.Context, ethFrom ethcmn.Address, peggyID ethcmn.Hash, - valset *types.Valset, + valset *peggytypes.Valset, ) error { metrics.ReportFuncCall(s.svcTags) doneFn := metrics.ReportFuncTiming(s.svcTags) @@ -178,7 +176,7 @@ func (s *peggyBroadcastClient) SendValsetConfirm( // signatures it is then possible for anyone to view these signatures in the // chain store and submit them to Ethereum to update the validator set // ------------- - msg := &types.MsgValsetConfirm{ + msg := &peggytypes.MsgValsetConfirm{ Orchestrator: s.AccFromAddress().String(), EthAddress: ethFrom.Hex(), Nonce: valset.Nonce, @@ -197,7 +195,7 @@ func (s *peggyBroadcastClient) SendBatchConfirm( ctx context.Context, ethFrom ethcmn.Address, peggyID ethcmn.Hash, - batch *types.OutgoingTxBatch, + batch *peggytypes.OutgoingTxBatch, ) error { metrics.ReportFuncCall(s.svcTags) doneFn := metrics.ReportFuncTiming(s.svcTags) @@ -219,7 +217,7 @@ func (s *peggyBroadcastClient) SendBatchConfirm( // (TODO determine this without nondeterminism) This message includes the batch // as well as an Ethereum signature over this batch by the validator // ------------- - msg := &types.MsgConfirmBatch{ + msg := &peggytypes.MsgConfirmBatch{ Orchestrator: s.AccFromAddress().String(), Nonce: batch.BatchNonce, Signature: ethcmn.Bytes2Hex(signature), @@ -256,7 +254,7 @@ func (s *peggyBroadcastClient) SendToEth( doneFn := metrics.ReportFuncTiming(s.svcTags) defer doneFn() - msg := &types.MsgSendToEth{ + msg := &peggytypes.MsgSendToEth{ Sender: s.AccFromAddress().String(), EthDest: destination.Hex(), Amount: amount, @@ -288,7 +286,7 @@ func (s *peggyBroadcastClient) SendRequestBatch( doneFn := metrics.ReportFuncTiming(s.svcTags) defer doneFn() - msg := &types.MsgRequestBatch{ + msg := &peggytypes.MsgRequestBatch{ Denom: denom, Orchestrator: s.AccFromAddress().String(), } @@ -301,137 +299,142 @@ func (s *peggyBroadcastClient) SendRequestBatch( return nil } +func (s *peggyBroadcastClient) SendEthereumClaims( + ctx context.Context, + lastClaimEventNonce uint64, + oldDeposits []*peggyevents.PeggySendToCosmosEvent, + deposits []*peggyevents.PeggySendToInjectiveEvent, + withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, + erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, + valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, +) (uint64, error) { + metrics.ReportFuncCall(s.svcTags) + doneFn := metrics.ReportFuncTiming(s.svcTags) + defer doneFn() + + events := sortEventsByNonce(oldDeposits, deposits, withdraws, erc20Deployed, valsetUpdates) + + // this can't happen outside of programmer error + if firstToSend := events[0]; firstToSend.Nonce() != lastClaimEventNonce+1 { + return 0, errors.Errorf("expected event with nonce %d, got %d", lastClaimEventNonce+1, firstToSend.Nonce()) + } + + for _, e := range events { + if err := s.sendEventClaim(ctx, e); err != nil { + return 0, err + } + + // Considering blockTime=1s on Injective chain, Adding Sleep to make sure new event is + // sent only after previous event is executed successfully. + // Otherwise it will through `non contiguous event nonce` failing CheckTx. + time.Sleep(1200 * time.Millisecond) + } + + lastClaimEventNonce = events[len(events)-1].Nonce() + + return lastClaimEventNonce, nil +} + type event interface { Nonce() uint64 } type ( - eventSendToCosmos wrappers.PeggySendToCosmosEvent - eventSendToInjective wrappers.PeggySendToInjectiveEvent - eventTransactionBatchExecuted wrappers.PeggyTransactionBatchExecutedEvent - eventERC20Deployed wrappers.PeggyERC20DeployedEvent - eventValsetUpdated wrappers.PeggyValsetUpdatedEvent + eventSendToCosmos peggyevents.PeggySendToCosmosEvent + eventSendToInjective peggyevents.PeggySendToInjectiveEvent + eventTransactionBatchExecuted peggyevents.PeggyTransactionBatchExecutedEvent + eventERC20Deployed peggyevents.PeggyERC20DeployedEvent + eventValsetUpdated peggyevents.PeggyValsetUpdatedEvent ) -func (e eventSendToCosmos) Nonce() uint64 { +func (e *eventSendToCosmos) Nonce() uint64 { return e.EventNonce.Uint64() } -func (e eventSendToInjective) Nonce() uint64 { +func (e *eventSendToInjective) Nonce() uint64 { return e.EventNonce.Uint64() } -func (e eventTransactionBatchExecuted) Nonce() uint64 { +func (e *eventTransactionBatchExecuted) Nonce() uint64 { return e.EventNonce.Uint64() } -func (e eventERC20Deployed) Nonce() uint64 { +func (e *eventERC20Deployed) Nonce() uint64 { return e.EventNonce.Uint64() } -func (e eventValsetUpdated) Nonce() uint64 { +func (e *eventValsetUpdated) Nonce() uint64 { return e.EventNonce.Uint64() } -func (s *peggyBroadcastClient) SendEthereumClaims( - ctx context.Context, - lastClaimEvent uint64, - oldDeposits []*wrappers.PeggySendToCosmosEvent, - deposits []*wrappers.PeggySendToInjectiveEvent, - withdraws []*wrappers.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*wrappers.PeggyERC20DeployedEvent, - valsetUpdates []*wrappers.PeggyValsetUpdatedEvent, -) error { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - events := make([]event, 0) +func sortEventsByNonce( + oldDeposits []*peggyevents.PeggySendToCosmosEvent, + deposits []*peggyevents.PeggySendToInjectiveEvent, + withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, + erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, + valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, +) []event { + total := len(oldDeposits) + len(deposits) + len(withdraws) + len(erc20Deployed) + len(valsetUpdates) + events := make([]event, 0, total) for _, deposit := range oldDeposits { - events = append(events, eventSendToCosmos(*deposit)) + e := eventSendToCosmos(*deposit) + events = append(events, &e) } for _, deposit := range deposits { - events = append(events, eventSendToInjective(*deposit)) + e := eventSendToInjective(*deposit) + events = append(events, &e) } for _, withdrawal := range withdraws { - events = append(events, eventTransactionBatchExecuted(*withdrawal)) + e := eventTransactionBatchExecuted(*withdrawal) + events = append(events, &e) } for _, deployment := range erc20Deployed { - events = append(events, eventERC20Deployed(*deployment)) + e := eventERC20Deployed(*deployment) + events = append(events, &e) } for _, vs := range valsetUpdates { - events = append(events, eventValsetUpdated(*vs)) + e := eventValsetUpdated(*vs) + events = append(events, &e) } - // todo: sort the events + // sort by nonce + sort.Slice(events, func(i, j int) bool { + return events[i].Nonce() < events[j].Nonce() + }) - totalClaimEvents := len(oldDeposits) + len(deposits) + len(withdraws) + len(erc20Deployed) + len(valsetUpdates) - var count, h, i, j, k, l int - - // Individual arrays (oldDeposits, deposits, withdraws, valsetUpdates) are sorted. - // Broadcast claim events sequentially starting with eventNonce = lastClaimEvent + 1. - for count < totalClaimEvents { - if h < len(oldDeposits) && oldDeposits[h].EventNonce.Uint64() == lastClaimEvent+1 { - // send old deposit - if err := s.sendOldDepositClaims(ctx, oldDeposits[h]); err != nil { - metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") - return err - } - h++ - } - if i < len(deposits) && deposits[i].EventNonce.Uint64() == lastClaimEvent+1 { - // send deposit - if err := s.sendDepositClaims(ctx, deposits[i]); err != nil { - metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") - return err - } - i++ - } else if j < len(withdraws) && withdraws[j].EventNonce.Uint64() == lastClaimEvent+1 { - // send withdraw claim - if err := s.sendWithdrawClaims(ctx, withdraws[j]); err != nil { - metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgWithdrawClaim failed") - return err - } - j++ - } else if k < len(valsetUpdates) && valsetUpdates[k].EventNonce.Uint64() == lastClaimEvent+1 { - // send valset update claim - if err := s.sendValsetUpdateClaims(ctx, valsetUpdates[k]); err != nil { - metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgValsetUpdateClaim failed") - return err - } - k++ - } else if l < len(erc20Deployed) && erc20Deployed[l].EventNonce.Uint64() == lastClaimEvent+1 { - // send erc20 deployed claim - if err := s.sendErc20DeployedClaims(ctx, erc20Deployed[l]); err != nil { - metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgERC20DeployedClaim failed") - return err - } - l++ - } - count = count + 1 - lastClaimEvent = lastClaimEvent + 1 + return events +} - // Considering blockTime=1s on Injective chain, Adding Sleep to make sure new event is - // sent only after previous event is executed successfully. - // Otherwise it will through `non contiguous event nonce` failing CheckTx. - time.Sleep(1200 * time.Millisecond) +func (s *peggyBroadcastClient) sendEventClaim(ctx context.Context, ev event) error { + switch ev := ev.(type) { + case *eventSendToCosmos: + e := peggyevents.PeggySendToCosmosEvent(*ev) + return s.sendOldDepositClaims(ctx, &e) + case *eventSendToInjective: + e := peggyevents.PeggySendToInjectiveEvent(*ev) + return s.sendDepositClaims(ctx, &e) + case *eventTransactionBatchExecuted: + e := peggyevents.PeggyTransactionBatchExecutedEvent(*ev) + return s.sendWithdrawClaims(ctx, &e) + case *eventERC20Deployed: + e := peggyevents.PeggyERC20DeployedEvent(*ev) + return s.sendErc20DeployedClaims(ctx, &e) + case *eventValsetUpdated: + e := peggyevents.PeggyValsetUpdatedEvent(*ev) + return s.sendValsetUpdateClaims(ctx, &e) } - return nil + + return errors.Errorf("unknown event type %T", ev) } func (s *peggyBroadcastClient) sendOldDepositClaims( ctx context.Context, - oldDeposit *wrappers.PeggySendToCosmosEvent, + oldDeposit *peggyevents.PeggySendToCosmosEvent, ) error { // EthereumBridgeDepositClaim // When more than 66% of the active validator set has @@ -449,7 +452,7 @@ func (s *peggyBroadcastClient) sendOldDepositClaims( "event_nonce": oldDeposit.EventNonce.String(), }).Debugln("observed SendToCosmosEvent") - msg := &types.MsgDepositClaim{ + msg := &peggytypes.MsgDepositClaim{ EventNonce: oldDeposit.EventNonce.Uint64(), BlockHeight: oldDeposit.Raw.BlockNumber, TokenContract: oldDeposit.TokenContract.Hex(), @@ -476,7 +479,7 @@ func (s *peggyBroadcastClient) sendOldDepositClaims( func (s *peggyBroadcastClient) sendDepositClaims( ctx context.Context, - deposit *wrappers.PeggySendToInjectiveEvent, + deposit *peggyevents.PeggySendToInjectiveEvent, ) error { // EthereumBridgeDepositClaim // When more than 66% of the active validator set has @@ -495,7 +498,7 @@ func (s *peggyBroadcastClient) sendDepositClaims( "data": deposit.Data, }).Debugln("observed SendToInjectiveEvent") - msg := &types.MsgDepositClaim{ + msg := &peggytypes.MsgDepositClaim{ EventNonce: deposit.EventNonce.Uint64(), BlockHeight: deposit.Raw.BlockNumber, TokenContract: deposit.TokenContract.Hex(), @@ -522,7 +525,7 @@ func (s *peggyBroadcastClient) sendDepositClaims( func (s *peggyBroadcastClient) sendWithdrawClaims( ctx context.Context, - withdraw *wrappers.PeggyTransactionBatchExecutedEvent, + withdraw *peggyevents.PeggyTransactionBatchExecutedEvent, ) error { metrics.ReportFuncCall(s.svcTags) doneFn := metrics.ReportFuncTiming(s.svcTags) @@ -536,7 +539,7 @@ func (s *peggyBroadcastClient) sendWithdrawClaims( // WithdrawClaim claims that a batch of withdrawal // operations on the bridge contract was executed. - msg := &types.MsgWithdrawClaim{ + msg := &peggytypes.MsgWithdrawClaim{ EventNonce: withdraw.EventNonce.Uint64(), BatchNonce: withdraw.BatchNonce.Uint64(), BlockHeight: withdraw.Raw.BlockNumber, @@ -560,7 +563,7 @@ func (s *peggyBroadcastClient) sendWithdrawClaims( func (s *peggyBroadcastClient) sendValsetUpdateClaims( ctx context.Context, - valsetUpdate *wrappers.PeggyValsetUpdatedEvent, + valsetUpdate *peggyevents.PeggyValsetUpdatedEvent, ) error { metrics.ReportFuncCall(s.svcTags) doneFn := metrics.ReportFuncTiming(s.svcTags) @@ -575,15 +578,15 @@ func (s *peggyBroadcastClient) sendValsetUpdateClaims( "reward_token": valsetUpdate.RewardToken.Hex(), }).Debugln("observed ValsetUpdatedEvent") - members := make([]*types.BridgeValidator, len(valsetUpdate.Validators)) + members := make([]*peggytypes.BridgeValidator, len(valsetUpdate.Validators)) for i, val := range valsetUpdate.Validators { - members[i] = &types.BridgeValidator{ + members[i] = &peggytypes.BridgeValidator{ EthereumAddress: val.Hex(), Power: valsetUpdate.Powers[i].Uint64(), } } - msg := &types.MsgValsetUpdatedClaim{ + msg := &peggytypes.MsgValsetUpdatedClaim{ EventNonce: valsetUpdate.EventNonce.Uint64(), ValsetNonce: valsetUpdate.NewValsetNonce.Uint64(), BlockHeight: valsetUpdate.Raw.BlockNumber, @@ -609,7 +612,7 @@ func (s *peggyBroadcastClient) sendValsetUpdateClaims( func (s *peggyBroadcastClient) sendErc20DeployedClaims( ctx context.Context, - erc20Deployed *wrappers.PeggyERC20DeployedEvent, + erc20Deployed *peggyevents.PeggyERC20DeployedEvent, ) error { metrics.ReportFuncCall(s.svcTags) doneFn := metrics.ReportFuncTiming(s.svcTags) @@ -624,7 +627,7 @@ func (s *peggyBroadcastClient) sendErc20DeployedClaims( "decimals": erc20Deployed.Decimals, }).Debugln("observed ERC20DeployedEvent") - msg := &types.MsgERC20DeployedClaim{ + msg := &peggytypes.MsgERC20DeployedClaim{ EventNonce: erc20Deployed.EventNonce.Uint64(), BlockHeight: erc20Deployed.Raw.BlockNumber, CosmosDenom: erc20Deployed.CosmosDenom, diff --git a/orchestrator/injective.go b/orchestrator/injective.go index 89af15e2..b2d2f7fa 100644 --- a/orchestrator/injective.go +++ b/orchestrator/injective.go @@ -25,7 +25,7 @@ type InjectiveNetwork interface { withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, - ) error + ) (uint64, error) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) SendRequestBatch(ctx context.Context, denom string) error diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 9dc6c3d6..2311d68d 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -200,30 +200,26 @@ func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { erc20Deployments = filterERC20DeployedEventsByNonce(erc20Deployments, lastClaimEvent.EthereumEventNonce) valsetUpdates = filterValsetUpdateEventsByNonce(valsetUpdates, lastClaimEvent.EthereumEventNonce) - if noEvents := len(legacyDeposits) == 0 && len(deposits) == 0 && len(withdrawals) == 0 && - len(erc20Deployments) == 0 && len(valsetUpdates) == 0; noEvents { + events := len(legacyDeposits) + len(deposits) + len(withdrawals) + len(erc20Deployments) + len(valsetUpdates) + if events == 0 { l.Logger().Infoln("no new events on Ethereum") return nil } - if err := l.inj.SendEthereumClaims(ctx, + latestEventNonce, err := l.inj.SendEthereumClaims(ctx, lastClaimEvent.EthereumEventNonce, legacyDeposits, deposits, withdrawals, erc20Deployments, valsetUpdates, - ); err != nil { + ) + + if err != nil { return errors.Wrap(err, "failed to send event claims to Injective") } - l.Logger().WithFields(log.Fields{ - "legacy_deposits": len(legacyDeposits), - "deposits": len(deposits), - "withdrawals": len(withdrawals), - "erc20_deployments": len(erc20Deployments), - "valset_updates": len(valsetUpdates), - }).Infoln("sent new event claims to Injective") + l.Logger().WithFields(log.Fields{"num": events, "latest_event_nonce": latestEventNonce}).Infoln("sent new event claims to Injective") return nil } From c9f906dc49e45e0e3e959117125166bc7975b983 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 19 Jan 2024 11:39:49 +0100 Subject: [PATCH 15/99] refactor relayEvents --- orchestrator/oracle.go | 250 +++++++++++++++++++++++++++++++++-------- 1 file changed, 202 insertions(+), 48 deletions(-) diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 2311d68d..9f6b2bc2 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "time" "github.com/avast/retry-go" @@ -132,99 +133,152 @@ func (l *ethOracleLoop) observeEthEvents(ctx context.Context) error { } func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { - var ( - currentHeight = l.lastCheckedEthHeight - latestHeight uint64 - ) - - scanEthBlocksAndRelayEventsFn := func() error { - metrics.ReportFuncCall(l.svcTags) - doneFn := metrics.ReportFuncTiming(l.svcTags) - defer doneFn() - + var latestHeight uint64 + getLatestEthHeightFn := func() error { latestHeader, err := l.eth.HeaderByNumber(ctx, nil) if err != nil { return errors.Wrap(err, "failed to get latest ethereum header") } - // add delay to ensure minimum confirmations are received and block is finalised - latestHeight = latestHeader.Number.Uint64() - ethBlockConfirmationDelay - if latestHeight < currentHeight { - return nil - } + latestHeight = latestHeader.Number.Uint64() + return nil + } - if latestHeight > currentHeight+defaultBlocksToSearch { - latestHeight = currentHeight + defaultBlocksToSearch - } + if err := retry.Do(getLatestEthHeightFn, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) + }), + ); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return 0, err + } + + // not enough blocks on ethereum yet + if latestHeight <= ethBlockConfirmationDelay { + return l.lastCheckedEthHeight, nil + } + + latestHeight = latestHeight - ethBlockConfirmationDelay // ensure minimum confirmations that a block is finalized + if latestHeight <= l.lastCheckedEthHeight { + return l.lastCheckedEthHeight, nil + } + + if latestHeight > l.lastCheckedEthHeight+defaultBlocksToSearch { + latestHeight = l.lastCheckedEthHeight + defaultBlocksToSearch + } - legacyDeposits, err := l.eth.GetSendToCosmosEvents(currentHeight, latestHeight) + // query all events within [lastCheckedEthHeight, latestHeight] range + events := struct { + oldDeposits []*peggyevents.PeggySendToCosmosEvent + deposits []*peggyevents.PeggySendToInjectiveEvent + withdrawals []*peggyevents.PeggyTransactionBatchExecutedEvent + valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent + erc20Deployments []*peggyevents.PeggyERC20DeployedEvent + }{} + + scanEthEventsFn := func() error { + legacyDeposits, err := l.eth.GetSendToCosmosEvents(l.lastCheckedEthHeight, latestHeight) if err != nil { return errors.Wrap(err, "failed to get SendToCosmos events") } - deposits, err := l.eth.GetSendToInjectiveEvents(currentHeight, latestHeight) + deposits, err := l.eth.GetSendToInjectiveEvents(l.lastCheckedEthHeight, latestHeight) if err != nil { return errors.Wrap(err, "failed to get SendToInjective events") } - withdrawals, err := l.eth.GetTransactionBatchExecutedEvents(currentHeight, latestHeight) + withdrawals, err := l.eth.GetTransactionBatchExecutedEvents(l.lastCheckedEthHeight, latestHeight) if err != nil { return errors.Wrap(err, "failed to get TransactionBatchExecuted events") } - erc20Deployments, err := l.eth.GetPeggyERC20DeployedEvents(currentHeight, latestHeight) + erc20Deployments, err := l.eth.GetPeggyERC20DeployedEvents(l.lastCheckedEthHeight, latestHeight) if err != nil { return errors.Wrap(err, "failed to get ERC20Deployed events") } - valsetUpdates, err := l.eth.GetValsetUpdatedEvents(currentHeight, latestHeight) + valsetUpdates, err := l.eth.GetValsetUpdatedEvents(l.lastCheckedEthHeight, latestHeight) if err != nil { return errors.Wrap(err, "failed to get ValsetUpdated events") } - // note that starting block overlaps with our last checked block, because we have to deal with - // the possibility that the relayer was killed after relaying only one of multiple events in a single - // block, so we also need this routine so make sure we don't send in the first event in this hypothetical - // multi event block again. In theory we only send all events for every block and that will pass of fail - // atomically but lets not take that risk. - lastClaimEvent, err := l.inj.LastClaimEvent(ctx) + events.oldDeposits = legacyDeposits + events.deposits = deposits + events.withdrawals = withdrawals + events.valsetUpdates = valsetUpdates + events.erc20Deployments = erc20Deployments + + return nil + } + + if err := retry.Do(scanEthEventsFn, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) + }), + ); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return 0, err + } + + var lastClaimEvent *types.LastClaimEvent + getLastClaimEventFn := func() error { + claim, err := l.inj.LastClaimEvent(ctx) if err != nil { return errors.Wrap(err, "failed to query last claim event from Injective") } - l.Logger().WithFields(log.Fields{"event_nonce": lastClaimEvent.EthereumEventNonce, "event_height": lastClaimEvent.EthereumEventHeight}).Debugln("last observed Ethereum event") + lastClaimEvent = claim + return nil + } - legacyDeposits = filterSendToCosmosEventsByNonce(legacyDeposits, lastClaimEvent.EthereumEventNonce) - deposits = filterSendToInjectiveEventsByNonce(deposits, lastClaimEvent.EthereumEventNonce) - withdrawals = filterTransactionBatchExecutedEventsByNonce(withdrawals, lastClaimEvent.EthereumEventNonce) - erc20Deployments = filterERC20DeployedEventsByNonce(erc20Deployments, lastClaimEvent.EthereumEventNonce) - valsetUpdates = filterValsetUpdateEventsByNonce(valsetUpdates, lastClaimEvent.EthereumEventNonce) + if err := retry.Do(getLastClaimEventFn, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) + }), + ); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return 0, err + } - events := len(legacyDeposits) + len(deposits) + len(withdrawals) + len(erc20Deployments) + len(valsetUpdates) - if events == 0 { - l.Logger().Infoln("no new events on Ethereum") - return nil - } + // filter processed events + events.oldDeposits = filterSendToCosmosEventsByNonce(events.oldDeposits, lastClaimEvent.EthereumEventNonce) + events.deposits = filterSendToInjectiveEventsByNonce(events.deposits, lastClaimEvent.EthereumEventNonce) + events.withdrawals = filterTransactionBatchExecutedEventsByNonce(events.withdrawals, lastClaimEvent.EthereumEventNonce) + events.erc20Deployments = filterERC20DeployedEventsByNonce(events.erc20Deployments, lastClaimEvent.EthereumEventNonce) + events.valsetUpdates = filterValsetUpdateEventsByNonce(events.valsetUpdates, lastClaimEvent.EthereumEventNonce) + + eventsNum := len(events.oldDeposits) + len(events.deposits) + len(events.valsetUpdates) + len(events.withdrawals) + len(events.erc20Deployments) + if eventsNum == 0 { + l.Logger().Infoln("no new events on Ethereum") + return latestHeight, nil + } - latestEventNonce, err := l.inj.SendEthereumClaims(ctx, + sendClaimsFn := func() error { + newEventNonce, err := l.inj.SendEthereumClaims(ctx, lastClaimEvent.EthereumEventNonce, - legacyDeposits, - deposits, - withdrawals, - erc20Deployments, - valsetUpdates, + events.oldDeposits, + events.deposits, + events.withdrawals, + events.erc20Deployments, + events.valsetUpdates, ) if err != nil { return errors.Wrap(err, "failed to send event claims to Injective") } - l.Logger().WithFields(log.Fields{"num": events, "latest_event_nonce": latestEventNonce}).Infoln("sent new event claims to Injective") + l.Logger().WithFields(log.Fields{"num": events, "latest_event_nonce": newEventNonce}).Infoln("sent new event claims to Injective") return nil } - if err := retry.Do(scanEthBlocksAndRelayEventsFn, + if err := retry.Do(sendClaimsFn, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -236,6 +290,106 @@ func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { } return latestHeight, nil + // + //scanEthBlocksAndRelayEventsFn := func() error { + // metrics.ReportFuncCall(l.svcTags) + // doneFn := metrics.ReportFuncTiming(l.svcTags) + // defer doneFn() + // + // latestHeader, err := l.eth.HeaderByNumber(ctx, nil) + // if err != nil { + // return errors.Wrap(err, "failed to get latest ethereum header") + // } + // + // // add delay to ensure minimum confirmations are received and block is finalised + // latestHeight = latestHeader.Number.Uint64() - ethBlockConfirmationDelay + // if latestHeight < currentHeight { + // return nil + // } + // + // if latestHeight > currentHeight+defaultBlocksToSearch { + // latestHeight = currentHeight + defaultBlocksToSearch + // } + // + // legacyDeposits, err := l.eth.GetSendToCosmosEvents(currentHeight, latestHeight) + // if err != nil { + // return errors.Wrap(err, "failed to get SendToCosmos events") + // } + // + // deposits, err := l.eth.GetSendToInjectiveEvents(currentHeight, latestHeight) + // if err != nil { + // return errors.Wrap(err, "failed to get SendToInjective events") + // } + // + // withdrawals, err := l.eth.GetTransactionBatchExecutedEvents(currentHeight, latestHeight) + // if err != nil { + // return errors.Wrap(err, "failed to get TransactionBatchExecuted events") + // } + // + // erc20Deployments, err := l.eth.GetPeggyERC20DeployedEvents(currentHeight, latestHeight) + // if err != nil { + // return errors.Wrap(err, "failed to get ERC20Deployed events") + // } + // + // valsetUpdates, err := l.eth.GetValsetUpdatedEvents(currentHeight, latestHeight) + // if err != nil { + // return errors.Wrap(err, "failed to get ValsetUpdated events") + // } + // + // // note that starting block overlaps with our last checked block, because we have to deal with + // // the possibility that the relayer was killed after relaying only one of multiple events in a single + // // block, so we also need this routine so make sure we don't send in the first event in this hypothetical + // // multi event block again. In theory we only send all events for every block and that will pass of fail + // // atomically but lets not take that risk. + // lastClaimEvent, err := l.inj.LastClaimEvent(ctx) + // if err != nil { + // return errors.Wrap(err, "failed to query last claim event from Injective") + // } + // + // l.Logger().WithFields(log.Fields{"event_nonce": lastClaimEvent.EthereumEventNonce, "event_height": lastClaimEvent.EthereumEventHeight}).Debugln("last observed Ethereum event") + // + // legacyDeposits = filterSendToCosmosEventsByNonce(legacyDeposits, lastClaimEvent.EthereumEventNonce) + // deposits = filterSendToInjectiveEventsByNonce(deposits, lastClaimEvent.EthereumEventNonce) + // withdrawals = filterTransactionBatchExecutedEventsByNonce(withdrawals, lastClaimEvent.EthereumEventNonce) + // erc20Deployments = filterERC20DeployedEventsByNonce(erc20Deployments, lastClaimEvent.EthereumEventNonce) + // valsetUpdates = filterValsetUpdateEventsByNonce(valsetUpdates, lastClaimEvent.EthereumEventNonce) + // + // events := len(legacyDeposits) + len(deposits) + len(withdrawals) + len(erc20Deployments) + len(valsetUpdates) + // if events == 0 { + // l.Logger().Infoln("no new events on Ethereum") + // return nil + // } + // + // latestEventNonce, err := l.inj.SendEthereumClaims(ctx, + // lastClaimEvent.EthereumEventNonce, + // legacyDeposits, + // deposits, + // withdrawals, + // erc20Deployments, + // valsetUpdates, + // ) + // + // if err != nil { + // return errors.Wrap(err, "failed to send event claims to Injective") + // } + // + // l.Logger().WithFields(log.Fields{"num": events, "latest_event_nonce": latestEventNonce}).Infoln("sent new event claims to Injective") + // + // return nil + //} + // + //if err := retry.Do(scanEthBlocksAndRelayEventsFn, + // retry.Context(ctx), + // retry.Attempts(l.maxAttempts), + // retry.OnRetry(func(n uint, err error) { + // l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) + // }), + //); err != nil { + // l.Logger().WithError(err).Errorln("got error, loop exits") + // return 0, err + //} + // + //return latestHeight, nil } func (l *ethOracleLoop) autoResync(ctx context.Context) error { From 4261edcbe423b117e00a2d5ab8825f6cbd5c88f3 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 19 Jan 2024 13:00:17 +0100 Subject: [PATCH 16/99] refactor event relaying --- orchestrator/oracle.go | 418 ++++++++++++++--------------------- test/peggo/peggy_params.json | 38 ++++ 2 files changed, 200 insertions(+), 256 deletions(-) create mode 100644 test/peggo/peggy_params.json diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 9f6b2bc2..9b1e8afa 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -133,25 +133,8 @@ func (l *ethOracleLoop) observeEthEvents(ctx context.Context) error { } func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { - var latestHeight uint64 - getLatestEthHeightFn := func() error { - latestHeader, err := l.eth.HeaderByNumber(ctx, nil) - if err != nil { - return errors.Wrap(err, "failed to get latest ethereum header") - } - - latestHeight = latestHeader.Number.Uint64() - return nil - } - - if err := retry.Do(getLatestEthHeightFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) - }), - ); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") + latestHeight, err := l.getLatestEthHeight(ctx) + if err != nil { return 0, err } @@ -160,7 +143,8 @@ func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { return l.lastCheckedEthHeight, nil } - latestHeight = latestHeight - ethBlockConfirmationDelay // ensure minimum confirmations that a block is finalized + // ensure that latest block has minimum confirmations + latestHeight = latestHeight - ethBlockConfirmationDelay if latestHeight <= l.lastCheckedEthHeight { return l.lastCheckedEthHeight, nil } @@ -169,69 +153,91 @@ func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { latestHeight = l.lastCheckedEthHeight + defaultBlocksToSearch } - // query all events within [lastCheckedEthHeight, latestHeight] range - events := struct { - oldDeposits []*peggyevents.PeggySendToCosmosEvent - deposits []*peggyevents.PeggySendToInjectiveEvent - withdrawals []*peggyevents.PeggyTransactionBatchExecutedEvent - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent - erc20Deployments []*peggyevents.PeggyERC20DeployedEvent - }{} + events, err := l.getEthEvents(ctx, l.lastCheckedEthHeight, latestHeight) + if err != nil { + return 0, err + } - scanEthEventsFn := func() error { - legacyDeposits, err := l.eth.GetSendToCosmosEvents(l.lastCheckedEthHeight, latestHeight) - if err != nil { - return errors.Wrap(err, "failed to get SendToCosmos events") - } + lastClaim, err := l.getLastEventClaim(ctx) + if err != nil { + return 0, err + } - deposits, err := l.eth.GetSendToInjectiveEvents(l.lastCheckedEthHeight, latestHeight) - if err != nil { - return errors.Wrap(err, "failed to get SendToInjective events") - } + // filter processed events + events = events.Filter(lastClaim.EthereumEventNonce) + if events.Num() == 0 { + l.Logger().Infoln("no new events on Ethereum") + return latestHeight, nil + } - withdrawals, err := l.eth.GetTransactionBatchExecutedEvents(l.lastCheckedEthHeight, latestHeight) - if err != nil { - return errors.Wrap(err, "failed to get TransactionBatchExecuted events") - } + if err := l.sendNewEventClaims(ctx, lastClaim, events); err != nil { + return 0, err + } - erc20Deployments, err := l.eth.GetPeggyERC20DeployedEvents(l.lastCheckedEthHeight, latestHeight) - if err != nil { - return errors.Wrap(err, "failed to get ERC20Deployed events") - } + return latestHeight, nil +} + +func (l *ethOracleLoop) autoResync(ctx context.Context) error { + var latestHeight uint64 + getLastClaimEventFn := func() (err error) { + latestHeight, err = l.getLastClaimBlockHeight(ctx) + return + } + + if err := retry.Do(getLastClaimEventFn, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("failed to get last confirmed eth height, will retry (%d)", n) + }), + ); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return err + } + + l.lastCheckedEthHeight = latestHeight + l.lastResyncWithInjective = time.Now() + + l.Logger().WithFields(log.Fields{"last_resync_time": l.lastResyncWithInjective.String(), "last_confirmed_eth_height": l.lastCheckedEthHeight}).Infoln("auto resync event nonce with Injective") + + return nil +} - valsetUpdates, err := l.eth.GetValsetUpdatedEvents(l.lastCheckedEthHeight, latestHeight) +func (l *ethOracleLoop) getLatestEthHeight(ctx context.Context) (uint64, error) { + var latestHeight uint64 + getLatestEthHeightFn := func() error { + latestHeader, err := l.eth.HeaderByNumber(ctx, nil) if err != nil { - return errors.Wrap(err, "failed to get ValsetUpdated events") + return errors.Wrap(err, "failed to get latest ethereum header") } - events.oldDeposits = legacyDeposits - events.deposits = deposits - events.withdrawals = withdrawals - events.valsetUpdates = valsetUpdates - events.erc20Deployments = erc20Deployments - + latestHeight = latestHeader.Number.Uint64() return nil } - if err := retry.Do(scanEthEventsFn, + if err := retry.Do(getLatestEthHeightFn, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to get latest eth header, will retry (%d)", n) }), ); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return 0, err } - var lastClaimEvent *types.LastClaimEvent + return latestHeight, nil +} + +func (l *ethOracleLoop) getLastEventClaim(ctx context.Context) (*types.LastClaimEvent, error) { + var claim *types.LastClaimEvent getLastClaimEventFn := func() error { - claim, err := l.inj.LastClaimEvent(ctx) + c, err := l.inj.LastClaimEvent(ctx) if err != nil { - return errors.Wrap(err, "failed to query last claim event from Injective") + return err } - lastClaimEvent = claim + claim = c return nil } @@ -239,41 +245,32 @@ func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to get last event claim, will retry (%d)", n) }), ); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") - return 0, err + return nil, err } - // filter processed events - events.oldDeposits = filterSendToCosmosEventsByNonce(events.oldDeposits, lastClaimEvent.EthereumEventNonce) - events.deposits = filterSendToInjectiveEventsByNonce(events.deposits, lastClaimEvent.EthereumEventNonce) - events.withdrawals = filterTransactionBatchExecutedEventsByNonce(events.withdrawals, lastClaimEvent.EthereumEventNonce) - events.erc20Deployments = filterERC20DeployedEventsByNonce(events.erc20Deployments, lastClaimEvent.EthereumEventNonce) - events.valsetUpdates = filterValsetUpdateEventsByNonce(events.valsetUpdates, lastClaimEvent.EthereumEventNonce) - - eventsNum := len(events.oldDeposits) + len(events.deposits) + len(events.valsetUpdates) + len(events.withdrawals) + len(events.erc20Deployments) - if eventsNum == 0 { - l.Logger().Infoln("no new events on Ethereum") - return latestHeight, nil - } + return claim, nil +} +func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, lastClaim *types.LastClaimEvent, events ethEvents) error { sendClaimsFn := func() error { newEventNonce, err := l.inj.SendEthereumClaims(ctx, - lastClaimEvent.EthereumEventNonce, - events.oldDeposits, - events.deposits, - events.withdrawals, - events.erc20Deployments, - events.valsetUpdates, + lastClaim.EthereumEventNonce, + events.OldDeposits, + events.Deposits, + events.Withdrawals, + events.ERC20Deployments, + events.ValsetUpdates, ) if err != nil { return errors.Wrap(err, "failed to send event claims to Injective") } - l.Logger().WithFields(log.Fields{"num": events, "latest_event_nonce": newEventNonce}).Infoln("sent new event claims to Injective") + l.Logger().WithFields(log.Fields{"claimed_events": events.Num(), "latest_event_nonce": newEventNonce}).Infoln("sent new event claims to Injective") return nil } @@ -284,210 +281,119 @@ func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { retry.OnRetry(func(n uint, err error) { l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) }), - ); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return 0, err - } - - return latestHeight, nil - // - //scanEthBlocksAndRelayEventsFn := func() error { - // metrics.ReportFuncCall(l.svcTags) - // doneFn := metrics.ReportFuncTiming(l.svcTags) - // defer doneFn() - // - // latestHeader, err := l.eth.HeaderByNumber(ctx, nil) - // if err != nil { - // return errors.Wrap(err, "failed to get latest ethereum header") - // } - // - // // add delay to ensure minimum confirmations are received and block is finalised - // latestHeight = latestHeader.Number.Uint64() - ethBlockConfirmationDelay - // if latestHeight < currentHeight { - // return nil - // } - // - // if latestHeight > currentHeight+defaultBlocksToSearch { - // latestHeight = currentHeight + defaultBlocksToSearch - // } - // - // legacyDeposits, err := l.eth.GetSendToCosmosEvents(currentHeight, latestHeight) - // if err != nil { - // return errors.Wrap(err, "failed to get SendToCosmos events") - // } - // - // deposits, err := l.eth.GetSendToInjectiveEvents(currentHeight, latestHeight) - // if err != nil { - // return errors.Wrap(err, "failed to get SendToInjective events") - // } - // - // withdrawals, err := l.eth.GetTransactionBatchExecutedEvents(currentHeight, latestHeight) - // if err != nil { - // return errors.Wrap(err, "failed to get TransactionBatchExecuted events") - // } - // - // erc20Deployments, err := l.eth.GetPeggyERC20DeployedEvents(currentHeight, latestHeight) - // if err != nil { - // return errors.Wrap(err, "failed to get ERC20Deployed events") - // } - // - // valsetUpdates, err := l.eth.GetValsetUpdatedEvents(currentHeight, latestHeight) - // if err != nil { - // return errors.Wrap(err, "failed to get ValsetUpdated events") - // } - // - // // note that starting block overlaps with our last checked block, because we have to deal with - // // the possibility that the relayer was killed after relaying only one of multiple events in a single - // // block, so we also need this routine so make sure we don't send in the first event in this hypothetical - // // multi event block again. In theory we only send all events for every block and that will pass of fail - // // atomically but lets not take that risk. - // lastClaimEvent, err := l.inj.LastClaimEvent(ctx) - // if err != nil { - // return errors.Wrap(err, "failed to query last claim event from Injective") - // } - // - // l.Logger().WithFields(log.Fields{"event_nonce": lastClaimEvent.EthereumEventNonce, "event_height": lastClaimEvent.EthereumEventHeight}).Debugln("last observed Ethereum event") - // - // legacyDeposits = filterSendToCosmosEventsByNonce(legacyDeposits, lastClaimEvent.EthereumEventNonce) - // deposits = filterSendToInjectiveEventsByNonce(deposits, lastClaimEvent.EthereumEventNonce) - // withdrawals = filterTransactionBatchExecutedEventsByNonce(withdrawals, lastClaimEvent.EthereumEventNonce) - // erc20Deployments = filterERC20DeployedEventsByNonce(erc20Deployments, lastClaimEvent.EthereumEventNonce) - // valsetUpdates = filterValsetUpdateEventsByNonce(valsetUpdates, lastClaimEvent.EthereumEventNonce) - // - // events := len(legacyDeposits) + len(deposits) + len(withdrawals) + len(erc20Deployments) + len(valsetUpdates) - // if events == 0 { - // l.Logger().Infoln("no new events on Ethereum") - // return nil - // } - // - // latestEventNonce, err := l.inj.SendEthereumClaims(ctx, - // lastClaimEvent.EthereumEventNonce, - // legacyDeposits, - // deposits, - // withdrawals, - // erc20Deployments, - // valsetUpdates, - // ) - // - // if err != nil { - // return errors.Wrap(err, "failed to send event claims to Injective") - // } - // - // l.Logger().WithFields(log.Fields{"num": events, "latest_event_nonce": latestEventNonce}).Infoln("sent new event claims to Injective") - // - // return nil - //} - // - //if err := retry.Do(scanEthBlocksAndRelayEventsFn, - // retry.Context(ctx), - // retry.Attempts(l.maxAttempts), - // retry.OnRetry(func(n uint, err error) { - // l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) - // }), - //); err != nil { - // l.Logger().WithError(err).Errorln("got error, loop exits") - // return 0, err - //} - // - //return latestHeight, nil -} - -func (l *ethOracleLoop) autoResync(ctx context.Context) error { - var latestHeight uint64 - getLastClaimEventFn := func() (err error) { - latestHeight, err = l.getLastClaimBlockHeight(ctx) - return - } - - if err := retry.Do(getLastClaimEventFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to get last confirmed eth height, will retry (%d)", n) - }), ); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return err } - l.lastCheckedEthHeight = latestHeight - l.lastResyncWithInjective = time.Now() + return nil +} - l.Logger().WithFields(log.Fields{"last_resync_time": l.lastResyncWithInjective.String(), "last_confirmed_eth_height": l.lastCheckedEthHeight}).Infoln("auto resync event nonce with Injective") +type ethEvents struct { + OldDeposits []*peggyevents.PeggySendToCosmosEvent + Deposits []*peggyevents.PeggySendToInjectiveEvent + Withdrawals []*peggyevents.PeggyTransactionBatchExecutedEvent + ValsetUpdates []*peggyevents.PeggyValsetUpdatedEvent + ERC20Deployments []*peggyevents.PeggyERC20DeployedEvent +} - return nil +func (e ethEvents) Num() int { + return len(e.OldDeposits) + len(e.Deposits) + len(e.Withdrawals) + len(e.ValsetUpdates) + len(e.ERC20Deployments) } -func filterSendToCosmosEventsByNonce( - events []*peggyevents.PeggySendToCosmosEvent, - nonce uint64, -) []*peggyevents.PeggySendToCosmosEvent { - res := make([]*peggyevents.PeggySendToCosmosEvent, 0, len(events)) +func (e ethEvents) Filter(nonce uint64) ethEvents { + var oldDeposits []*peggyevents.PeggySendToCosmosEvent + for _, d := range e.OldDeposits { + if d.EventNonce.Uint64() > nonce { + oldDeposits = append(oldDeposits, d) + } + } - for _, ev := range events { - if ev.EventNonce.Uint64() > nonce { - res = append(res, ev) + var deposits []*peggyevents.PeggySendToInjectiveEvent + for _, d := range e.Deposits { + if d.EventNonce.Uint64() > nonce { + deposits = append(deposits, d) } } - return res -} + var withdrawals []*peggyevents.PeggyTransactionBatchExecutedEvent + for _, w := range e.Withdrawals { + if w.EventNonce.Uint64() > nonce { + withdrawals = append(withdrawals, w) + } + } -func filterSendToInjectiveEventsByNonce( - events []*peggyevents.PeggySendToInjectiveEvent, - nonce uint64, -) []*peggyevents.PeggySendToInjectiveEvent { - res := make([]*peggyevents.PeggySendToInjectiveEvent, 0, len(events)) + var valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent + for _, vs := range e.ValsetUpdates { + if vs.EventNonce.Uint64() > nonce { + valsetUpdates = append(valsetUpdates, vs) + } + } - for _, ev := range events { - if ev.EventNonce.Uint64() > nonce { - res = append(res, ev) + var erc20Deployments []*peggyevents.PeggyERC20DeployedEvent + for _, d := range e.ERC20Deployments { + if d.EventNonce.Uint64() > nonce { + erc20Deployments = append(erc20Deployments, d) } } - return res + return ethEvents{ + OldDeposits: oldDeposits, + Deposits: deposits, + Withdrawals: withdrawals, + ValsetUpdates: valsetUpdates, + ERC20Deployments: erc20Deployments, + } } -func filterTransactionBatchExecutedEventsByNonce( - events []*peggyevents.PeggyTransactionBatchExecutedEvent, - nonce uint64, -) []*peggyevents.PeggyTransactionBatchExecutedEvent { - res := make([]*peggyevents.PeggyTransactionBatchExecutedEvent, 0, len(events)) +func (l *ethOracleLoop) getEthEvents(ctx context.Context, startBlock, endBlock uint64) (ethEvents, error) { + events := ethEvents{} + + scanEthEventsFn := func() error { + legacyDeposits, err := l.eth.GetSendToCosmosEvents(startBlock, endBlock) + if err != nil { + return errors.Wrap(err, "failed to get SendToCosmos events") + } - for _, ev := range events { - if ev.EventNonce.Uint64() > nonce { - res = append(res, ev) + deposits, err := l.eth.GetSendToInjectiveEvents(startBlock, endBlock) + if err != nil { + return errors.Wrap(err, "failed to get SendToInjective events") } - } - return res -} + withdrawals, err := l.eth.GetTransactionBatchExecutedEvents(startBlock, endBlock) + if err != nil { + return errors.Wrap(err, "failed to get TransactionBatchExecuted events") + } -func filterERC20DeployedEventsByNonce( - events []*peggyevents.PeggyERC20DeployedEvent, - nonce uint64, -) []*peggyevents.PeggyERC20DeployedEvent { - res := make([]*peggyevents.PeggyERC20DeployedEvent, 0, len(events)) + erc20Deployments, err := l.eth.GetPeggyERC20DeployedEvents(startBlock, endBlock) + if err != nil { + return errors.Wrap(err, "failed to get ERC20Deployed events") + } - for _, ev := range events { - if ev.EventNonce.Uint64() > nonce { - res = append(res, ev) + valsetUpdates, err := l.eth.GetValsetUpdatedEvents(startBlock, endBlock) + if err != nil { + return errors.Wrap(err, "failed to get ValsetUpdated events") } - } - return res -} + events.OldDeposits = legacyDeposits + events.Deposits = deposits + events.Withdrawals = withdrawals + events.ValsetUpdates = valsetUpdates + events.ERC20Deployments = erc20Deployments -func filterValsetUpdateEventsByNonce( - events []*peggyevents.PeggyValsetUpdatedEvent, - nonce uint64, -) []*peggyevents.PeggyValsetUpdatedEvent { - res := make([]*peggyevents.PeggyValsetUpdatedEvent, 0, len(events)) + return nil + } - for _, ev := range events { - if ev.EventNonce.Uint64() > nonce { - res = append(res, ev) - } + if err := retry.Do(scanEthEventsFn, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) + }), + ); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return ethEvents{}, err } - return res + + return events, nil } diff --git a/test/peggo/peggy_params.json b/test/peggo/peggy_params.json new file mode 100644 index 00000000..f551c6f0 --- /dev/null +++ b/test/peggo/peggy_params.json @@ -0,0 +1,38 @@ +{ + "messages": [ + { + "@type": "/injective.peggy.v1.MsgUpdateParams", + "authority": "inj10d07y265gmmuvt4z0w9aw880jnsr700jstypyt", + "params": { + "peggy_id": "injective-peggyid", + "contract_source_hash": "", + "bridge_ethereum_address": "0x5048019d259217e6b7BC8e1E6aEfa9976B1ADFfe", + "bridge_chain_id": "50", + "signed_valsets_window": "25000", + "signed_batches_window": "25000", + "signed_claims_window": "25000", + "target_batch_timeout": "43200000", + "average_block_time": "1000", + "average_ethereum_block_time": "15000", + "slash_fraction_valset": "0.001000000000000000", + "slash_fraction_batch": "0.001000000000000000", + "slash_fraction_claim": "0.001000000000000000", + "slash_fraction_conflicting_claim": "0.001000000000000000", + "unbond_slashing_valsets_window": "25000", + "slash_fraction_bad_eth_signature": "0.001000000000000000", + "cosmos_coin_denom": "inj", + "cosmos_coin_erc20_contract": "0x7E5C521F8515017487750c13C3bF3B15f3f5f654", + "claim_slashing_enabled": false, + "bridge_contract_start_height": "23", + "valset_reward": { + "denom": "inj", + "amount": "0" + } + } + } + ], + "metadata": "ipfs://CID", + "deposit": "100000001inj", + "title": "Peggo local testing env", + "summary": "The time I needed a working environment to test the bridge" +} From 42222e13acc713b88f75829e180b216be8062504 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 19 Jan 2024 14:23:17 +0100 Subject: [PATCH 17/99] refactor --- orchestrator/oracle.go | 213 ++++++++++++++++++----------------------- 1 file changed, 92 insertions(+), 121 deletions(-) diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 9b1e8afa..f3ce6eac 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -2,7 +2,6 @@ package orchestrator import ( "context" - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "time" "github.com/avast/retry-go" @@ -108,73 +107,60 @@ func (l *ethOracleLoop) Run(ctx context.Context) error { } func (l *ethOracleLoop) observeEthEvents(ctx context.Context) error { - newHeight, err := l.relayEvents(ctx) - if err != nil { - return err - } - - l.Logger().WithFields(log.Fields{"block_start": l.lastCheckedEthHeight, "block_end": newHeight}).Debugln("scanned Ethereum blocks") - l.lastCheckedEthHeight = newHeight - - if time.Since(l.lastResyncWithInjective) >= 48*time.Hour { - /** - Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. - 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. - we need to re-scan this block to ensure events are not missed due to indexing delay. - 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. - 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. - **/ - if err := l.autoResync(ctx); err != nil { - return err - } - } - - return nil -} - -func (l *ethOracleLoop) relayEvents(ctx context.Context) (uint64, error) { latestHeight, err := l.getLatestEthHeight(ctx) if err != nil { - return 0, err + return err } // not enough blocks on ethereum yet if latestHeight <= ethBlockConfirmationDelay { - return l.lastCheckedEthHeight, nil + return nil } // ensure that latest block has minimum confirmations latestHeight = latestHeight - ethBlockConfirmationDelay if latestHeight <= l.lastCheckedEthHeight { - return l.lastCheckedEthHeight, nil + return nil } if latestHeight > l.lastCheckedEthHeight+defaultBlocksToSearch { latestHeight = l.lastCheckedEthHeight + defaultBlocksToSearch } - events, err := l.getEthEvents(ctx, l.lastCheckedEthHeight, latestHeight) - if err != nil { - return 0, err + if err := l.relayEvents(ctx, latestHeight); err != nil { + return err + } - lastClaim, err := l.getLastEventClaim(ctx) - if err != nil { - return 0, err + l.Logger().WithFields(log.Fields{"block_start": l.lastCheckedEthHeight, "block_end": latestHeight}).Debugln("scanned Ethereum blocks") + l.lastCheckedEthHeight = latestHeight + + /** Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. + 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. + we need to re-scan this block to ensure events are not missed due to indexing delay. + 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. + 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. + */ + if time.Since(l.lastResyncWithInjective) >= 48*time.Hour { + if err := l.autoResync(ctx); err != nil { + return err + } } - // filter processed events - events = events.Filter(lastClaim.EthereumEventNonce) - if events.Num() == 0 { - l.Logger().Infoln("no new events on Ethereum") - return latestHeight, nil + return nil +} + +func (l *ethOracleLoop) relayEvents(ctx context.Context, latestHeight uint64) error { + events, err := l.getEthEvents(ctx, l.lastCheckedEthHeight, latestHeight) + if err != nil { + return err } - if err := l.sendNewEventClaims(ctx, lastClaim, events); err != nil { - return 0, err + if err := l.sendNewEventClaims(ctx, events); err != nil { + return err } - return latestHeight, nil + return nil } func (l *ethOracleLoop) autoResync(ctx context.Context) error { @@ -203,61 +189,98 @@ func (l *ethOracleLoop) autoResync(ctx context.Context) error { return nil } -func (l *ethOracleLoop) getLatestEthHeight(ctx context.Context) (uint64, error) { - var latestHeight uint64 - getLatestEthHeightFn := func() error { - latestHeader, err := l.eth.HeaderByNumber(ctx, nil) +func (l *ethOracleLoop) getEthEvents(ctx context.Context, startBlock, endBlock uint64) (ethEvents, error) { + events := ethEvents{} + + scanEthEventsFn := func() error { + legacyDeposits, err := l.eth.GetSendToCosmosEvents(startBlock, endBlock) if err != nil { - return errors.Wrap(err, "failed to get latest ethereum header") + return errors.Wrap(err, "failed to get SendToCosmos events") } - latestHeight = latestHeader.Number.Uint64() + deposits, err := l.eth.GetSendToInjectiveEvents(startBlock, endBlock) + if err != nil { + return errors.Wrap(err, "failed to get SendToInjective events") + } + + withdrawals, err := l.eth.GetTransactionBatchExecutedEvents(startBlock, endBlock) + if err != nil { + return errors.Wrap(err, "failed to get TransactionBatchExecuted events") + } + + erc20Deployments, err := l.eth.GetPeggyERC20DeployedEvents(startBlock, endBlock) + if err != nil { + return errors.Wrap(err, "failed to get ERC20Deployed events") + } + + valsetUpdates, err := l.eth.GetValsetUpdatedEvents(startBlock, endBlock) + if err != nil { + return errors.Wrap(err, "failed to get ValsetUpdated events") + } + + events.OldDeposits = legacyDeposits + events.Deposits = deposits + events.Withdrawals = withdrawals + events.ValsetUpdates = valsetUpdates + events.ERC20Deployments = erc20Deployments + return nil } - if err := retry.Do(getLatestEthHeightFn, + if err := retry.Do(scanEthEventsFn, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to get latest eth header, will retry (%d)", n) + l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) }), ); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") - return 0, err + return ethEvents{}, err } - return latestHeight, nil + return events, nil } -func (l *ethOracleLoop) getLastEventClaim(ctx context.Context) (*types.LastClaimEvent, error) { - var claim *types.LastClaimEvent - getLastClaimEventFn := func() error { - c, err := l.inj.LastClaimEvent(ctx) +func (l *ethOracleLoop) getLatestEthHeight(ctx context.Context) (uint64, error) { + var latestHeight uint64 + getLatestEthHeightFn := func() error { + latestHeader, err := l.eth.HeaderByNumber(ctx, nil) if err != nil { - return err + return errors.Wrap(err, "failed to get latest ethereum header") } - claim = c + latestHeight = latestHeader.Number.Uint64() return nil } - if err := retry.Do(getLastClaimEventFn, + if err := retry.Do(getLatestEthHeightFn, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to get last event claim, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to get latest eth header, will retry (%d)", n) }), ); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") - return nil, err + return 0, err } - return claim, nil + return latestHeight, nil } -func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, lastClaim *types.LastClaimEvent, events ethEvents) error { - sendClaimsFn := func() error { - newEventNonce, err := l.inj.SendEthereumClaims(ctx, +func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents) error { + sendEventsFn := func() error { + lastClaim, err := l.inj.LastClaimEvent(ctx) + if err != nil { + return err + } + + newEvents := events.Filter(lastClaim.EthereumEventNonce) + if newEvents.Num() == 0 { + l.Logger().WithField("last_claim_event_nonce", lastClaim.EthereumEventNonce).Infoln("no new events on Ethereum") + return nil + } + + latestEventNonce, err := l.inj.SendEthereumClaims(ctx, lastClaim.EthereumEventNonce, events.OldDeposits, events.Deposits, @@ -267,15 +290,15 @@ func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, lastClaim *types ) if err != nil { - return errors.Wrap(err, "failed to send event claims to Injective") + return err } - l.Logger().WithFields(log.Fields{"claimed_events": events.Num(), "latest_event_nonce": newEventNonce}).Infoln("sent new event claims to Injective") + l.Logger().WithFields(log.Fields{"claimed_events": events.Num(), "latest_event_nonce": latestEventNonce}).Infoln("sent new event claims to Injective") return nil } - if err := retry.Do(sendClaimsFn, + if err := retry.Do(sendEventsFn, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -345,55 +368,3 @@ func (e ethEvents) Filter(nonce uint64) ethEvents { ERC20Deployments: erc20Deployments, } } - -func (l *ethOracleLoop) getEthEvents(ctx context.Context, startBlock, endBlock uint64) (ethEvents, error) { - events := ethEvents{} - - scanEthEventsFn := func() error { - legacyDeposits, err := l.eth.GetSendToCosmosEvents(startBlock, endBlock) - if err != nil { - return errors.Wrap(err, "failed to get SendToCosmos events") - } - - deposits, err := l.eth.GetSendToInjectiveEvents(startBlock, endBlock) - if err != nil { - return errors.Wrap(err, "failed to get SendToInjective events") - } - - withdrawals, err := l.eth.GetTransactionBatchExecutedEvents(startBlock, endBlock) - if err != nil { - return errors.Wrap(err, "failed to get TransactionBatchExecuted events") - } - - erc20Deployments, err := l.eth.GetPeggyERC20DeployedEvents(startBlock, endBlock) - if err != nil { - return errors.Wrap(err, "failed to get ERC20Deployed events") - } - - valsetUpdates, err := l.eth.GetValsetUpdatedEvents(startBlock, endBlock) - if err != nil { - return errors.Wrap(err, "failed to get ValsetUpdated events") - } - - events.OldDeposits = legacyDeposits - events.Deposits = deposits - events.Withdrawals = withdrawals - events.ValsetUpdates = valsetUpdates - events.ERC20Deployments = erc20Deployments - - return nil - } - - if err := retry.Do(scanEthEventsFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) - }), - ); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return ethEvents{}, err - } - - return events, nil -} From 22ef00a8d330907d819d6790ea196b8498537949 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 22 Jan 2024 12:35:56 +0100 Subject: [PATCH 18/99] query last observed eth block and peggy id during orchestrator start up --- orchestrator/oracle.go | 61 +++--------------------------------- orchestrator/orchestrator.go | 34 ++++++++++++++++++-- orchestrator/signer.go | 7 +---- 3 files changed, 37 insertions(+), 65 deletions(-) diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index f3ce6eac..76f415f8 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -8,8 +8,6 @@ import ( "github.com/pkg/errors" log "github.com/xlab/suplog" - "github.com/InjectiveLabs/metrics" - "github.com/InjectiveLabs/peggo/orchestrator/loops" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" ) @@ -25,19 +23,11 @@ const ( // EthOracleMainLoop is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain // and ferried over to Cosmos where they will be used to issue tokens or process batches. -func (s *PeggyOrchestrator) EthOracleMainLoop(ctx context.Context) error { - - lastConfirmedEthHeight, err := s.getLastConfirmedEthHeightOnInjective(ctx) - if err != nil { - return err - } - - s.logger.Debugln("last observed Ethereum block", lastConfirmedEthHeight) - +func (s *PeggyOrchestrator) EthOracleMainLoop(ctx context.Context, lastObservedBlock uint64) error { loop := ethOracleLoop{ PeggyOrchestrator: s, loopDuration: defaultLoopDur, - lastCheckedEthHeight: lastConfirmedEthHeight, + lastCheckedEthHeight: lastObservedBlock, lastResyncWithInjective: time.Now(), } @@ -46,49 +36,6 @@ func (s *PeggyOrchestrator) EthOracleMainLoop(ctx context.Context) error { return loop.Run(ctx) } -func (s *PeggyOrchestrator) getLastConfirmedEthHeightOnInjective(ctx context.Context) (uint64, error) { - var lastConfirmedEthHeight uint64 - getLastConfirmedEthHeightFn := func() (err error) { - lastConfirmedEthHeight, err = s.getLastClaimBlockHeight(ctx) - if lastConfirmedEthHeight == 0 { - peggyParams, err := s.inj.PeggyParams(ctx) - if err != nil { - s.logger.WithError(err).Fatalln("unable to query peggy module params, is injectived running?") - return err - } - - lastConfirmedEthHeight = peggyParams.BridgeContractStartHeight - } - return - } - - if err := retry.Do(getLastConfirmedEthHeightFn, - retry.Context(ctx), - retry.Attempts(s.maxAttempts), - retry.OnRetry(func(n uint, err error) { - s.logger.WithError(err).Warningf("failed to get last confirmed Ethereum height on Injective, will retry (%d)", n) - }), - ); err != nil { - s.logger.WithError(err).Errorln("got error, loop exits") - return 0, err - } - - return lastConfirmedEthHeight, nil -} - -func (s *PeggyOrchestrator) getLastClaimBlockHeight(ctx context.Context) (uint64, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - claim, err := s.inj.LastClaimEvent(ctx) - if err != nil { - return 0, err - } - - return claim.EthereumEventHeight, nil -} - type ethOracleLoop struct { *PeggyOrchestrator loopDuration time.Duration @@ -276,7 +223,7 @@ func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents newEvents := events.Filter(lastClaim.EthereumEventNonce) if newEvents.Num() == 0 { - l.Logger().WithField("last_claim_event_nonce", lastClaim.EthereumEventNonce).Infoln("no new events on Ethereum") + l.Logger().WithField("last_claimed_event", lastClaim.EthereumEventNonce).Infoln("no new events on Ethereum") return nil } @@ -293,7 +240,7 @@ func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents return err } - l.Logger().WithFields(log.Fields{"claimed_events": events.Num(), "latest_event_nonce": latestEventNonce}).Infoln("sent new event claims to Injective") + l.Logger().WithFields(log.Fields{"events": events.Num(), "latest_event_nonce": latestEventNonce}).Infoln("sent new event claims to Injective") return nil } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 810453c3..159f63ff 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -108,11 +108,28 @@ func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context) bool { func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context) error { log.Infoln("running orchestrator in validator mode") + // get eth block observed by this validator + lastObservedEthBlock, _ := s.getLastClaimBlockHeight(ctx) + if lastObservedEthBlock == 0 { + peggyParams, err := s.inj.PeggyParams(ctx) + if err != nil { + s.logger.WithError(err).Fatalln("unable to query peggy module params, is injectived running?") + } + + lastObservedEthBlock = peggyParams.BridgeContractStartHeight + } + + // get peggy ID from contract + peggyContractID, err := s.eth.GetPeggyID(ctx) + if err != nil { + s.logger.WithError(err).Fatalln("unable to query peggy ID from contract") + } + var pg loops.ParanoidGroup - pg.Go(func() error { return s.EthOracleMainLoop(ctx) }) + pg.Go(func() error { return s.EthOracleMainLoop(ctx, lastObservedEthBlock) }) pg.Go(func() error { return s.BatchRequesterLoop(ctx) }) - pg.Go(func() error { return s.EthSignerMainLoop(ctx) }) + pg.Go(func() error { return s.EthSignerMainLoop(ctx, peggyContractID) }) pg.Go(func() error { return s.RelayerMainLoop(ctx) }) return pg.Wait() @@ -131,3 +148,16 @@ func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context) error { return pg.Wait() } + +func (s *PeggyOrchestrator) getLastClaimBlockHeight(ctx context.Context) (uint64, error) { + metrics.ReportFuncCall(s.svcTags) + doneFn := metrics.ReportFuncTiming(s.svcTags) + defer doneFn() + + claim, err := s.inj.LastClaimEvent(ctx) + if err != nil { + return 0, err + } + + return claim.EthereumEventHeight, nil +} diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 94c31553..f8c63a38 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -17,12 +17,7 @@ import ( // EthSignerMainLoop simply signs off on any batches or validator sets provided by the validator // since these are provided directly by a trusted Injective node they can simply be assumed to be // valid and signed off on. -func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context) error { - peggyID, err := s.getPeggyID(ctx) - if err != nil { - return err - } - +func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, peggyID common.Hash) error { loop := ethSignerLoop{ PeggyOrchestrator: s, loopDuration: defaultLoopDur, From 4d21cb5c2f95c3fd1d093ad80e50ecc6564958b1 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 22 Jan 2024 13:03:52 +0100 Subject: [PATCH 19/99] move logs --- orchestrator/batch_request.go | 5 ++--- orchestrator/oracle.go | 4 ++-- orchestrator/relayer.go | 37 +++++++++++++++++++---------------- orchestrator/signer.go | 27 ++----------------------- 4 files changed, 26 insertions(+), 47 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index b2a819f7..817b457c 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -15,14 +15,11 @@ import ( ) func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context) (err error) { - loop := batchRequestLoop{ PeggyOrchestrator: s, loopDuration: defaultLoopDur, } - s.logger.WithField("loop_duration", loop.loopDuration.String()).Debugln("starting BatchRequester loop...") - return loop.Run(ctx) } @@ -36,6 +33,8 @@ func (l *batchRequestLoop) Logger() log.Logger { } func (l *batchRequestLoop) Run(ctx context.Context) error { + l.logger.WithField("loop_duration", l.loopDuration.String()).Debugln("starting BatchRequester loop...") + return loops.RunLoop(ctx, l.loopDuration, func() error { return l.requestBatches(ctx) }) diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 76f415f8..4eeeae22 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -31,8 +31,6 @@ func (s *PeggyOrchestrator) EthOracleMainLoop(ctx context.Context, lastObservedB lastResyncWithInjective: time.Now(), } - s.logger.WithField("loop_duration", loop.loopDuration.String()).Debugln("starting EthOracle loop...") - return loop.Run(ctx) } @@ -48,6 +46,8 @@ func (l *ethOracleLoop) Logger() log.Logger { } func (l *ethOracleLoop) Run(ctx context.Context) error { + l.logger.WithField("loop_duration", l.loopDuration.String()).Debugln("starting EthOracle loop...") + return loops.RunLoop(ctx, l.loopDuration, func() error { return l.observeEthEvents(ctx) }) diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 739c08ba..417eee5e 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -15,7 +15,7 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/ethereum/util" "github.com/InjectiveLabs/peggo/orchestrator/loops" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) const ( @@ -24,17 +24,15 @@ const ( ) func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context) (err error) { + if noRelay := !s.batchRelayEnabled && !s.valsetRelayEnabled; noRelay { + return nil + } + loop := relayerLoop{ PeggyOrchestrator: s, loopDuration: defaultRelayerLoopDur, } - s.logger.WithFields(log.Fields{ - "loop_duration": loop.loopDuration.String(), - "relay_batches": s.batchRelayEnabled, - "relay_valsets": s.valsetRelayEnabled, - }).Debugln("starting Relayer loop...") - return loop.Run(ctx) } @@ -48,6 +46,11 @@ func (l *relayerLoop) Logger() log.Logger { } func (l *relayerLoop) Run(ctx context.Context) error { + l.Logger().WithFields(log.Fields{ + "loop_duration": l.loopDuration.String(), + "relay_batches": l.batchRelayEnabled, + "relay_valsets": l.valsetRelayEnabled, + }).Debugln("starting Relayer loop...") return loops.RunLoop(ctx, l.loopDuration, func() error { return l.relayValsetsAndBatches(ctx) @@ -104,8 +107,8 @@ func (l *relayerLoop) relayValset(ctx context.Context) error { } var ( - oldestConfirmedValset *types.Valset - oldestConfirmedValsetSigs []*types.MsgValsetConfirm + oldestConfirmedValset *peggytypes.Valset + oldestConfirmedValsetSigs []*peggytypes.MsgValsetConfirm ) for _, set := range latestValsets { @@ -187,8 +190,8 @@ func (l *relayerLoop) relayBatch(ctx context.Context) error { } var ( - oldestConfirmedBatch *types.OutgoingTxBatch - oldestConfirmedBatchSigs []*types.MsgConfirmBatch + oldestConfirmedBatch *peggytypes.OutgoingTxBatch + oldestConfirmedBatchSigs []*peggytypes.MsgConfirmBatch ) for _, batch := range latestBatches { @@ -266,7 +269,7 @@ func (l *relayerLoop) relayBatch(ctx context.Context) error { // as the latest update will be in recent blockchain history and the search moves from the present // backwards in time. In the case that the validator set has not been updated for a very long time // this will take longer. -func (l *relayerLoop) findLatestValsetOnEth(ctx context.Context, injective InjectiveNetwork, ethereum EthereumNetwork) (*types.Valset, error) { +func (l *relayerLoop) findLatestValsetOnEth(ctx context.Context, injective InjectiveNetwork, ethereum EthereumNetwork) (*peggytypes.Valset, error) { latestHeader, err := ethereum.HeaderByNumber(ctx, nil) if err != nil { return nil, errors.Wrap(err, "failed to get latest eth header") @@ -310,15 +313,15 @@ func (l *relayerLoop) findLatestValsetOnEth(ctx context.Context, injective Injec // we take only the first event if we find any at all. event := valsetUpdatedEvents[0] - valset := &types.Valset{ + valset := &peggytypes.Valset{ Nonce: event.NewValsetNonce.Uint64(), - Members: make([]*types.BridgeValidator, 0, len(event.Powers)), + Members: make([]*peggytypes.BridgeValidator, 0, len(event.Powers)), RewardAmount: sdk.NewIntFromBigInt(event.RewardAmount), RewardToken: event.RewardToken.Hex(), } for idx, p := range event.Powers { - valset.Members = append(valset.Members, &types.BridgeValidator{ + valset.Members = append(valset.Members, &peggytypes.BridgeValidator{ Power: p.Uint64(), EthereumAddress: event.Validators[idx].Hex(), }) @@ -352,7 +355,7 @@ func (a PeggyValsetUpdatedEvents) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // The other (and far worse) way a disagreement here could occur is if validators are colluding to steal // funds from the Peggy contract and have submitted a hijacking update. If slashing for off Cosmos chain // Ethereum signatures is implemented you would put that handler here. -func checkIfValsetsDiffer(cosmosValset, ethereumValset *types.Valset) { +func checkIfValsetsDiffer(cosmosValset, ethereumValset *peggytypes.Valset) { if cosmosValset == nil && ethereumValset.Nonce == 0 { // bootstrapping case return @@ -393,7 +396,7 @@ func checkIfValsetsDiffer(cosmosValset, ethereumValset *types.Valset) { } } -type BridgeValidators []*types.BridgeValidator +type BridgeValidators []*peggytypes.BridgeValidator // Sort sorts the validators by power func (b BridgeValidators) Sort() { diff --git a/orchestrator/signer.go b/orchestrator/signer.go index f8c63a38..8d274316 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -25,34 +25,9 @@ func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, peggyID commo ethFrom: s.eth.FromAddress(), } - s.logger.WithField("loop_duration", loop.loopDuration.String()).Debugln("starting EthSigner loop...") - return loop.Run(ctx) } -func (s *PeggyOrchestrator) getPeggyID(ctx context.Context) (common.Hash, error) { - var peggyID common.Hash - getPeggyIDFn := func() (err error) { - peggyID, err = s.eth.GetPeggyID(ctx) - return err - } - - if err := retry.Do(getPeggyIDFn, - retry.Context(ctx), - retry.Attempts(s.maxAttempts), - retry.OnRetry(func(n uint, err error) { - log.WithError(err).Warningf("failed to get Peggy ID from Ethereum contract, will retry (%d)", n) - }), - ); err != nil { - log.WithError(err).Errorln("got error, loop exits") - return [32]byte{}, err - } - - log.WithField("id", peggyID.Hex()).Debugln("got peggy ID from Ethereum contract") - - return peggyID, nil -} - type ethSignerLoop struct { *PeggyOrchestrator loopDuration time.Duration @@ -65,6 +40,8 @@ func (l *ethSignerLoop) Logger() log.Logger { } func (l *ethSignerLoop) Run(ctx context.Context) error { + l.logger.WithField("loop_duration", l.loopDuration.String()).Debugln("starting EthSigner loop...") + return loops.RunLoop(ctx, l.loopDuration, func() error { if err := l.signNewValsetUpdates(ctx); err != nil { return err From 22dcb58ec2cfcefdbe4a721fe0a7dccc62de5f8b Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 22 Jan 2024 13:34:53 +0100 Subject: [PATCH 20/99] relayer: query latest eth valset once --- orchestrator/relayer.go | 94 ++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 417eee5e..dd08d9ef 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -58,11 +58,31 @@ func (l *relayerLoop) Run(ctx context.Context) error { } func (l *relayerLoop) relayValsetsAndBatches(ctx context.Context) error { + var latestEthValset *peggytypes.Valset + getLatestEthValsetFn := func() error { + vs, err := l.findLatestValsetOnEth(ctx) + if err != nil { + return err + } + + latestEthValset = vs + return nil + } + + if err := retry.Do(getLatestEthValsetFn, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("failed to relay valset, will retry (%d)", n) + })); err != nil { + return err + } + var pg loops.ParanoidGroup if l.valsetRelayEnabled { pg.Go(func() error { - return retry.Do(func() error { return l.relayValset(ctx) }, + return retry.Do(func() error { return l.relayValset(ctx, latestEthValset) }, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -74,7 +94,7 @@ func (l *relayerLoop) relayValsetsAndBatches(ctx context.Context) error { if l.batchRelayEnabled { pg.Go(func() error { - return retry.Do(func() error { return l.relayBatch(ctx) }, + return retry.Do(func() error { return l.relayBatch(ctx, latestEthValset) }, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -94,7 +114,7 @@ func (l *relayerLoop) relayValsetsAndBatches(ctx context.Context) error { return nil } -func (l *relayerLoop) relayValset(ctx context.Context) error { +func (l *relayerLoop) relayValset(ctx context.Context, latestEthValset *peggytypes.Valset) error { metrics.ReportFuncCall(l.svcTags) doneFn := metrics.ReportFuncTiming(l.svcTags) defer doneFn() @@ -129,13 +149,8 @@ func (l *relayerLoop) relayValset(ctx context.Context) error { return nil } - currentEthValset, err := l.findLatestValsetOnEth(ctx, l.inj, l.eth) - if err != nil { - return errors.Wrap(err, "failed to find latest confirmed valset update on Ethereum") - } - - if oldestConfirmedValset.Nonce <= currentEthValset.Nonce { - l.Logger().WithFields(log.Fields{"eth_nonce": currentEthValset.Nonce, "inj_nonce": currentEthValset.Nonce}).Debugln("valset already updated on Ethereum") + if oldestConfirmedValset.Nonce <= latestEthValset.Nonce { + l.Logger().WithFields(log.Fields{"eth_nonce": latestEthValset.Nonce, "inj_nonce": oldestConfirmedValset.Nonce}).Debugln("valset already updated on Ethereum") return nil } @@ -146,7 +161,7 @@ func (l *relayerLoop) relayValset(ctx context.Context) error { // Check if other validators already updated the valset if oldestConfirmedValset.Nonce <= latestEthereumValsetNonce.Uint64() { - l.Logger().WithFields(log.Fields{"eth_nonce": latestEthereumValsetNonce, "inj_nonce": currentEthValset.Nonce}).Debugln("valset already updated on Ethereum") + l.Logger().WithFields(log.Fields{"eth_nonce": latestEthereumValsetNonce, "inj_nonce": latestEthValset.Nonce}).Debugln("valset already updated on Ethereum") return nil } @@ -164,12 +179,12 @@ func (l *relayerLoop) relayValset(ctx context.Context) error { return nil } - txHash, err := l.eth.SendEthValsetUpdate( - ctx, - currentEthValset, + txHash, err := l.eth.SendEthValsetUpdate(ctx, + latestEthValset, oldestConfirmedValset, oldestConfirmedValsetSigs, ) + if err != nil { return err } @@ -179,7 +194,7 @@ func (l *relayerLoop) relayValset(ctx context.Context) error { return nil } -func (l *relayerLoop) relayBatch(ctx context.Context) error { +func (l *relayerLoop) relayBatch(ctx context.Context, latestEthValset *peggytypes.Valset) error { metrics.ReportFuncCall(l.svcTags) doneFn := metrics.ReportFuncTiming(l.svcTags) defer doneFn() @@ -190,8 +205,8 @@ func (l *relayerLoop) relayBatch(ctx context.Context) error { } var ( - oldestConfirmedBatch *peggytypes.OutgoingTxBatch - oldestConfirmedBatchSigs []*peggytypes.MsgConfirmBatch + oldestConfirmedInjBatch *peggytypes.OutgoingTxBatch + oldestConfirmedInjBatchSigs []*peggytypes.MsgConfirmBatch ) for _, batch := range latestBatches { @@ -202,47 +217,30 @@ func (l *relayerLoop) relayBatch(ctx context.Context) error { continue } - oldestConfirmedBatch = batch - oldestConfirmedBatchSigs = sigs + oldestConfirmedInjBatch = batch + oldestConfirmedInjBatchSigs = sigs } - if oldestConfirmedBatch == nil { + if oldestConfirmedInjBatch == nil { l.Logger().Infoln("no batch to relay") return nil } - latestEthereumBatch, err := l.eth.GetTxBatchNonce(ctx, common.HexToAddress(oldestConfirmedBatch.TokenContract)) - if err != nil { - return err - } - - currentValset, err := l.findLatestValsetOnEth(ctx, l.inj, l.eth) - if err != nil { - return errors.Wrap(err, "failed to find latest valset") - } else if currentValset == nil { - return errors.Wrap(err, "latest valset not found") - } - - if oldestConfirmedBatch.BatchNonce <= latestEthereumBatch.Uint64() { - l.Logger().WithFields(log.Fields{"eth_nonce": latestEthereumBatch.Uint64(), "inj_nonce": oldestConfirmedBatch.BatchNonce}).Debugln("batch already updated on Ethereum") - return nil - } - - latestEthereumBatch, err = l.eth.GetTxBatchNonce(ctx, common.HexToAddress(oldestConfirmedBatch.TokenContract)) + latestEthBatch, err := l.eth.GetTxBatchNonce(ctx, common.HexToAddress(oldestConfirmedInjBatch.TokenContract)) if err != nil { return err } // Check if ethereum batch was updated by other validators - if oldestConfirmedBatch.BatchNonce <= latestEthereumBatch.Uint64() { - l.Logger().WithFields(log.Fields{"eth_nonce": latestEthereumBatch.Uint64(), "inj_nonce": oldestConfirmedBatch.BatchNonce}).Debugln("batch already updated on Ethereum") + if oldestConfirmedInjBatch.BatchNonce <= latestEthBatch.Uint64() { + l.Logger().WithFields(log.Fields{"eth_nonce": latestEthBatch.Uint64(), "inj_nonce": oldestConfirmedInjBatch.BatchNonce}).Debugln("batch already updated on Ethereum") return nil } - l.Logger().WithFields(log.Fields{"inj_nonce": oldestConfirmedBatch.BatchNonce, "eth_nonce": latestEthereumBatch.Uint64()}).Debugln("new batch update") + l.Logger().WithFields(log.Fields{"inj_nonce": oldestConfirmedInjBatch.BatchNonce, "eth_nonce": latestEthBatch.Uint64()}).Debugln("new batch update") // Check custom time delay offset - blockTime, err := l.inj.GetBlockCreationTime(ctx, int64(oldestConfirmedBatch.Block)) + blockTime, err := l.inj.GetBlockCreationTime(ctx, int64(oldestConfirmedInjBatch.Block)) if err != nil { return errors.Wrap(err, "failed to parse timestamp from block") } @@ -254,7 +252,7 @@ func (l *relayerLoop) relayBatch(ctx context.Context) error { } // Send SendTransactionBatch to Ethereum - txHash, err := l.eth.SendTransactionBatch(ctx, currentValset, oldestConfirmedBatch, oldestConfirmedBatchSigs) + txHash, err := l.eth.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedInjBatch, oldestConfirmedInjBatchSigs) if err != nil { return err } @@ -269,18 +267,18 @@ func (l *relayerLoop) relayBatch(ctx context.Context) error { // as the latest update will be in recent blockchain history and the search moves from the present // backwards in time. In the case that the validator set has not been updated for a very long time // this will take longer. -func (l *relayerLoop) findLatestValsetOnEth(ctx context.Context, injective InjectiveNetwork, ethereum EthereumNetwork) (*peggytypes.Valset, error) { - latestHeader, err := ethereum.HeaderByNumber(ctx, nil) +func (l *relayerLoop) findLatestValsetOnEth(ctx context.Context) (*peggytypes.Valset, error) { + latestHeader, err := l.eth.HeaderByNumber(ctx, nil) if err != nil { return nil, errors.Wrap(err, "failed to get latest eth header") } - latestEthereumValsetNonce, err := ethereum.GetValsetNonce(ctx) + latestEthereumValsetNonce, err := l.eth.GetValsetNonce(ctx) if err != nil { return nil, errors.Wrap(err, "failed to get latest valset nonce on Ethereum") } - cosmosValset, err := injective.ValsetAt(ctx, latestEthereumValsetNonce.Uint64()) + cosmosValset, err := l.inj.ValsetAt(ctx, latestEthereumValsetNonce.Uint64()) if err != nil { return nil, errors.Wrap(err, "failed to get Injective valset") } @@ -295,7 +293,7 @@ func (l *relayerLoop) findLatestValsetOnEth(ctx context.Context, injective Injec startSearchBlock = currentBlock - findValsetBlocksToSearch } - valsetUpdatedEvents, err := ethereum.GetValsetUpdatedEvents(startSearchBlock, currentBlock) + valsetUpdatedEvents, err := l.eth.GetValsetUpdatedEvents(startSearchBlock, currentBlock) if err != nil { return nil, errors.Wrap(err, "failed to filter past ValsetUpdated events from Ethereum") } From 7b33c3e827c4aa229ddecad39ee5aa2630ec660e Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 22 Jan 2024 14:11:55 +0100 Subject: [PATCH 21/99] move logs --- cmd/peggo/orchestrator.go | 4 ---- orchestrator/cosmos/custom_rpc.go | 1 + orchestrator/cosmos/load_balanced.go | 1 + orchestrator/ethereum/network.go | 5 +++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index baeb07c5..d7257fc2 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -97,8 +97,6 @@ func orchestratorCmd(cmd *cli.Cmd) { ) } - log.Infoln("using", valAddress.String(), "for Injective") - orShutdown(err) ctx, cancelFn := context.WithCancel(context.Background()) @@ -129,8 +127,6 @@ func orchestratorCmd(cmd *cli.Cmd) { ) orShutdown(err) - log.Infoln("using", ethKeyFromAddress.Hex(), "for Ethereum") - coingeckoFeed := coingecko.NewCoingeckoPriceFeed(100, &coingecko.Config{BaseURL: *cfg.coingeckoApi}) // Create peggo and run it diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index 38263539..ea3d940c 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -82,6 +82,7 @@ func NewCustomRPCNetwork( log.WithFields(log.Fields{ "chain_id": chainID, + "addr": validatorAddress, "injective": injectiveGRPC, "tendermint": tendermintRPC, }).Infoln("connected to custom Injective endpoints") diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index dd871d7e..db5e41aa 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -81,6 +81,7 @@ func NewLoadBalancedNetwork( } log.WithFields(log.Fields{ + "addr": validatorAddress, "chain_id": chainID, "injective": netCfg.ChainGrpcEndpoint, "tendermint": netCfg.TmEndpoint, diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index 2d3b72f0..cf206442 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -61,8 +61,9 @@ func NewNetwork( } log.WithFields(log.Fields{ - "rpc": ethNodeRPC, - "peggy_contract_addr": peggyContractAddr, + "rpc": ethNodeRPC, + "addr": fromAddr.String(), + "peggy_contract": peggyContractAddr, }).Infoln("connected to Ethereum network") // If Alchemy Websocket URL is set, then Subscribe to Pending Transaction of Peggy Contract. From 0d6558b9da65eee227e7c2b9b9f3f259c59a2cdd Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 23 Jan 2024 09:29:07 +0100 Subject: [PATCH 22/99] fix oracle logs to be more accurate --- orchestrator/batch_request.go | 2 - orchestrator/cosmos/broadcast.go | 100 ++++++++++++++++--------------- orchestrator/oracle.go | 8 +-- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 817b457c..e67486d7 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -55,8 +55,6 @@ func (l *batchRequestLoop) requestBatches(ctx context.Context) error { for _, fee := range fees { l.requestBatch(ctx, fee) - - // todo: in case of multiple requests, we should sleep in between (non-continuous nonce) } return nil diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index 366a1790..a0bb861a 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -463,17 +463,18 @@ func (s *peggyBroadcastClient) sendOldDepositClaims( Data: "", } - if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { + resp, err := s.broadcastClient.SyncBroadcastMsg(msg) + if err != nil { metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") - return err - } else { - log.WithFields(log.Fields{ - "event_nonce": oldDeposit.EventNonce.String(), - "tx_hash": txResponse.TxResponse.TxHash, - }).Debugln("Oracle sent MsgDepositClaim") + return errors.Wrap(err, "broadcasting MsgDepositClaim failed") } + log.WithFields(log.Fields{ + "event_height": msg.BlockHeight, + "event_nonce": msg.EventNonce, + "tx_hash": resp.TxResponse.TxHash, + }).Infoln("Oracle sent MsgDepositClaim") + return nil } @@ -491,11 +492,11 @@ func (s *peggyBroadcastClient) sendDepositClaims( defer doneFn() log.WithFields(log.Fields{ - "sender": deposit.Sender.Hex(), - "destination": sdk.AccAddress(deposit.Destination[12:32]).String(), - "amount": deposit.Amount.String(), - "event_nonce": deposit.EventNonce.String(), - "data": deposit.Data, + "sender": deposit.Sender.Hex(), + "destination": sdk.AccAddress(deposit.Destination[12:32]).String(), + "amount": deposit.Amount.String(), + "data": deposit.Data, + "token_contract": deposit.TokenContract.Hex(), }).Debugln("observed SendToInjectiveEvent") msg := &peggytypes.MsgDepositClaim{ @@ -509,17 +510,18 @@ func (s *peggyBroadcastClient) sendDepositClaims( Data: deposit.Data, } - if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { + resp, err := s.broadcastClient.SyncBroadcastMsg(msg) + if err != nil { metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgDepositClaim failed") - return err - } else { - log.WithFields(log.Fields{ - "event_nonce": deposit.EventNonce.String(), - "tx_hash": txResponse.TxResponse.TxHash, - }).Debugln("Oracle sent MsgDepositClaim") + return errors.Wrap(err, "broadcasting MsgDepositClaim failed") } + log.WithFields(log.Fields{ + "event_nonce": msg.EventNonce, + "event_height": msg.BlockHeight, + "tx_hash": resp.TxResponse.TxHash, + }).Infoln("EthOracle sent MsgDepositClaim") + return nil } @@ -532,9 +534,8 @@ func (s *peggyBroadcastClient) sendWithdrawClaims( defer doneFn() log.WithFields(log.Fields{ - "nonce": withdraw.BatchNonce.String(), + "batch_nonce": withdraw.BatchNonce.String(), "token_contract": withdraw.Token.Hex(), - "event_nonce": withdraw.EventNonce.String(), }).Debugln("observed TransactionBatchExecutedEvent") // WithdrawClaim claims that a batch of withdrawal @@ -547,17 +548,18 @@ func (s *peggyBroadcastClient) sendWithdrawClaims( Orchestrator: s.AccFromAddress().String(), } - if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { + resp, err := s.broadcastClient.SyncBroadcastMsg(msg) + if err != nil { metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgWithdrawClaim failed") - return err - } else { - log.WithFields(log.Fields{ - "event_nonce": withdraw.EventNonce.String(), - "tx_hash": txResponse.TxResponse.TxHash, - }).Debugln("Oracle sent MsgWithdrawClaim") + return errors.Wrap(err, "broadcasting MsgWithdrawClaim failed") } + log.WithFields(log.Fields{ + "event_height": msg.BlockHeight, + "event_nonce": msg.EventNonce, + "tx_hash": resp.TxResponse.TxHash, + }).Infoln("EthOracle sent MsgWithdrawClaim") + return nil } @@ -570,7 +572,6 @@ func (s *peggyBroadcastClient) sendValsetUpdateClaims( defer doneFn() log.WithFields(log.Fields{ - "event_nonce": valsetUpdate.EventNonce.Uint64(), "valset_nonce": valsetUpdate.NewValsetNonce.Uint64(), "validators": valsetUpdate.Validators, "powers": valsetUpdate.Powers, @@ -596,17 +597,18 @@ func (s *peggyBroadcastClient) sendValsetUpdateClaims( Orchestrator: s.AccFromAddress().String(), } - if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { + resp, err := s.broadcastClient.SyncBroadcastMsg(msg) + if err != nil { metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgValsetUpdatedClaim failed") - return err - } else { - log.WithFields(log.Fields{ - "event_nonce": valsetUpdate.EventNonce.String(), - "tx_hash": txResponse.TxResponse.TxHash, - }).Debugln("Oracle sent MsgValsetUpdatedClaim") + return errors.Wrap(err, "broadcasting MsgValsetUpdatedClaim failed") } + log.WithFields(log.Fields{ + "event_nonce": msg.EventNonce, + "event__height": msg.BlockHeight, + "tx_hash": resp.TxResponse.TxHash, + }).Infoln("Oracle sent MsgValsetUpdatedClaim") + return nil } @@ -619,7 +621,6 @@ func (s *peggyBroadcastClient) sendErc20DeployedClaims( defer doneFn() log.WithFields(log.Fields{ - "event_nonce": erc20Deployed.EventNonce.Uint64(), "cosmos_denom": erc20Deployed.CosmosDenom, "token_contract": erc20Deployed.TokenContract.Hex(), "name": erc20Deployed.Name, @@ -638,16 +639,17 @@ func (s *peggyBroadcastClient) sendErc20DeployedClaims( Orchestrator: s.AccFromAddress().String(), } - if txResponse, err := s.broadcastClient.SyncBroadcastMsg(msg); err != nil { + resp, err := s.broadcastClient.SyncBroadcastMsg(msg) + if err != nil { metrics.ReportFuncError(s.svcTags) - log.WithError(err).Errorln("broadcasting MsgERC20DeployedClaim failed") - return err - } else { - log.WithFields(log.Fields{ - "event_nonce": erc20Deployed.EventNonce.String(), - "tx_hash": txResponse.TxResponse.TxHash, - }).Debugln("Oracle sent MsgERC20DeployedClaim") + return errors.Wrap(err, "broadcasting MsgERC20DeployedClaim failed") } + log.WithFields(log.Fields{ + "event_nonce": msg.EventNonce, + "event_height": msg.BlockHeight, + "tx_hash": resp.TxResponse.TxHash, + }).Infoln("Oracle sent MsgERC20DeployedClaim") + return nil } diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 4eeeae22..3e24bb1d 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -121,7 +121,7 @@ func (l *ethOracleLoop) autoResync(ctx context.Context) error { retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to get last confirmed eth height, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to get last claimed event height, will retry (%d)", n) }), ); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") @@ -131,7 +131,7 @@ func (l *ethOracleLoop) autoResync(ctx context.Context) error { l.lastCheckedEthHeight = latestHeight l.lastResyncWithInjective = time.Now() - l.Logger().WithFields(log.Fields{"last_resync_time": l.lastResyncWithInjective.String(), "last_confirmed_eth_height": l.lastCheckedEthHeight}).Infoln("auto resync event nonce with Injective") + l.Logger().WithFields(log.Fields{"last_resync_time": l.lastResyncWithInjective.String(), "last_claimed_eth_height": l.lastCheckedEthHeight}).Infoln("auto resync last claimed event height with Injective") return nil } @@ -223,7 +223,7 @@ func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents newEvents := events.Filter(lastClaim.EthereumEventNonce) if newEvents.Num() == 0 { - l.Logger().WithField("last_claimed_event", lastClaim.EthereumEventNonce).Infoln("no new events on Ethereum") + l.Logger().WithField("last_claimed_event_nonce", lastClaim.EthereumEventNonce).Infoln("no new events on Ethereum") return nil } @@ -249,7 +249,7 @@ func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to send events to Injective, will retry (%d)", n) }), ); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") From 87df4b0e9dac99a25509ef044a2dbf2f77e31787 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 23 Jan 2024 11:44:19 +0100 Subject: [PATCH 23/99] add descriptive errors --- orchestrator/cosmos/load_balanced.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index db5e41aa..6d0ca2a2 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -57,12 +57,12 @@ func NewLoadBalancedNetwork( netCfg := common.LoadNetwork(networkName, "lb") explorer, err := explorerclient.NewExplorerClient(netCfg) if err != nil { - return nil, err + return nil, errors.Wrap(err, "failed to initialize explorer client") } daemonClient, err := chainclient.NewChainClient(clientCtx, netCfg, common.OptionGasPrices(injectiveGasPrices)) if err != nil { - return nil, errors.Wrapf(err, "failed to connect to Injective network: %s", networkName) + return nil, errors.Wrapf(err, "failed to intialize chain client (%s)", networkName) } time.Sleep(1 * time.Second) From a6b8d927cdc986fb1e422a3b4b2417b33f300e27 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 23 Jan 2024 12:16:26 +0100 Subject: [PATCH 24/99] logs --- orchestrator/batch_request_test.go | 6 ----- orchestrator/relayer.go | 2 +- test/peggo/peggy_params.json | 38 ------------------------------ 3 files changed, 1 insertion(+), 45 deletions(-) delete mode 100644 test/peggo/peggy_params.json diff --git a/orchestrator/batch_request_test.go b/orchestrator/batch_request_test.go index e710915e..3ae46e36 100644 --- a/orchestrator/batch_request_test.go +++ b/orchestrator/batch_request_test.go @@ -13,12 +13,6 @@ import ( peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) -func TestDusan(t *testing.T) { - logger := suplog.DefaultLogger - d := "dusan" - logger.Infoln("using", d, "for me") -} - func TestRequestBatches(t *testing.T) { t.Parallel() diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index dd08d9ef..d69a6e13 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -73,7 +73,7 @@ func (l *relayerLoop) relayValsetsAndBatches(ctx context.Context) error { retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay valset, will retry (%d)", n) + l.Logger().WithError(err).Warningf("failed to find latest valset on Ethereum, will retry (%d)", n) })); err != nil { return err } diff --git a/test/peggo/peggy_params.json b/test/peggo/peggy_params.json deleted file mode 100644 index f551c6f0..00000000 --- a/test/peggo/peggy_params.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "messages": [ - { - "@type": "/injective.peggy.v1.MsgUpdateParams", - "authority": "inj10d07y265gmmuvt4z0w9aw880jnsr700jstypyt", - "params": { - "peggy_id": "injective-peggyid", - "contract_source_hash": "", - "bridge_ethereum_address": "0x5048019d259217e6b7BC8e1E6aEfa9976B1ADFfe", - "bridge_chain_id": "50", - "signed_valsets_window": "25000", - "signed_batches_window": "25000", - "signed_claims_window": "25000", - "target_batch_timeout": "43200000", - "average_block_time": "1000", - "average_ethereum_block_time": "15000", - "slash_fraction_valset": "0.001000000000000000", - "slash_fraction_batch": "0.001000000000000000", - "slash_fraction_claim": "0.001000000000000000", - "slash_fraction_conflicting_claim": "0.001000000000000000", - "unbond_slashing_valsets_window": "25000", - "slash_fraction_bad_eth_signature": "0.001000000000000000", - "cosmos_coin_denom": "inj", - "cosmos_coin_erc20_contract": "0x7E5C521F8515017487750c13C3bF3B15f3f5f654", - "claim_slashing_enabled": false, - "bridge_contract_start_height": "23", - "valset_reward": { - "denom": "inj", - "amount": "0" - } - } - } - ], - "metadata": "ipfs://CID", - "deposit": "100000001inj", - "title": "Peggo local testing env", - "summary": "The time I needed a working environment to test the bridge" -} From db5a1d03263320a742154d98dd43716bf6c8d391 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 23 Jan 2024 13:44:28 +0100 Subject: [PATCH 25/99] fix oracle filter --- orchestrator/oracle.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 3e24bb1d..d0933003 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -229,18 +229,18 @@ func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents latestEventNonce, err := l.inj.SendEthereumClaims(ctx, lastClaim.EthereumEventNonce, - events.OldDeposits, - events.Deposits, - events.Withdrawals, - events.ERC20Deployments, - events.ValsetUpdates, + newEvents.OldDeposits, + newEvents.Deposits, + newEvents.Withdrawals, + newEvents.ERC20Deployments, + newEvents.ValsetUpdates, ) if err != nil { return err } - l.Logger().WithFields(log.Fields{"events": events.Num(), "latest_event_nonce": latestEventNonce}).Infoln("sent new event claims to Injective") + l.Logger().WithFields(log.Fields{"events": newEvents.Num(), "latest_event_nonce": latestEventNonce}).Infoln("sent new event claims to Injective") return nil } From cdd1ae776b1941ed502043fe93b25aba9d716408 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 23 Jan 2024 13:53:29 +0100 Subject: [PATCH 26/99] bump sdk-go to v1.50.0 --- go.mod | 56 ++++++++++++------------ go.sum | 135 +++++++++++++++++++++++++++++++++------------------------ 2 files changed, 106 insertions(+), 85 deletions(-) diff --git a/go.mod b/go.mod index 54a7d699..3d5222d9 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,10 @@ go 1.19 require ( github.com/InjectiveLabs/etherman v1.7.0 github.com/InjectiveLabs/metrics v0.0.1 - github.com/InjectiveLabs/sdk-go v1.48.16 + github.com/InjectiveLabs/sdk-go v1.50.0 github.com/avast/retry-go v3.0.0+incompatible github.com/cometbft/cometbft v0.37.2 - github.com/cosmos/cosmos-sdk v0.47.3 + github.com/cosmos/cosmos-sdk v0.47.5 github.com/ethereum/go-ethereum v1.11.5 github.com/hashicorp/go-multierror v1.1.1 github.com/jawher/mow.cli v1.2.0 @@ -19,22 +19,22 @@ require ( github.com/stretchr/testify v1.8.4 github.com/xlab/closer v0.0.0-20190328110542-03326addb7c2 github.com/xlab/suplog v1.3.1 - golang.org/x/crypto v0.9.0 - google.golang.org/grpc v1.55.0 + golang.org/x/crypto v0.11.0 + google.golang.org/grpc v1.56.2 ) require ( cosmossdk.io/api v0.3.1 // indirect - cosmossdk.io/core v0.5.1 // indirect - cosmossdk.io/depinject v1.0.0-alpha.3 // indirect - cosmossdk.io/errors v1.0.0-beta.7 // indirect - cosmossdk.io/math v1.0.1 // indirect + cosmossdk.io/core v0.6.1 // indirect + cosmossdk.io/depinject v1.0.0-alpha.4 // indirect + cosmossdk.io/errors v1.0.0 // indirect + cosmossdk.io/math v1.1.2 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect - github.com/99designs/keyring v1.2.1 // indirect + github.com/99designs/keyring v1.2.2 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect - github.com/CosmWasm/wasmd v0.40.0 // indirect - github.com/CosmWasm/wasmvm v1.2.4 // indirect + github.com/CosmWasm/wasmd v0.40.2 // indirect + github.com/CosmWasm/wasmvm v1.5.0 // indirect github.com/DataDog/datadog-go/v5 v5.1.0 // indirect github.com/InjectiveLabs/suplog v1.3.3 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect @@ -42,7 +42,7 @@ require ( github.com/alexcesaro/statsd v2.0.0+incompatible // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/aws/aws-sdk-go v1.44.203 // indirect - github.com/bandprotocol/bandchain-packet v0.0.2 // indirect + github.com/bandprotocol/bandchain-packet v0.0.4 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/btcsuite/btcd v0.23.4 // indirect @@ -57,13 +57,13 @@ require ( github.com/cometbft/cometbft-db v0.8.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogoproto v1.4.10 // indirect - github.com/cosmos/iavl v0.20.0 // indirect - github.com/cosmos/ibc-go/v7 v7.0.1 // indirect + github.com/cosmos/iavl v0.20.1 // indirect + github.com/cosmos/ibc-go/v7 v7.3.0 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect - github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect + github.com/cosmos/ledger-cosmos-go v0.12.2 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect @@ -88,7 +88,7 @@ require ( github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.4.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect @@ -115,7 +115,7 @@ require ( github.com/linxGnu/grocksdb v1.7.16 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect @@ -128,10 +128,10 @@ require ( github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.15.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect @@ -157,15 +157,15 @@ require ( github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.14.1 // indirect go.etcd.io/bbolt v1.3.7 // indirect - golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect - golang.org/x/mod v0.9.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/term v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb // indirect + golang.org/x/mod v0.11.0 // indirect + golang.org/x/net v0.12.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/term v0.10.0 // indirect + golang.org/x/text v0.12.0 // indirect golang.org/x/tools v0.7.0 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index 890cccac..4b6250e5 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34h cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -159,7 +159,7 @@ cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARy cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= +cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= @@ -283,7 +283,7 @@ cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQE cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= +cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= @@ -474,8 +474,8 @@ cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= @@ -544,19 +544,23 @@ cosmossdk.io/api v0.3.0/go.mod h1:2HDRQHwVIyklENrrXko0E/waZrRFZWHhPyhcBO4qHq4= cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= cosmossdk.io/core v0.3.2/go.mod h1:CO7vbe+evrBvHc0setFHL/u7nlY7HJGzdRSBkT/sirc= -cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI= cosmossdk.io/core v0.5.1/go.mod h1:KZtwHCLjcFuo0nmDc24Xy6CRNEL9Vl/MeimQ2aC7NLE= -cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw= +cosmossdk.io/core v0.6.1 h1:OBy7TI2W+/gyn2z40vVvruK3di+cAluinA6cybFbE7s= +cosmossdk.io/core v0.6.1/go.mod h1:g3MMBCBXtxbDWBURDVnJE7XML4BG5qENhs0gzkcpuFA= cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU= -cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w= +cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= +cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE= -cosmossdk.io/log v1.1.0 h1:v0ogPHYeTzPcBTcPR1A3j1hkei4pZama8kz8LKlCMv0= +cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= +cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= cosmossdk.io/log v1.1.0/go.mod h1:6zjroETlcDs+mm62gd8Ig7mZ+N+fVOZS91V17H+M4N4= +cosmossdk.io/log v1.1.1-0.20230704160919-88f2c830b0ca h1:msenprh2BLLRwNT7zN56TbBHOGk/7ARQckXHxXyvjoQ= cosmossdk.io/math v1.0.0-beta.3/go.mod h1:3LYasri3Zna4XpbrTNdKsWmD5fHHkaNAod/mNT9XdE4= cosmossdk.io/math v1.0.0-beta.4/go.mod h1:An0MllWJY6PxibUpnwGk8jOm+a/qIxlKmL5Zyp9NnaM= cosmossdk.io/math v1.0.0-beta.6/go.mod h1:gUVtWwIzfSXqcOT+lBVz2jyjfua8DoBdzRsIyaUAT/8= -cosmossdk.io/math v1.0.1 h1:Qx3ifyOPaMLNH/89WeZFH268yCvU4xEcnPLu3sJqPPg= cosmossdk.io/math v1.0.1/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k= +cosmossdk.io/math v1.1.2 h1:ORZetZCTyWkI5GlZ6CZS28fMHi83ZYf+A2vVnHNzZBM= +cosmossdk.io/math v1.1.2/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw= cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8MhhO9Hw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -569,8 +573,9 @@ git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqbl git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= -github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= +github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= github.com/Abirdcfly/dupword v0.0.7/go.mod h1:K/4M1kj+Zh39d2aotRwypvasonOyAMH1c/IZJzE0dmk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0/go.mod h1:0mMDvQFeLbbn1Wy8P2j3hwFhqBq+FKn8OZPno8WLmp8= @@ -649,8 +654,8 @@ github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3 github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/CloudyKit/jet/v6 v6.1.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= -github.com/CosmWasm/wasmvm v1.2.4 h1:6OfeZuEcEH/9iqwrg2pkeVtDCkMoj9U6PpKtcrCyVrQ= -github.com/CosmWasm/wasmvm v1.2.4/go.mod h1:vW/E3h8j9xBQs9bCoijDuawKo9kCtxOaS8N8J7KFtkc= +github.com/CosmWasm/wasmvm v1.5.0 h1:3hKeT9SfwfLhxTGKH3vXaKFzBz1yuvP8SlfwfQXbQfw= +github.com/CosmWasm/wasmvm v1.5.0/go.mod h1:fXB+m2gyh4v9839zlIXdMZGeLAxqUdYdFQqYsTha2hc= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go/v5 v5.1.0 h1:Zmq3tCk9+Tdq8Du73M71Zo6Dyx+cEo9QkCSCqQlHFaQ= @@ -676,8 +681,8 @@ github.com/InjectiveLabs/etherman v1.7.0 h1:ryYW87fqpnhRP33x+ykDhy6Lx9215Fl5T5LI github.com/InjectiveLabs/etherman v1.7.0/go.mod h1:7unNlA/9UVNZhneEWtNnFWfGDJ5KijkGprkpEF9I1BA= github.com/InjectiveLabs/metrics v0.0.1 h1:MXNj8JWOdIqiGZw83JdUTR+i6hgBrb12HatIUvaly9I= github.com/InjectiveLabs/metrics v0.0.1/go.mod h1:Dmgd60Z0pfi7uOGSUzyqZ00tbMYmZK25u8Sjgk3Ay4A= -github.com/InjectiveLabs/sdk-go v1.48.16 h1:OAraiGNKHk/O1scFIjUh6CtgnjpYDnC/jsqK8OKDuZU= -github.com/InjectiveLabs/sdk-go v1.48.16/go.mod h1:IPc0yp3d0++M7b/c+Frfb3PpFQz8kMF4rb49IXj3Drw= +github.com/InjectiveLabs/sdk-go v1.50.0 h1:dKHUZ1AC7BLxP5srUFsdRjGaP3cyo//Okr8KRW06tDk= +github.com/InjectiveLabs/sdk-go v1.50.0/go.mod h1:ecmY701q1cAZaNdeuzs08x7diCC8z5qjvcpbETR81oY= github.com/InjectiveLabs/suplog v1.3.3 h1:ARIR3lWD9BxcrmqTwgcGBt8t7e10gwOqllUAXa/MfxI= github.com/InjectiveLabs/suplog v1.3.3/go.mod h1:+I9WRgUhzmo1V/n7IkW24kFBFB9ZTPAiXXXCogWxmTM= github.com/InjectiveLabs/wasmd v0.40.0-inj h1:HBienMKEZufMHIK8gaqBukzPNmIzj4NQcLL7YbZm7No= @@ -762,7 +767,6 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3 github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= -github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= github.com/alecthomas/participle/v2 v2.0.0-alpha7/go.mod h1:NumScqsC42o9x+dGj8/YqsIfhrIQjFEOFovxotbBirA= github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -862,8 +866,8 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= @@ -1004,7 +1008,6 @@ github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= -github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= github.com/cockroachdb/apd/v3 v3.1.0/go.mod h1:6qgPBMXjATAdD/VefbRP9NoSLKjbB4LCoA7gN4LpHs4= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= @@ -1013,18 +1016,20 @@ github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSU github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= -github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= +github.com/cockroachdb/errors v1.10.0 h1:lfxS8zZz1+OjtV4MtNWgboi/W5tyLEB6VQZBXN+0VUU= +github.com/cockroachdb/errors v1.10.0/go.mod h1:lknhIsEVQ9Ss/qKDBQS/UqFSvPQjOwNq2qyKAxtHRqE= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/pebble v0.0.0-20220817183557-09c6e030a677/go.mod h1:890yq1fUb9b6dGNwssgeUO5vQV9qfXnCPxAJhBQfXw0= -github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= +github.com/cockroachdb/pebble v0.0.0-20230226194802-02d779ffbc46 h1:yMaoO76pV9knZ6bzEwzPSHnPSCTnrJohwkIQirmii70= github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= @@ -1181,8 +1186,9 @@ github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32/go.mod h1:kwMlEC4 github.com/cosmos/cosmos-proto v1.0.0-alpha7/go.mod h1:dosO4pSAbJF8zWCzCoTWP7nNsjcvSUBQmniFxDg5daw= github.com/cosmos/cosmos-proto v1.0.0-alpha8/go.mod h1:6/p+Bc4O8JKeZqe0VqUGTX31eoYqemTT4C1hLCWsO7I= github.com/cosmos/cosmos-proto v1.0.0-beta.1/go.mod h1:8k2GNZghi5sDRFw/scPL8gMSowT1vDA+5ouxL8GjaUE= -github.com/cosmos/cosmos-proto v1.0.0-beta.2 h1:X3OKvWgK9Gsejo0F1qs5l8Qn6xJV/AzgIWR2wZ8Nua8= github.com/cosmos/cosmos-proto v1.0.0-beta.2/go.mod h1:+XRCLJ14pr5HFEHIUcn51IKXD1Fy3rkEQqt4WqmN4V0= +github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o= +github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= github.com/cosmos/cosmos-sdk/db v1.0.0-beta.1.0.20220726092710-f848e4300a8a/go.mod h1:c8IO23vgNxueCCJlSI9awQtcxsvc+buzaeThB85qfBU= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= @@ -1198,14 +1204,16 @@ github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoK github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= github.com/cosmos/iavl v0.20.0-alpha4/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= -github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= -github.com/cosmos/ibc-go/v7 v7.0.1 h1:NIBNRWjlOoFvFQu1ZlgwkaSeHO5avf4C1YQiWegt8jw= -github.com/cosmos/ibc-go/v7 v7.0.1/go.mod h1:vEaapV6nuLPQlS+g8IKmxMo6auPi0i7HMv1PhViht/E= +github.com/cosmos/iavl v0.20.1 h1:rM1kqeG3/HBT85vsZdoSNsehciqUQPWrR4BYmqE2+zg= +github.com/cosmos/iavl v0.20.1/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= +github.com/cosmos/ibc-go/v7 v7.3.0 h1:QtGeVMi/3JeLWuvEuC60sBHpAF40Oenx/y+bP8+wRRw= +github.com/cosmos/ibc-go/v7 v7.3.0/go.mod h1:mUmaHFXpXrEdcxfdXyau+utZf14pGKVUiXwYftRZZfQ= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= -github.com/cosmos/ledger-cosmos-go v0.12.1 h1:sMBxza5p/rNK/06nBSNmsI/WDqI0pVJFVNihy1Y984w= github.com/cosmos/ledger-cosmos-go v0.12.1/go.mod h1:dhO6kj+Y+AHIOgAe4L9HL/6NDdyyth4q238I9yFpD2g= +github.com/cosmos/ledger-cosmos-go v0.12.2 h1:/XYaBlE2BJxtvpkHiBm97gFGSGmYGKunKyF3nNqAXZA= +github.com/cosmos/ledger-cosmos-go v0.12.2/go.mod h1:ZcqYgnfNJ6lAXe4HPtWgarNEY+B74i+2/8MhZw4ziiI= github.com/cosmos/rosetta-sdk-go v0.10.0 h1:E5RhTruuoA7KTIXUcMicL76cffyeoyvNybzUGSKFTcM= github.com/cosmos/rosetta-sdk-go v0.10.0/go.mod h1:SImAZkb96YbwvoRkzSMQB6noNJXFgWl/ENIznEoYQI4= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= @@ -1223,9 +1231,7 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cristalhq/acmd v0.8.1/go.mod h1:LG5oa43pE/BbxtfMoImHCQN++0Su7dzipdgBjMCBVDQ= -github.com/cucumber/common/gherkin/go/v22 v22.0.0 h1:4K8NqptbvdOrjL9DEea6HFjSpbdT9+Q5kgLpmmsHYl0= github.com/cucumber/common/gherkin/go/v22 v22.0.0/go.mod h1:3mJT10B2GGn3MvVPd3FwR7m2u4tLhSRhWUqJU4KN4Fg= -github.com/cucumber/common/messages/go/v17 v17.1.1 h1:RNqopvIFyLWnKv0LfATh34SWBhXeoFTJnSrgm9cT/Ts= github.com/cucumber/common/messages/go/v17 v17.1.1/go.mod h1:bpGxb57tDE385Rb2EohgUadLkAbhoC4IyCFi89u/JQI= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= @@ -1427,8 +1433,9 @@ github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSy github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= github.com/getsentry/sentry-go v0.17.0/go.mod h1:B82dxtBvxG0KaPD8/hfSV+VcHD+Lg/xUS4JuQn1P4cM= -github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/getsentry/sentry-go v0.23.0 h1:dn+QRCeJv4pPt9OjVXiMcGIBIefaTJPw/h0bZWO05nE= +github.com/getsentry/sentry-go v0.23.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -1446,6 +1453,7 @@ github.com/go-critic/go-critic v0.4.1/go.mod h1:7/14rZGnZbY6E38VEGk2kVhoq6itzc1E github.com/go-critic/go-critic v0.4.3/go.mod h1:j4O3D4RoIwRqlZw5jJpx0BNfXWWbpcJoKu5cYSe4YmQ= github.com/go-critic/go-critic v0.6.5/go.mod h1:ezfP/Lh7MA6dBNn4c6ab5ALv3sKnZVLx37tr00uuaOY= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= @@ -1745,7 +1753,7 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= -github.com/google/s2a-go v0.1.3 h1:FAgZmpLl/SXurPEZyCMPBIiiYeTbqfjlbdnCNTAkbGE= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= @@ -1755,8 +1763,9 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= @@ -1778,7 +1787,7 @@ github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= +github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= @@ -2255,8 +2264,9 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -2562,6 +2572,7 @@ github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= @@ -2604,8 +2615,8 @@ github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrb github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -2651,8 +2662,9 @@ github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY= github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= @@ -2673,7 +2685,6 @@ github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Ung github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/regen-network/gocuke v0.6.2 h1:pHviZ0kKAq2U2hN2q3smKNxct6hS0mGByFMHGnWA97M= github.com/regen-network/gocuke v0.6.2/go.mod h1:zYaqIHZobHyd0xOrHGPQjbhGJsuZ1oElx150u2o1xuk= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= @@ -2690,8 +2701,9 @@ github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= @@ -3237,8 +3249,8 @@ golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80 golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -3264,8 +3276,9 @@ golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZ golang.org/x/exp v0.0.0-20230131160201-f062dba9d201/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb h1:xIApU0ow1zwMa2uL1VDNeQlNVFTWMQxZUZCMDy0Q4Us= +golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= @@ -3316,8 +3329,9 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -3429,8 +3443,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -3468,7 +3482,7 @@ golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -3487,8 +3501,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -3680,8 +3694,8 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -3696,8 +3710,8 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -3714,8 +3728,9 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -3729,8 +3744,8 @@ golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -3961,7 +3976,7 @@ google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/ google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.122.0 h1:zDobeejm3E7pEG1mNHvdxvjs5XJoCMzyNH+CmwL94Es= +google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -4128,8 +4143,12 @@ google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 h1:Au6te5hbKUV8pIYWHqOUZ1pva5qK/rwbIhoXEUB9Lu8= +google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= +google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 h1:s5YSX+ZH5b5vS9rnpGymvIyMpLRJizowqDlOuyjXnTk= +google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -4185,8 +4204,9 @@ google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsA google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI= +google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -4207,8 +4227,9 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.2-0.20230208135220-49eaa78c6c9c/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.2-0.20230222093303-bc1253ad3743/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVCwMc= @@ -4279,8 +4300,8 @@ gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= -gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From a12b06a9c6f163f55dfd9e1b590d65c5cb9c90df Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 23 Jan 2024 13:55:13 +0100 Subject: [PATCH 27/99] bump cosmos-sdk to v0.47.3-inj-9 --- go.mod | 11 ++++++++++- go.sum | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3d5222d9..fb47e37f 100644 --- a/go.mod +++ b/go.mod @@ -54,6 +54,9 @@ require ( github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect + github.com/cockroachdb/errors v1.10.0 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/redact v1.1.5 // indirect github.com/cometbft/cometbft-db v0.8.0 // indirect github.com/confio/ics23/go v0.9.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect @@ -75,6 +78,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/getsentry/sentry-go v0.23.0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect @@ -111,6 +115,8 @@ require ( github.com/karalabe/usb v0.0.2 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.16.3 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/linxGnu/grocksdb v1.7.16 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -134,6 +140,7 @@ require ( github.com/prometheus/procfs v0.10.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/shirou/gopsutil v3.21.6+incompatible // indirect github.com/sirupsen/logrus v1.9.0 // indirect @@ -165,6 +172,8 @@ require ( golang.org/x/text v0.12.0 // indirect golang.org/x/tools v0.7.0 // indirect google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect @@ -179,6 +188,6 @@ replace ( github.com/CosmWasm/wasmd => github.com/InjectiveLabs/wasmd v0.40.0-inj github.com/bandprotocol/bandchain-packet => github.com/InjectiveLabs/bandchain-packet v0.0.4-0.20230327115226-35199d4659d5 github.com/cometbft/cometbft => github.com/InjectiveLabs/cometbft v0.37.1-inj - github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.47.3-inj-5 + github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.47.3-inj-9 github.com/ethereum/go-ethereum => github.com/ethereum/go-ethereum v1.12.0 ) diff --git a/go.sum b/go.sum index 4b6250e5..345ac46d 100644 --- a/go.sum +++ b/go.sum @@ -675,8 +675,8 @@ github.com/InjectiveLabs/bandchain-packet v0.0.4-0.20230327115226-35199d4659d5 h github.com/InjectiveLabs/bandchain-packet v0.0.4-0.20230327115226-35199d4659d5/go.mod h1:VoNDHSybdPQ35/3zxNwjewaGpzWHhYyTgV7cJzFglEE= github.com/InjectiveLabs/cometbft v0.37.1-inj h1:mNSorEwP72ovlb2HrYsH3L+uIgNgrUj5rI4DDXhatxk= github.com/InjectiveLabs/cometbft v0.37.1-inj/go.mod h1:Y2MMMN//O5K4YKd8ze4r9jmk4Y7h0ajqILXbH5JQFVs= -github.com/InjectiveLabs/cosmos-sdk v0.47.3-inj-5 h1:WbNT7o2AbXBqOvmPptAd1nVmx67l1LtXIs4GjR0gvXo= -github.com/InjectiveLabs/cosmos-sdk v0.47.3-inj-5/go.mod h1:c4OfLdAykA9zsj1CqrxBRqXzVz48I++JSvIMPSPcEmk= +github.com/InjectiveLabs/cosmos-sdk v0.47.3-inj-9 h1:/x20L4ZfYIhGu/wsmXb2nV6DF6t5QVNq7ecAFqAcrMU= +github.com/InjectiveLabs/cosmos-sdk v0.47.3-inj-9/go.mod h1:c4OfLdAykA9zsj1CqrxBRqXzVz48I++JSvIMPSPcEmk= github.com/InjectiveLabs/etherman v1.7.0 h1:ryYW87fqpnhRP33x+ykDhy6Lx9215Fl5T5LIiA78W88= github.com/InjectiveLabs/etherman v1.7.0/go.mod h1:7unNlA/9UVNZhneEWtNnFWfGDJ5KijkGprkpEF9I1BA= github.com/InjectiveLabs/metrics v0.0.1 h1:MXNj8JWOdIqiGZw83JdUTR+i6hgBrb12HatIUvaly9I= From b9e7aa26b2098a5c049da9548ebbfdf364585fb5 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 23 Jan 2024 14:05:55 +0100 Subject: [PATCH 28/99] bump wasmd version to v0.45.0-inj --- go.mod | 2 +- go.sum | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index fb47e37f..1dc2e099 100644 --- a/go.mod +++ b/go.mod @@ -185,7 +185,7 @@ require ( ) replace ( - github.com/CosmWasm/wasmd => github.com/InjectiveLabs/wasmd v0.40.0-inj + github.com/CosmWasm/wasmd => github.com/InjectiveLabs/wasmd v0.45.0-inj github.com/bandprotocol/bandchain-packet => github.com/InjectiveLabs/bandchain-packet v0.0.4-0.20230327115226-35199d4659d5 github.com/cometbft/cometbft => github.com/InjectiveLabs/cometbft v0.37.1-inj github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.47.3-inj-9 diff --git a/go.sum b/go.sum index 345ac46d..e1589e66 100644 --- a/go.sum +++ b/go.sum @@ -554,7 +554,7 @@ cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAm cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= cosmossdk.io/log v1.1.0/go.mod h1:6zjroETlcDs+mm62gd8Ig7mZ+N+fVOZS91V17H+M4N4= -cosmossdk.io/log v1.1.1-0.20230704160919-88f2c830b0ca h1:msenprh2BLLRwNT7zN56TbBHOGk/7ARQckXHxXyvjoQ= +cosmossdk.io/log v1.2.1 h1:Xc1GgTCicniwmMiKwDxUjO4eLhPxoVdI9vtMW8Ti/uk= cosmossdk.io/math v1.0.0-beta.3/go.mod h1:3LYasri3Zna4XpbrTNdKsWmD5fHHkaNAod/mNT9XdE4= cosmossdk.io/math v1.0.0-beta.4/go.mod h1:An0MllWJY6PxibUpnwGk8jOm+a/qIxlKmL5Zyp9NnaM= cosmossdk.io/math v1.0.0-beta.6/go.mod h1:gUVtWwIzfSXqcOT+lBVz2jyjfua8DoBdzRsIyaUAT/8= @@ -685,8 +685,8 @@ github.com/InjectiveLabs/sdk-go v1.50.0 h1:dKHUZ1AC7BLxP5srUFsdRjGaP3cyo//Okr8KR github.com/InjectiveLabs/sdk-go v1.50.0/go.mod h1:ecmY701q1cAZaNdeuzs08x7diCC8z5qjvcpbETR81oY= github.com/InjectiveLabs/suplog v1.3.3 h1:ARIR3lWD9BxcrmqTwgcGBt8t7e10gwOqllUAXa/MfxI= github.com/InjectiveLabs/suplog v1.3.3/go.mod h1:+I9WRgUhzmo1V/n7IkW24kFBFB9ZTPAiXXXCogWxmTM= -github.com/InjectiveLabs/wasmd v0.40.0-inj h1:HBienMKEZufMHIK8gaqBukzPNmIzj4NQcLL7YbZm7No= -github.com/InjectiveLabs/wasmd v0.40.0-inj/go.mod h1:GKEn2k43oSu/WX4hd9tMGZi3ykgfcn01rLa116B/Dhs= +github.com/InjectiveLabs/wasmd v0.45.0-inj h1:hYRzxEiBNJqJA7EoE9kMTFpWayjT2ICo/FsmtAf8A+o= +github.com/InjectiveLabs/wasmd v0.45.0-inj/go.mod h1:M8hxvHoOzZkzdrFNqDYcPPcQw7EmJwYNZkslhHbCj0I= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= @@ -2711,8 +2711,8 @@ github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= -github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= +github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= From 66eacdec5dc37d6950cf103be2f14ef2c460756c Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 12:49:28 +0100 Subject: [PATCH 29/99] init tendermint client for load balanced connection --- orchestrator/cosmos/load_balanced.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index 6d0ca2a2..cc3fd66c 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -2,6 +2,7 @@ package cosmos import ( "context" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" "strconv" "time" @@ -37,11 +38,6 @@ func NewLoadBalancedNetwork( keyring keyring.Keyring, personalSignerFn keystore.PersonalSignFn, ) (*LoadBalancedNetwork, error) { - clientCtx, err := chainclient.NewClientContext(chainID, validatorAddress, keyring) - if err != nil { - return nil, errors.Wrapf(err, "failed to create client context for Injective chain") - } - var networkName string switch chainID { case "injective-1": @@ -60,6 +56,18 @@ func NewLoadBalancedNetwork( return nil, errors.Wrap(err, "failed to initialize explorer client") } + clientCtx, err := chainclient.NewClientContext(chainID, validatorAddress, keyring) + if err != nil { + return nil, errors.Wrapf(err, "failed to create client context for Injective chain") + } + + tmClient, err := rpchttp.New(netCfg.TmEndpoint, "/websocket") + if err != nil { + return nil, errors.Wrap(err, "failed to initialize tendermint client") + } + + clientCtx = clientCtx.WithNodeURI(netCfg.TmEndpoint).WithClient(tmClient) + daemonClient, err := chainclient.NewChainClient(clientCtx, netCfg, common.OptionGasPrices(injectiveGasPrices)) if err != nil { return nil, errors.Wrapf(err, "failed to intialize chain client (%s)", networkName) From 77414bd24a819ddc3d359b619a473999fe19385b Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 16:33:27 +0100 Subject: [PATCH 30/99] separate query client impl --- orchestrator/cosmos/query/client.go | 276 ++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 orchestrator/cosmos/query/client.go diff --git a/orchestrator/cosmos/query/client.go b/orchestrator/cosmos/query/client.go new file mode 100644 index 00000000..16e8e9a0 --- /dev/null +++ b/orchestrator/cosmos/query/client.go @@ -0,0 +1,276 @@ +package query + +import ( + "context" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + + "github.com/InjectiveLabs/metrics" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" +) + +var ErrNotFound = errors.New("not found") + +type PeggyQueryClient struct { + peggytypes.QueryClient + + svcTags metrics.Tags +} + +func (c PeggyQueryClient) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + req := &peggytypes.QueryValsetRequestRequest{Nonce: nonce} + + resp, err := c.QueryClient.ValsetRequest(ctx, req) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query ValsetRequest from client") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return resp.Valset, nil +} + +func (c PeggyQueryClient) CurrentValset(ctx context.Context) (*peggytypes.Valset, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + resp, err := c.QueryClient.CurrentValset(ctx, &peggytypes.QueryCurrentValsetRequest{}) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query CurrentValset from client") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return resp.Valset, nil +} + +func (c PeggyQueryClient) OldestUnsignedValsets(ctx context.Context, valAccountAddress sdktypes.AccAddress) ([]*peggytypes.Valset, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + req := &peggytypes.QueryLastPendingValsetRequestByAddrRequest{ + Address: valAccountAddress.String(), + } + + resp, err := c.QueryClient.LastPendingValsetRequestByAddr(ctx, req) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query LastPendingValsetRequestByAddr from client") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return resp.Valsets, nil +} + +func (c PeggyQueryClient) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + resp, err := c.QueryClient.LastValsetRequests(ctx, &peggytypes.QueryLastValsetRequestsRequest{}) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query LastValsetRequests from daemon") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return resp.Valsets, nil +} + +func (c PeggyQueryClient) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + resp, err := c.QueryClient.ValsetConfirmsByNonce(ctx, &peggytypes.QueryValsetConfirmsByNonceRequest{Nonce: nonce}) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query ValsetConfirmsByNonce from daemon") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return resp.Confirms, nil +} + +func (c PeggyQueryClient) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress sdktypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + req := &peggytypes.QueryLastPendingBatchRequestByAddrRequest{ + Address: valAccountAddress.String(), + } + + resp, err := c.QueryClient.LastPendingBatchRequestByAddr(ctx, req) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query LastPendingBatchRequestByAddr from daemon") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return resp.Batch, nil +} + +func (c PeggyQueryClient) LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + resp, err := c.QueryClient.OutgoingTxBatches(ctx, &peggytypes.QueryOutgoingTxBatchesRequest{}) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query OutgoingTxBatches from daemon") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return resp.Batches, nil +} + +func (c PeggyQueryClient) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + resp, err := c.QueryClient.BatchFees(ctx, &peggytypes.QueryBatchFeeRequest{}) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query BatchFees from daemon") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return resp.BatchFees, nil +} + +func (c PeggyQueryClient) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract common.Address) ([]*peggytypes.MsgConfirmBatch, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + req := &peggytypes.QueryBatchConfirmsRequest{ + Nonce: nonce, + ContractAddress: tokenContract.String(), + } + + resp, err := c.QueryClient.BatchConfirms(ctx, req) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query BatchConfirms from daemon") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return resp.Confirms, nil +} + +func (c PeggyQueryClient) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress sdktypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + req := &peggytypes.QueryLastEventByAddrRequest{ + Address: validatorAccountAddress.String(), + } + + resp, err := c.QueryClient.LastEventByAddr(ctx, req) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query LastEventByAddr from daemon") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return resp.LastClaimEvent, nil +} + +func (c PeggyQueryClient) PeggyParams(ctx context.Context) (*peggytypes.Params, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + resp, err := c.QueryClient.Params(ctx, &peggytypes.QueryParamsRequest{}) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query PeggyParams from daemon") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + return &resp.Params, nil +} + +func (c PeggyQueryClient) GetValidatorAddress(ctx context.Context, addr common.Address) (sdktypes.AccAddress, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + req := &peggytypes.QueryDelegateKeysByEthAddress{ + EthAddress: addr.Hex(), + } + + resp, err := c.QueryClient.GetDelegateKeyByEth(ctx, req) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return nil, errors.Wrap(err, "failed to query GetDelegateKeyByEth from client") + } + + if resp == nil { + metrics.ReportFuncError(c.svcTags) + return nil, ErrNotFound + } + + valAddr, err := sdktypes.AccAddressFromBech32(resp.ValidatorAddress) + if err != nil { + return nil, errors.Wrapf(err, "failed to decode validator address: %v", resp.ValidatorAddress) + } + + return valAddr, nil +} From 09df95363a17c51cecaf9b25d153e998851216f8 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 16:47:56 +0100 Subject: [PATCH 31/99] replace new impl at call site --- orchestrator/cosmos/custom_rpc.go | 3 ++- orchestrator/cosmos/load_balanced.go | 3 ++- .../{query/client.go => peggyclient/query.go} | 25 ++++++++++++------- 3 files changed, 20 insertions(+), 11 deletions(-) rename orchestrator/cosmos/{query/client.go => peggyclient/query.go} (89%) diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index ea3d940c..ea3e08a5 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -2,6 +2,7 @@ package cosmos import ( "context" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggyclient" "time" "github.com/InjectiveLabs/sdk-go/client/common" @@ -76,7 +77,7 @@ func NewCustomRPCNetwork( n := &CustomRPCNetwork{ TendermintClient: tmclient.NewRPCClient(tendermintRPC), - PeggyQueryClient: NewPeggyQueryClient(peggyQuerier), + PeggyQueryClient: peggyclient.NewPeggyQueryClient(peggyQuerier), PeggyBroadcastClient: NewPeggyBroadcastClient(peggyQuerier, daemonClient, personalSignerFn), } diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index cc3fd66c..cd580f5f 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -2,6 +2,7 @@ package cosmos import ( "context" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggyclient" rpchttp "github.com/cometbft/cometbft/rpc/client/http" "strconv" "time" @@ -83,7 +84,7 @@ func NewLoadBalancedNetwork( peggyQuerier := types.NewQueryClient(grpcConn) n := &LoadBalancedNetwork{ - PeggyQueryClient: NewPeggyQueryClient(peggyQuerier), + PeggyQueryClient: peggyclient.NewPeggyQueryClient(peggyQuerier), PeggyBroadcastClient: NewPeggyBroadcastClient(peggyQuerier, daemonClient, personalSignerFn), ExplorerClient: explorer, } diff --git a/orchestrator/cosmos/query/client.go b/orchestrator/cosmos/peggyclient/query.go similarity index 89% rename from orchestrator/cosmos/query/client.go rename to orchestrator/cosmos/peggyclient/query.go index 16e8e9a0..fba4149c 100644 --- a/orchestrator/cosmos/query/client.go +++ b/orchestrator/cosmos/peggyclient/query.go @@ -1,10 +1,10 @@ -package query +package peggyclient import ( "context" - sdktypes "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" "github.com/InjectiveLabs/metrics" @@ -19,6 +19,13 @@ type PeggyQueryClient struct { svcTags metrics.Tags } +func NewPeggyQueryClient(client peggytypes.QueryClient) *PeggyQueryClient { + return &PeggyQueryClient{ + QueryClient: client, + svcTags: metrics.Tags{"svc": "peggy_query"}, + } +} + func (c PeggyQueryClient) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) @@ -59,7 +66,7 @@ func (c PeggyQueryClient) CurrentValset(ctx context.Context) (*peggytypes.Valset return resp.Valset, nil } -func (c PeggyQueryClient) OldestUnsignedValsets(ctx context.Context, valAccountAddress sdktypes.AccAddress) ([]*peggytypes.Valset, error) { +func (c PeggyQueryClient) OldestUnsignedValsets(ctx context.Context, valAccountAddress cosmostypes.AccAddress) ([]*peggytypes.Valset, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -120,7 +127,7 @@ func (c PeggyQueryClient) AllValsetConfirms(ctx context.Context, nonce uint64) ( return resp.Confirms, nil } -func (c PeggyQueryClient) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress sdktypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { +func (c PeggyQueryClient) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -181,7 +188,7 @@ func (c PeggyQueryClient) UnbatchedTokensWithFees(ctx context.Context) ([]*peggy return resp.BatchFees, nil } -func (c PeggyQueryClient) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract common.Address) ([]*peggytypes.MsgConfirmBatch, error) { +func (c PeggyQueryClient) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -205,7 +212,7 @@ func (c PeggyQueryClient) TransactionBatchSignatures(ctx context.Context, nonce return resp.Confirms, nil } -func (c PeggyQueryClient) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress sdktypes.AccAddress) (*peggytypes.LastClaimEvent, error) { +func (c PeggyQueryClient) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -247,7 +254,7 @@ func (c PeggyQueryClient) PeggyParams(ctx context.Context) (*peggytypes.Params, return &resp.Params, nil } -func (c PeggyQueryClient) GetValidatorAddress(ctx context.Context, addr common.Address) (sdktypes.AccAddress, error) { +func (c PeggyQueryClient) GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (cosmostypes.AccAddress, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -267,7 +274,7 @@ func (c PeggyQueryClient) GetValidatorAddress(ctx context.Context, addr common.A return nil, ErrNotFound } - valAddr, err := sdktypes.AccAddressFromBech32(resp.ValidatorAddress) + valAddr, err := cosmostypes.AccAddressFromBech32(resp.ValidatorAddress) if err != nil { return nil, errors.Wrapf(err, "failed to decode validator address: %v", resp.ValidatorAddress) } From fa12054850d142517002ddd1f48932f5eb4d6fa8 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 16:53:25 +0100 Subject: [PATCH 32/99] fix log --- orchestrator/cosmos/broadcast.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index a0bb861a..72380289 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -604,9 +604,9 @@ func (s *peggyBroadcastClient) sendValsetUpdateClaims( } log.WithFields(log.Fields{ - "event_nonce": msg.EventNonce, - "event__height": msg.BlockHeight, - "tx_hash": resp.TxResponse.TxHash, + "event_nonce": msg.EventNonce, + "event_height": msg.BlockHeight, + "tx_hash": resp.TxResponse.TxHash, }).Infoln("Oracle sent MsgValsetUpdatedClaim") return nil From c0606e827669e9cafd98f1384bf520c876882ab6 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 17:22:07 +0100 Subject: [PATCH 33/99] remove unused field --- orchestrator/cosmos/broadcast.go | 2 - orchestrator/cosmos/query.go | 257 +------------------------------ 2 files changed, 1 insertion(+), 258 deletions(-) diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index a0bb861a..5de60376 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -75,12 +75,10 @@ type PeggyBroadcastClient interface { } func NewPeggyBroadcastClient( - queryClient peggytypes.QueryClient, broadcastClient chainclient.ChainClient, ethPersonalSignFn keystore.PersonalSignFn, ) PeggyBroadcastClient { return &peggyBroadcastClient{ - daemonQueryClient: queryClient, broadcastClient: broadcastClient, ethPersonalSignFn: ethPersonalSignFn, diff --git a/orchestrator/cosmos/query.go b/orchestrator/cosmos/query.go index 386814db..c22b94e6 100644 --- a/orchestrator/cosmos/query.go +++ b/orchestrator/cosmos/query.go @@ -3,12 +3,9 @@ package cosmos import ( "context" + "github.com/InjectiveLabs/sdk-go/chain/peggy/types" sdk "github.com/cosmos/cosmos-sdk/types" ethcmn "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - - "github.com/InjectiveLabs/metrics" - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) type PeggyQueryClient interface { @@ -27,255 +24,3 @@ type PeggyQueryClient interface { PeggyParams(ctx context.Context) (*types.Params, error) GetValidatorAddress(ctx context.Context, addr ethcmn.Address) (sdk.AccAddress, error) } - -func NewPeggyQueryClient(client types.QueryClient) PeggyQueryClient { - return &peggyQueryClient{ - daemonQueryClient: client, - svcTags: metrics.Tags{ - "svc": "peggy_query", - }, - } -} - -type peggyQueryClient struct { - daemonQueryClient types.QueryClient - svcTags metrics.Tags -} - -var ErrNotFound = errors.New("not found") - -func (s *peggyQueryClient) GetValidatorAddress(ctx context.Context, addr ethcmn.Address) (sdk.AccAddress, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - resp, err := s.daemonQueryClient.GetDelegateKeyByEth(ctx, &types.QueryDelegateKeysByEthAddress{ - EthAddress: addr.Hex(), - }) - - if err != nil { - return nil, err - } - - if resp == nil { - return nil, ErrNotFound - } - - valAddr, err := sdk.AccAddressFromBech32(resp.ValidatorAddress) - if err != nil { - return nil, errors.Wrapf(err, "failed to decode validator address: %v", resp.ValidatorAddress) - } - - return valAddr, nil -} - -func (s *peggyQueryClient) ValsetAt(ctx context.Context, nonce uint64) (*types.Valset, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.ValsetRequest(ctx, &types.QueryValsetRequestRequest{ - Nonce: nonce, - }) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query ValsetRequest from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return daemonResp.Valset, nil -} - -func (s *peggyQueryClient) CurrentValset(ctx context.Context) (*types.Valset, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.CurrentValset(ctx, &types.QueryCurrentValsetRequest{}) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query CurrentValset from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return daemonResp.Valset, nil -} - -func (s *peggyQueryClient) OldestUnsignedValsets(ctx context.Context, valAccountAddress sdk.AccAddress) ([]*types.Valset, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.LastPendingValsetRequestByAddr(ctx, &types.QueryLastPendingValsetRequestByAddrRequest{ - Address: valAccountAddress.String(), - }) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query LastPendingValsetRequestByAddr from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return daemonResp.Valsets, nil -} - -func (s *peggyQueryClient) LatestValsets(ctx context.Context) ([]*types.Valset, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.LastValsetRequests(ctx, &types.QueryLastValsetRequestsRequest{}) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query LastValsetRequests from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return daemonResp.Valsets, nil -} - -func (s *peggyQueryClient) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*types.MsgValsetConfirm, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.ValsetConfirmsByNonce(ctx, &types.QueryValsetConfirmsByNonceRequest{ - Nonce: nonce, - }) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query ValsetConfirmsByNonce from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return daemonResp.Confirms, nil -} - -func (s *peggyQueryClient) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress sdk.AccAddress) (*types.OutgoingTxBatch, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.LastPendingBatchRequestByAddr(ctx, &types.QueryLastPendingBatchRequestByAddrRequest{ - Address: valAccountAddress.String(), - }) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query LastPendingBatchRequestByAddr from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return daemonResp.Batch, nil -} - -func (s *peggyQueryClient) LatestTransactionBatches(ctx context.Context) ([]*types.OutgoingTxBatch, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.OutgoingTxBatches(ctx, &types.QueryOutgoingTxBatchesRequest{}) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query OutgoingTxBatches from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return daemonResp.Batches, nil -} - -func (s *peggyQueryClient) UnbatchedTokensWithFees(ctx context.Context) ([]*types.BatchFees, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.BatchFees(ctx, &types.QueryBatchFeeRequest{}) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query BatchFees from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return daemonResp.BatchFees, nil -} - -func (s *peggyQueryClient) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract ethcmn.Address) ([]*types.MsgConfirmBatch, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.BatchConfirms(ctx, &types.QueryBatchConfirmsRequest{ - Nonce: nonce, - ContractAddress: tokenContract.String(), - }) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query BatchConfirms from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return daemonResp.Confirms, nil -} - -func (s *peggyQueryClient) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress sdk.AccAddress) (*types.LastClaimEvent, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.LastEventByAddr(ctx, &types.QueryLastEventByAddrRequest{ - Address: validatorAccountAddress.String(), - }) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query LastEventByAddr from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return daemonResp.LastClaimEvent, nil -} - -func (s *peggyQueryClient) PeggyParams(ctx context.Context) (*types.Params, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - daemonResp, err := s.daemonQueryClient.Params(ctx, &types.QueryParamsRequest{}) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "failed to query PeggyParams from daemon") - return nil, err - } else if daemonResp == nil { - metrics.ReportFuncError(s.svcTags) - return nil, ErrNotFound - } - - return &daemonResp.Params, nil -} From 9bf47550c5ab6e75410bd117bae6d6c094399a3c Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 18:51:02 +0100 Subject: [PATCH 34/99] refactor peggy query and broadcast clients --- orchestrator/cosmos/broadcast.go | 596 +----------------- orchestrator/cosmos/custom_rpc.go | 21 +- orchestrator/cosmos/load_balanced.go | 21 +- orchestrator/cosmos/network.go | 13 + orchestrator/cosmos/peggy/broadcast.go | 548 ++++++++++++++++ .../cosmos/{peggyclient => peggy}/query.go | 32 +- orchestrator/signer.go | 6 +- 7 files changed, 611 insertions(+), 626 deletions(-) create mode 100644 orchestrator/cosmos/network.go create mode 100644 orchestrator/cosmos/peggy/broadcast.go rename orchestrator/cosmos/{peggyclient => peggy}/query.go (79%) diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go index 5de60376..3d006e8b 100644 --- a/orchestrator/cosmos/broadcast.go +++ b/orchestrator/cosmos/broadcast.go @@ -2,27 +2,13 @@ package cosmos import ( "context" - "fmt" - "sort" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - ethcmn "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - log "github.com/xlab/suplog" - - "github.com/InjectiveLabs/metrics" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - chainclient "github.com/InjectiveLabs/sdk-go/client/chain" + sdk "github.com/cosmos/cosmos-sdk/types" + ethcmn "github.com/ethereum/go-ethereum/common" ) type PeggyBroadcastClient interface { - ValFromAddress() sdk.ValAddress - AccFromAddress() sdk.AccAddress - /// Send a transaction updating the eth address for the sending /// Cosmos address. The sending Cosmos address should be a validator UpdatePeggyOrchestratorAddresses( @@ -73,581 +59,3 @@ type PeggyBroadcastClient interface { denom string, ) error } - -func NewPeggyBroadcastClient( - broadcastClient chainclient.ChainClient, - ethPersonalSignFn keystore.PersonalSignFn, -) PeggyBroadcastClient { - return &peggyBroadcastClient{ - broadcastClient: broadcastClient, - ethPersonalSignFn: ethPersonalSignFn, - - svcTags: metrics.Tags{ - "svc": "peggy_broadcast", - }, - } -} - -func (s *peggyBroadcastClient) ValFromAddress() sdk.ValAddress { - return sdk.ValAddress(s.broadcastClient.FromAddress().Bytes()) -} - -func (s *peggyBroadcastClient) AccFromAddress() sdk.AccAddress { - return s.broadcastClient.FromAddress() -} - -type peggyBroadcastClient struct { - daemonQueryClient peggytypes.QueryClient - broadcastClient chainclient.ChainClient - ethSignerFn keystore.SignerFn - ethPersonalSignFn keystore.PersonalSignFn - - svcTags metrics.Tags -} - -func (s *peggyBroadcastClient) UpdatePeggyOrchestratorAddresses( - ctx context.Context, - ethFrom ethcmn.Address, - orchestratorAddr sdk.AccAddress, -) error { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - // SetOrchestratorAddresses - - // This message allows validators to delegate their voting responsibilities - // to a given key. This key is then used as an optional authentication method - // for sigining oracle claims - // This is used by the validators to set the Ethereum address that represents - // them on the Ethereum side of the bridge. They must sign their Cosmos address - // using the Ethereum address they have submitted. Like ValsetResponse this - // message can in theory be submitted by anyone, but only the current validator - // sets submissions carry any weight. - - // ------------- - msg := &peggytypes.MsgSetOrchestratorAddresses{ - Sender: s.AccFromAddress().String(), - EthAddress: ethFrom.Hex(), - Orchestrator: orchestratorAddr.String(), - } - - res, err := s.broadcastClient.SyncBroadcastMsg(msg) - fmt.Println("Response of set eth address", "res", res) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgSetOrchestratorAddresses failed") - return err - } - - return nil -} - -func (s *peggyBroadcastClient) SendValsetConfirm( - ctx context.Context, - ethFrom ethcmn.Address, - peggyID ethcmn.Hash, - valset *peggytypes.Valset, -) error { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - confirmHash := peggy.EncodeValsetConfirm(peggyID, valset) - signature, err := s.ethPersonalSignFn(ethFrom, confirmHash.Bytes()) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.New("failed to sign validator address") - return err - } - // MsgValsetConfirm - // this is the message sent by the validators when they wish to submit their - // signatures over the validator set at a given block height. A validator must - // first call MsgSetEthAddress to set their Ethereum address to be used for - // signing. Then someone (anyone) must make a ValsetRequest the request is - // essentially a messaging mechanism to determine which block all validators - // should submit signatures over. Finally validators sign the validator set, - // powers, and Ethereum addresses of the entire validator set at the height of a - // ValsetRequest and submit that signature with this message. - // - // If a sufficient number of validators (66% of voting power) (A) have set - // Ethereum addresses and (B) submit ValsetConfirm messages with their - // signatures it is then possible for anyone to view these signatures in the - // chain store and submit them to Ethereum to update the validator set - // ------------- - msg := &peggytypes.MsgValsetConfirm{ - Orchestrator: s.AccFromAddress().String(), - EthAddress: ethFrom.Hex(), - Nonce: valset.Nonce, - Signature: ethcmn.Bytes2Hex(signature), - } - if err = s.broadcastClient.QueueBroadcastMsg(msg); err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgValsetConfirm failed") - return err - } - - return nil -} - -func (s *peggyBroadcastClient) SendBatchConfirm( - ctx context.Context, - ethFrom ethcmn.Address, - peggyID ethcmn.Hash, - batch *peggytypes.OutgoingTxBatch, -) error { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - confirmHash := peggy.EncodeTxBatchConfirm(peggyID, batch) - signature, err := s.ethPersonalSignFn(ethFrom, confirmHash.Bytes()) - if err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.New("failed to sign validator address") - return err - } - - // MsgConfirmBatch - // When validators observe a MsgRequestBatch they form a batch by ordering - // transactions currently in the txqueue in order of highest to lowest fee, - // cutting off when the batch either reaches a hardcoded maximum size (to be - // decided, probably around 100) or when transactions stop being profitable - // (TODO determine this without nondeterminism) This message includes the batch - // as well as an Ethereum signature over this batch by the validator - // ------------- - msg := &peggytypes.MsgConfirmBatch{ - Orchestrator: s.AccFromAddress().String(), - Nonce: batch.BatchNonce, - Signature: ethcmn.Bytes2Hex(signature), - EthSigner: ethFrom.Hex(), - TokenContract: batch.TokenContract, - } - if err = s.broadcastClient.QueueBroadcastMsg(msg); err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgConfirmBatch failed") - return err - } - - return nil -} - -func (s *peggyBroadcastClient) SendToEth( - ctx context.Context, - destination ethcmn.Address, - amount, fee sdk.Coin, -) error { - // MsgSendToEth - // This is the message that a user calls when they want to bridge an asset - // it will later be removed when it is included in a batch and successfully - // submitted tokens are removed from the users balance immediately - // ------------- - // AMOUNT: - // the coin to send across the bridge, note the restriction that this is a - // single coin not a set of coins that is normal in other Cosmos messages - // FEE: - // the fee paid for the bridge, distinct from the fee paid to the chain to - // actually send this message in the first place. So a successful send has - // two layers of fees for the user - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - msg := &peggytypes.MsgSendToEth{ - Sender: s.AccFromAddress().String(), - EthDest: destination.Hex(), - Amount: amount, - BridgeFee: fee, // TODO: use exactly that fee for transaction - } - if err := s.broadcastClient.QueueBroadcastMsg(msg); err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgSendToEth failed") - return err - } - - return nil -} - -func (s *peggyBroadcastClient) SendRequestBatch( - ctx context.Context, - denom string, -) error { - // MsgRequestBatch - // this is a message anyone can send that requests a batch of transactions to - // send across the bridge be created for whatever block height this message is - // included in. This acts as a coordination point, the handler for this message - // looks at the AddToOutgoingPool tx's in the store and generates a batch, also - // available in the store tied to this message. The validators then grab this - // batch, sign it, submit the signatures with a MsgConfirmBatch before a relayer - // can finally submit the batch - // ------------- - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - msg := &peggytypes.MsgRequestBatch{ - Denom: denom, - Orchestrator: s.AccFromAddress().String(), - } - if err := s.broadcastClient.QueueBroadcastMsg(msg); err != nil { - metrics.ReportFuncError(s.svcTags) - err = errors.Wrap(err, "broadcasting MsgRequestBatch failed") - return err - } - - return nil -} - -func (s *peggyBroadcastClient) SendEthereumClaims( - ctx context.Context, - lastClaimEventNonce uint64, - oldDeposits []*peggyevents.PeggySendToCosmosEvent, - deposits []*peggyevents.PeggySendToInjectiveEvent, - withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, -) (uint64, error) { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - events := sortEventsByNonce(oldDeposits, deposits, withdraws, erc20Deployed, valsetUpdates) - - // this can't happen outside of programmer error - if firstToSend := events[0]; firstToSend.Nonce() != lastClaimEventNonce+1 { - return 0, errors.Errorf("expected event with nonce %d, got %d", lastClaimEventNonce+1, firstToSend.Nonce()) - } - - for _, e := range events { - if err := s.sendEventClaim(ctx, e); err != nil { - return 0, err - } - - // Considering blockTime=1s on Injective chain, Adding Sleep to make sure new event is - // sent only after previous event is executed successfully. - // Otherwise it will through `non contiguous event nonce` failing CheckTx. - time.Sleep(1200 * time.Millisecond) - } - - lastClaimEventNonce = events[len(events)-1].Nonce() - - return lastClaimEventNonce, nil -} - -type event interface { - Nonce() uint64 -} - -type ( - eventSendToCosmos peggyevents.PeggySendToCosmosEvent - eventSendToInjective peggyevents.PeggySendToInjectiveEvent - eventTransactionBatchExecuted peggyevents.PeggyTransactionBatchExecutedEvent - eventERC20Deployed peggyevents.PeggyERC20DeployedEvent - eventValsetUpdated peggyevents.PeggyValsetUpdatedEvent -) - -func (e *eventSendToCosmos) Nonce() uint64 { - return e.EventNonce.Uint64() -} - -func (e *eventSendToInjective) Nonce() uint64 { - return e.EventNonce.Uint64() -} - -func (e *eventTransactionBatchExecuted) Nonce() uint64 { - return e.EventNonce.Uint64() -} - -func (e *eventERC20Deployed) Nonce() uint64 { - return e.EventNonce.Uint64() -} - -func (e *eventValsetUpdated) Nonce() uint64 { - return e.EventNonce.Uint64() -} - -func sortEventsByNonce( - oldDeposits []*peggyevents.PeggySendToCosmosEvent, - deposits []*peggyevents.PeggySendToInjectiveEvent, - withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, -) []event { - total := len(oldDeposits) + len(deposits) + len(withdraws) + len(erc20Deployed) + len(valsetUpdates) - events := make([]event, 0, total) - - for _, deposit := range oldDeposits { - e := eventSendToCosmos(*deposit) - events = append(events, &e) - } - - for _, deposit := range deposits { - e := eventSendToInjective(*deposit) - events = append(events, &e) - } - - for _, withdrawal := range withdraws { - e := eventTransactionBatchExecuted(*withdrawal) - events = append(events, &e) - } - - for _, deployment := range erc20Deployed { - e := eventERC20Deployed(*deployment) - events = append(events, &e) - } - - for _, vs := range valsetUpdates { - e := eventValsetUpdated(*vs) - events = append(events, &e) - } - - // sort by nonce - sort.Slice(events, func(i, j int) bool { - return events[i].Nonce() < events[j].Nonce() - }) - - return events -} - -func (s *peggyBroadcastClient) sendEventClaim(ctx context.Context, ev event) error { - switch ev := ev.(type) { - case *eventSendToCosmos: - e := peggyevents.PeggySendToCosmosEvent(*ev) - return s.sendOldDepositClaims(ctx, &e) - case *eventSendToInjective: - e := peggyevents.PeggySendToInjectiveEvent(*ev) - return s.sendDepositClaims(ctx, &e) - case *eventTransactionBatchExecuted: - e := peggyevents.PeggyTransactionBatchExecutedEvent(*ev) - return s.sendWithdrawClaims(ctx, &e) - case *eventERC20Deployed: - e := peggyevents.PeggyERC20DeployedEvent(*ev) - return s.sendErc20DeployedClaims(ctx, &e) - case *eventValsetUpdated: - e := peggyevents.PeggyValsetUpdatedEvent(*ev) - return s.sendValsetUpdateClaims(ctx, &e) - } - - return errors.Errorf("unknown event type %T", ev) -} - -func (s *peggyBroadcastClient) sendOldDepositClaims( - ctx context.Context, - oldDeposit *peggyevents.PeggySendToCosmosEvent, -) error { - // EthereumBridgeDepositClaim - // When more than 66% of the active validator set has - // claimed to have seen the deposit enter the ethereum blockchain coins are - // issued to the Cosmos address in question - // ------------- - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - log.WithFields(log.Fields{ - "sender": oldDeposit.Sender.Hex(), - "destination": sdk.AccAddress(oldDeposit.Destination[12:32]).String(), - "amount": oldDeposit.Amount.String(), - "event_nonce": oldDeposit.EventNonce.String(), - }).Debugln("observed SendToCosmosEvent") - - msg := &peggytypes.MsgDepositClaim{ - EventNonce: oldDeposit.EventNonce.Uint64(), - BlockHeight: oldDeposit.Raw.BlockNumber, - TokenContract: oldDeposit.TokenContract.Hex(), - Amount: sdk.NewIntFromBigInt(oldDeposit.Amount), - EthereumSender: oldDeposit.Sender.Hex(), - CosmosReceiver: sdk.AccAddress(oldDeposit.Destination[12:32]).String(), - Orchestrator: s.broadcastClient.FromAddress().String(), - Data: "", - } - - resp, err := s.broadcastClient.SyncBroadcastMsg(msg) - if err != nil { - metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "broadcasting MsgDepositClaim failed") - } - - log.WithFields(log.Fields{ - "event_height": msg.BlockHeight, - "event_nonce": msg.EventNonce, - "tx_hash": resp.TxResponse.TxHash, - }).Infoln("Oracle sent MsgDepositClaim") - - return nil -} - -func (s *peggyBroadcastClient) sendDepositClaims( - ctx context.Context, - deposit *peggyevents.PeggySendToInjectiveEvent, -) error { - // EthereumBridgeDepositClaim - // When more than 66% of the active validator set has - // claimed to have seen the deposit enter the ethereum blockchain coins are - // issued to the Cosmos address in question - // ------------- - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - log.WithFields(log.Fields{ - "sender": deposit.Sender.Hex(), - "destination": sdk.AccAddress(deposit.Destination[12:32]).String(), - "amount": deposit.Amount.String(), - "data": deposit.Data, - "token_contract": deposit.TokenContract.Hex(), - }).Debugln("observed SendToInjectiveEvent") - - msg := &peggytypes.MsgDepositClaim{ - EventNonce: deposit.EventNonce.Uint64(), - BlockHeight: deposit.Raw.BlockNumber, - TokenContract: deposit.TokenContract.Hex(), - Amount: sdk.NewIntFromBigInt(deposit.Amount), - EthereumSender: deposit.Sender.Hex(), - CosmosReceiver: sdk.AccAddress(deposit.Destination[12:32]).String(), - Orchestrator: s.broadcastClient.FromAddress().String(), - Data: deposit.Data, - } - - resp, err := s.broadcastClient.SyncBroadcastMsg(msg) - if err != nil { - metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "broadcasting MsgDepositClaim failed") - } - - log.WithFields(log.Fields{ - "event_nonce": msg.EventNonce, - "event_height": msg.BlockHeight, - "tx_hash": resp.TxResponse.TxHash, - }).Infoln("EthOracle sent MsgDepositClaim") - - return nil -} - -func (s *peggyBroadcastClient) sendWithdrawClaims( - ctx context.Context, - withdraw *peggyevents.PeggyTransactionBatchExecutedEvent, -) error { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - log.WithFields(log.Fields{ - "batch_nonce": withdraw.BatchNonce.String(), - "token_contract": withdraw.Token.Hex(), - }).Debugln("observed TransactionBatchExecutedEvent") - - // WithdrawClaim claims that a batch of withdrawal - // operations on the bridge contract was executed. - msg := &peggytypes.MsgWithdrawClaim{ - EventNonce: withdraw.EventNonce.Uint64(), - BatchNonce: withdraw.BatchNonce.Uint64(), - BlockHeight: withdraw.Raw.BlockNumber, - TokenContract: withdraw.Token.Hex(), - Orchestrator: s.AccFromAddress().String(), - } - - resp, err := s.broadcastClient.SyncBroadcastMsg(msg) - if err != nil { - metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "broadcasting MsgWithdrawClaim failed") - } - - log.WithFields(log.Fields{ - "event_height": msg.BlockHeight, - "event_nonce": msg.EventNonce, - "tx_hash": resp.TxResponse.TxHash, - }).Infoln("EthOracle sent MsgWithdrawClaim") - - return nil -} - -func (s *peggyBroadcastClient) sendValsetUpdateClaims( - ctx context.Context, - valsetUpdate *peggyevents.PeggyValsetUpdatedEvent, -) error { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - log.WithFields(log.Fields{ - "valset_nonce": valsetUpdate.NewValsetNonce.Uint64(), - "validators": valsetUpdate.Validators, - "powers": valsetUpdate.Powers, - "reward_amount": valsetUpdate.RewardAmount, - "reward_token": valsetUpdate.RewardToken.Hex(), - }).Debugln("observed ValsetUpdatedEvent") - - members := make([]*peggytypes.BridgeValidator, len(valsetUpdate.Validators)) - for i, val := range valsetUpdate.Validators { - members[i] = &peggytypes.BridgeValidator{ - EthereumAddress: val.Hex(), - Power: valsetUpdate.Powers[i].Uint64(), - } - } - - msg := &peggytypes.MsgValsetUpdatedClaim{ - EventNonce: valsetUpdate.EventNonce.Uint64(), - ValsetNonce: valsetUpdate.NewValsetNonce.Uint64(), - BlockHeight: valsetUpdate.Raw.BlockNumber, - RewardAmount: sdk.NewIntFromBigInt(valsetUpdate.RewardAmount), - RewardToken: valsetUpdate.RewardToken.Hex(), - Members: members, - Orchestrator: s.AccFromAddress().String(), - } - - resp, err := s.broadcastClient.SyncBroadcastMsg(msg) - if err != nil { - metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "broadcasting MsgValsetUpdatedClaim failed") - } - - log.WithFields(log.Fields{ - "event_nonce": msg.EventNonce, - "event__height": msg.BlockHeight, - "tx_hash": resp.TxResponse.TxHash, - }).Infoln("Oracle sent MsgValsetUpdatedClaim") - - return nil -} - -func (s *peggyBroadcastClient) sendErc20DeployedClaims( - ctx context.Context, - erc20Deployed *peggyevents.PeggyERC20DeployedEvent, -) error { - metrics.ReportFuncCall(s.svcTags) - doneFn := metrics.ReportFuncTiming(s.svcTags) - defer doneFn() - - log.WithFields(log.Fields{ - "cosmos_denom": erc20Deployed.CosmosDenom, - "token_contract": erc20Deployed.TokenContract.Hex(), - "name": erc20Deployed.Name, - "symbol": erc20Deployed.Symbol, - "decimals": erc20Deployed.Decimals, - }).Debugln("observed ERC20DeployedEvent") - - msg := &peggytypes.MsgERC20DeployedClaim{ - EventNonce: erc20Deployed.EventNonce.Uint64(), - BlockHeight: erc20Deployed.Raw.BlockNumber, - CosmosDenom: erc20Deployed.CosmosDenom, - TokenContract: erc20Deployed.TokenContract.Hex(), - Name: erc20Deployed.Name, - Symbol: erc20Deployed.Symbol, - Decimals: uint64(erc20Deployed.Decimals), - Orchestrator: s.AccFromAddress().String(), - } - - resp, err := s.broadcastClient.SyncBroadcastMsg(msg) - if err != nil { - metrics.ReportFuncError(s.svcTags) - return errors.Wrap(err, "broadcasting MsgERC20DeployedClaim failed") - } - - log.WithFields(log.Fields{ - "event_nonce": msg.EventNonce, - "event_height": msg.BlockHeight, - "tx_hash": resp.TxResponse.TxHash, - }).Infoln("Oracle sent MsgERC20DeployedClaim") - - return nil -} diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index ea3e08a5..e4f37be4 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -2,7 +2,8 @@ package cosmos import ( "context" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggyclient" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" + sdk "github.com/cosmos/cosmos-sdk/types" "time" "github.com/InjectiveLabs/sdk-go/client/common" @@ -18,6 +19,8 @@ import ( ) type CustomRPCNetwork struct { + addr sdk.AccAddress + tmclient.TendermintClient PeggyQueryClient PeggyBroadcastClient @@ -47,6 +50,11 @@ func NewCustomRPCNetwork( keyring keyring.Keyring, personalSignerFn keystore.PersonalSignFn, ) (*CustomRPCNetwork, error) { + addr, err := sdk.AccAddressFromBech32(validatorAddress) + if err != nil { + return nil, errors.Wrap(err, "invalid address") + } + clientCtx, err := chainclient.NewClientContext(chainID, validatorAddress, keyring) if err != nil { return nil, errors.Wrapf(err, "failed to create client context for Injective chain") @@ -76,9 +84,10 @@ func NewCustomRPCNetwork( peggyQuerier := peggytypes.NewQueryClient(grpcConn) n := &CustomRPCNetwork{ + addr: addr, TendermintClient: tmclient.NewRPCClient(tendermintRPC), - PeggyQueryClient: peggyclient.NewPeggyQueryClient(peggyQuerier), - PeggyBroadcastClient: NewPeggyBroadcastClient(peggyQuerier, daemonClient, personalSignerFn), + PeggyQueryClient: peggy.NewQueryClient(peggyQuerier), + PeggyBroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), } log.WithFields(log.Fields{ @@ -101,13 +110,13 @@ func (n *CustomRPCNetwork) GetBlockCreationTime(ctx context.Context, height int6 } func (n *CustomRPCNetwork) LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) { - return n.LastClaimEventByAddr(ctx, n.AccFromAddress()) + return n.LastClaimEventByAddr(ctx, n.addr) } func (n *CustomRPCNetwork) OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { - return n.PeggyQueryClient.OldestUnsignedValsets(ctx, n.AccFromAddress()) + return n.PeggyQueryClient.OldestUnsignedValsets(ctx, n.addr) } func (n *CustomRPCNetwork) OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { - return n.PeggyQueryClient.OldestUnsignedTransactionBatch(ctx, n.AccFromAddress()) + return n.PeggyQueryClient.OldestUnsignedTransactionBatch(ctx, n.addr) } diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index cd580f5f..f7db90a0 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -2,8 +2,9 @@ package cosmos import ( "context" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggyclient" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" rpchttp "github.com/cometbft/cometbft/rpc/client/http" + sdk "github.com/cosmos/cosmos-sdk/types" "strconv" "time" @@ -22,6 +23,8 @@ import ( ) type LoadBalancedNetwork struct { + addr sdk.AccAddress + PeggyQueryClient PeggyBroadcastClient explorerclient.ExplorerClient @@ -39,6 +42,11 @@ func NewLoadBalancedNetwork( keyring keyring.Keyring, personalSignerFn keystore.PersonalSignFn, ) (*LoadBalancedNetwork, error) { + addr, err := sdk.AccAddressFromBech32(validatorAddress) + if err != nil { + return nil, errors.Wrap(err, "invalid address") + } + var networkName string switch chainID { case "injective-1": @@ -84,8 +92,9 @@ func NewLoadBalancedNetwork( peggyQuerier := types.NewQueryClient(grpcConn) n := &LoadBalancedNetwork{ - PeggyQueryClient: peggyclient.NewPeggyQueryClient(peggyQuerier), - PeggyBroadcastClient: NewPeggyBroadcastClient(peggyQuerier, daemonClient, personalSignerFn), + addr: addr, + PeggyQueryClient: peggy.NewQueryClient(peggyQuerier), + PeggyBroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), ExplorerClient: explorer, } @@ -114,15 +123,15 @@ func (n *LoadBalancedNetwork) GetBlockCreationTime(ctx context.Context, height i } func (n *LoadBalancedNetwork) LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) { - return n.LastClaimEventByAddr(ctx, n.AccFromAddress()) + return n.LastClaimEventByAddr(ctx, n.addr) } func (n *LoadBalancedNetwork) OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { - return n.PeggyQueryClient.OldestUnsignedValsets(ctx, n.AccFromAddress()) + return n.PeggyQueryClient.OldestUnsignedValsets(ctx, n.addr) } func (n *LoadBalancedNetwork) OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { - return n.PeggyQueryClient.OldestUnsignedTransactionBatch(ctx, n.AccFromAddress()) + return n.PeggyQueryClient.OldestUnsignedTransactionBatch(ctx, n.addr) } // waitForService awaits an active ClientConn to a GRPC service. diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go new file mode 100644 index 00000000..f8ffb793 --- /dev/null +++ b/orchestrator/cosmos/network.go @@ -0,0 +1,13 @@ +package cosmos + +import ( + "context" + "time" +) + +type Network interface { + GetBlockTime(ctx context.Context, height int64) (time.Time, error) + + PeggyQueryClient + PeggyBroadcastClient +} diff --git a/orchestrator/cosmos/peggy/broadcast.go b/orchestrator/cosmos/peggy/broadcast.go new file mode 100644 index 00000000..b704af32 --- /dev/null +++ b/orchestrator/cosmos/peggy/broadcast.go @@ -0,0 +1,548 @@ +package peggy + +import ( + "context" + "fmt" + "github.com/InjectiveLabs/metrics" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + chainclient "github.com/InjectiveLabs/sdk-go/client/chain" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + log "github.com/xlab/suplog" + "sort" + "time" +) + +type BroadcastClient struct { + chainclient.ChainClient + + ethSignFn keystore.PersonalSignFn + svcTags metrics.Tags +} + +func NewBroadcastClient(client chainclient.ChainClient, signFn keystore.PersonalSignFn) BroadcastClient { + return BroadcastClient{ + ChainClient: client, + ethSignFn: signFn, + svcTags: metrics.Tags{"svc": "peggy_broadcast"}, + } +} + +func (c BroadcastClient) UpdatePeggyOrchestratorAddresses(_ context.Context, ethFrom common.Address, orchAddr cosmostypes.AccAddress) error { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + // SetOrchestratorAddresses + + // This message allows validators to delegate their voting responsibilities + // to a given key. This key is then used as an optional authentication method + // for sigining oracle claims + // This is used by the validators to set the Ethereum address that represents + // them on the Ethereum side of the bridge. They must sign their Cosmos address + // using the Ethereum address they have submitted. Like ValsetResponse this + // message can in theory be submitted by anyone, but only the current validator + // sets submissions carry any weight. + // ------------- + msg := &peggytypes.MsgSetOrchestratorAddresses{ + Sender: c.ChainClient.FromAddress().String(), + EthAddress: ethFrom.Hex(), + Orchestrator: orchAddr.String(), + } + + res, err := c.ChainClient.SyncBroadcastMsg(msg) + fmt.Println("Response of set eth address", "res", res) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.Wrap(err, "broadcasting MsgSetOrchestratorAddresses failed") + } + + return nil +} + +func (c BroadcastClient) SendValsetConfirm(_ context.Context, ethFrom common.Address, peggyID common.Hash, valset *peggytypes.Valset) error { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + confirmHash := peggy.EncodeValsetConfirm(peggyID, valset) + signature, err := c.ethSignFn(ethFrom, confirmHash.Bytes()) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.New("failed to sign validator address") + } + + // MsgValsetConfirm + // this is the message sent by the validators when they wish to submit their + // signatures over the validator set at a given block height. A validator must + // first call MsgSetEthAddress to set their Ethereum address to be used for + // signing. Then someone (anyone) must make a ValsetRequest the request is + // essentially a messaging mechanism to determine which block all validators + // should submit signatures over. Finally validators sign the validator set, + // powers, and Ethereum addresses of the entire validator set at the height of a + // ValsetRequest and submit that signature with this message. + // + // If a sufficient number of validators (66% of voting power) (A) have set + // Ethereum addresses and (B) submit ValsetConfirm messages with their + // signatures it is then possible for anyone to view these signatures in the + // chain store and submit them to Ethereum to update the validator set + // ------------- + msg := &peggytypes.MsgValsetConfirm{ + Orchestrator: c.FromAddress().String(), + EthAddress: ethFrom.Hex(), + Nonce: valset.Nonce, + Signature: common.Bytes2Hex(signature), + } + + if err = c.ChainClient.QueueBroadcastMsg(msg); err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.Wrap(err, "broadcasting MsgValsetConfirm failed") + } + + return nil +} + +func (c BroadcastClient) SendBatchConfirm(_ context.Context, ethFrom common.Address, peggyID common.Hash, batch *peggytypes.OutgoingTxBatch) error { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + confirmHash := peggy.EncodeTxBatchConfirm(peggyID, batch) + signature, err := c.ethSignFn(ethFrom, confirmHash.Bytes()) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.New("failed to sign validator address") + } + + // MsgConfirmBatch + // When validators observe a MsgRequestBatch they form a batch by ordering + // transactions currently in the txqueue in order of highest to lowest fee, + // cutting off when the batch either reaches a hardcoded maximum size (to be + // decided, probably around 100) or when transactions stop being profitable + // (TODO determine this without nondeterminism) This message includes the batch + // as well as an Ethereum signature over this batch by the validator + // ------------- + msg := &peggytypes.MsgConfirmBatch{ + Orchestrator: c.FromAddress().String(), + Nonce: batch.BatchNonce, + Signature: common.Bytes2Hex(signature), + EthSigner: ethFrom.Hex(), + TokenContract: batch.TokenContract, + } + + if err = c.ChainClient.QueueBroadcastMsg(msg); err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.Wrap(err, "broadcasting MsgConfirmBatch failed") + } + + return nil +} + +func (c BroadcastClient) SendToEth(ctx context.Context, destination common.Address, amount, fee cosmostypes.Coin) error { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + // MsgSendToEth + // This is the message that a user calls when they want to bridge an asset + // it will later be removed when it is included in a batch and successfully + // submitted tokens are removed from the users balance immediately + // ------------- + // AMOUNT: + // the coin to send across the bridge, note the restriction that this is a + // single coin not a set of coins that is normal in other Cosmos messages + // FEE: + // the fee paid for the bridge, distinct from the fee paid to the chain to + // actually send this message in the first place. So a successful send has + // two layers of fees for the user + // ------------- + msg := &peggytypes.MsgSendToEth{ + Sender: c.FromAddress().String(), + EthDest: destination.Hex(), + Amount: amount, + BridgeFee: fee, // TODO: use exactly that fee for transaction + } + + if err := c.ChainClient.QueueBroadcastMsg(msg); err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.Wrap(err, "broadcasting MsgSendToEth failed") + } + + return nil +} + +func (c BroadcastClient) SendRequestBatch(ctx context.Context, denom string) error { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + // MsgRequestBatch + // this is a message anyone can send that requests a batch of transactions to + // send across the bridge be created for whatever block height this message is + // included in. This acts as a coordination point, the handler for this message + // looks at the AddToOutgoingPool tx's in the store and generates a batch, also + // available in the store tied to this message. The validators then grab this + // batch, sign it, submit the signatures with a MsgConfirmBatch before a relayer + // can finally submit the batch + // ------------- + msg := &peggytypes.MsgRequestBatch{ + Denom: denom, + Orchestrator: c.FromAddress().String(), + } + if err := c.ChainClient.QueueBroadcastMsg(msg); err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.Wrap(err, "broadcasting MsgRequestBatch failed") + } + + return nil +} + +func (c BroadcastClient) SendEthereumClaims(ctx context.Context, lastClaimEventNonce uint64, oldDeposits []*peggyevents.PeggySendToCosmosEvent, deposits []*peggyevents.PeggySendToInjectiveEvent, withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent) (uint64, error) { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + events := sortEventsByNonce(oldDeposits, deposits, withdraws, erc20Deployed, valsetUpdates) + + // this can't happen outside of programmer error + if firstToSend := events[0]; firstToSend.Nonce() != lastClaimEventNonce+1 { + return 0, errors.Errorf("expected event with nonce %d, got %d", lastClaimEventNonce+1, firstToSend.Nonce()) + } + + for _, e := range events { + if err := c.sendEventClaim(ctx, e); err != nil { + return 0, err + } + + // Considering blockTime=1s on Injective chain, Adding Sleep to make sure new event is + // sent only after previous event is executed successfully. + // Otherwise it will through `non contiguous event nonce` failing CheckTx. + time.Sleep(1200 * time.Millisecond) + } + + lastClaimEventNonce = events[len(events)-1].Nonce() + + return lastClaimEventNonce, nil +} + +type event interface { + Nonce() uint64 +} + +type ( + eventSendToCosmos peggyevents.PeggySendToCosmosEvent + eventSendToInjective peggyevents.PeggySendToInjectiveEvent + eventTransactionBatchExecuted peggyevents.PeggyTransactionBatchExecutedEvent + eventERC20Deployed peggyevents.PeggyERC20DeployedEvent + eventValsetUpdated peggyevents.PeggyValsetUpdatedEvent +) + +func (e *eventSendToCosmos) Nonce() uint64 { + return e.EventNonce.Uint64() +} + +func (e *eventSendToInjective) Nonce() uint64 { + return e.EventNonce.Uint64() +} + +func (e *eventTransactionBatchExecuted) Nonce() uint64 { + return e.EventNonce.Uint64() +} + +func (e *eventERC20Deployed) Nonce() uint64 { + return e.EventNonce.Uint64() +} + +func (e *eventValsetUpdated) Nonce() uint64 { + return e.EventNonce.Uint64() +} + +func sortEventsByNonce( + oldDeposits []*peggyevents.PeggySendToCosmosEvent, + deposits []*peggyevents.PeggySendToInjectiveEvent, + withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, + erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, + valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, +) []event { + total := len(oldDeposits) + len(deposits) + len(withdraws) + len(erc20Deployed) + len(valsetUpdates) + events := make([]event, 0, total) + + for _, deposit := range oldDeposits { + e := eventSendToCosmos(*deposit) + events = append(events, &e) + } + + for _, deposit := range deposits { + e := eventSendToInjective(*deposit) + events = append(events, &e) + } + + for _, withdrawal := range withdraws { + e := eventTransactionBatchExecuted(*withdrawal) + events = append(events, &e) + } + + for _, deployment := range erc20Deployed { + e := eventERC20Deployed(*deployment) + events = append(events, &e) + } + + for _, vs := range valsetUpdates { + e := eventValsetUpdated(*vs) + events = append(events, &e) + } + + // sort by nonce + sort.Slice(events, func(i, j int) bool { + return events[i].Nonce() < events[j].Nonce() + }) + + return events +} + +func (c BroadcastClient) sendEventClaim(ctx context.Context, ev event) error { + switch ev := ev.(type) { + case *eventSendToCosmos: + e := peggyevents.PeggySendToCosmosEvent(*ev) + return c.sendOldDepositClaims(ctx, &e) + case *eventSendToInjective: + e := peggyevents.PeggySendToInjectiveEvent(*ev) + return c.sendDepositClaims(ctx, &e) + case *eventTransactionBatchExecuted: + e := peggyevents.PeggyTransactionBatchExecutedEvent(*ev) + return c.sendWithdrawClaims(ctx, &e) + case *eventERC20Deployed: + e := peggyevents.PeggyERC20DeployedEvent(*ev) + return c.sendErc20DeployedClaims(ctx, &e) + case *eventValsetUpdated: + e := peggyevents.PeggyValsetUpdatedEvent(*ev) + return c.sendValsetUpdateClaims(ctx, &e) + } + + return errors.Errorf("unknown event type %T", ev) +} + +func (c BroadcastClient) sendOldDepositClaims( + ctx context.Context, + oldDeposit *peggyevents.PeggySendToCosmosEvent, +) error { + // EthereumBridgeDepositClaim + // When more than 66% of the active validator set has + // claimed to have seen the deposit enter the ethereum blockchain coins are + // issued to the Cosmos address in question + // ------------- + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + log.WithFields(log.Fields{ + "sender": oldDeposit.Sender.Hex(), + "destination": cosmostypes.AccAddress(oldDeposit.Destination[12:32]).String(), + "amount": oldDeposit.Amount.String(), + "event_nonce": oldDeposit.EventNonce.String(), + }).Debugln("observed SendToCosmosEvent") + + msg := &peggytypes.MsgDepositClaim{ + EventNonce: oldDeposit.EventNonce.Uint64(), + BlockHeight: oldDeposit.Raw.BlockNumber, + TokenContract: oldDeposit.TokenContract.Hex(), + Amount: cosmostypes.NewIntFromBigInt(oldDeposit.Amount), + EthereumSender: oldDeposit.Sender.Hex(), + CosmosReceiver: cosmostypes.AccAddress(oldDeposit.Destination[12:32]).String(), + Orchestrator: c.ChainClient.FromAddress().String(), + Data: "", + } + + resp, err := c.ChainClient.SyncBroadcastMsg(msg) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.Wrap(err, "broadcasting MsgDepositClaim failed") + } + + log.WithFields(log.Fields{ + "event_height": msg.BlockHeight, + "event_nonce": msg.EventNonce, + "tx_hash": resp.TxResponse.TxHash, + }).Infoln("Oracle sent MsgDepositClaim") + + return nil +} + +func (c BroadcastClient) sendDepositClaims( + ctx context.Context, + deposit *peggyevents.PeggySendToInjectiveEvent, +) error { + // EthereumBridgeDepositClaim + // When more than 66% of the active validator set has + // claimed to have seen the deposit enter the ethereum blockchain coins are + // issued to the Cosmos address in question + // ------------- + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + log.WithFields(log.Fields{ + "sender": deposit.Sender.Hex(), + "destination": cosmostypes.AccAddress(deposit.Destination[12:32]).String(), + "amount": deposit.Amount.String(), + "data": deposit.Data, + "token_contract": deposit.TokenContract.Hex(), + }).Debugln("observed SendToInjectiveEvent") + + msg := &peggytypes.MsgDepositClaim{ + EventNonce: deposit.EventNonce.Uint64(), + BlockHeight: deposit.Raw.BlockNumber, + TokenContract: deposit.TokenContract.Hex(), + Amount: cosmostypes.NewIntFromBigInt(deposit.Amount), + EthereumSender: deposit.Sender.Hex(), + CosmosReceiver: cosmostypes.AccAddress(deposit.Destination[12:32]).String(), + Orchestrator: c.ChainClient.FromAddress().String(), + Data: deposit.Data, + } + + resp, err := c.ChainClient.SyncBroadcastMsg(msg) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.Wrap(err, "broadcasting MsgDepositClaim failed") + } + + log.WithFields(log.Fields{ + "event_nonce": msg.EventNonce, + "event_height": msg.BlockHeight, + "tx_hash": resp.TxResponse.TxHash, + }).Infoln("EthOracle sent MsgDepositClaim") + + return nil +} + +func (c BroadcastClient) sendWithdrawClaims( + ctx context.Context, + withdraw *peggyevents.PeggyTransactionBatchExecutedEvent, +) error { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + log.WithFields(log.Fields{ + "batch_nonce": withdraw.BatchNonce.String(), + "token_contract": withdraw.Token.Hex(), + }).Debugln("observed TransactionBatchExecutedEvent") + + // WithdrawClaim claims that a batch of withdrawal + // operations on the bridge contract was executed. + msg := &peggytypes.MsgWithdrawClaim{ + EventNonce: withdraw.EventNonce.Uint64(), + BatchNonce: withdraw.BatchNonce.Uint64(), + BlockHeight: withdraw.Raw.BlockNumber, + TokenContract: withdraw.Token.Hex(), + Orchestrator: c.FromAddress().String(), + } + + resp, err := c.ChainClient.SyncBroadcastMsg(msg) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.Wrap(err, "broadcasting MsgWithdrawClaim failed") + } + + log.WithFields(log.Fields{ + "event_height": msg.BlockHeight, + "event_nonce": msg.EventNonce, + "tx_hash": resp.TxResponse.TxHash, + }).Infoln("EthOracle sent MsgWithdrawClaim") + + return nil +} + +func (c BroadcastClient) sendValsetUpdateClaims( + ctx context.Context, + valsetUpdate *peggyevents.PeggyValsetUpdatedEvent, +) error { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + log.WithFields(log.Fields{ + "valset_nonce": valsetUpdate.NewValsetNonce.Uint64(), + "validators": valsetUpdate.Validators, + "powers": valsetUpdate.Powers, + "reward_amount": valsetUpdate.RewardAmount, + "reward_token": valsetUpdate.RewardToken.Hex(), + }).Debugln("observed ValsetUpdatedEvent") + + members := make([]*peggytypes.BridgeValidator, len(valsetUpdate.Validators)) + for i, val := range valsetUpdate.Validators { + members[i] = &peggytypes.BridgeValidator{ + EthereumAddress: val.Hex(), + Power: valsetUpdate.Powers[i].Uint64(), + } + } + + msg := &peggytypes.MsgValsetUpdatedClaim{ + EventNonce: valsetUpdate.EventNonce.Uint64(), + ValsetNonce: valsetUpdate.NewValsetNonce.Uint64(), + BlockHeight: valsetUpdate.Raw.BlockNumber, + RewardAmount: cosmostypes.NewIntFromBigInt(valsetUpdate.RewardAmount), + RewardToken: valsetUpdate.RewardToken.Hex(), + Members: members, + Orchestrator: c.FromAddress().String(), + } + + resp, err := c.ChainClient.SyncBroadcastMsg(msg) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.Wrap(err, "broadcasting MsgValsetUpdatedClaim failed") + } + + log.WithFields(log.Fields{ + "event_nonce": msg.EventNonce, + "event__height": msg.BlockHeight, + "tx_hash": resp.TxResponse.TxHash, + }).Infoln("Oracle sent MsgValsetUpdatedClaim") + + return nil +} + +func (c BroadcastClient) sendErc20DeployedClaims( + ctx context.Context, + erc20Deployed *peggyevents.PeggyERC20DeployedEvent, +) error { + metrics.ReportFuncCall(c.svcTags) + doneFn := metrics.ReportFuncTiming(c.svcTags) + defer doneFn() + + log.WithFields(log.Fields{ + "cosmos_denom": erc20Deployed.CosmosDenom, + "token_contract": erc20Deployed.TokenContract.Hex(), + "name": erc20Deployed.Name, + "symbol": erc20Deployed.Symbol, + "decimals": erc20Deployed.Decimals, + }).Debugln("observed ERC20DeployedEvent") + + msg := &peggytypes.MsgERC20DeployedClaim{ + EventNonce: erc20Deployed.EventNonce.Uint64(), + BlockHeight: erc20Deployed.Raw.BlockNumber, + CosmosDenom: erc20Deployed.CosmosDenom, + TokenContract: erc20Deployed.TokenContract.Hex(), + Name: erc20Deployed.Name, + Symbol: erc20Deployed.Symbol, + Decimals: uint64(erc20Deployed.Decimals), + Orchestrator: c.FromAddress().String(), + } + + resp, err := c.ChainClient.SyncBroadcastMsg(msg) + if err != nil { + metrics.ReportFuncError(c.svcTags) + return errors.Wrap(err, "broadcasting MsgERC20DeployedClaim failed") + } + + log.WithFields(log.Fields{ + "event_nonce": msg.EventNonce, + "event_height": msg.BlockHeight, + "tx_hash": resp.TxResponse.TxHash, + }).Infoln("Oracle sent MsgERC20DeployedClaim") + + return nil +} diff --git a/orchestrator/cosmos/peggyclient/query.go b/orchestrator/cosmos/peggy/query.go similarity index 79% rename from orchestrator/cosmos/peggyclient/query.go rename to orchestrator/cosmos/peggy/query.go index fba4149c..8939fd82 100644 --- a/orchestrator/cosmos/peggyclient/query.go +++ b/orchestrator/cosmos/peggy/query.go @@ -1,4 +1,4 @@ -package peggyclient +package peggy import ( "context" @@ -13,20 +13,20 @@ import ( var ErrNotFound = errors.New("not found") -type PeggyQueryClient struct { +type QueryClient struct { peggytypes.QueryClient svcTags metrics.Tags } -func NewPeggyQueryClient(client peggytypes.QueryClient) *PeggyQueryClient { - return &PeggyQueryClient{ +func NewQueryClient(client peggytypes.QueryClient) QueryClient { + return QueryClient{ QueryClient: client, svcTags: metrics.Tags{"svc": "peggy_query"}, } } -func (c PeggyQueryClient) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) { +func (c QueryClient) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -47,7 +47,7 @@ func (c PeggyQueryClient) ValsetAt(ctx context.Context, nonce uint64) (*peggytyp return resp.Valset, nil } -func (c PeggyQueryClient) CurrentValset(ctx context.Context) (*peggytypes.Valset, error) { +func (c QueryClient) CurrentValset(ctx context.Context) (*peggytypes.Valset, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -66,7 +66,7 @@ func (c PeggyQueryClient) CurrentValset(ctx context.Context) (*peggytypes.Valset return resp.Valset, nil } -func (c PeggyQueryClient) OldestUnsignedValsets(ctx context.Context, valAccountAddress cosmostypes.AccAddress) ([]*peggytypes.Valset, error) { +func (c QueryClient) OldestUnsignedValsets(ctx context.Context, valAccountAddress cosmostypes.AccAddress) ([]*peggytypes.Valset, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -89,7 +89,7 @@ func (c PeggyQueryClient) OldestUnsignedValsets(ctx context.Context, valAccountA return resp.Valsets, nil } -func (c PeggyQueryClient) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) { +func (c QueryClient) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -108,7 +108,7 @@ func (c PeggyQueryClient) LatestValsets(ctx context.Context) ([]*peggytypes.Vals return resp.Valsets, nil } -func (c PeggyQueryClient) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) { +func (c QueryClient) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -127,7 +127,7 @@ func (c PeggyQueryClient) AllValsetConfirms(ctx context.Context, nonce uint64) ( return resp.Confirms, nil } -func (c PeggyQueryClient) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { +func (c QueryClient) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -150,7 +150,7 @@ func (c PeggyQueryClient) OldestUnsignedTransactionBatch(ctx context.Context, va return resp.Batch, nil } -func (c PeggyQueryClient) LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) { +func (c QueryClient) LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -169,7 +169,7 @@ func (c PeggyQueryClient) LatestTransactionBatches(ctx context.Context) ([]*pegg return resp.Batches, nil } -func (c PeggyQueryClient) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { +func (c QueryClient) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -188,7 +188,7 @@ func (c PeggyQueryClient) UnbatchedTokensWithFees(ctx context.Context) ([]*peggy return resp.BatchFees, nil } -func (c PeggyQueryClient) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { +func (c QueryClient) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -212,7 +212,7 @@ func (c PeggyQueryClient) TransactionBatchSignatures(ctx context.Context, nonce return resp.Confirms, nil } -func (c PeggyQueryClient) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { +func (c QueryClient) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -235,7 +235,7 @@ func (c PeggyQueryClient) LastClaimEventByAddr(ctx context.Context, validatorAcc return resp.LastClaimEvent, nil } -func (c PeggyQueryClient) PeggyParams(ctx context.Context) (*peggytypes.Params, error) { +func (c QueryClient) PeggyParams(ctx context.Context) (*peggytypes.Params, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -254,7 +254,7 @@ func (c PeggyQueryClient) PeggyParams(ctx context.Context) (*peggytypes.Params, return &resp.Params, nil } -func (c PeggyQueryClient) GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (cosmostypes.AccAddress, error) { +func (c QueryClient) GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (cosmostypes.AccAddress, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 8d274316..60da2044 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -6,10 +6,8 @@ import ( "github.com/avast/retry-go" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" log "github.com/xlab/suplog" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/loops" "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) @@ -100,7 +98,7 @@ func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context) (*types.OutgoingTx getOldestUnsignedBatchFn := func() (err error) { // sign the last unsigned batch, TODO check if we already have signed this oldestUnsignedBatch, err = l.inj.OldestUnsignedTransactionBatch(ctx) - if errors.Is(err, cosmos.ErrNotFound) || oldestUnsignedBatch == nil { + if oldestUnsignedBatch == nil { return nil } @@ -146,7 +144,7 @@ func (l *ethSignerLoop) getUnsignedValsets(ctx context.Context) ([]*types.Valset var oldestUnsignedValsets []*types.Valset getOldestUnsignedValsetsFn := func() (err error) { oldestUnsignedValsets, err = l.inj.OldestUnsignedValsets(ctx) - if errors.Is(err, cosmos.ErrNotFound) || oldestUnsignedValsets == nil { + if oldestUnsignedValsets == nil { return nil } From 21d78a428d1064607981bf982cc50bd9164f756a Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 19:10:10 +0100 Subject: [PATCH 35/99] refactor files --- orchestrator/cosmos/broadcast.go | 61 ------------------- orchestrator/cosmos/custom_rpc.go | 22 +++---- orchestrator/cosmos/net.go | 31 ---------- orchestrator/cosmos/network.go | 6 +- orchestrator/cosmos/peggy/broadcast.go | 58 +++++++++++++----- orchestrator/cosmos/peggy/query.go | 43 ++++++++----- orchestrator/cosmos/query.go | 26 -------- .../tmclient.go => tendermint/client.go} | 8 +-- 8 files changed, 92 insertions(+), 163 deletions(-) delete mode 100644 orchestrator/cosmos/broadcast.go delete mode 100644 orchestrator/cosmos/net.go delete mode 100644 orchestrator/cosmos/query.go rename orchestrator/cosmos/{tmclient/tmclient.go => tendermint/client.go} (95%) diff --git a/orchestrator/cosmos/broadcast.go b/orchestrator/cosmos/broadcast.go deleted file mode 100644 index 3d006e8b..00000000 --- a/orchestrator/cosmos/broadcast.go +++ /dev/null @@ -1,61 +0,0 @@ -package cosmos - -import ( - "context" - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - sdk "github.com/cosmos/cosmos-sdk/types" - ethcmn "github.com/ethereum/go-ethereum/common" -) - -type PeggyBroadcastClient interface { - /// Send a transaction updating the eth address for the sending - /// Cosmos address. The sending Cosmos address should be a validator - UpdatePeggyOrchestratorAddresses( - ctx context.Context, - ethFrom ethcmn.Address, - orchAddr sdk.AccAddress, - ) error - - // SendValsetConfirm broadcasts in a confirmation for a specific validator set for a specific block height. - SendValsetConfirm( - ctx context.Context, - ethFrom ethcmn.Address, - peggyID ethcmn.Hash, - valset *peggytypes.Valset, - ) error - - // SendBatchConfirm broadcasts in a confirmation for a specific transaction batch set for a specific block height - // since transaction batches also include validator sets this has all the arguments - SendBatchConfirm( - ctx context.Context, - ethFrom ethcmn.Address, - peggyID ethcmn.Hash, - batch *peggytypes.OutgoingTxBatch, - ) error - - SendEthereumClaims( - ctx context.Context, - lastClaimEvent uint64, - oldDeposits []*peggyevents.PeggySendToCosmosEvent, - deposits []*peggyevents.PeggySendToInjectiveEvent, - withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, - ) (uint64, error) - - // SendToEth broadcasts a Tx that tokens from Cosmos to Ethereum. - // These tokens will not be sent immediately. Instead, they will require - // some time to be included in a batch. - SendToEth( - ctx context.Context, - destination ethcmn.Address, - amount, fee sdk.Coin, - ) error - - // SendRequestBatch broadcasts a requests a batch of withdrawal transactions to be generated on the chain. - SendRequestBatch( - ctx context.Context, - denom string, - ) error -} diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index e4f37be4..15352fba 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -12,7 +12,7 @@ import ( "github.com/pkg/errors" log "github.com/xlab/suplog" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tmclient" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" chainclient "github.com/InjectiveLabs/sdk-go/client/chain" @@ -21,9 +21,9 @@ import ( type CustomRPCNetwork struct { addr sdk.AccAddress - tmclient.TendermintClient - PeggyQueryClient - PeggyBroadcastClient + tendermint.Client + peggy.QueryClient + peggy.BroadcastClient } func loadCustomNetworkConfig(chainID, feeDenom, cosmosGRPC, tendermintRPC string) common.Network { @@ -84,10 +84,10 @@ func NewCustomRPCNetwork( peggyQuerier := peggytypes.NewQueryClient(grpcConn) n := &CustomRPCNetwork{ - addr: addr, - TendermintClient: tmclient.NewRPCClient(tendermintRPC), - PeggyQueryClient: peggy.NewQueryClient(peggyQuerier), - PeggyBroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), + addr: addr, + Client: tendermint.NewRPCClient(tendermintRPC), + QueryClient: peggy.NewQueryClient(peggyQuerier), + BroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), } log.WithFields(log.Fields{ @@ -101,7 +101,7 @@ func NewCustomRPCNetwork( } func (n *CustomRPCNetwork) GetBlockCreationTime(ctx context.Context, height int64) (time.Time, error) { - block, err := n.TendermintClient.GetBlock(ctx, height) + block, err := n.Client.GetBlock(ctx, height) if err != nil { return time.Time{}, err } @@ -114,9 +114,9 @@ func (n *CustomRPCNetwork) LastClaimEvent(ctx context.Context) (*peggytypes.Last } func (n *CustomRPCNetwork) OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { - return n.PeggyQueryClient.OldestUnsignedValsets(ctx, n.addr) + return n.QueryClient.OldestUnsignedValsets(ctx, n.addr) } func (n *CustomRPCNetwork) OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { - return n.PeggyQueryClient.OldestUnsignedTransactionBatch(ctx, n.addr) + return n.QueryClient.OldestUnsignedTransactionBatch(ctx, n.addr) } diff --git a/orchestrator/cosmos/net.go b/orchestrator/cosmos/net.go deleted file mode 100644 index cda0becc..00000000 --- a/orchestrator/cosmos/net.go +++ /dev/null @@ -1,31 +0,0 @@ -package cosmos - -import ( - "context" - "net" - "strings" -) - -func DialerFunc(ctx context.Context, addr string) (net.Conn, error) { - return Connect(addr) -} - -// Connect dials the given address and returns a net.Conn. The protoAddr argument should be prefixed with the protocol, -// eg. "tcp://127.0.0.1:8080" or "unix:///tmp/test.sock" -func Connect(protoAddr string) (net.Conn, error) { - proto, address := ProtocolAndAddress(protoAddr) - conn, err := net.Dial(proto, address) - return conn, err -} - -// ProtocolAndAddress splits an address into the protocol and address components. -// For instance, "tcp://127.0.0.1:8080" will be split into "tcp" and "127.0.0.1:8080". -// If the address has no protocol prefix, the default is "tcp". -func ProtocolAndAddress(listenAddr string) (string, string) { - protocol, address := "tcp", listenAddr - parts := strings.SplitN(address, "://", 2) - if len(parts) == 2 { - protocol, address = parts[0], parts[1] - } - return protocol, address -} diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index f8ffb793..9fc8f02a 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -3,11 +3,13 @@ package cosmos import ( "context" "time" + + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" ) type Network interface { GetBlockTime(ctx context.Context, height int64) (time.Time, error) - PeggyQueryClient - PeggyBroadcastClient + peggy.QueryClient + peggy.BroadcastClient } diff --git a/orchestrator/cosmos/peggy/broadcast.go b/orchestrator/cosmos/peggy/broadcast.go index b704af32..d900e4fd 100644 --- a/orchestrator/cosmos/peggy/broadcast.go +++ b/orchestrator/cosmos/peggy/broadcast.go @@ -17,7 +17,37 @@ import ( "time" ) -type BroadcastClient struct { +type BroadcastClient interface { + /// Send a transaction updating the eth address for the sending + /// Cosmos address. The sending Cosmos address should be a validator + UpdatePeggyOrchestratorAddresses(ctx context.Context, ethFrom common.Address, orchAddr cosmostypes.AccAddress) error + + // SendValsetConfirm broadcasts in a confirmation for a specific validator set for a specific block height. + SendValsetConfirm(ctx context.Context, ethFrom common.Address, peggyID common.Hash, valset *peggytypes.Valset) error + + // SendBatchConfirm broadcasts in a confirmation for a specific transaction batch set for a specific block height + // since transaction batches also include validator sets this has all the arguments + SendBatchConfirm(ctx context.Context, ethFrom common.Address, peggyID common.Hash, batch *peggytypes.OutgoingTxBatch) error + + SendEthereumClaims(ctx context.Context, + lastClaimEvent uint64, + oldDeposits []*peggyevents.PeggySendToCosmosEvent, + deposits []*peggyevents.PeggySendToInjectiveEvent, + withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, + erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, + valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, + ) (uint64, error) + + // SendToEth broadcasts a Tx that tokens from Cosmos to Ethereum. + // These tokens will not be sent immediately. Instead, they will require + // some time to be included in a batch. + SendToEth(ctx context.Context, destination common.Address, amount, fee cosmostypes.Coin) error + + // SendRequestBatch broadcasts a requests a batch of withdrawal transactions to be generated on the chain. + SendRequestBatch(ctx context.Context, denom string) error +} + +type broadcastClient struct { chainclient.ChainClient ethSignFn keystore.PersonalSignFn @@ -25,14 +55,14 @@ type BroadcastClient struct { } func NewBroadcastClient(client chainclient.ChainClient, signFn keystore.PersonalSignFn) BroadcastClient { - return BroadcastClient{ + return broadcastClient{ ChainClient: client, ethSignFn: signFn, svcTags: metrics.Tags{"svc": "peggy_broadcast"}, } } -func (c BroadcastClient) UpdatePeggyOrchestratorAddresses(_ context.Context, ethFrom common.Address, orchAddr cosmostypes.AccAddress) error { +func (c broadcastClient) UpdatePeggyOrchestratorAddresses(_ context.Context, ethFrom common.Address, orchAddr cosmostypes.AccAddress) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -63,7 +93,7 @@ func (c BroadcastClient) UpdatePeggyOrchestratorAddresses(_ context.Context, eth return nil } -func (c BroadcastClient) SendValsetConfirm(_ context.Context, ethFrom common.Address, peggyID common.Hash, valset *peggytypes.Valset) error { +func (c broadcastClient) SendValsetConfirm(_ context.Context, ethFrom common.Address, peggyID common.Hash, valset *peggytypes.Valset) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -105,7 +135,7 @@ func (c BroadcastClient) SendValsetConfirm(_ context.Context, ethFrom common.Add return nil } -func (c BroadcastClient) SendBatchConfirm(_ context.Context, ethFrom common.Address, peggyID common.Hash, batch *peggytypes.OutgoingTxBatch) error { +func (c broadcastClient) SendBatchConfirm(_ context.Context, ethFrom common.Address, peggyID common.Hash, batch *peggytypes.OutgoingTxBatch) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -141,7 +171,7 @@ func (c BroadcastClient) SendBatchConfirm(_ context.Context, ethFrom common.Addr return nil } -func (c BroadcastClient) SendToEth(ctx context.Context, destination common.Address, amount, fee cosmostypes.Coin) error { +func (c broadcastClient) SendToEth(ctx context.Context, destination common.Address, amount, fee cosmostypes.Coin) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -174,7 +204,7 @@ func (c BroadcastClient) SendToEth(ctx context.Context, destination common.Addre return nil } -func (c BroadcastClient) SendRequestBatch(ctx context.Context, denom string) error { +func (c broadcastClient) SendRequestBatch(ctx context.Context, denom string) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -200,7 +230,7 @@ func (c BroadcastClient) SendRequestBatch(ctx context.Context, denom string) err return nil } -func (c BroadcastClient) SendEthereumClaims(ctx context.Context, lastClaimEventNonce uint64, oldDeposits []*peggyevents.PeggySendToCosmosEvent, deposits []*peggyevents.PeggySendToInjectiveEvent, withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent) (uint64, error) { +func (c broadcastClient) SendEthereumClaims(ctx context.Context, lastClaimEventNonce uint64, oldDeposits []*peggyevents.PeggySendToCosmosEvent, deposits []*peggyevents.PeggySendToInjectiveEvent, withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent) (uint64, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -303,7 +333,7 @@ func sortEventsByNonce( return events } -func (c BroadcastClient) sendEventClaim(ctx context.Context, ev event) error { +func (c broadcastClient) sendEventClaim(ctx context.Context, ev event) error { switch ev := ev.(type) { case *eventSendToCosmos: e := peggyevents.PeggySendToCosmosEvent(*ev) @@ -325,7 +355,7 @@ func (c BroadcastClient) sendEventClaim(ctx context.Context, ev event) error { return errors.Errorf("unknown event type %T", ev) } -func (c BroadcastClient) sendOldDepositClaims( +func (c broadcastClient) sendOldDepositClaims( ctx context.Context, oldDeposit *peggyevents.PeggySendToCosmosEvent, ) error { @@ -371,7 +401,7 @@ func (c BroadcastClient) sendOldDepositClaims( return nil } -func (c BroadcastClient) sendDepositClaims( +func (c broadcastClient) sendDepositClaims( ctx context.Context, deposit *peggyevents.PeggySendToInjectiveEvent, ) error { @@ -418,7 +448,7 @@ func (c BroadcastClient) sendDepositClaims( return nil } -func (c BroadcastClient) sendWithdrawClaims( +func (c broadcastClient) sendWithdrawClaims( ctx context.Context, withdraw *peggyevents.PeggyTransactionBatchExecutedEvent, ) error { @@ -456,7 +486,7 @@ func (c BroadcastClient) sendWithdrawClaims( return nil } -func (c BroadcastClient) sendValsetUpdateClaims( +func (c broadcastClient) sendValsetUpdateClaims( ctx context.Context, valsetUpdate *peggyevents.PeggyValsetUpdatedEvent, ) error { @@ -505,7 +535,7 @@ func (c BroadcastClient) sendValsetUpdateClaims( return nil } -func (c BroadcastClient) sendErc20DeployedClaims( +func (c broadcastClient) sendErc20DeployedClaims( ctx context.Context, erc20Deployed *peggyevents.PeggyERC20DeployedEvent, ) error { diff --git a/orchestrator/cosmos/peggy/query.go b/orchestrator/cosmos/peggy/query.go index 8939fd82..cc8574ac 100644 --- a/orchestrator/cosmos/peggy/query.go +++ b/orchestrator/cosmos/peggy/query.go @@ -13,20 +13,35 @@ import ( var ErrNotFound = errors.New("not found") -type QueryClient struct { +type QueryClient interface { + ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) + CurrentValset(ctx context.Context) (*peggytypes.Valset, error) + OldestUnsignedValsets(ctx context.Context, valAccountAddress cosmostypes.AccAddress) ([]*peggytypes.Valset, error) + LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) + AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) + OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) + LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) + UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) + TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) + LastClaimEventByAddr(ctx context.Context, validatorAccountAddress cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) + PeggyParams(ctx context.Context) (*peggytypes.Params, error) + GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (cosmostypes.AccAddress, error) +} + +type queryClient struct { peggytypes.QueryClient svcTags metrics.Tags } func NewQueryClient(client peggytypes.QueryClient) QueryClient { - return QueryClient{ + return queryClient{ QueryClient: client, svcTags: metrics.Tags{"svc": "peggy_query"}, } } -func (c QueryClient) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) { +func (c queryClient) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -47,7 +62,7 @@ func (c QueryClient) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Va return resp.Valset, nil } -func (c QueryClient) CurrentValset(ctx context.Context) (*peggytypes.Valset, error) { +func (c queryClient) CurrentValset(ctx context.Context) (*peggytypes.Valset, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -66,7 +81,7 @@ func (c QueryClient) CurrentValset(ctx context.Context) (*peggytypes.Valset, err return resp.Valset, nil } -func (c QueryClient) OldestUnsignedValsets(ctx context.Context, valAccountAddress cosmostypes.AccAddress) ([]*peggytypes.Valset, error) { +func (c queryClient) OldestUnsignedValsets(ctx context.Context, valAccountAddress cosmostypes.AccAddress) ([]*peggytypes.Valset, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -89,7 +104,7 @@ func (c QueryClient) OldestUnsignedValsets(ctx context.Context, valAccountAddres return resp.Valsets, nil } -func (c QueryClient) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) { +func (c queryClient) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -108,7 +123,7 @@ func (c QueryClient) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, e return resp.Valsets, nil } -func (c QueryClient) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) { +func (c queryClient) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -127,7 +142,7 @@ func (c QueryClient) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*pe return resp.Confirms, nil } -func (c QueryClient) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { +func (c queryClient) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -150,7 +165,7 @@ func (c QueryClient) OldestUnsignedTransactionBatch(ctx context.Context, valAcco return resp.Batch, nil } -func (c QueryClient) LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) { +func (c queryClient) LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -169,7 +184,7 @@ func (c QueryClient) LatestTransactionBatches(ctx context.Context) ([]*peggytype return resp.Batches, nil } -func (c QueryClient) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { +func (c queryClient) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -188,7 +203,7 @@ func (c QueryClient) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes return resp.BatchFees, nil } -func (c QueryClient) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { +func (c queryClient) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -212,7 +227,7 @@ func (c QueryClient) TransactionBatchSignatures(ctx context.Context, nonce uint6 return resp.Confirms, nil } -func (c QueryClient) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { +func (c queryClient) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -235,7 +250,7 @@ func (c QueryClient) LastClaimEventByAddr(ctx context.Context, validatorAccountA return resp.LastClaimEvent, nil } -func (c QueryClient) PeggyParams(ctx context.Context) (*peggytypes.Params, error) { +func (c queryClient) PeggyParams(ctx context.Context) (*peggytypes.Params, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -254,7 +269,7 @@ func (c QueryClient) PeggyParams(ctx context.Context) (*peggytypes.Params, error return &resp.Params, nil } -func (c QueryClient) GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (cosmostypes.AccAddress, error) { +func (c queryClient) GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (cosmostypes.AccAddress, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() diff --git a/orchestrator/cosmos/query.go b/orchestrator/cosmos/query.go deleted file mode 100644 index c22b94e6..00000000 --- a/orchestrator/cosmos/query.go +++ /dev/null @@ -1,26 +0,0 @@ -package cosmos - -import ( - "context" - - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - sdk "github.com/cosmos/cosmos-sdk/types" - ethcmn "github.com/ethereum/go-ethereum/common" -) - -type PeggyQueryClient interface { - ValsetAt(ctx context.Context, nonce uint64) (*types.Valset, error) - CurrentValset(ctx context.Context) (*types.Valset, error) - OldestUnsignedValsets(ctx context.Context, valAccountAddress sdk.AccAddress) ([]*types.Valset, error) - LatestValsets(ctx context.Context) ([]*types.Valset, error) - AllValsetConfirms(ctx context.Context, nonce uint64) ([]*types.MsgValsetConfirm, error) - OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress sdk.AccAddress) (*types.OutgoingTxBatch, error) - LatestTransactionBatches(ctx context.Context) ([]*types.OutgoingTxBatch, error) - UnbatchedTokensWithFees(ctx context.Context) ([]*types.BatchFees, error) - - TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract ethcmn.Address) ([]*types.MsgConfirmBatch, error) - LastClaimEventByAddr(ctx context.Context, validatorAccountAddress sdk.AccAddress) (*types.LastClaimEvent, error) - - PeggyParams(ctx context.Context) (*types.Params, error) - GetValidatorAddress(ctx context.Context, addr ethcmn.Address) (sdk.AccAddress, error) -} diff --git a/orchestrator/cosmos/tmclient/tmclient.go b/orchestrator/cosmos/tendermint/client.go similarity index 95% rename from orchestrator/cosmos/tmclient/tmclient.go rename to orchestrator/cosmos/tendermint/client.go index aaf753d1..9f758aa2 100644 --- a/orchestrator/cosmos/tmclient/tmclient.go +++ b/orchestrator/cosmos/tendermint/client.go @@ -1,4 +1,4 @@ -package tmclient +package tendermint import ( "context" @@ -14,7 +14,7 @@ import ( tmctypes "github.com/cometbft/cometbft/rpc/core/types" ) -type TendermintClient interface { +type Client interface { GetBlock(ctx context.Context, height int64) (*tmctypes.ResultBlock, error) GetLatestBlockHeight(ctx context.Context) (int64, error) GetTxs(ctx context.Context, block *tmctypes.ResultBlock) ([]*ctypes.ResultTx, error) @@ -26,7 +26,7 @@ type tmClient struct { svcTags metrics.Tags } -func NewRPCClient(rpcNodeAddr string) TendermintClient { +func NewRPCClient(rpcNodeAddr string) Client { rpcClient, err := rpchttp.NewWithTimeout(rpcNodeAddr, "/websocket", 10) if err != nil { log.WithError(err).Fatalln("failed to init rpcClient") @@ -35,7 +35,7 @@ func NewRPCClient(rpcNodeAddr string) TendermintClient { return &tmClient{ rpcClient: rpcClient, svcTags: metrics.Tags{ - "svc": string("tmclient"), + "svc": string("tendermint"), }, } } From 6c330c1f34fb576fe19baea7cffacb1e45966be5 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 19:28:34 +0100 Subject: [PATCH 36/99] refactor interface usage --- cmd/peggo/orchestrator.go | 17 +++---- cmd/peggo/tx.go | 13 +++--- orchestrator/cosmos/custom_rpc.go | 27 ++--------- orchestrator/cosmos/load_balanced.go | 36 +++------------ orchestrator/injective.go | 67 ++++++++++++---------------- orchestrator/oracle.go | 2 +- orchestrator/orchestrator.go | 12 +++-- orchestrator/relayer.go | 4 +- orchestrator/signer.go | 4 +- 9 files changed, 64 insertions(+), 118 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index d7257fc2..2a30ce10 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -71,13 +71,9 @@ func orchestratorCmd(cmd *cli.Cmd) { log.WithError(err).Fatalln("failed to initialize Ethereum account") } - var ( - injectiveNet orchestrator.InjectiveNetwork - customEndpointRPCs = *cfg.cosmosGRPC != "" && *cfg.tendermintRPC != "" - ) - - if customEndpointRPCs { - injectiveNet, err = cosmos.NewCustomRPCNetwork( + var cosmosNetwork cosmos.Network + if customEndpointRPCs := *cfg.cosmosGRPC != "" && *cfg.tendermintRPC != ""; customEndpointRPCs { + cosmosNetwork, err = cosmos.NewCustomRPCNetwork( *cfg.cosmosChainID, valAddress.String(), *cfg.cosmosGRPC, @@ -88,7 +84,7 @@ func orchestratorCmd(cmd *cli.Cmd) { ) } else { // load balanced connection - injectiveNet, err = cosmos.NewLoadBalancedNetwork( + cosmosNetwork, err = cosmos.NewLoadBalancedNetwork( *cfg.cosmosChainID, valAddress.String(), *cfg.cosmosGasPrices, @@ -103,7 +99,7 @@ func orchestratorCmd(cmd *cli.Cmd) { closer.Bind(cancelFn) // Construct erc20 token mapping - peggyParams, err := injectiveNet.PeggyParams(ctx) + peggyParams, err := cosmosNetwork.PeggyParams(ctx) if err != nil { log.WithError(err).Fatalln("failed to query peggy params, is injectived running?") } @@ -131,7 +127,8 @@ func orchestratorCmd(cmd *cli.Cmd) { // Create peggo and run it peggo, err := orchestrator.NewPeggyOrchestrator( - injectiveNet, + valAddress, + cosmosNetwork, ethereumNet, coingeckoFeed, erc20ContractMapping, diff --git a/cmd/peggo/tx.go b/cmd/peggo/tx.go index c69c41ff..eb180109 100644 --- a/cmd/peggo/tx.go +++ b/cmd/peggo/tx.go @@ -2,6 +2,7 @@ package main import ( "context" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" "time" cli "github.com/jawher/mow.cli" @@ -126,12 +127,8 @@ func registerEthKeyCmd(cmd *cli.Cmd) { return } - var ( - peggyBroadcastClient cosmos.PeggyBroadcastClient - customCosmosRPC = *cosmosGRPC != "" && *tendermintRPC != "" - ) - - if customCosmosRPC { + var peggyBroadcastClient peggy.BroadcastClient + if customCosmosRPC := *cosmosGRPC != "" && *tendermintRPC != ""; customCosmosRPC { net, err := cosmos.NewCustomRPCNetwork( *cosmosChainID, valAddress.String(), @@ -146,7 +143,7 @@ func registerEthKeyCmd(cmd *cli.Cmd) { log.Fatalln("failed to connect to Injective network") } - peggyBroadcastClient = net.PeggyBroadcastClient + peggyBroadcastClient = peggy.BroadcastClient(net) } else { net, err := cosmos.NewLoadBalancedNetwork( *cosmosChainID, @@ -160,7 +157,7 @@ func registerEthKeyCmd(cmd *cli.Cmd) { log.Fatalln("failed to connect to Injective network") } - peggyBroadcastClient = net.PeggyBroadcastClient + peggyBroadcastClient = peggy.BroadcastClient(net) } broadcastCtx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second) diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index 15352fba..9c00d8b1 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -3,7 +3,6 @@ package cosmos import ( "context" "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" - sdk "github.com/cosmos/cosmos-sdk/types" "time" "github.com/InjectiveLabs/sdk-go/client/common" @@ -19,11 +18,9 @@ import ( ) type CustomRPCNetwork struct { - addr sdk.AccAddress - - tendermint.Client peggy.QueryClient peggy.BroadcastClient + tendermint.Client } func loadCustomNetworkConfig(chainID, feeDenom, cosmosGRPC, tendermintRPC string) common.Network { @@ -49,12 +46,7 @@ func NewCustomRPCNetwork( tendermintRPC string, keyring keyring.Keyring, personalSignerFn keystore.PersonalSignFn, -) (*CustomRPCNetwork, error) { - addr, err := sdk.AccAddressFromBech32(validatorAddress) - if err != nil { - return nil, errors.Wrap(err, "invalid address") - } - +) (Network, error) { clientCtx, err := chainclient.NewClientContext(chainID, validatorAddress, keyring) if err != nil { return nil, errors.Wrapf(err, "failed to create client context for Injective chain") @@ -84,7 +76,6 @@ func NewCustomRPCNetwork( peggyQuerier := peggytypes.NewQueryClient(grpcConn) n := &CustomRPCNetwork{ - addr: addr, Client: tendermint.NewRPCClient(tendermintRPC), QueryClient: peggy.NewQueryClient(peggyQuerier), BroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), @@ -100,7 +91,7 @@ func NewCustomRPCNetwork( return n, nil } -func (n *CustomRPCNetwork) GetBlockCreationTime(ctx context.Context, height int64) (time.Time, error) { +func (n *CustomRPCNetwork) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { block, err := n.Client.GetBlock(ctx, height) if err != nil { return time.Time{}, err @@ -108,15 +99,3 @@ func (n *CustomRPCNetwork) GetBlockCreationTime(ctx context.Context, height int6 return block.Block.Time, nil } - -func (n *CustomRPCNetwork) LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) { - return n.LastClaimEventByAddr(ctx, n.addr) -} - -func (n *CustomRPCNetwork) OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { - return n.QueryClient.OldestUnsignedValsets(ctx, n.addr) -} - -func (n *CustomRPCNetwork) OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { - return n.QueryClient.OldestUnsignedTransactionBatch(ctx, n.addr) -} diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index f7db90a0..53001f79 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -4,7 +4,6 @@ import ( "context" "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" rpchttp "github.com/cometbft/cometbft/rpc/client/http" - sdk "github.com/cosmos/cosmos-sdk/types" "strconv" "time" @@ -16,17 +15,14 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" chainclient "github.com/InjectiveLabs/sdk-go/client/chain" "github.com/InjectiveLabs/sdk-go/client/common" explorerclient "github.com/InjectiveLabs/sdk-go/client/explorer" ) type LoadBalancedNetwork struct { - addr sdk.AccAddress - - PeggyQueryClient - PeggyBroadcastClient + peggy.QueryClient + peggy.BroadcastClient explorerclient.ExplorerClient } @@ -41,12 +37,7 @@ func NewLoadBalancedNetwork( injectiveGasPrices string, keyring keyring.Keyring, personalSignerFn keystore.PersonalSignFn, -) (*LoadBalancedNetwork, error) { - addr, err := sdk.AccAddressFromBech32(validatorAddress) - if err != nil { - return nil, errors.Wrap(err, "invalid address") - } - +) (Network, error) { var networkName string switch chainID { case "injective-1": @@ -92,10 +83,9 @@ func NewLoadBalancedNetwork( peggyQuerier := types.NewQueryClient(grpcConn) n := &LoadBalancedNetwork{ - addr: addr, - PeggyQueryClient: peggy.NewQueryClient(peggyQuerier), - PeggyBroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), - ExplorerClient: explorer, + QueryClient: peggy.NewQueryClient(peggyQuerier), + BroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), + ExplorerClient: explorer, } log.WithFields(log.Fields{ @@ -108,7 +98,7 @@ func NewLoadBalancedNetwork( return n, nil } -func (n *LoadBalancedNetwork) GetBlockCreationTime(ctx context.Context, height int64) (time.Time, error) { +func (n *LoadBalancedNetwork) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { block, err := n.ExplorerClient.GetBlock(ctx, strconv.FormatInt(height, 10)) if err != nil { return time.Time{}, err @@ -122,18 +112,6 @@ func (n *LoadBalancedNetwork) GetBlockCreationTime(ctx context.Context, height i return blockTime, nil } -func (n *LoadBalancedNetwork) LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) { - return n.LastClaimEventByAddr(ctx, n.addr) -} - -func (n *LoadBalancedNetwork) OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { - return n.PeggyQueryClient.OldestUnsignedValsets(ctx, n.addr) -} - -func (n *LoadBalancedNetwork) OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { - return n.PeggyQueryClient.OldestUnsignedTransactionBatch(ctx, n.addr) -} - // waitForService awaits an active ClientConn to a GRPC service. func waitForService(ctx context.Context, clientConn *grpc.ClientConn) { for { diff --git a/orchestrator/injective.go b/orchestrator/injective.go index b2d2f7fa..9648c894 100644 --- a/orchestrator/injective.go +++ b/orchestrator/injective.go @@ -1,42 +1,31 @@ package orchestrator -import ( - "context" - sdk "github.com/cosmos/cosmos-sdk/types" - "time" - - gethcommon "github.com/ethereum/go-ethereum/common" - - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" -) - // InjectiveNetwork is the orchestrator's reference endpoint to the Injective network -type InjectiveNetwork interface { - PeggyParams(ctx context.Context) (*peggytypes.Params, error) - GetBlockCreationTime(ctx context.Context, height int64) (time.Time, error) - GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (sdk.AccAddress, error) - - LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) - SendEthereumClaims(ctx context.Context, - lastClaimEvent uint64, - oldDeposits []*peggyevents.PeggySendToCosmosEvent, - deposits []*peggyevents.PeggySendToInjectiveEvent, - withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, - ) (uint64, error) - - UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) - SendRequestBatch(ctx context.Context, denom string) error - OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) - SendBatchConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch) error - LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) - TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) - - OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) - SendValsetConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, valset *peggytypes.Valset) error - LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) - AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) - ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) -} +//type InjectiveNetwork interface { +// PeggyParams(ctx context.Context) (*peggytypes.Params, error) +// GetBlockCreationTime(ctx context.Context, height int64) (time.Time, error) +// GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (sdk.AccAddress, error) +// +// LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) +// SendEthereumClaims(ctx context.Context, +// lastClaimEvent uint64, +// oldDeposits []*peggyevents.PeggySendToCosmosEvent, +// deposits []*peggyevents.PeggySendToInjectiveEvent, +// withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, +// erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, +// valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, +// ) (uint64, error) +// +// UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) +// SendRequestBatch(ctx context.Context, denom string) error +// OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) +// SendBatchConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch) error +// LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) +// TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) +// +// OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) +// SendValsetConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, valset *peggytypes.Valset) error +// LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) +// AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) +// ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) +//} diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index d0933003..748357fd 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -216,7 +216,7 @@ func (l *ethOracleLoop) getLatestEthHeight(ctx context.Context) (uint64, error) func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents) error { sendEventsFn := func() error { - lastClaim, err := l.inj.LastClaimEvent(ctx) + lastClaim, err := l.inj.LastClaimEventByAddr(ctx, l.orchestratorAddr) if err != nil { return err } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 159f63ff..b20c5455 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -2,6 +2,8 @@ package orchestrator import ( "context" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" + "github.com/cosmos/cosmos-sdk/types" "time" eth "github.com/ethereum/go-ethereum/common" @@ -18,7 +20,9 @@ type PeggyOrchestrator struct { logger log.Logger svcTags metrics.Tags - inj InjectiveNetwork + inj cosmos.Network + orchestratorAddr types.AccAddress + eth EthereumNetwork pricefeed PriceFeed @@ -34,7 +38,8 @@ type PeggyOrchestrator struct { } func NewPeggyOrchestrator( - injective InjectiveNetwork, + orchestratorAddr types.AccAddress, + injective cosmos.Network, ethereum EthereumNetwork, priceFeed PriceFeed, erc20ContractMapping map[eth.Address]string, @@ -48,6 +53,7 @@ func NewPeggyOrchestrator( logger: log.DefaultLogger, svcTags: metrics.Tags{"svc": "peggy_orchestrator"}, inj: injective, + orchestratorAddr: orchestratorAddr, eth: ethereum, pricefeed: priceFeed, erc20ContractMapping: erc20ContractMapping, @@ -154,7 +160,7 @@ func (s *PeggyOrchestrator) getLastClaimBlockHeight(ctx context.Context) (uint64 doneFn := metrics.ReportFuncTiming(s.svcTags) defer doneFn() - claim, err := s.inj.LastClaimEvent(ctx) + claim, err := s.inj.LastClaimEventByAddr(ctx, s.orchestratorAddr) if err != nil { return 0, err } diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index d69a6e13..05b07c22 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -168,7 +168,7 @@ func (l *relayerLoop) relayValset(ctx context.Context, latestEthValset *peggytyp l.Logger().WithFields(log.Fields{"inj_nonce": oldestConfirmedValset.Nonce, "eth_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("new valset update") // Check custom time delay offset - blockTime, err := l.inj.GetBlockCreationTime(ctx, int64(oldestConfirmedValset.Height)) + blockTime, err := l.inj.GetBlockTime(ctx, int64(oldestConfirmedValset.Height)) if err != nil { return errors.Wrap(err, "failed to parse timestamp from block") } @@ -240,7 +240,7 @@ func (l *relayerLoop) relayBatch(ctx context.Context, latestEthValset *peggytype l.Logger().WithFields(log.Fields{"inj_nonce": oldestConfirmedInjBatch.BatchNonce, "eth_nonce": latestEthBatch.Uint64()}).Debugln("new batch update") // Check custom time delay offset - blockTime, err := l.inj.GetBlockCreationTime(ctx, int64(oldestConfirmedInjBatch.Block)) + blockTime, err := l.inj.GetBlockTime(ctx, int64(oldestConfirmedInjBatch.Block)) if err != nil { return errors.Wrap(err, "failed to parse timestamp from block") } diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 60da2044..903f81bc 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -97,7 +97,7 @@ func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context) (*types.OutgoingTx var oldestUnsignedBatch *types.OutgoingTxBatch getOldestUnsignedBatchFn := func() (err error) { // sign the last unsigned batch, TODO check if we already have signed this - oldestUnsignedBatch, err = l.inj.OldestUnsignedTransactionBatch(ctx) + oldestUnsignedBatch, err = l.inj.OldestUnsignedTransactionBatch(ctx, l.orchestratorAddr) if oldestUnsignedBatch == nil { return nil } @@ -143,7 +143,7 @@ func (l *ethSignerLoop) signBatch(ctx context.Context, batch *types.OutgoingTxBa func (l *ethSignerLoop) getUnsignedValsets(ctx context.Context) ([]*types.Valset, error) { var oldestUnsignedValsets []*types.Valset getOldestUnsignedValsetsFn := func() (err error) { - oldestUnsignedValsets, err = l.inj.OldestUnsignedValsets(ctx) + oldestUnsignedValsets, err = l.inj.OldestUnsignedValsets(ctx, l.orchestratorAddr) if oldestUnsignedValsets == nil { return nil } From 62077b0bee0e2302b6a0afd4bb8222f098cb359c Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 19:53:14 +0100 Subject: [PATCH 37/99] remove file --- orchestrator/cosmos/load_balanced.go | 10 ++++----- orchestrator/injective.go | 31 ---------------------------- 2 files changed, 5 insertions(+), 36 deletions(-) delete mode 100644 orchestrator/injective.go diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index 53001f79..d3513683 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -49,12 +49,7 @@ func NewLoadBalancedNetwork( default: return nil, errors.Errorf("provided chain id %v does not belong to any known Injective network", chainID) } - netCfg := common.LoadNetwork(networkName, "lb") - explorer, err := explorerclient.NewExplorerClient(netCfg) - if err != nil { - return nil, errors.Wrap(err, "failed to initialize explorer client") - } clientCtx, err := chainclient.NewClientContext(chainID, validatorAddress, keyring) if err != nil { @@ -82,6 +77,11 @@ func NewLoadBalancedNetwork( waitForService(daemonWaitCtx, grpcConn) peggyQuerier := types.NewQueryClient(grpcConn) + explorer, err := explorerclient.NewExplorerClient(netCfg) + if err != nil { + return nil, errors.Wrap(err, "failed to initialize explorer client") + } + n := &LoadBalancedNetwork{ QueryClient: peggy.NewQueryClient(peggyQuerier), BroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), diff --git a/orchestrator/injective.go b/orchestrator/injective.go deleted file mode 100644 index 9648c894..00000000 --- a/orchestrator/injective.go +++ /dev/null @@ -1,31 +0,0 @@ -package orchestrator - -// InjectiveNetwork is the orchestrator's reference endpoint to the Injective network -//type InjectiveNetwork interface { -// PeggyParams(ctx context.Context) (*peggytypes.Params, error) -// GetBlockCreationTime(ctx context.Context, height int64) (time.Time, error) -// GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (sdk.AccAddress, error) -// -// LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) -// SendEthereumClaims(ctx context.Context, -// lastClaimEvent uint64, -// oldDeposits []*peggyevents.PeggySendToCosmosEvent, -// deposits []*peggyevents.PeggySendToInjectiveEvent, -// withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, -// erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, -// valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, -// ) (uint64, error) -// -// UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) -// SendRequestBatch(ctx context.Context, denom string) error -// OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) -// SendBatchConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch) error -// LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) -// TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) -// -// OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) -// SendValsetConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, valset *peggytypes.Valset) error -// LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) -// AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) -// ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) -//} From e237f1616b3354332bfe8b6c875f03a81b881f2f Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 20:32:56 +0100 Subject: [PATCH 38/99] tidy --- orchestrator/batch_request.go | 60 ++++++------ orchestrator/cosmos/custom_rpc.go | 20 ++-- orchestrator/cosmos/load_balanced.go | 27 +++--- orchestrator/cosmos/peggy/broadcast.go | 32 ++++--- orchestrator/cosmos/peggy/query.go | 5 +- orchestrator/cosmos/tendermint/client.go | 21 ++--- orchestrator/ethereum.go | 16 ++-- orchestrator/oracle.go | 72 +++++++------- orchestrator/orchestrator.go | 17 ++-- orchestrator/price_feed.go | 8 -- orchestrator/relayer.go | 114 +++++++++++------------ orchestrator/signer.go | 22 ++--- 12 files changed, 198 insertions(+), 216 deletions(-) delete mode 100644 orchestrator/price_feed.go diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index e67486d7..84879a0f 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -4,14 +4,14 @@ import ( "context" "time" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/avast/retry-go" - cosmtypes "github.com/cosmos/cosmos-sdk/types" - eth "github.com/ethereum/go-ethereum/common" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/shopspring/decimal" log "github.com/xlab/suplog" "github.com/InjectiveLabs/peggo/orchestrator/loops" - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context) (err error) { @@ -36,32 +36,28 @@ func (l *batchRequestLoop) Run(ctx context.Context) error { l.logger.WithField("loop_duration", l.loopDuration.String()).Debugln("starting BatchRequester loop...") return loops.RunLoop(ctx, l.loopDuration, func() error { - return l.requestBatches(ctx) - }) -} - -func (l *batchRequestLoop) requestBatches(ctx context.Context) error { - fees, err := l.getUnbatchedTokenFees(ctx) - if err != nil { - // non-fatal, just alert - l.Logger().WithError(err).Warningln("unable to get outgoing withdrawal fees") - return nil - } + fees, err := l.getUnbatchedTokenFees(ctx) + if err != nil { + // non-fatal, just alert + l.Logger().WithError(err).Warningln("unable to get outgoing withdrawal fees") + return nil + } + + if len(fees) == 0 { + l.Logger().Infoln("no withdrawals to batch") + return nil + } + + for _, fee := range fees { + l.requestBatch(ctx, fee) + } - if len(fees) == 0 { - l.Logger().Infoln("no withdrawals to batch") return nil - } - - for _, fee := range fees { - l.requestBatch(ctx, fee) - } - - return nil + }) } -func (l *batchRequestLoop) getUnbatchedTokenFees(ctx context.Context) ([]*types.BatchFees, error) { - var unbatchedFees []*types.BatchFees +func (l *batchRequestLoop) getUnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { + var unbatchedFees []*peggytypes.BatchFees getUnbatchedTokenFeesFn := func() (err error) { unbatchedFees, err = l.inj.UnbatchedTokensWithFees(ctx) return err @@ -80,9 +76,9 @@ func (l *batchRequestLoop) getUnbatchedTokenFees(ctx context.Context) ([]*types. return unbatchedFees, nil } -func (l *batchRequestLoop) requestBatch(ctx context.Context, fee *types.BatchFees) { +func (l *batchRequestLoop) requestBatch(ctx context.Context, fee *peggytypes.BatchFees) { var ( - tokenAddr = eth.HexToAddress(fee.Token) + tokenAddr = gethcommon.HexToAddress(fee.Token) tokenDenom = l.tokenDenom(tokenAddr) ) @@ -95,18 +91,16 @@ func (l *batchRequestLoop) requestBatch(ctx context.Context, fee *types.BatchFee _ = l.inj.SendRequestBatch(ctx, tokenDenom) } -func (l *batchRequestLoop) tokenDenom(tokenAddr eth.Address) string { +func (l *batchRequestLoop) tokenDenom(tokenAddr gethcommon.Address) string { if cosmosDenom, ok := l.erc20ContractMapping[tokenAddr]; ok { return cosmosDenom } - // peggy denom - // todo: in reality, peggy denominators don't have an actual price listing - // So it seems that bridge fee must always be inj - return types.PeggyDenomString(tokenAddr) + // todo: revisit peggy denom addresses + return peggytypes.PeggyDenomString(tokenAddr) } -func (l *batchRequestLoop) checkFeeThreshold(tokenAddr eth.Address, fees cosmtypes.Int) bool { +func (l *batchRequestLoop) checkFeeThreshold(tokenAddr gethcommon.Address, fees cosmostypes.Int) bool { if l.minBatchFeeUSD == 0 { return true } diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index 9c00d8b1..175417db 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -2,19 +2,19 @@ package cosmos import ( "context" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" "time" - "github.com/InjectiveLabs/sdk-go/client/common" - rpchttp "github.com/cometbft/cometbft/rpc/client/http" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/sdk-go/client/chain" + clientcommon "github.com/InjectiveLabs/sdk-go/client/common" + comethttp "github.com/cometbft/cometbft/rpc/client/http" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/pkg/errors" log "github.com/xlab/suplog" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - chainclient "github.com/InjectiveLabs/sdk-go/client/chain" ) type CustomRPCNetwork struct { @@ -23,8 +23,8 @@ type CustomRPCNetwork struct { tendermint.Client } -func loadCustomNetworkConfig(chainID, feeDenom, cosmosGRPC, tendermintRPC string) common.Network { - cfg := common.LoadNetwork("devnet", "") +func loadCustomNetworkConfig(chainID, feeDenom, cosmosGRPC, tendermintRPC string) clientcommon.Network { + cfg := clientcommon.LoadNetwork("devnet", "") cfg.Name = "custom" cfg.ChainId = chainID cfg.Fee_denom = feeDenom @@ -47,12 +47,12 @@ func NewCustomRPCNetwork( keyring keyring.Keyring, personalSignerFn keystore.PersonalSignFn, ) (Network, error) { - clientCtx, err := chainclient.NewClientContext(chainID, validatorAddress, keyring) + clientCtx, err := chain.NewClientContext(chainID, validatorAddress, keyring) if err != nil { return nil, errors.Wrapf(err, "failed to create client context for Injective chain") } - tmRPC, err := rpchttp.New(tendermintRPC, "/websocket") + tmRPC, err := comethttp.New(tendermintRPC, "/websocket") if err != nil { return nil, errors.Wrapf(err, "failed to connect to Tendermint RPC %s", tendermintRPC) } @@ -61,7 +61,7 @@ func NewCustomRPCNetwork( clientCtx = clientCtx.WithClient(tmRPC) netCfg := loadCustomNetworkConfig(chainID, "inj", injectiveGRPC, tendermintRPC) - daemonClient, err := chainclient.NewChainClient(clientCtx, netCfg, common.OptionGasPrices(injectiveGasPrices)) + daemonClient, err := chain.NewChainClient(clientCtx, netCfg, clientcommon.OptionGasPrices(injectiveGasPrices)) if err != nil { return nil, errors.Wrapf(err, "failed to connect to Injective GRPC %s", injectiveGRPC) } diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index d3513683..b8eb1f9e 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -2,28 +2,29 @@ package cosmos import ( "context" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" - rpchttp "github.com/cometbft/cometbft/rpc/client/http" "strconv" "time" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/pkg/errors" log "github.com/xlab/suplog" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/sdk-go/client/chain" + clientcommon "github.com/InjectiveLabs/sdk-go/client/common" + explorer "github.com/InjectiveLabs/sdk-go/client/explorer" + + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - chainclient "github.com/InjectiveLabs/sdk-go/client/chain" - "github.com/InjectiveLabs/sdk-go/client/common" - explorerclient "github.com/InjectiveLabs/sdk-go/client/explorer" ) type LoadBalancedNetwork struct { peggy.QueryClient peggy.BroadcastClient - explorerclient.ExplorerClient + explorer.ExplorerClient } // NewLoadBalancedNetwork creates a load balanced connection to the Injective network. @@ -49,9 +50,9 @@ func NewLoadBalancedNetwork( default: return nil, errors.Errorf("provided chain id %v does not belong to any known Injective network", chainID) } - netCfg := common.LoadNetwork(networkName, "lb") + netCfg := clientcommon.LoadNetwork(networkName, "lb") - clientCtx, err := chainclient.NewClientContext(chainID, validatorAddress, keyring) + clientCtx, err := chain.NewClientContext(chainID, validatorAddress, keyring) if err != nil { return nil, errors.Wrapf(err, "failed to create client context for Injective chain") } @@ -63,7 +64,7 @@ func NewLoadBalancedNetwork( clientCtx = clientCtx.WithNodeURI(netCfg.TmEndpoint).WithClient(tmClient) - daemonClient, err := chainclient.NewChainClient(clientCtx, netCfg, common.OptionGasPrices(injectiveGasPrices)) + daemonClient, err := chain.NewChainClient(clientCtx, netCfg, clientcommon.OptionGasPrices(injectiveGasPrices)) if err != nil { return nil, errors.Wrapf(err, "failed to intialize chain client (%s)", networkName) } @@ -75,9 +76,9 @@ func NewLoadBalancedNetwork( grpcConn := daemonClient.QueryClient() waitForService(daemonWaitCtx, grpcConn) - peggyQuerier := types.NewQueryClient(grpcConn) + peggyQuerier := peggytypes.NewQueryClient(grpcConn) - explorer, err := explorerclient.NewExplorerClient(netCfg) + explorerCLient, err := explorer.NewExplorerClient(netCfg) if err != nil { return nil, errors.Wrap(err, "failed to initialize explorer client") } @@ -85,7 +86,7 @@ func NewLoadBalancedNetwork( n := &LoadBalancedNetwork{ QueryClient: peggy.NewQueryClient(peggyQuerier), BroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), - ExplorerClient: explorer, + ExplorerClient: explorerCLient, } log.WithFields(log.Fields{ diff --git a/orchestrator/cosmos/peggy/broadcast.go b/orchestrator/cosmos/peggy/broadcast.go index d900e4fd..d1b77f1c 100644 --- a/orchestrator/cosmos/peggy/broadcast.go +++ b/orchestrator/cosmos/peggy/broadcast.go @@ -3,31 +3,33 @@ package peggy import ( "context" "fmt" + "sort" + "time" + "github.com/InjectiveLabs/metrics" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" chainclient "github.com/InjectiveLabs/sdk-go/client/chain" cosmostypes "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" log "github.com/xlab/suplog" - "sort" - "time" + + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" ) type BroadcastClient interface { /// Send a transaction updating the eth address for the sending /// Cosmos address. The sending Cosmos address should be a validator - UpdatePeggyOrchestratorAddresses(ctx context.Context, ethFrom common.Address, orchAddr cosmostypes.AccAddress) error + UpdatePeggyOrchestratorAddresses(ctx context.Context, ethFrom gethcommon.Address, orchAddr cosmostypes.AccAddress) error // SendValsetConfirm broadcasts in a confirmation for a specific validator set for a specific block height. - SendValsetConfirm(ctx context.Context, ethFrom common.Address, peggyID common.Hash, valset *peggytypes.Valset) error + SendValsetConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, valset *peggytypes.Valset) error // SendBatchConfirm broadcasts in a confirmation for a specific transaction batch set for a specific block height // since transaction batches also include validator sets this has all the arguments - SendBatchConfirm(ctx context.Context, ethFrom common.Address, peggyID common.Hash, batch *peggytypes.OutgoingTxBatch) error + SendBatchConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch) error SendEthereumClaims(ctx context.Context, lastClaimEvent uint64, @@ -41,7 +43,7 @@ type BroadcastClient interface { // SendToEth broadcasts a Tx that tokens from Cosmos to Ethereum. // These tokens will not be sent immediately. Instead, they will require // some time to be included in a batch. - SendToEth(ctx context.Context, destination common.Address, amount, fee cosmostypes.Coin) error + SendToEth(ctx context.Context, destination gethcommon.Address, amount, fee cosmostypes.Coin) error // SendRequestBatch broadcasts a requests a batch of withdrawal transactions to be generated on the chain. SendRequestBatch(ctx context.Context, denom string) error @@ -62,7 +64,7 @@ func NewBroadcastClient(client chainclient.ChainClient, signFn keystore.Personal } } -func (c broadcastClient) UpdatePeggyOrchestratorAddresses(_ context.Context, ethFrom common.Address, orchAddr cosmostypes.AccAddress) error { +func (c broadcastClient) UpdatePeggyOrchestratorAddresses(_ context.Context, ethFrom gethcommon.Address, orchAddr cosmostypes.AccAddress) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -93,7 +95,7 @@ func (c broadcastClient) UpdatePeggyOrchestratorAddresses(_ context.Context, eth return nil } -func (c broadcastClient) SendValsetConfirm(_ context.Context, ethFrom common.Address, peggyID common.Hash, valset *peggytypes.Valset) error { +func (c broadcastClient) SendValsetConfirm(_ context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, valset *peggytypes.Valset) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -124,7 +126,7 @@ func (c broadcastClient) SendValsetConfirm(_ context.Context, ethFrom common.Add Orchestrator: c.FromAddress().String(), EthAddress: ethFrom.Hex(), Nonce: valset.Nonce, - Signature: common.Bytes2Hex(signature), + Signature: gethcommon.Bytes2Hex(signature), } if err = c.ChainClient.QueueBroadcastMsg(msg); err != nil { @@ -135,7 +137,7 @@ func (c broadcastClient) SendValsetConfirm(_ context.Context, ethFrom common.Add return nil } -func (c broadcastClient) SendBatchConfirm(_ context.Context, ethFrom common.Address, peggyID common.Hash, batch *peggytypes.OutgoingTxBatch) error { +func (c broadcastClient) SendBatchConfirm(_ context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() @@ -158,7 +160,7 @@ func (c broadcastClient) SendBatchConfirm(_ context.Context, ethFrom common.Addr msg := &peggytypes.MsgConfirmBatch{ Orchestrator: c.FromAddress().String(), Nonce: batch.BatchNonce, - Signature: common.Bytes2Hex(signature), + Signature: gethcommon.Bytes2Hex(signature), EthSigner: ethFrom.Hex(), TokenContract: batch.TokenContract, } @@ -171,7 +173,7 @@ func (c broadcastClient) SendBatchConfirm(_ context.Context, ethFrom common.Addr return nil } -func (c broadcastClient) SendToEth(ctx context.Context, destination common.Address, amount, fee cosmostypes.Coin) error { +func (c broadcastClient) SendToEth(ctx context.Context, destination gethcommon.Address, amount, fee cosmostypes.Coin) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() diff --git a/orchestrator/cosmos/peggy/query.go b/orchestrator/cosmos/peggy/query.go index cc8574ac..34368e6e 100644 --- a/orchestrator/cosmos/peggy/query.go +++ b/orchestrator/cosmos/peggy/query.go @@ -3,12 +3,11 @@ package peggy import ( "context" + "github.com/InjectiveLabs/metrics" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - - "github.com/InjectiveLabs/metrics" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) var ErrNotFound = errors.New("not found") diff --git a/orchestrator/cosmos/tendermint/client.go b/orchestrator/cosmos/tendermint/client.go index 9f758aa2..80804732 100644 --- a/orchestrator/cosmos/tendermint/client.go +++ b/orchestrator/cosmos/tendermint/client.go @@ -5,20 +5,17 @@ import ( "strings" "github.com/InjectiveLabs/metrics" - - log "github.com/xlab/suplog" - rpcclient "github.com/cometbft/cometbft/rpc/client" rpchttp "github.com/cometbft/cometbft/rpc/client/http" - ctypes "github.com/cometbft/cometbft/rpc/core/types" - tmctypes "github.com/cometbft/cometbft/rpc/core/types" + comettypes "github.com/cometbft/cometbft/rpc/core/types" + log "github.com/xlab/suplog" ) type Client interface { - GetBlock(ctx context.Context, height int64) (*tmctypes.ResultBlock, error) + GetBlock(ctx context.Context, height int64) (*comettypes.ResultBlock, error) GetLatestBlockHeight(ctx context.Context) (int64, error) - GetTxs(ctx context.Context, block *tmctypes.ResultBlock) ([]*ctypes.ResultTx, error) - GetValidatorSet(ctx context.Context, height int64) (*tmctypes.ResultValidators, error) + GetTxs(ctx context.Context, block *comettypes.ResultBlock) ([]*comettypes.ResultTx, error) + GetValidatorSet(ctx context.Context, height int64) (*comettypes.ResultValidators, error) } type tmClient struct { @@ -41,7 +38,7 @@ func NewRPCClient(rpcNodeAddr string) Client { } // GetBlock queries for a block by height. An error is returned if the query fails. -func (c *tmClient) GetBlock(ctx context.Context, height int64) (*tmctypes.ResultBlock, error) { +func (c *tmClient) GetBlock(ctx context.Context, height int64) (*comettypes.ResultBlock, error) { return c.rpcClient.Block(ctx, &height) } @@ -64,12 +61,12 @@ func (c *tmClient) GetLatestBlockHeight(ctx context.Context) (int64, error) { // GetTxs queries for all the transactions in a block height. // It uses `Tx` RPC method to query for the transaction. -func (c *tmClient) GetTxs(ctx context.Context, block *tmctypes.ResultBlock) ([]*ctypes.ResultTx, error) { +func (c *tmClient) GetTxs(ctx context.Context, block *comettypes.ResultBlock) ([]*comettypes.ResultTx, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() - txs := make([]*ctypes.ResultTx, 0, len(block.Block.Txs)) + txs := make([]*comettypes.ResultTx, 0, len(block.Block.Txs)) for _, tmTx := range block.Block.Txs { tx, err := c.rpcClient.Tx(ctx, tmTx.Hash(), true) if err != nil { @@ -90,7 +87,7 @@ func (c *tmClient) GetTxs(ctx context.Context, block *tmctypes.ResultBlock) ([]* // GetValidatorSet returns all the known Tendermint validators for a given block // height. An error is returned if the query fails. -func (c *tmClient) GetValidatorSet(ctx context.Context, height int64) (*tmctypes.ResultValidators, error) { +func (c *tmClient) GetValidatorSet(ctx context.Context, height int64) (*comettypes.ResultValidators, error) { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() diff --git a/orchestrator/ethereum.go b/orchestrator/ethereum.go index c35ad852..53e396fb 100644 --- a/orchestrator/ethereum.go +++ b/orchestrator/ethereum.go @@ -4,8 +4,8 @@ import ( "context" "math/big" - eth "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethtypes "github.com/ethereum/go-ethereum/core/types" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" @@ -13,9 +13,9 @@ import ( // EthereumNetwork is the orchestrator's reference endpoint to the Ethereum network type EthereumNetwork interface { - FromAddress() eth.Address - HeaderByNumber(ctx context.Context, number *big.Int) (*ethtypes.Header, error) - GetPeggyID(ctx context.Context) (eth.Hash, error) + FromAddress() gethcommon.Address + HeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) + GetPeggyID(ctx context.Context) (gethcommon.Hash, error) GetSendToCosmosEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) GetSendToInjectiveEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) @@ -28,12 +28,12 @@ type EthereumNetwork interface { oldValset *peggytypes.Valset, newValset *peggytypes.Valset, confirms []*peggytypes.MsgValsetConfirm, - ) (*eth.Hash, error) + ) (*gethcommon.Hash, error) - GetTxBatchNonce(ctx context.Context, erc20ContractAddress eth.Address) (*big.Int, error) + GetTxBatchNonce(ctx context.Context, erc20ContractAddress gethcommon.Address) (*big.Int, error) SendTransactionBatch(ctx context.Context, currentValset *peggytypes.Valset, batch *peggytypes.OutgoingTxBatch, confirms []*peggytypes.MsgConfirmBatch, - ) (*eth.Hash, error) + ) (*gethcommon.Hash, error) } diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 748357fd..7023e1bc 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -49,52 +49,48 @@ func (l *ethOracleLoop) Run(ctx context.Context) error { l.logger.WithField("loop_duration", l.loopDuration.String()).Debugln("starting EthOracle loop...") return loops.RunLoop(ctx, l.loopDuration, func() error { - return l.observeEthEvents(ctx) - }) -} - -func (l *ethOracleLoop) observeEthEvents(ctx context.Context) error { - latestHeight, err := l.getLatestEthHeight(ctx) - if err != nil { - return err - } - - // not enough blocks on ethereum yet - if latestHeight <= ethBlockConfirmationDelay { - return nil - } + latestHeight, err := l.getLatestEthHeight(ctx) + if err != nil { + return err + } - // ensure that latest block has minimum confirmations - latestHeight = latestHeight - ethBlockConfirmationDelay - if latestHeight <= l.lastCheckedEthHeight { - return nil - } + // not enough blocks on ethereum yet + if latestHeight <= ethBlockConfirmationDelay { + return nil + } - if latestHeight > l.lastCheckedEthHeight+defaultBlocksToSearch { - latestHeight = l.lastCheckedEthHeight + defaultBlocksToSearch - } + // ensure that latest block has minimum confirmations + latestHeight = latestHeight - ethBlockConfirmationDelay + if latestHeight <= l.lastCheckedEthHeight { + return nil + } - if err := l.relayEvents(ctx, latestHeight); err != nil { - return err + if latestHeight > l.lastCheckedEthHeight+defaultBlocksToSearch { + latestHeight = l.lastCheckedEthHeight + defaultBlocksToSearch + } - } + if err := l.relayEvents(ctx, latestHeight); err != nil { + return err - l.Logger().WithFields(log.Fields{"block_start": l.lastCheckedEthHeight, "block_end": latestHeight}).Debugln("scanned Ethereum blocks") - l.lastCheckedEthHeight = latestHeight + } - /** Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. - 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. - we need to re-scan this block to ensure events are not missed due to indexing delay. - 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. - 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. - */ - if time.Since(l.lastResyncWithInjective) >= 48*time.Hour { - if err := l.autoResync(ctx); err != nil { - return err + l.Logger().WithFields(log.Fields{"block_start": l.lastCheckedEthHeight, "block_end": latestHeight}).Debugln("scanned Ethereum blocks") + l.lastCheckedEthHeight = latestHeight + + /** Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. + 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. + we need to re-scan this block to ensure events are not missed due to indexing delay. + 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. + 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. + */ + if time.Since(l.lastResyncWithInjective) >= 48*time.Hour { + if err := l.autoResync(ctx); err != nil { + return err + } } - } - return nil + return nil + }) } func (l *ethOracleLoop) relayEvents(ctx context.Context, latestHeight uint64) error { diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index b20c5455..35fd2e0e 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -2,20 +2,25 @@ package orchestrator import ( "context" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/cosmos/cosmos-sdk/types" "time" - eth "github.com/ethereum/go-ethereum/common" + "github.com/InjectiveLabs/metrics" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" log "github.com/xlab/suplog" - "github.com/InjectiveLabs/metrics" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/loops" ) const defaultLoopDur = 60 * time.Second +// PriceFeed provides token price for a given contract address +type PriceFeed interface { + QueryUSDPrice(address gethcommon.Address) (float64, error) +} + type PeggyOrchestrator struct { logger log.Logger svcTags metrics.Tags @@ -26,7 +31,7 @@ type PeggyOrchestrator struct { eth EthereumNetwork pricefeed PriceFeed - erc20ContractMapping map[eth.Address]string + erc20ContractMapping map[gethcommon.Address]string relayValsetOffsetDur time.Duration relayBatchOffsetDur time.Duration minBatchFeeUSD float64 @@ -42,7 +47,7 @@ func NewPeggyOrchestrator( injective cosmos.Network, ethereum EthereumNetwork, priceFeed PriceFeed, - erc20ContractMapping map[eth.Address]string, + erc20ContractMapping map[gethcommon.Address]string, minBatchFeeUSD float64, valsetRelayingEnabled, batchRelayingEnabled bool, @@ -114,7 +119,7 @@ func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context) bool { func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context) error { log.Infoln("running orchestrator in validator mode") - // get eth block observed by this validator + // get gethcommon block observed by this validator lastObservedEthBlock, _ := s.getLastClaimBlockHeight(ctx) if lastObservedEthBlock == 0 { peggyParams, err := s.inj.PeggyParams(ctx) diff --git a/orchestrator/price_feed.go b/orchestrator/price_feed.go deleted file mode 100644 index 2ff81c8c..00000000 --- a/orchestrator/price_feed.go +++ /dev/null @@ -1,8 +0,0 @@ -package orchestrator - -import eth "github.com/ethereum/go-ethereum/common" - -// PriceFeed provides token price for a given contract address -type PriceFeed interface { - QueryUSDPrice(address eth.Address) (float64, error) -} diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 05b07c22..28e827c9 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -5,17 +5,17 @@ import ( "sort" "time" + "github.com/InjectiveLabs/metrics" + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/avast/retry-go" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" log "github.com/xlab/suplog" - "github.com/InjectiveLabs/metrics" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/util" "github.com/InjectiveLabs/peggo/orchestrator/loops" - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) const ( @@ -53,65 +53,61 @@ func (l *relayerLoop) Run(ctx context.Context) error { }).Debugln("starting Relayer loop...") return loops.RunLoop(ctx, l.loopDuration, func() error { - return l.relayValsetsAndBatches(ctx) - }) -} + var latestEthValset *peggytypes.Valset + getLatestEthValsetFn := func() error { + vs, err := l.findLatestValsetOnEth(ctx) + if err != nil { + return err + } + + latestEthValset = vs + return nil + } -func (l *relayerLoop) relayValsetsAndBatches(ctx context.Context) error { - var latestEthValset *peggytypes.Valset - getLatestEthValsetFn := func() error { - vs, err := l.findLatestValsetOnEth(ctx) - if err != nil { + if err := retry.Do(getLatestEthValsetFn, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("failed to find latest valset on Ethereum, will retry (%d)", n) + })); err != nil { return err } - latestEthValset = vs - return nil - } - - if err := retry.Do(getLatestEthValsetFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to find latest valset on Ethereum, will retry (%d)", n) - })); err != nil { - return err - } - - var pg loops.ParanoidGroup - - if l.valsetRelayEnabled { - pg.Go(func() error { - return retry.Do(func() error { return l.relayValset(ctx, latestEthValset) }, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay valset, will retry (%d)", n) - }), - ) - }) - } + var pg loops.ParanoidGroup + + if l.valsetRelayEnabled { + pg.Go(func() error { + return retry.Do(func() error { return l.relayValset(ctx, latestEthValset) }, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("failed to relay valset, will retry (%d)", n) + }), + ) + }) + } - if l.batchRelayEnabled { - pg.Go(func() error { - return retry.Do(func() error { return l.relayBatch(ctx, latestEthValset) }, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay batch, will retry (%d)", n) - }), - ) - }) - } + if l.batchRelayEnabled { + pg.Go(func() error { + return retry.Do(func() error { return l.relayBatch(ctx, latestEthValset) }, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("failed to relay batch, will retry (%d)", n) + }), + ) + }) + } - if pg.Initialized() { - if err := pg.Wait(); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return err + if pg.Initialized() { + if err := pg.Wait(); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return err + } } - } - return nil + return nil + }) } func (l *relayerLoop) relayValset(ctx context.Context, latestEthValset *peggytypes.Valset) error { @@ -210,7 +206,7 @@ func (l *relayerLoop) relayBatch(ctx context.Context, latestEthValset *peggytype ) for _, batch := range latestBatches { - sigs, err := l.inj.TransactionBatchSignatures(ctx, batch.BatchNonce, common.HexToAddress(batch.TokenContract)) + sigs, err := l.inj.TransactionBatchSignatures(ctx, batch.BatchNonce, gethcommon.HexToAddress(batch.TokenContract)) if err != nil { return err } else if len(sigs) == 0 { @@ -226,7 +222,7 @@ func (l *relayerLoop) relayBatch(ctx context.Context, latestEthValset *peggytype return nil } - latestEthBatch, err := l.eth.GetTxBatchNonce(ctx, common.HexToAddress(oldestConfirmedInjBatch.TokenContract)) + latestEthBatch, err := l.eth.GetTxBatchNonce(ctx, gethcommon.HexToAddress(oldestConfirmedInjBatch.TokenContract)) if err != nil { return err } @@ -314,7 +310,7 @@ func (l *relayerLoop) findLatestValsetOnEth(ctx context.Context) (*peggytypes.Va valset := &peggytypes.Valset{ Nonce: event.NewValsetNonce.Uint64(), Members: make([]*peggytypes.BridgeValidator, 0, len(event.Powers)), - RewardAmount: sdk.NewIntFromBigInt(event.RewardAmount), + RewardAmount: cosmostypes.NewIntFromBigInt(event.RewardAmount), RewardToken: event.RewardToken.Hex(), } diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 903f81bc..586c2ec2 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -4,18 +4,18 @@ import ( "context" "time" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/avast/retry-go" - "github.com/ethereum/go-ethereum/common" + gethcommon "github.com/ethereum/go-ethereum/common" log "github.com/xlab/suplog" "github.com/InjectiveLabs/peggo/orchestrator/loops" - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) // EthSignerMainLoop simply signs off on any batches or validator sets provided by the validator // since these are provided directly by a trusted Injective node they can simply be assumed to be // valid and signed off on. -func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, peggyID common.Hash) error { +func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, peggyID gethcommon.Hash) error { loop := ethSignerLoop{ PeggyOrchestrator: s, loopDuration: defaultLoopDur, @@ -29,8 +29,8 @@ func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, peggyID commo type ethSignerLoop struct { *PeggyOrchestrator loopDuration time.Duration - peggyID common.Hash - ethFrom common.Address + peggyID gethcommon.Hash + ethFrom gethcommon.Address } func (l *ethSignerLoop) Logger() log.Logger { @@ -93,8 +93,8 @@ func (l *ethSignerLoop) signNewBatch(ctx context.Context) error { return nil } -func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context) (*types.OutgoingTxBatch, error) { - var oldestUnsignedBatch *types.OutgoingTxBatch +func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { + var oldestUnsignedBatch *peggytypes.OutgoingTxBatch getOldestUnsignedBatchFn := func() (err error) { // sign the last unsigned batch, TODO check if we already have signed this oldestUnsignedBatch, err = l.inj.OldestUnsignedTransactionBatch(ctx, l.orchestratorAddr) @@ -119,7 +119,7 @@ func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context) (*types.OutgoingTx return oldestUnsignedBatch, nil } -func (l *ethSignerLoop) signBatch(ctx context.Context, batch *types.OutgoingTxBatch) error { +func (l *ethSignerLoop) signBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) error { signFn := func() error { return l.inj.SendBatchConfirm(ctx, l.ethFrom, l.peggyID, batch) } @@ -140,8 +140,8 @@ func (l *ethSignerLoop) signBatch(ctx context.Context, batch *types.OutgoingTxBa return nil } -func (l *ethSignerLoop) getUnsignedValsets(ctx context.Context) ([]*types.Valset, error) { - var oldestUnsignedValsets []*types.Valset +func (l *ethSignerLoop) getUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { + var oldestUnsignedValsets []*peggytypes.Valset getOldestUnsignedValsetsFn := func() (err error) { oldestUnsignedValsets, err = l.inj.OldestUnsignedValsets(ctx, l.orchestratorAddr) if oldestUnsignedValsets == nil { @@ -165,7 +165,7 @@ func (l *ethSignerLoop) getUnsignedValsets(ctx context.Context) ([]*types.Valset return oldestUnsignedValsets, nil } -func (l *ethSignerLoop) signValset(ctx context.Context, vs *types.Valset) error { +func (l *ethSignerLoop) signValset(ctx context.Context, vs *peggytypes.Valset) error { signFn := func() error { return l.inj.SendValsetConfirm(ctx, l.ethFrom, l.peggyID, vs) } From 262466f444d6454dc99a293a6cf9c7d5433f9176 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 24 Jan 2024 21:22:28 +0100 Subject: [PATCH 39/99] introduce common ctor for cosmos network --- cmd/peggo/orchestrator.go | 53 ++++++++++++++++++++-------------- orchestrator/cosmos/network.go | 37 ++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 2a30ce10..c31456d0 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -4,6 +4,7 @@ import ( "context" "os" + chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" gethcommon "github.com/ethereum/go-ethereum/common" cli "github.com/jawher/mow.cli" "github.com/xlab/closer" @@ -14,7 +15,6 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/version" - chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" ) // startOrchestrator action runs an infinite loop, @@ -71,28 +71,39 @@ func orchestratorCmd(cmd *cli.Cmd) { log.WithError(err).Fatalln("failed to initialize Ethereum account") } - var cosmosNetwork cosmos.Network - if customEndpointRPCs := *cfg.cosmosGRPC != "" && *cfg.tendermintRPC != ""; customEndpointRPCs { - cosmosNetwork, err = cosmos.NewCustomRPCNetwork( - *cfg.cosmosChainID, - valAddress.String(), - *cfg.cosmosGRPC, - *cfg.cosmosGasPrices, - *cfg.tendermintRPC, - cosmosKeyring, - personalSignFn, - ) - } else { - // load balanced connection - cosmosNetwork, err = cosmos.NewLoadBalancedNetwork( - *cfg.cosmosChainID, - valAddress.String(), - *cfg.cosmosGasPrices, - cosmosKeyring, - personalSignFn, - ) + cosmosCfg := cosmos.NetworkConfig{ + ChainID: *cfg.cosmosChainID, + ValidatorAddress: valAddress.String(), + CosmosGRPC: *cfg.cosmosGRPC, + TendermintRPC: *cfg.tendermintRPC, + GasPrice: *cfg.cosmosGasPrices, } + cosmosNetwork, err := cosmos.NewCosmosNetwork(cosmosKeyring, personalSignFn, cosmosCfg) + orShutdown(err) + + //var cosmosNetwork cosmos.Network + //if customEndpointRPCs := *cfg.cosmosGRPC != "" && *cfg.tendermintRPC != ""; customEndpointRPCs { + // cosmosNetwork, err = cosmos.NewCustomRPCNetwork( + // *cfg.cosmosChainID, + // valAddress.String(), + // *cfg.cosmosGRPC, + // *cfg.cosmosGasPrices, + // *cfg.tendermintRPC, + // cosmosKeyring, + // personalSignFn, + // ) + //} else { + // // load balanced connection + // cosmosNetwork, err = cosmos.NewLoadBalancedNetwork( + // *cfg.cosmosChainID, + // valAddress.String(), + // *cfg.cosmosGasPrices, + // cosmosKeyring, + // personalSignFn, + // ) + //} + orShutdown(err) ctx, cancelFn := context.WithCancel(context.Background()) diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 9fc8f02a..1fe5aebf 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -4,7 +4,10 @@ import ( "context" "time" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" ) type Network interface { @@ -13,3 +16,37 @@ type Network interface { peggy.QueryClient peggy.BroadcastClient } + +type NetworkConfig struct { + ChainID, + ValidatorAddress, + CosmosGRPC, + TendermintRPC, + GasPrice string +} + +func NewCosmosNetwork( + k keyring.Keyring, + ethSignFn keystore.PersonalSignFn, + cfg NetworkConfig, +) (Network, error) { + if isCustom := cfg.CosmosGRPC != "" && cfg.TendermintRPC != ""; isCustom { + return NewCustomRPCNetwork( + cfg.ChainID, + cfg.ValidatorAddress, + cfg.CosmosGRPC, + cfg.GasPrice, + cfg.TendermintRPC, + k, + ethSignFn, + ) + } + + return NewLoadBalancedNetwork( + cfg.ChainID, + cfg.ValidatorAddress, + cfg.GasPrice, + k, + ethSignFn, + ) +} From 48fda1ae277d122b4117dba0f3a59f15eb6abddd Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 00:10:41 +0100 Subject: [PATCH 40/99] simplify cosmos pkg api --- cmd/peggo/orchestrator.go | 24 ---------- cmd/peggo/tx.go | 43 +++++------------- orchestrator/cosmos/custom_rpc.go | 67 +++++++++++++-------------- orchestrator/cosmos/load_balanced.go | 68 +++++++++++++++------------- orchestrator/cosmos/network.go | 24 ++-------- 5 files changed, 83 insertions(+), 143 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index c31456d0..8641b0fd 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -82,30 +82,6 @@ func orchestratorCmd(cmd *cli.Cmd) { cosmosNetwork, err := cosmos.NewCosmosNetwork(cosmosKeyring, personalSignFn, cosmosCfg) orShutdown(err) - //var cosmosNetwork cosmos.Network - //if customEndpointRPCs := *cfg.cosmosGRPC != "" && *cfg.tendermintRPC != ""; customEndpointRPCs { - // cosmosNetwork, err = cosmos.NewCustomRPCNetwork( - // *cfg.cosmosChainID, - // valAddress.String(), - // *cfg.cosmosGRPC, - // *cfg.cosmosGasPrices, - // *cfg.tendermintRPC, - // cosmosKeyring, - // personalSignFn, - // ) - //} else { - // // load balanced connection - // cosmosNetwork, err = cosmos.NewLoadBalancedNetwork( - // *cfg.cosmosChainID, - // valAddress.String(), - // *cfg.cosmosGasPrices, - // cosmosKeyring, - // personalSignFn, - // ) - //} - - orShutdown(err) - ctx, cancelFn := context.WithCancel(context.Background()) closer.Bind(cancelFn) diff --git a/cmd/peggo/tx.go b/cmd/peggo/tx.go index eb180109..3cad7c55 100644 --- a/cmd/peggo/tx.go +++ b/cmd/peggo/tx.go @@ -127,43 +127,22 @@ func registerEthKeyCmd(cmd *cli.Cmd) { return } - var peggyBroadcastClient peggy.BroadcastClient - if customCosmosRPC := *cosmosGRPC != "" && *tendermintRPC != ""; customCosmosRPC { - net, err := cosmos.NewCustomRPCNetwork( - *cosmosChainID, - valAddress.String(), - *cosmosGRPC, - *cosmosGasPrices, - *tendermintRPC, - cosmosKeyring, - personalSignFn, - ) - - if err != nil { - log.Fatalln("failed to connect to Injective network") - } - - peggyBroadcastClient = peggy.BroadcastClient(net) - } else { - net, err := cosmos.NewLoadBalancedNetwork( - *cosmosChainID, - valAddress.String(), - *cosmosGasPrices, - cosmosKeyring, - personalSignFn, - ) - - if err != nil { - log.Fatalln("failed to connect to Injective network") - } - - peggyBroadcastClient = peggy.BroadcastClient(net) + net, err := cosmos.NewCosmosNetwork(cosmosKeyring, personalSignFn, cosmos.NetworkConfig{ + ChainID: *cosmosChainID, + ValidatorAddress: valAddress.String(), + CosmosGRPC: *cosmosGRPC, + TendermintRPC: *cosmosGasPrices, + GasPrice: *tendermintRPC, + }) + + if err != nil { + log.Fatalln("failed to connect to Injective network") } broadcastCtx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second) defer cancelFn() - if err = peggyBroadcastClient.UpdatePeggyOrchestratorAddresses(broadcastCtx, ethKeyFromAddress, valAddress); err != nil { + if err = peggy.BroadcastClient(net).UpdatePeggyOrchestratorAddresses(broadcastCtx, ethKeyFromAddress, valAddress); err != nil { log.WithError(err).Errorln("failed to broadcast Tx") time.Sleep(time.Second) return diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index 175417db..e573fcdf 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -17,53 +17,36 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" ) -type CustomRPCNetwork struct { +type customNetwork struct { peggy.QueryClient peggy.BroadcastClient tendermint.Client } -func loadCustomNetworkConfig(chainID, feeDenom, cosmosGRPC, tendermintRPC string) clientcommon.Network { - cfg := clientcommon.LoadNetwork("devnet", "") - cfg.Name = "custom" - cfg.ChainId = chainID - cfg.Fee_denom = feeDenom - cfg.TmEndpoint = tendermintRPC - cfg.ChainGrpcEndpoint = cosmosGRPC - cfg.ExplorerGrpcEndpoint = "" - cfg.LcdEndpoint = "" - cfg.ExplorerGrpcEndpoint = "" - - return cfg -} - // NewCustomRPCNetwork creates a single endpoint connection to the Injective network -func NewCustomRPCNetwork( - chainID, - validatorAddress, - injectiveGRPC, - injectiveGasPrices, - tendermintRPC string, +func newCustomNetwork( + cfg NetworkConfig, keyring keyring.Keyring, personalSignerFn keystore.PersonalSignFn, ) (Network, error) { - clientCtx, err := chain.NewClientContext(chainID, validatorAddress, keyring) + netCfg := loadCustomNetworkConfig(cfg.ChainID, "inj", cfg.CosmosGRPC, cfg.TendermintRPC) + + clientCtx, err := chain.NewClientContext(cfg.ChainID, cfg.ValidatorAddress, keyring) if err != nil { return nil, errors.Wrapf(err, "failed to create client context for Injective chain") } - tmRPC, err := comethttp.New(tendermintRPC, "/websocket") + tmRPC, err := comethttp.New(cfg.TendermintRPC, "/websocket") if err != nil { - return nil, errors.Wrapf(err, "failed to connect to Tendermint RPC %s", tendermintRPC) + return nil, errors.Wrapf(err, "failed to connect to Tendermint RPC %s", netCfg.TmEndpoint) } - clientCtx = clientCtx.WithNodeURI(tendermintRPC) + clientCtx = clientCtx.WithNodeURI(netCfg.TmEndpoint) clientCtx = clientCtx.WithClient(tmRPC) - netCfg := loadCustomNetworkConfig(chainID, "inj", injectiveGRPC, tendermintRPC) - daemonClient, err := chain.NewChainClient(clientCtx, netCfg, clientcommon.OptionGasPrices(injectiveGasPrices)) + daemonClient, err := chain.NewChainClient(clientCtx, netCfg, clientcommon.OptionGasPrices(cfg.GasPrice)) if err != nil { - return nil, errors.Wrapf(err, "failed to connect to Injective GRPC %s", injectiveGRPC) + return nil, errors.Wrapf(err, "failed to connect to Injective GRPC %s", cfg.CosmosGRPC) } time.Sleep(1 * time.Second) @@ -75,23 +58,23 @@ func NewCustomRPCNetwork( waitForService(daemonWaitCtx, grpcConn) peggyQuerier := peggytypes.NewQueryClient(grpcConn) - n := &CustomRPCNetwork{ - Client: tendermint.NewRPCClient(tendermintRPC), + n := &customNetwork{ QueryClient: peggy.NewQueryClient(peggyQuerier), BroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), + Client: tendermint.NewRPCClient(netCfg.TmEndpoint), } log.WithFields(log.Fields{ - "chain_id": chainID, - "addr": validatorAddress, - "injective": injectiveGRPC, - "tendermint": tendermintRPC, + "chain_id": cfg.ChainID, + "addr": cfg.ValidatorAddress, + "injective": cfg.CosmosGRPC, + "tendermint": netCfg.TmEndpoint, }).Infoln("connected to custom Injective endpoints") return n, nil } -func (n *CustomRPCNetwork) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { +func (n *customNetwork) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { block, err := n.Client.GetBlock(ctx, height) if err != nil { return time.Time{}, err @@ -99,3 +82,17 @@ func (n *CustomRPCNetwork) GetBlockTime(ctx context.Context, height int64) (time return block.Block.Time, nil } + +func loadCustomNetworkConfig(chainID, feeDenom, cosmosGRPC, tendermintRPC string) clientcommon.Network { + cfg := clientcommon.LoadNetwork("devnet", "") + cfg.Name = "custom" + cfg.ChainId = chainID + cfg.Fee_denom = feeDenom + cfg.TmEndpoint = tendermintRPC + cfg.ChainGrpcEndpoint = cosmosGRPC + cfg.ExplorerGrpcEndpoint = "" + cfg.LcdEndpoint = "" + cfg.ExplorerGrpcEndpoint = "" + + return cfg +} diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go index b8eb1f9e..97a45db8 100644 --- a/orchestrator/cosmos/load_balanced.go +++ b/orchestrator/cosmos/load_balanced.go @@ -2,7 +2,7 @@ package cosmos import ( "context" - "strconv" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" "time" rpchttp "github.com/cometbft/cometbft/rpc/client/http" @@ -12,19 +12,17 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/connectivity" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/InjectiveLabs/sdk-go/client/chain" clientcommon "github.com/InjectiveLabs/sdk-go/client/common" - explorer "github.com/InjectiveLabs/sdk-go/client/explorer" - - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" ) -type LoadBalancedNetwork struct { +type providerNetwork struct { peggy.QueryClient peggy.BroadcastClient - explorer.ExplorerClient + tendermint.Client } // NewLoadBalancedNetwork creates a load balanced connection to the Injective network. @@ -32,15 +30,13 @@ type LoadBalancedNetwork struct { // - injective-1 (mainnet) // - injective-777 (devnet) // - injective-888 (testnet) -func NewLoadBalancedNetwork( - chainID, - validatorAddress, - injectiveGasPrices string, +func newProviderNetwork( + cfg NetworkConfig, keyring keyring.Keyring, personalSignerFn keystore.PersonalSignFn, ) (Network, error) { var networkName string - switch chainID { + switch cfg.ChainID { case "injective-1": networkName = "mainnet" case "injective-777": @@ -48,11 +44,11 @@ func NewLoadBalancedNetwork( case "injective-888": networkName = "testnet" default: - return nil, errors.Errorf("provided chain id %v does not belong to any known Injective network", chainID) + return nil, errors.Errorf("provided chain id %v does not belong to any known Injective network", cfg.ChainID) } netCfg := clientcommon.LoadNetwork(networkName, "lb") - clientCtx, err := chain.NewClientContext(chainID, validatorAddress, keyring) + clientCtx, err := chain.NewClientContext(cfg.ChainID, cfg.ValidatorAddress, keyring) if err != nil { return nil, errors.Wrapf(err, "failed to create client context for Injective chain") } @@ -64,7 +60,7 @@ func NewLoadBalancedNetwork( clientCtx = clientCtx.WithNodeURI(netCfg.TmEndpoint).WithClient(tmClient) - daemonClient, err := chain.NewChainClient(clientCtx, netCfg, clientcommon.OptionGasPrices(injectiveGasPrices)) + daemonClient, err := chain.NewChainClient(clientCtx, netCfg, clientcommon.OptionGasPrices(cfg.GasPrice)) if err != nil { return nil, errors.Wrapf(err, "failed to intialize chain client (%s)", networkName) } @@ -78,20 +74,20 @@ func NewLoadBalancedNetwork( waitForService(daemonWaitCtx, grpcConn) peggyQuerier := peggytypes.NewQueryClient(grpcConn) - explorerCLient, err := explorer.NewExplorerClient(netCfg) - if err != nil { - return nil, errors.Wrap(err, "failed to initialize explorer client") - } + //explorerCLient, err := explorer.NewExplorerClient(netCfg) + //if err != nil { + // return nil, errors.Wrap(err, "failed to initialize explorer client") + //} - n := &LoadBalancedNetwork{ + n := &providerNetwork{ + Client: tendermint.NewRPCClient(netCfg.TmEndpoint), QueryClient: peggy.NewQueryClient(peggyQuerier), BroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), - ExplorerClient: explorerCLient, } log.WithFields(log.Fields{ - "addr": validatorAddress, - "chain_id": chainID, + "addr": cfg.ValidatorAddress, + "chain_id": cfg.ChainID, "injective": netCfg.ChainGrpcEndpoint, "tendermint": netCfg.TmEndpoint, }).Infoln("connected to Injective's load balanced endpoints") @@ -99,18 +95,28 @@ func NewLoadBalancedNetwork( return n, nil } -func (n *LoadBalancedNetwork) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { - block, err := n.ExplorerClient.GetBlock(ctx, strconv.FormatInt(height, 10)) +// +//func (n *providerNetwork) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { +// block, err := n.ExplorerClient.GetBlock(ctx, strconv.FormatInt(height, 10)) +// if err != nil { +// return time.Time{}, err +// } +// +// blockTime, err := time.Parse("2006-01-02 15:04:05.999 -0700 MST", block.Data.Timestamp) +// if err != nil { +// return time.Time{}, errors.Wrap(err, "failed to parse timestamp from block") +// } +// +// return blockTime, nil +//} + +func (n *providerNetwork) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { + block, err := n.Client.GetBlock(ctx, height) if err != nil { return time.Time{}, err } - blockTime, err := time.Parse("2006-01-02 15:04:05.999 -0700 MST", block.Data.Timestamp) - if err != nil { - return time.Time{}, errors.Wrap(err, "failed to parse timestamp from block") - } - - return blockTime, nil + return block.Block.Time, nil } // waitForService awaits an active ClientConn to a GRPC service. diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 1fe5aebf..817badec 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -25,28 +25,10 @@ type NetworkConfig struct { GasPrice string } -func NewCosmosNetwork( - k keyring.Keyring, - ethSignFn keystore.PersonalSignFn, - cfg NetworkConfig, -) (Network, error) { +func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg NetworkConfig) (Network, error) { if isCustom := cfg.CosmosGRPC != "" && cfg.TendermintRPC != ""; isCustom { - return NewCustomRPCNetwork( - cfg.ChainID, - cfg.ValidatorAddress, - cfg.CosmosGRPC, - cfg.GasPrice, - cfg.TendermintRPC, - k, - ethSignFn, - ) + return newCustomNetwork(cfg, k, ethSignFn) } - return NewLoadBalancedNetwork( - cfg.ChainID, - cfg.ValidatorAddress, - cfg.GasPrice, - k, - ethSignFn, - ) + return newProviderNetwork(cfg, k, ethSignFn) } From 91a587873c7b50f8f233eafe97b85df138ed676a Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 00:39:35 +0100 Subject: [PATCH 41/99] refactor --- orchestrator/cosmos/custom_rpc.go | 4 +- orchestrator/cosmos/network.go | 109 +++++++++++++++++++++++++++--- 2 files changed, 102 insertions(+), 11 deletions(-) diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go index e573fcdf..367cacdf 100644 --- a/orchestrator/cosmos/custom_rpc.go +++ b/orchestrator/cosmos/custom_rpc.go @@ -31,12 +31,12 @@ func newCustomNetwork( ) (Network, error) { netCfg := loadCustomNetworkConfig(cfg.ChainID, "inj", cfg.CosmosGRPC, cfg.TendermintRPC) - clientCtx, err := chain.NewClientContext(cfg.ChainID, cfg.ValidatorAddress, keyring) + clientCtx, err := chain.NewClientContext(netCfg.ChainId, cfg.ValidatorAddress, keyring) if err != nil { return nil, errors.Wrapf(err, "failed to create client context for Injective chain") } - tmRPC, err := comethttp.New(cfg.TendermintRPC, "/websocket") + tmRPC, err := comethttp.New(netCfg.TmEndpoint, "/websocket") if err != nil { return nil, errors.Wrapf(err, "failed to connect to Tendermint RPC %s", netCfg.TmEndpoint) } diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 817badec..d841e968 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -2,14 +2,60 @@ package cosmos import ( "context" + "fmt" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/sdk-go/client/chain" + comethttp "github.com/cometbft/cometbft/rpc/client/http" + "github.com/pkg/errors" + log "github.com/xlab/suplog" "time" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" + clientcommon "github.com/InjectiveLabs/sdk-go/client/common" ) +type NetworkConfig struct { + ChainID, + ValidatorAddress, + CosmosGRPC, + TendermintRPC, + GasPrice string +} + +func (cfg NetworkConfig) loadClientConfig() clientcommon.Network { + if custom := cfg.CosmosGRPC != "" && cfg.TendermintRPC != ""; custom { + c := clientcommon.LoadNetwork("devnet", "") + c.Name = "custom" + c.ChainId = cfg.ChainID + c.Fee_denom = "inj" + c.TmEndpoint = cfg.TendermintRPC + c.ChainGrpcEndpoint = cfg.CosmosGRPC + c.ExplorerGrpcEndpoint = "" + c.LcdEndpoint = "" + c.ExplorerGrpcEndpoint = "" + + return c + } + + var networkName string + switch cfg.ChainID { + case "injective-1": + networkName = "mainnet" + case "injective-777": + networkName = "devnet" + case "injective-888": + networkName = "testnet" + default: + panic(fmt.Errorf("no provider for chain id %s", cfg.ChainID)) + } + + return clientcommon.LoadNetwork(networkName, "lb") +} + type Network interface { GetBlockTime(ctx context.Context, height int64) (time.Time, error) @@ -17,18 +63,63 @@ type Network interface { peggy.BroadcastClient } -type NetworkConfig struct { - ChainID, - ValidatorAddress, - CosmosGRPC, - TendermintRPC, - GasPrice string +type network struct { + peggy.QueryClient + peggy.BroadcastClient + tendermint.Client } func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg NetworkConfig) (Network, error) { - if isCustom := cfg.CosmosGRPC != "" && cfg.TendermintRPC != ""; isCustom { - return newCustomNetwork(cfg, k, ethSignFn) + clientCfg := cfg.loadClientConfig() + + clientCtx, err := chain.NewClientContext(clientCfg.ChainId, cfg.ValidatorAddress, k) + if err != nil { + return nil, errors.Wrapf(err, "failed to create client context for Injective chain") + } + + tmRPC, err := comethttp.New(clientCfg.TmEndpoint, "/websocket") + if err != nil { + return nil, errors.Wrapf(err, "failed to connect to Tendermint RPC %s", clientCfg.TmEndpoint) + } + + clientCtx = clientCtx.WithNodeURI(clientCfg.TmEndpoint) + clientCtx = clientCtx.WithClient(tmRPC) + + chainClient, err := chain.NewChainClient(clientCtx, clientCfg, clientcommon.OptionGasPrices(cfg.GasPrice)) + if err != nil { + return nil, errors.Wrapf(err, "failed to connect to Injective GRPC %s", cfg.CosmosGRPC) + } + + time.Sleep(1 * time.Second) + + daemonWaitCtx, cancelWait := context.WithTimeout(context.Background(), time.Minute) + defer cancelWait() + + grpcConn := chainClient.QueryClient() + waitForService(daemonWaitCtx, grpcConn) + peggyQuerier := peggytypes.NewQueryClient(grpcConn) + + n := network{ + QueryClient: peggy.NewQueryClient(peggyQuerier), + BroadcastClient: peggy.NewBroadcastClient(chainClient, ethSignFn), + Client: tendermint.NewRPCClient(clientCfg.TmEndpoint), + } + + log.WithFields(log.Fields{ + "chain_id": cfg.ChainID, + "addr": cfg.ValidatorAddress, + "injective": cfg.CosmosGRPC, + "tendermint": clientCfg.TmEndpoint, + }).Infoln("connected to custom Injective endpoints") + + return n, nil +} + +func (n network) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { + block, err := n.Client.GetBlock(ctx, height) + if err != nil { + return time.Time{}, err } - return newProviderNetwork(cfg, k, ethSignFn) + return block.Block.Time, nil } From 8fcdf786c510a89b575765cab940812ad4fdbe8a Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 00:57:57 +0100 Subject: [PATCH 42/99] refactor 2 --- orchestrator/cosmos/config.go | 58 +++++++++++ orchestrator/cosmos/custom_rpc.go | 98 ------------------- orchestrator/cosmos/load_balanced.go | 140 --------------------------- orchestrator/cosmos/network.go | 110 ++++++++++----------- 4 files changed, 113 insertions(+), 293 deletions(-) create mode 100644 orchestrator/cosmos/config.go delete mode 100644 orchestrator/cosmos/custom_rpc.go delete mode 100644 orchestrator/cosmos/load_balanced.go diff --git a/orchestrator/cosmos/config.go b/orchestrator/cosmos/config.go new file mode 100644 index 00000000..a9aaf93b --- /dev/null +++ b/orchestrator/cosmos/config.go @@ -0,0 +1,58 @@ +package cosmos + +import ( + "fmt" + + clientcommon "github.com/InjectiveLabs/sdk-go/client/common" + log "github.com/xlab/suplog" +) + +type NetworkConfig struct { + ChainID, + ValidatorAddress, + CosmosGRPC, + TendermintRPC, + GasPrice string +} + +func (cfg NetworkConfig) loadClientConfig() clientcommon.Network { + if custom := cfg.CosmosGRPC != "" && cfg.TendermintRPC != ""; custom { + return customEndpoints(cfg) + } + + return loadBalancedEndpoints(cfg) +} + +func customEndpoints(cfg NetworkConfig) clientcommon.Network { + c := clientcommon.LoadNetwork("devnet", "") + c.Name = "custom" + c.ChainId = cfg.ChainID + c.Fee_denom = "inj" + c.TmEndpoint = cfg.TendermintRPC + c.ChainGrpcEndpoint = cfg.CosmosGRPC + c.ExplorerGrpcEndpoint = "" + c.LcdEndpoint = "" + c.ExplorerGrpcEndpoint = "" + + log.Infoln("using custom endpoints for Injective") + + return c +} + +func loadBalancedEndpoints(cfg NetworkConfig) clientcommon.Network { + var networkName string + switch cfg.ChainID { + case "injective-1": + networkName = "mainnet" + case "injective-777": + networkName = "devnet" + case "injective-888": + networkName = "testnet" + default: + panic(fmt.Errorf("no provider for chain id %s", cfg.ChainID)) + } + + log.Infoln("using load balanced endpoints for Injective") + + return clientcommon.LoadNetwork(networkName, "lb") +} diff --git a/orchestrator/cosmos/custom_rpc.go b/orchestrator/cosmos/custom_rpc.go deleted file mode 100644 index 367cacdf..00000000 --- a/orchestrator/cosmos/custom_rpc.go +++ /dev/null @@ -1,98 +0,0 @@ -package cosmos - -import ( - "context" - "time" - - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - "github.com/InjectiveLabs/sdk-go/client/chain" - clientcommon "github.com/InjectiveLabs/sdk-go/client/common" - comethttp "github.com/cometbft/cometbft/rpc/client/http" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/pkg/errors" - log "github.com/xlab/suplog" - - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" -) - -type customNetwork struct { - peggy.QueryClient - peggy.BroadcastClient - tendermint.Client -} - -// NewCustomRPCNetwork creates a single endpoint connection to the Injective network -func newCustomNetwork( - cfg NetworkConfig, - keyring keyring.Keyring, - personalSignerFn keystore.PersonalSignFn, -) (Network, error) { - netCfg := loadCustomNetworkConfig(cfg.ChainID, "inj", cfg.CosmosGRPC, cfg.TendermintRPC) - - clientCtx, err := chain.NewClientContext(netCfg.ChainId, cfg.ValidatorAddress, keyring) - if err != nil { - return nil, errors.Wrapf(err, "failed to create client context for Injective chain") - } - - tmRPC, err := comethttp.New(netCfg.TmEndpoint, "/websocket") - if err != nil { - return nil, errors.Wrapf(err, "failed to connect to Tendermint RPC %s", netCfg.TmEndpoint) - } - - clientCtx = clientCtx.WithNodeURI(netCfg.TmEndpoint) - clientCtx = clientCtx.WithClient(tmRPC) - - daemonClient, err := chain.NewChainClient(clientCtx, netCfg, clientcommon.OptionGasPrices(cfg.GasPrice)) - if err != nil { - return nil, errors.Wrapf(err, "failed to connect to Injective GRPC %s", cfg.CosmosGRPC) - } - - time.Sleep(1 * time.Second) - - daemonWaitCtx, cancelWait := context.WithTimeout(context.Background(), time.Minute) - defer cancelWait() - - grpcConn := daemonClient.QueryClient() - waitForService(daemonWaitCtx, grpcConn) - peggyQuerier := peggytypes.NewQueryClient(grpcConn) - - n := &customNetwork{ - QueryClient: peggy.NewQueryClient(peggyQuerier), - BroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), - Client: tendermint.NewRPCClient(netCfg.TmEndpoint), - } - - log.WithFields(log.Fields{ - "chain_id": cfg.ChainID, - "addr": cfg.ValidatorAddress, - "injective": cfg.CosmosGRPC, - "tendermint": netCfg.TmEndpoint, - }).Infoln("connected to custom Injective endpoints") - - return n, nil -} - -func (n *customNetwork) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { - block, err := n.Client.GetBlock(ctx, height) - if err != nil { - return time.Time{}, err - } - - return block.Block.Time, nil -} - -func loadCustomNetworkConfig(chainID, feeDenom, cosmosGRPC, tendermintRPC string) clientcommon.Network { - cfg := clientcommon.LoadNetwork("devnet", "") - cfg.Name = "custom" - cfg.ChainId = chainID - cfg.Fee_denom = feeDenom - cfg.TmEndpoint = tendermintRPC - cfg.ChainGrpcEndpoint = cosmosGRPC - cfg.ExplorerGrpcEndpoint = "" - cfg.LcdEndpoint = "" - cfg.ExplorerGrpcEndpoint = "" - - return cfg -} diff --git a/orchestrator/cosmos/load_balanced.go b/orchestrator/cosmos/load_balanced.go deleted file mode 100644 index 97a45db8..00000000 --- a/orchestrator/cosmos/load_balanced.go +++ /dev/null @@ -1,140 +0,0 @@ -package cosmos - -import ( - "context" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" - "time" - - rpchttp "github.com/cometbft/cometbft/rpc/client/http" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/pkg/errors" - log "github.com/xlab/suplog" - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" - - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - "github.com/InjectiveLabs/sdk-go/client/chain" - clientcommon "github.com/InjectiveLabs/sdk-go/client/common" -) - -type providerNetwork struct { - peggy.QueryClient - peggy.BroadcastClient - tendermint.Client -} - -// NewLoadBalancedNetwork creates a load balanced connection to the Injective network. -// The chainID argument decides which network Peggo will be connecting to: -// - injective-1 (mainnet) -// - injective-777 (devnet) -// - injective-888 (testnet) -func newProviderNetwork( - cfg NetworkConfig, - keyring keyring.Keyring, - personalSignerFn keystore.PersonalSignFn, -) (Network, error) { - var networkName string - switch cfg.ChainID { - case "injective-1": - networkName = "mainnet" - case "injective-777": - networkName = "devnet" - case "injective-888": - networkName = "testnet" - default: - return nil, errors.Errorf("provided chain id %v does not belong to any known Injective network", cfg.ChainID) - } - netCfg := clientcommon.LoadNetwork(networkName, "lb") - - clientCtx, err := chain.NewClientContext(cfg.ChainID, cfg.ValidatorAddress, keyring) - if err != nil { - return nil, errors.Wrapf(err, "failed to create client context for Injective chain") - } - - tmClient, err := rpchttp.New(netCfg.TmEndpoint, "/websocket") - if err != nil { - return nil, errors.Wrap(err, "failed to initialize tendermint client") - } - - clientCtx = clientCtx.WithNodeURI(netCfg.TmEndpoint).WithClient(tmClient) - - daemonClient, err := chain.NewChainClient(clientCtx, netCfg, clientcommon.OptionGasPrices(cfg.GasPrice)) - if err != nil { - return nil, errors.Wrapf(err, "failed to intialize chain client (%s)", networkName) - } - - time.Sleep(1 * time.Second) - - daemonWaitCtx, cancelWait := context.WithTimeout(context.Background(), time.Minute) - defer cancelWait() - - grpcConn := daemonClient.QueryClient() - waitForService(daemonWaitCtx, grpcConn) - peggyQuerier := peggytypes.NewQueryClient(grpcConn) - - //explorerCLient, err := explorer.NewExplorerClient(netCfg) - //if err != nil { - // return nil, errors.Wrap(err, "failed to initialize explorer client") - //} - - n := &providerNetwork{ - Client: tendermint.NewRPCClient(netCfg.TmEndpoint), - QueryClient: peggy.NewQueryClient(peggyQuerier), - BroadcastClient: peggy.NewBroadcastClient(daemonClient, personalSignerFn), - } - - log.WithFields(log.Fields{ - "addr": cfg.ValidatorAddress, - "chain_id": cfg.ChainID, - "injective": netCfg.ChainGrpcEndpoint, - "tendermint": netCfg.TmEndpoint, - }).Infoln("connected to Injective's load balanced endpoints") - - return n, nil -} - -// -//func (n *providerNetwork) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { -// block, err := n.ExplorerClient.GetBlock(ctx, strconv.FormatInt(height, 10)) -// if err != nil { -// return time.Time{}, err -// } -// -// blockTime, err := time.Parse("2006-01-02 15:04:05.999 -0700 MST", block.Data.Timestamp) -// if err != nil { -// return time.Time{}, errors.Wrap(err, "failed to parse timestamp from block") -// } -// -// return blockTime, nil -//} - -func (n *providerNetwork) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { - block, err := n.Client.GetBlock(ctx, height) - if err != nil { - return time.Time{}, err - } - - return block.Block.Time, nil -} - -// waitForService awaits an active ClientConn to a GRPC service. -func waitForService(ctx context.Context, clientConn *grpc.ClientConn) { - for { - select { - case <-ctx.Done(): - log.Fatalln("GRPC service wait timed out") - default: - state := clientConn.GetState() - - if state != connectivity.Ready { - log.WithField("state", state.String()).Warningln("state of GRPC connection not ready") - time.Sleep(5 * time.Second) - continue - } - - return - } - } -} diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index d841e968..1ad291a4 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -2,60 +2,23 @@ package cosmos import ( "context" - "fmt" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" + "time" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/InjectiveLabs/sdk-go/client/chain" + clientcommon "github.com/InjectiveLabs/sdk-go/client/common" comethttp "github.com/cometbft/cometbft/rpc/client/http" + "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/pkg/errors" log "github.com/xlab/suplog" - "time" - - "github.com/cosmos/cosmos-sdk/crypto/keyring" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" - clientcommon "github.com/InjectiveLabs/sdk-go/client/common" ) -type NetworkConfig struct { - ChainID, - ValidatorAddress, - CosmosGRPC, - TendermintRPC, - GasPrice string -} - -func (cfg NetworkConfig) loadClientConfig() clientcommon.Network { - if custom := cfg.CosmosGRPC != "" && cfg.TendermintRPC != ""; custom { - c := clientcommon.LoadNetwork("devnet", "") - c.Name = "custom" - c.ChainId = cfg.ChainID - c.Fee_denom = "inj" - c.TmEndpoint = cfg.TendermintRPC - c.ChainGrpcEndpoint = cfg.CosmosGRPC - c.ExplorerGrpcEndpoint = "" - c.LcdEndpoint = "" - c.ExplorerGrpcEndpoint = "" - - return c - } - - var networkName string - switch cfg.ChainID { - case "injective-1": - networkName = "mainnet" - case "injective-777": - networkName = "devnet" - case "injective-888": - networkName = "testnet" - default: - panic(fmt.Errorf("no provider for chain id %s", cfg.ChainID)) - } - - return clientcommon.LoadNetwork(networkName, "lb") -} - type Network interface { GetBlockTime(ctx context.Context, height int64) (time.Time, error) @@ -82,8 +45,7 @@ func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg return nil, errors.Wrapf(err, "failed to connect to Tendermint RPC %s", clientCfg.TmEndpoint) } - clientCtx = clientCtx.WithNodeURI(clientCfg.TmEndpoint) - clientCtx = clientCtx.WithClient(tmRPC) + clientCtx = clientCtx.WithNodeURI(clientCfg.TmEndpoint).WithClient(tmRPC) chainClient, err := chain.NewChainClient(clientCtx, clientCfg, clientcommon.OptionGasPrices(cfg.GasPrice)) if err != nil { @@ -92,15 +54,10 @@ func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg time.Sleep(1 * time.Second) - daemonWaitCtx, cancelWait := context.WithTimeout(context.Background(), time.Minute) - defer cancelWait() - - grpcConn := chainClient.QueryClient() - waitForService(daemonWaitCtx, grpcConn) - peggyQuerier := peggytypes.NewQueryClient(grpcConn) + conn := awaitConnection(1*time.Minute, chainClient) n := network{ - QueryClient: peggy.NewQueryClient(peggyQuerier), + QueryClient: peggy.NewQueryClient(peggytypes.NewQueryClient(conn)), BroadcastClient: peggy.NewBroadcastClient(chainClient, ethSignFn), Client: tendermint.NewRPCClient(clientCfg.TmEndpoint), } @@ -108,9 +65,9 @@ func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg log.WithFields(log.Fields{ "chain_id": cfg.ChainID, "addr": cfg.ValidatorAddress, - "injective": cfg.CosmosGRPC, + "injective": clientCfg.ChainGrpcEndpoint, "tendermint": clientCfg.TmEndpoint, - }).Infoln("connected to custom Injective endpoints") + }).Infoln("connected to Injective network") return n, nil } @@ -123,3 +80,46 @@ func (n network) GetBlockTime(ctx context.Context, height int64) (time.Time, err return block.Block.Time, nil } + +// waitForService awaits an active ClientConn to a GRPC service. +func waitForService(ctx context.Context, clientConn *grpc.ClientConn) { + for { + select { + case <-ctx.Done(): + log.Fatalln("GRPC service wait timed out") + default: + state := clientConn.GetState() + + if state != connectivity.Ready { + log.WithField("state", state.String()).Warningln("state of GRPC connection not ready") + time.Sleep(5 * time.Second) + continue + } + + return + } + } +} + +func awaitConnection(timeout time.Duration, client chain.ChainClient) *grpc.ClientConn { + ctx, cancelWait := context.WithTimeout(context.Background(), timeout) + defer cancelWait() + + grpcConn := client.QueryClient() + + for { + select { + case <-ctx.Done(): + log.Fatalln("GRPC service wait timed out") + default: + state := grpcConn.GetState() + if state != connectivity.Ready { + log.WithField("state", state.String()).Warningln("state of GRPC connection not ready") + time.Sleep(5 * time.Second) + continue + } + + return grpcConn + } + } +} From 2e74f9f36b9964f290c547354c7d9748611bcbb5 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 01:33:40 +0100 Subject: [PATCH 43/99] cleanup --- orchestrator/cosmos/network.go | 39 ++++---------- orchestrator/relayer.go | 95 ++++++++++++++++++++-------------- 2 files changed, 65 insertions(+), 69 deletions(-) diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 1ad291a4..96ccf2ed 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -4,12 +4,8 @@ import ( "context" "time" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - "github.com/InjectiveLabs/sdk-go/client/chain" - clientcommon "github.com/InjectiveLabs/sdk-go/client/common" comethttp "github.com/cometbft/cometbft/rpc/client/http" "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/pkg/errors" log "github.com/xlab/suplog" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" @@ -17,6 +13,9 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/sdk-go/client/chain" + clientcommon "github.com/InjectiveLabs/sdk-go/client/common" ) type Network interface { @@ -37,19 +36,21 @@ func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg clientCtx, err := chain.NewClientContext(clientCfg.ChainId, cfg.ValidatorAddress, k) if err != nil { - return nil, errors.Wrapf(err, "failed to create client context for Injective chain") + return nil, err } + clientCtx.WithNodeURI(clientCfg.TmEndpoint) + tmRPC, err := comethttp.New(clientCfg.TmEndpoint, "/websocket") if err != nil { - return nil, errors.Wrapf(err, "failed to connect to Tendermint RPC %s", clientCfg.TmEndpoint) + return nil, err } - clientCtx = clientCtx.WithNodeURI(clientCfg.TmEndpoint).WithClient(tmRPC) + clientCtx = clientCtx.WithClient(tmRPC) chainClient, err := chain.NewChainClient(clientCtx, clientCfg, clientcommon.OptionGasPrices(cfg.GasPrice)) if err != nil { - return nil, errors.Wrapf(err, "failed to connect to Injective GRPC %s", cfg.CosmosGRPC) + return nil, err } time.Sleep(1 * time.Second) @@ -65,7 +66,7 @@ func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg log.WithFields(log.Fields{ "chain_id": cfg.ChainID, "addr": cfg.ValidatorAddress, - "injective": clientCfg.ChainGrpcEndpoint, + "chain_grpc": clientCfg.ChainGrpcEndpoint, "tendermint": clientCfg.TmEndpoint, }).Infoln("connected to Injective network") @@ -81,26 +82,6 @@ func (n network) GetBlockTime(ctx context.Context, height int64) (time.Time, err return block.Block.Time, nil } -// waitForService awaits an active ClientConn to a GRPC service. -func waitForService(ctx context.Context, clientConn *grpc.ClientConn) { - for { - select { - case <-ctx.Done(): - log.Fatalln("GRPC service wait timed out") - default: - state := clientConn.GetState() - - if state != connectivity.Ready { - log.WithField("state", state.String()).Warningln("state of GRPC connection not ready") - time.Sleep(5 * time.Second) - continue - } - - return - } - } -} - func awaitConnection(timeout time.Duration, client chain.ChainClient) *grpc.ClientConn { ctx, cancelWait := context.WithTimeout(context.Background(), timeout) defer cancelWait() diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 28e827c9..2d3505a2 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -145,49 +145,54 @@ func (l *relayerLoop) relayValset(ctx context.Context, latestEthValset *peggytyp return nil } - if oldestConfirmedValset.Nonce <= latestEthValset.Nonce { - l.Logger().WithFields(log.Fields{"eth_nonce": latestEthValset.Nonce, "inj_nonce": oldestConfirmedValset.Nonce}).Debugln("valset already updated on Ethereum") + if !l.shouldRelayValset(ctx, oldestConfirmedValset) { return nil } + txHash, err := l.eth.SendEthValsetUpdate(ctx, + latestEthValset, + oldestConfirmedValset, + oldestConfirmedValsetSigs, + ) + + if err != nil { + return err + } + + l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent valset tx to Ethereum") + + return nil +} + +func (l *relayerLoop) shouldRelayValset(ctx context.Context, vs *peggytypes.Valset) bool { latestEthereumValsetNonce, err := l.eth.GetValsetNonce(ctx) if err != nil { - return errors.Wrap(err, "failed to get latest valset nonce from Ethereum") + l.Logger().WithError(err).Warningln("failed to get latest valset nonce from Ethereum") + return false } // Check if other validators already updated the valset - if oldestConfirmedValset.Nonce <= latestEthereumValsetNonce.Uint64() { - l.Logger().WithFields(log.Fields{"eth_nonce": latestEthereumValsetNonce, "inj_nonce": latestEthValset.Nonce}).Debugln("valset already updated on Ethereum") - return nil + if vs.Nonce <= latestEthereumValsetNonce.Uint64() { + l.Logger().WithFields(log.Fields{"eth_nonce": latestEthereumValsetNonce, "inj_nonce": vs.Nonce}).Debugln("valset already updated on Ethereum") + return false } - l.Logger().WithFields(log.Fields{"inj_nonce": oldestConfirmedValset.Nonce, "eth_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("new valset update") - // Check custom time delay offset - blockTime, err := l.inj.GetBlockTime(ctx, int64(oldestConfirmedValset.Height)) + blockTime, err := l.inj.GetBlockTime(ctx, int64(vs.Height)) if err != nil { - return errors.Wrap(err, "failed to parse timestamp from block") + l.Logger().WithError(err).Warningln("unable to get latest block from Injective") + return false } if timeElapsed := time.Since(blockTime); timeElapsed <= l.relayValsetOffsetDur { timeRemaining := time.Duration(int64(l.relayValsetOffsetDur) - int64(timeElapsed)) l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("valset relay offset not reached yet") - return nil - } - - txHash, err := l.eth.SendEthValsetUpdate(ctx, - latestEthValset, - oldestConfirmedValset, - oldestConfirmedValsetSigs, - ) - - if err != nil { - return err + return false } - l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent valset tx to Ethereum") + l.Logger().WithFields(log.Fields{"inj_nonce": vs.Nonce, "eth_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("new valset update") - return nil + return true } func (l *relayerLoop) relayBatch(ctx context.Context, latestEthValset *peggytypes.Valset) error { @@ -222,40 +227,50 @@ func (l *relayerLoop) relayBatch(ctx context.Context, latestEthValset *peggytype return nil } - latestEthBatch, err := l.eth.GetTxBatchNonce(ctx, gethcommon.HexToAddress(oldestConfirmedInjBatch.TokenContract)) + if !l.shouldRelayBatch(ctx, oldestConfirmedInjBatch) { + return nil + } + + // Send SendTransactionBatch to Ethereum + txHash, err := l.eth.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedInjBatch, oldestConfirmedInjBatchSigs) if err != nil { return err } - // Check if ethereum batch was updated by other validators - if oldestConfirmedInjBatch.BatchNonce <= latestEthBatch.Uint64() { - l.Logger().WithFields(log.Fields{"eth_nonce": latestEthBatch.Uint64(), "inj_nonce": oldestConfirmedInjBatch.BatchNonce}).Debugln("batch already updated on Ethereum") - return nil + l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent batch tx to Ethereum") + + return nil +} + +func (l *relayerLoop) shouldRelayBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) bool { + latestEthBatch, err := l.eth.GetTxBatchNonce(ctx, gethcommon.HexToAddress(batch.TokenContract)) + if err != nil { + l.Logger().WithError(err).Warningf("unable to get latest batch nonce from Ethereum: token_contract=%s", gethcommon.HexToAddress(batch.TokenContract)) + return false } - l.Logger().WithFields(log.Fields{"inj_nonce": oldestConfirmedInjBatch.BatchNonce, "eth_nonce": latestEthBatch.Uint64()}).Debugln("new batch update") + // Check if ethereum batch was updated by other validators + if batch.BatchNonce <= latestEthBatch.Uint64() { + l.Logger().WithFields(log.Fields{"eth_nonce": latestEthBatch.Uint64(), "inj_nonce": batch.BatchNonce}).Debugln("batch already updated on Ethereum") + return false + } // Check custom time delay offset - blockTime, err := l.inj.GetBlockTime(ctx, int64(oldestConfirmedInjBatch.Block)) + blockTime, err := l.inj.GetBlockTime(ctx, int64(batch.Block)) if err != nil { - return errors.Wrap(err, "failed to parse timestamp from block") + l.Logger().WithError(err).Warningln("unable to get latest block from Injective") + return false } if timeElapsed := time.Since(blockTime); timeElapsed <= l.relayBatchOffsetDur { timeRemaining := time.Duration(int64(l.relayBatchOffsetDur) - int64(timeElapsed)) l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("batch relay offset not reached yet") - return nil + return false } - // Send SendTransactionBatch to Ethereum - txHash, err := l.eth.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedInjBatch, oldestConfirmedInjBatchSigs) - if err != nil { - return err - } + l.Logger().WithFields(log.Fields{"inj_nonce": batch.BatchNonce, "eth_nonce": latestEthBatch.Uint64()}).Debugln("new batch update") - l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent batch tx to Ethereum") - - return nil + return true } // FindLatestValset finds the latest valset on the Peggy contract by looking back through the event From e478c71fec95eca8c58e487e55712f444716022d Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 02:17:59 +0100 Subject: [PATCH 44/99] cleanup 2 --- orchestrator/cosmos/network.go | 28 ++++++++-------------------- orchestrator/relayer.go | 8 ++++---- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 96ccf2ed..85605b5e 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -19,13 +19,6 @@ import ( ) type Network interface { - GetBlockTime(ctx context.Context, height int64) (time.Time, error) - - peggy.QueryClient - peggy.BroadcastClient -} - -type network struct { peggy.QueryClient peggy.BroadcastClient tendermint.Client @@ -57,10 +50,14 @@ func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg conn := awaitConnection(1*time.Minute, chainClient) - n := network{ - QueryClient: peggy.NewQueryClient(peggytypes.NewQueryClient(conn)), - BroadcastClient: peggy.NewBroadcastClient(chainClient, ethSignFn), - Client: tendermint.NewRPCClient(clientCfg.TmEndpoint), + n := struct { + peggy.QueryClient + peggy.BroadcastClient + tendermint.Client + }{ + peggy.NewQueryClient(peggytypes.NewQueryClient(conn)), + peggy.NewBroadcastClient(chainClient, ethSignFn), + tendermint.NewRPCClient(clientCfg.TmEndpoint), } log.WithFields(log.Fields{ @@ -73,15 +70,6 @@ func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg return n, nil } -func (n network) GetBlockTime(ctx context.Context, height int64) (time.Time, error) { - block, err := n.Client.GetBlock(ctx, height) - if err != nil { - return time.Time{}, err - } - - return block.Block.Time, nil -} - func awaitConnection(timeout time.Duration, client chain.ChainClient) *grpc.ClientConn { ctx, cancelWait := context.WithTimeout(context.Background(), timeout) defer cancelWait() diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 2d3505a2..837cf8dd 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -178,13 +178,13 @@ func (l *relayerLoop) shouldRelayValset(ctx context.Context, vs *peggytypes.Vals } // Check custom time delay offset - blockTime, err := l.inj.GetBlockTime(ctx, int64(vs.Height)) + block, err := l.inj.GetBlock(ctx, int64(vs.Height)) if err != nil { l.Logger().WithError(err).Warningln("unable to get latest block from Injective") return false } - if timeElapsed := time.Since(blockTime); timeElapsed <= l.relayValsetOffsetDur { + if timeElapsed := time.Since(block.Block.Time); timeElapsed <= l.relayValsetOffsetDur { timeRemaining := time.Duration(int64(l.relayValsetOffsetDur) - int64(timeElapsed)) l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("valset relay offset not reached yet") return false @@ -256,13 +256,13 @@ func (l *relayerLoop) shouldRelayBatch(ctx context.Context, batch *peggytypes.Ou } // Check custom time delay offset - blockTime, err := l.inj.GetBlockTime(ctx, int64(batch.Block)) + blockTime, err := l.inj.GetBlock(ctx, int64(batch.Block)) if err != nil { l.Logger().WithError(err).Warningln("unable to get latest block from Injective") return false } - if timeElapsed := time.Since(blockTime); timeElapsed <= l.relayBatchOffsetDur { + if timeElapsed := time.Since(blockTime.Block.Time); timeElapsed <= l.relayBatchOffsetDur { timeRemaining := time.Duration(int64(l.relayBatchOffsetDur) - int64(timeElapsed)) l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("batch relay offset not reached yet") return false From eb2848740b4b7208a0faebfcd178fbd1b8246c0a Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 10:33:01 +0100 Subject: [PATCH 45/99] cleanup 3 --- orchestrator/cosmos/network.go | 15 ++++++++------- orchestrator/cosmos/peggy/query.go | 8 +++++--- orchestrator/oracle.go | 14 ++++++++------ orchestrator/orchestrator.go | 10 +++++----- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 85605b5e..56dc3f02 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -10,12 +10,13 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/connectivity" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/InjectiveLabs/sdk-go/client/chain" clientcommon "github.com/InjectiveLabs/sdk-go/client/common" + + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" ) type Network interface { @@ -48,9 +49,9 @@ func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg time.Sleep(1 * time.Second) - conn := awaitConnection(1*time.Minute, chainClient) + conn := awaitConnection(chainClient, 1*time.Minute) - n := struct { + net := struct { peggy.QueryClient peggy.BroadcastClient tendermint.Client @@ -67,10 +68,10 @@ func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg "tendermint": clientCfg.TmEndpoint, }).Infoln("connected to Injective network") - return n, nil + return net, nil } -func awaitConnection(timeout time.Duration, client chain.ChainClient) *grpc.ClientConn { +func awaitConnection(client chain.ChainClient, timeout time.Duration) *grpc.ClientConn { ctx, cancelWait := context.WithTimeout(context.Background(), timeout) defer cancelWait() diff --git a/orchestrator/cosmos/peggy/query.go b/orchestrator/cosmos/peggy/query.go index 34368e6e..42bfbfca 100644 --- a/orchestrator/cosmos/peggy/query.go +++ b/orchestrator/cosmos/peggy/query.go @@ -13,18 +13,20 @@ import ( var ErrNotFound = errors.New("not found") type QueryClient interface { + PeggyParams(ctx context.Context) (*peggytypes.Params, error) + LastClaimEventByAddr(ctx context.Context, validatorAccountAddress cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) + GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (cosmostypes.AccAddress, error) + ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) CurrentValset(ctx context.Context) (*peggytypes.Valset, error) OldestUnsignedValsets(ctx context.Context, valAccountAddress cosmostypes.AccAddress) ([]*peggytypes.Valset, error) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) + OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress cosmostypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) - LastClaimEventByAddr(ctx context.Context, validatorAccountAddress cosmostypes.AccAddress) (*peggytypes.LastClaimEvent, error) - PeggyParams(ctx context.Context) (*peggytypes.Params, error) - GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (cosmostypes.AccAddress, error) } type queryClient struct { diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 7023e1bc..c6e122ed 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -12,13 +12,14 @@ import ( peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" ) -// todo: this is outdated, need to update -// Considering blocktime of up to 3 seconds approx on the Injective Chain and an oracle loop duration = 1 minute, -// we broadcast only 20 events in each iteration. -// So better to search only 20 blocks to ensure all the events are broadcast to Injective Chain without misses. const ( + // Minimum number of confirmations for an Ethereum block to be considered valid ethBlockConfirmationDelay uint64 = 12 - defaultBlocksToSearch uint64 = 2000 + + // Maximum block range for Ethereum event query. If the orchestrator has been offline for a long time, + // the oracle loop can potentially run longer than defaultLoopDur due to a surge of events. This usually happens + // when there are more than ~50 events to claim in a single run. + defaultBlocksToSearch uint64 = 2000 ) // EthOracleMainLoop is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain @@ -65,6 +66,7 @@ func (l *ethOracleLoop) Run(ctx context.Context) error { return nil } + // ensure the block range is within defaultBlocksToSearch if latestHeight > l.lastCheckedEthHeight+defaultBlocksToSearch { latestHeight = l.lastCheckedEthHeight + defaultBlocksToSearch } @@ -127,7 +129,7 @@ func (l *ethOracleLoop) autoResync(ctx context.Context) error { l.lastCheckedEthHeight = latestHeight l.lastResyncWithInjective = time.Now() - l.Logger().WithFields(log.Fields{"last_resync_time": l.lastResyncWithInjective.String(), "last_claimed_eth_height": l.lastCheckedEthHeight}).Infoln("auto resync last claimed event height with Injective") + l.Logger().WithFields(log.Fields{"last_resync_time": l.lastResyncWithInjective.String(), "last_claimed_eth_height": l.lastCheckedEthHeight}).Infoln("auto resync with last claimed event on Injective") return nil } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 35fd2e0e..28088b4f 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -2,14 +2,14 @@ package orchestrator import ( "context" - "github.com/cosmos/cosmos-sdk/types" "time" - "github.com/InjectiveLabs/metrics" + cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" log "github.com/xlab/suplog" + "github.com/InjectiveLabs/metrics" "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/loops" ) @@ -26,7 +26,7 @@ type PeggyOrchestrator struct { svcTags metrics.Tags inj cosmos.Network - orchestratorAddr types.AccAddress + orchestratorAddr cosmostypes.AccAddress eth EthereumNetwork pricefeed PriceFeed @@ -43,7 +43,7 @@ type PeggyOrchestrator struct { } func NewPeggyOrchestrator( - orchestratorAddr types.AccAddress, + orchestratorAddr cosmostypes.AccAddress, injective cosmos.Network, ethereum EthereumNetwork, priceFeed PriceFeed, @@ -109,7 +109,7 @@ func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context) bool { return false } - s.logger.WithField("validator_addr", validator.String()).Debugln("found delegate validator address") + s.logger.WithField("addr", validator.String()).Debugln("found delegate validator") return true } From 7eea7313bba30ae703e16be567f9d7f63a558bf5 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 11:15:22 +0100 Subject: [PATCH 46/99] refactor SendEthereumClaims --- orchestrator/cosmos/peggy/broadcast.go | 276 ++++++------------------- orchestrator/oracle.go | 157 +++++++++----- 2 files changed, 165 insertions(+), 268 deletions(-) diff --git a/orchestrator/cosmos/peggy/broadcast.go b/orchestrator/cosmos/peggy/broadcast.go index b8dee3e1..473595f0 100644 --- a/orchestrator/cosmos/peggy/broadcast.go +++ b/orchestrator/cosmos/peggy/broadcast.go @@ -3,60 +3,42 @@ package peggy import ( "context" "fmt" - "sort" - "time" - "github.com/InjectiveLabs/metrics" - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - chainclient "github.com/InjectiveLabs/sdk-go/client/chain" cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" log "github.com/xlab/suplog" + "github.com/InjectiveLabs/metrics" + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/sdk-go/client/chain" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" ) type BroadcastClient interface { - /// Send a transaction updating the eth address for the sending - /// Cosmos address. The sending Cosmos address should be a validator UpdatePeggyOrchestratorAddresses(ctx context.Context, ethFrom gethcommon.Address, orchAddr cosmostypes.AccAddress) error - - // SendValsetConfirm broadcasts in a confirmation for a specific validator set for a specific block height. SendValsetConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, valset *peggytypes.Valset) error - - // SendBatchConfirm broadcasts in a confirmation for a specific transaction batch set for a specific block height - // since transaction batches also include validator sets this has all the arguments SendBatchConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch) error - - SendEthereumClaims(ctx context.Context, - lastClaimEvent uint64, - oldDeposits []*peggyevents.PeggySendToCosmosEvent, - deposits []*peggyevents.PeggySendToInjectiveEvent, - withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, - ) (uint64, error) - - // SendToEth broadcasts a Tx that tokens from Cosmos to Ethereum. - // These tokens will not be sent immediately. Instead, they will require - // some time to be included in a batch. - SendToEth(ctx context.Context, destination gethcommon.Address, amount, fee cosmostypes.Coin) error - - // SendRequestBatch broadcasts a requests a batch of withdrawal transactions to be generated on the chain. SendRequestBatch(ctx context.Context, denom string) error + SendToEth(ctx context.Context, destination gethcommon.Address, amount, fee cosmostypes.Coin) error + SendOldDepositClaim(ctx context.Context, deposit *peggyevents.PeggySendToCosmosEvent) error + SendDepositClaim(ctx context.Context, deposit *peggyevents.PeggySendToInjectiveEvent) error + SendWithdrawalClaim(ctx context.Context, withdrawal *peggyevents.PeggyTransactionBatchExecutedEvent) error + SendValsetClaim(ctx context.Context, vs *peggyevents.PeggyValsetUpdatedEvent) error + SendERC20DeployedClaim(ctx context.Context, erc20 *peggyevents.PeggyERC20DeployedEvent) error } type broadcastClient struct { - chainclient.ChainClient + chain.ChainClient ethSignFn keystore.PersonalSignFn svcTags metrics.Tags } -func NewBroadcastClient(client chainclient.ChainClient, signFn keystore.PersonalSignFn) BroadcastClient { +func NewBroadcastClient(client chain.ChainClient, signFn keystore.PersonalSignFn) BroadcastClient { return broadcastClient{ ChainClient: client, ethSignFn: signFn, @@ -232,135 +214,7 @@ func (c broadcastClient) SendRequestBatch(ctx context.Context, denom string) err return nil } -func (c broadcastClient) SendEthereumClaims(ctx context.Context, lastClaimEventNonce uint64, oldDeposits []*peggyevents.PeggySendToCosmosEvent, deposits []*peggyevents.PeggySendToInjectiveEvent, withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent) (uint64, error) { - metrics.ReportFuncCall(c.svcTags) - doneFn := metrics.ReportFuncTiming(c.svcTags) - defer doneFn() - - events := sortEventsByNonce(oldDeposits, deposits, withdraws, erc20Deployed, valsetUpdates) - - // this can't happen outside of programmer error - if firstToSend := events[0]; firstToSend.Nonce() != lastClaimEventNonce+1 { - return 0, errors.Errorf("expected event with nonce %d, got %d", lastClaimEventNonce+1, firstToSend.Nonce()) - } - - for _, e := range events { - if err := c.sendEventClaim(ctx, e); err != nil { - return 0, err - } - - // Considering blockTime=1s on Injective chain, Adding Sleep to make sure new event is - // sent only after previous event is executed successfully. - // Otherwise it will through `non contiguous event nonce` failing CheckTx. - time.Sleep(1200 * time.Millisecond) - } - - lastClaimEventNonce = events[len(events)-1].Nonce() - - return lastClaimEventNonce, nil -} - -type event interface { - Nonce() uint64 -} - -type ( - eventSendToCosmos peggyevents.PeggySendToCosmosEvent - eventSendToInjective peggyevents.PeggySendToInjectiveEvent - eventTransactionBatchExecuted peggyevents.PeggyTransactionBatchExecutedEvent - eventERC20Deployed peggyevents.PeggyERC20DeployedEvent - eventValsetUpdated peggyevents.PeggyValsetUpdatedEvent -) - -func (e *eventSendToCosmos) Nonce() uint64 { - return e.EventNonce.Uint64() -} - -func (e *eventSendToInjective) Nonce() uint64 { - return e.EventNonce.Uint64() -} - -func (e *eventTransactionBatchExecuted) Nonce() uint64 { - return e.EventNonce.Uint64() -} - -func (e *eventERC20Deployed) Nonce() uint64 { - return e.EventNonce.Uint64() -} - -func (e *eventValsetUpdated) Nonce() uint64 { - return e.EventNonce.Uint64() -} - -func sortEventsByNonce( - oldDeposits []*peggyevents.PeggySendToCosmosEvent, - deposits []*peggyevents.PeggySendToInjectiveEvent, - withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, -) []event { - total := len(oldDeposits) + len(deposits) + len(withdraws) + len(erc20Deployed) + len(valsetUpdates) - events := make([]event, 0, total) - - for _, deposit := range oldDeposits { - e := eventSendToCosmos(*deposit) - events = append(events, &e) - } - - for _, deposit := range deposits { - e := eventSendToInjective(*deposit) - events = append(events, &e) - } - - for _, withdrawal := range withdraws { - e := eventTransactionBatchExecuted(*withdrawal) - events = append(events, &e) - } - - for _, deployment := range erc20Deployed { - e := eventERC20Deployed(*deployment) - events = append(events, &e) - } - - for _, vs := range valsetUpdates { - e := eventValsetUpdated(*vs) - events = append(events, &e) - } - - // sort by nonce - sort.Slice(events, func(i, j int) bool { - return events[i].Nonce() < events[j].Nonce() - }) - - return events -} - -func (c broadcastClient) sendEventClaim(ctx context.Context, ev event) error { - switch ev := ev.(type) { - case *eventSendToCosmos: - e := peggyevents.PeggySendToCosmosEvent(*ev) - return c.sendOldDepositClaims(ctx, &e) - case *eventSendToInjective: - e := peggyevents.PeggySendToInjectiveEvent(*ev) - return c.sendDepositClaims(ctx, &e) - case *eventTransactionBatchExecuted: - e := peggyevents.PeggyTransactionBatchExecutedEvent(*ev) - return c.sendWithdrawClaims(ctx, &e) - case *eventERC20Deployed: - e := peggyevents.PeggyERC20DeployedEvent(*ev) - return c.sendErc20DeployedClaims(ctx, &e) - case *eventValsetUpdated: - e := peggyevents.PeggyValsetUpdatedEvent(*ev) - return c.sendValsetUpdateClaims(ctx, &e) - } - - return errors.Errorf("unknown event type %T", ev) -} - -func (c broadcastClient) sendOldDepositClaims( - ctx context.Context, - oldDeposit *peggyevents.PeggySendToCosmosEvent, -) error { +func (c broadcastClient) SendOldDepositClaim(_ context.Context, deposit *peggyevents.PeggySendToCosmosEvent) error { // EthereumBridgeDepositClaim // When more than 66% of the active validator set has // claimed to have seen the deposit enter the ethereum blockchain coins are @@ -371,19 +225,19 @@ func (c broadcastClient) sendOldDepositClaims( defer doneFn() log.WithFields(log.Fields{ - "sender": oldDeposit.Sender.Hex(), - "destination": cosmostypes.AccAddress(oldDeposit.Destination[12:32]).String(), - "amount": oldDeposit.Amount.String(), - "event_nonce": oldDeposit.EventNonce.String(), + "sender": deposit.Sender.Hex(), + "destination": cosmostypes.AccAddress(deposit.Destination[12:32]).String(), + "amount": deposit.Amount.String(), + "event_nonce": deposit.EventNonce.String(), }).Debugln("observed SendToCosmosEvent") msg := &peggytypes.MsgDepositClaim{ - EventNonce: oldDeposit.EventNonce.Uint64(), - BlockHeight: oldDeposit.Raw.BlockNumber, - TokenContract: oldDeposit.TokenContract.Hex(), - Amount: cosmostypes.NewIntFromBigInt(oldDeposit.Amount), - EthereumSender: oldDeposit.Sender.Hex(), - CosmosReceiver: cosmostypes.AccAddress(oldDeposit.Destination[12:32]).String(), + EventNonce: deposit.EventNonce.Uint64(), + BlockHeight: deposit.Raw.BlockNumber, + TokenContract: deposit.TokenContract.Hex(), + Amount: cosmostypes.NewIntFromBigInt(deposit.Amount), + EthereumSender: deposit.Sender.Hex(), + CosmosReceiver: cosmostypes.AccAddress(deposit.Destination[12:32]).String(), Orchestrator: c.ChainClient.FromAddress().String(), Data: "", } @@ -403,10 +257,7 @@ func (c broadcastClient) sendOldDepositClaims( return nil } -func (c broadcastClient) sendDepositClaims( - ctx context.Context, - deposit *peggyevents.PeggySendToInjectiveEvent, -) error { +func (c broadcastClient) SendDepositClaim(_ context.Context, deposit *peggyevents.PeggySendToInjectiveEvent) error { // EthereumBridgeDepositClaim // When more than 66% of the active validator set has // claimed to have seen the deposit enter the ethereum blockchain coins are @@ -450,26 +301,23 @@ func (c broadcastClient) sendDepositClaims( return nil } -func (c broadcastClient) sendWithdrawClaims( - ctx context.Context, - withdraw *peggyevents.PeggyTransactionBatchExecutedEvent, -) error { +func (c broadcastClient) SendWithdrawalClaim(_ context.Context, withdrawal *peggyevents.PeggyTransactionBatchExecutedEvent) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() log.WithFields(log.Fields{ - "batch_nonce": withdraw.BatchNonce.String(), - "token_contract": withdraw.Token.Hex(), + "batch_nonce": withdrawal.BatchNonce.String(), + "token_contract": withdrawal.Token.Hex(), }).Debugln("observed TransactionBatchExecutedEvent") // WithdrawClaim claims that a batch of withdrawal // operations on the bridge contract was executed. msg := &peggytypes.MsgWithdrawClaim{ - EventNonce: withdraw.EventNonce.Uint64(), - BatchNonce: withdraw.BatchNonce.Uint64(), - BlockHeight: withdraw.Raw.BlockNumber, - TokenContract: withdraw.Token.Hex(), + EventNonce: withdrawal.EventNonce.Uint64(), + BatchNonce: withdrawal.BatchNonce.Uint64(), + BlockHeight: withdrawal.Raw.BlockNumber, + TokenContract: withdrawal.Token.Hex(), Orchestrator: c.FromAddress().String(), } @@ -488,36 +336,33 @@ func (c broadcastClient) sendWithdrawClaims( return nil } -func (c broadcastClient) sendValsetUpdateClaims( - ctx context.Context, - valsetUpdate *peggyevents.PeggyValsetUpdatedEvent, -) error { +func (c broadcastClient) SendValsetClaim(_ context.Context, vs *peggyevents.PeggyValsetUpdatedEvent) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() log.WithFields(log.Fields{ - "valset_nonce": valsetUpdate.NewValsetNonce.Uint64(), - "validators": valsetUpdate.Validators, - "powers": valsetUpdate.Powers, - "reward_amount": valsetUpdate.RewardAmount, - "reward_token": valsetUpdate.RewardToken.Hex(), + "valset_nonce": vs.NewValsetNonce.Uint64(), + "validators": vs.Validators, + "powers": vs.Powers, + "reward_amount": vs.RewardAmount, + "reward_token": vs.RewardToken.Hex(), }).Debugln("observed ValsetUpdatedEvent") - members := make([]*peggytypes.BridgeValidator, len(valsetUpdate.Validators)) - for i, val := range valsetUpdate.Validators { + members := make([]*peggytypes.BridgeValidator, len(vs.Validators)) + for i, val := range vs.Validators { members[i] = &peggytypes.BridgeValidator{ EthereumAddress: val.Hex(), - Power: valsetUpdate.Powers[i].Uint64(), + Power: vs.Powers[i].Uint64(), } } msg := &peggytypes.MsgValsetUpdatedClaim{ - EventNonce: valsetUpdate.EventNonce.Uint64(), - ValsetNonce: valsetUpdate.NewValsetNonce.Uint64(), - BlockHeight: valsetUpdate.Raw.BlockNumber, - RewardAmount: cosmostypes.NewIntFromBigInt(valsetUpdate.RewardAmount), - RewardToken: valsetUpdate.RewardToken.Hex(), + EventNonce: vs.EventNonce.Uint64(), + ValsetNonce: vs.NewValsetNonce.Uint64(), + BlockHeight: vs.Raw.BlockNumber, + RewardAmount: cosmostypes.NewIntFromBigInt(vs.RewardAmount), + RewardToken: vs.RewardToken.Hex(), Members: members, Orchestrator: c.FromAddress().String(), } @@ -537,30 +382,27 @@ func (c broadcastClient) sendValsetUpdateClaims( return nil } -func (c broadcastClient) sendErc20DeployedClaims( - ctx context.Context, - erc20Deployed *peggyevents.PeggyERC20DeployedEvent, -) error { +func (c broadcastClient) SendERC20DeployedClaim(_ context.Context, erc20 *peggyevents.PeggyERC20DeployedEvent) error { metrics.ReportFuncCall(c.svcTags) doneFn := metrics.ReportFuncTiming(c.svcTags) defer doneFn() log.WithFields(log.Fields{ - "cosmos_denom": erc20Deployed.CosmosDenom, - "token_contract": erc20Deployed.TokenContract.Hex(), - "name": erc20Deployed.Name, - "symbol": erc20Deployed.Symbol, - "decimals": erc20Deployed.Decimals, + "cosmos_denom": erc20.CosmosDenom, + "token_contract": erc20.TokenContract.Hex(), + "name": erc20.Name, + "symbol": erc20.Symbol, + "decimals": erc20.Decimals, }).Debugln("observed ERC20DeployedEvent") msg := &peggytypes.MsgERC20DeployedClaim{ - EventNonce: erc20Deployed.EventNonce.Uint64(), - BlockHeight: erc20Deployed.Raw.BlockNumber, - CosmosDenom: erc20Deployed.CosmosDenom, - TokenContract: erc20Deployed.TokenContract.Hex(), - Name: erc20Deployed.Name, - Symbol: erc20Deployed.Symbol, - Decimals: uint64(erc20Deployed.Decimals), + EventNonce: erc20.EventNonce.Uint64(), + BlockHeight: erc20.Raw.BlockNumber, + CosmosDenom: erc20.CosmosDenom, + TokenContract: erc20.TokenContract.Hex(), + Name: erc20.Name, + Symbol: erc20.Symbol, + Decimals: uint64(erc20.Decimals), Orchestrator: c.FromAddress().String(), } diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index c6e122ed..287e8971 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "sort" "time" "github.com/avast/retry-go" @@ -71,9 +72,13 @@ func (l *ethOracleLoop) Run(ctx context.Context) error { latestHeight = l.lastCheckedEthHeight + defaultBlocksToSearch } - if err := l.relayEvents(ctx, latestHeight); err != nil { + events, err := l.getEthEvents(ctx, l.lastCheckedEthHeight, latestHeight) + if err != nil { return err + } + if err := l.sendNewEventClaims(ctx, events); err != nil { + return err } l.Logger().WithFields(log.Fields{"block_start": l.lastCheckedEthHeight, "block_end": latestHeight}).Debugln("scanned Ethereum blocks") @@ -95,45 +100,6 @@ func (l *ethOracleLoop) Run(ctx context.Context) error { }) } -func (l *ethOracleLoop) relayEvents(ctx context.Context, latestHeight uint64) error { - events, err := l.getEthEvents(ctx, l.lastCheckedEthHeight, latestHeight) - if err != nil { - return err - } - - if err := l.sendNewEventClaims(ctx, events); err != nil { - return err - } - - return nil -} - -func (l *ethOracleLoop) autoResync(ctx context.Context) error { - var latestHeight uint64 - getLastClaimEventFn := func() (err error) { - latestHeight, err = l.getLastClaimBlockHeight(ctx) - return - } - - if err := retry.Do(getLastClaimEventFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to get last claimed event height, will retry (%d)", n) - }), - ); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return err - } - - l.lastCheckedEthHeight = latestHeight - l.lastResyncWithInjective = time.Now() - - l.Logger().WithFields(log.Fields{"last_resync_time": l.lastResyncWithInjective.String(), "last_claimed_eth_height": l.lastCheckedEthHeight}).Infoln("auto resync with last claimed event on Injective") - - return nil -} - func (l *ethOracleLoop) getEthEvents(ctx context.Context, startBlock, endBlock uint64) (ethEvents, error) { events := ethEvents{} @@ -225,20 +191,18 @@ func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents return nil } - latestEventNonce, err := l.inj.SendEthereumClaims(ctx, - lastClaim.EthereumEventNonce, - newEvents.OldDeposits, - newEvents.Deposits, - newEvents.Withdrawals, - newEvents.ERC20Deployments, - newEvents.ValsetUpdates, - ) + sortedEvents := newEvents.Sort() + for _, event := range sortedEvents { + if err := l.sendEthEventClaim(ctx, event); err != nil { + return err + } - if err != nil { - return err + // Considering blockTime=1s on Injective chain, adding Sleep to make sure new event is sent + // only after previous event is executed successfully. Otherwise it will through `non contiguous event nonce` failing CheckTx. + time.Sleep(1200 * time.Millisecond) } - l.Logger().WithFields(log.Fields{"events": newEvents.Num(), "latest_event_nonce": latestEventNonce}).Infoln("sent new event claims to Injective") + l.Logger().WithField("claims", len(sortedEvents)).Infoln("sent new event claims to Injective") return nil } @@ -257,6 +221,49 @@ func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents return nil } +func (l *ethOracleLoop) autoResync(ctx context.Context) error { + var latestHeight uint64 + getLastClaimEventFn := func() (err error) { + latestHeight, err = l.getLastClaimBlockHeight(ctx) + return + } + + if err := retry.Do(getLastClaimEventFn, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("failed to get last claimed event height, will retry (%d)", n) + }), + ); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return err + } + + l.lastCheckedEthHeight = latestHeight + l.lastResyncWithInjective = time.Now() + + l.Logger().WithFields(log.Fields{"last_resync_time": l.lastResyncWithInjective.String(), "last_claimed_eth_height": l.lastCheckedEthHeight}).Infoln("auto resync with last claimed event on Injective") + + return nil +} + +func (l *ethOracleLoop) sendEthEventClaim(ctx context.Context, event any) error { + switch e := event.(type) { + case *peggyevents.PeggySendToCosmosEvent: + return l.inj.SendOldDepositClaim(ctx, e) + case *peggyevents.PeggySendToInjectiveEvent: + return l.inj.SendDepositClaim(ctx, e) + case *peggyevents.PeggyValsetUpdatedEvent: + return l.inj.SendValsetClaim(ctx, e) + case *peggyevents.PeggyTransactionBatchExecutedEvent: + return l.inj.SendWithdrawalClaim(ctx, e) + case *peggyevents.PeggyERC20DeployedEvent: + return l.inj.SendERC20DeployedClaim(ctx, e) + default: + panic(errors.Errorf("unknown event type %T", e)) + } +} + type ethEvents struct { OldDeposits []*peggyevents.PeggySendToCosmosEvent Deposits []*peggyevents.PeggySendToInjectiveEvent @@ -313,3 +320,51 @@ func (e ethEvents) Filter(nonce uint64) ethEvents { ERC20Deployments: erc20Deployments, } } + +func (e ethEvents) Sort() []any { + events := make([]any, 0, e.Num()) + + for _, deposit := range e.OldDeposits { + events = append(events, deposit) + } + + for _, deposit := range e.Deposits { + events = append(events, deposit) + } + + for _, withdrawal := range e.Withdrawals { + events = append(events, withdrawal) + } + + for _, deployment := range e.ERC20Deployments { + events = append(events, deployment) + } + + for _, vs := range e.ValsetUpdates { + events = append(events, vs) + } + + eventNonce := func(event any) uint64 { + switch e := event.(type) { + case *peggyevents.PeggySendToCosmosEvent: + return e.EventNonce.Uint64() + case *peggyevents.PeggySendToInjectiveEvent: + return e.EventNonce.Uint64() + case *peggyevents.PeggyValsetUpdatedEvent: + return e.EventNonce.Uint64() + case *peggyevents.PeggyTransactionBatchExecutedEvent: + return e.EventNonce.Uint64() + case *peggyevents.PeggyERC20DeployedEvent: + return e.EventNonce.Uint64() + default: + panic(errors.Errorf("unknown event type %T", e)) + } + } + + // sort by nonce + sort.Slice(events, func(i, j int) bool { + return eventNonce(events[i]) < eventNonce(events[j]) + }) + + return events +} From 282871a32c4087bbae5f86b9c152043f7102d8f4 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 18:23:29 +0100 Subject: [PATCH 47/99] refactor cosmos keyring --- cmd/peggo/orchestrator.go | 39 ++++-- orchestrator/cosmos/keyring.go | 244 +++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+), 13 deletions(-) create mode 100644 orchestrator/cosmos/keyring.go diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 8641b0fd..bc63399e 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -46,19 +46,32 @@ func orchestratorCmd(cmd *cli.Cmd) { log.Fatalln("cannot use Ledger for orchestrator, since signatures must be realtime") } - valAddress, cosmosKeyring, err := initCosmosKeyring( - cfg.cosmosKeyringDir, - cfg.cosmosKeyringAppName, - cfg.cosmosKeyringBackend, - cfg.cosmosKeyFrom, - cfg.cosmosKeyPassphrase, - cfg.cosmosPrivKey, - cfg.cosmosUseLedger, - ) - if err != nil { - log.WithError(err).Fatalln("failed to initialize Injective keyring") + keyringCfg := cosmos.KeyringConfig{ + KeyringDir: *cfg.cosmosKeyringDir, + KeyringAppName: *cfg.cosmosKeyringAppName, + KeyringBackend: *cfg.cosmosKeyringBackend, + KeyFrom: *cfg.cosmosKeyFrom, + KeyPassphrase: *cfg.cosmosKeyPassphrase, + PrivateKey: *cfg.cosmosPrivKey, + UseLedger: *cfg.cosmosUseLedger, } + cosmosKeyring, err := cosmos.NewKeyring(keyringCfg) + orShutdown(err) + + //valAddress, cosmosKeyring, err := initCosmosKeyring( + // cfg.cosmosKeyringDir, + // cfg.cosmosKeyringAppName, + // cfg.cosmosKeyringBackend, + // cfg.cosmosKeyFrom, + // cfg.cosmosKeyPassphrase, + // cfg.cosmosPrivKey, + // cfg.cosmosUseLedger, + //) + //if err != nil { + // log.WithError(err).Fatalln("failed to initialize Injective keyring") + //} + ethKeyFromAddress, signerFn, personalSignFn, err := initEthereumAccountsManager( uint64(*cfg.ethChainID), cfg.ethKeystoreDir, @@ -73,7 +86,7 @@ func orchestratorCmd(cmd *cli.Cmd) { cosmosCfg := cosmos.NetworkConfig{ ChainID: *cfg.cosmosChainID, - ValidatorAddress: valAddress.String(), + ValidatorAddress: cosmosKeyring.Addr.String(), CosmosGRPC: *cfg.cosmosGRPC, TendermintRPC: *cfg.tendermintRPC, GasPrice: *cfg.cosmosGasPrices, @@ -114,7 +127,7 @@ func orchestratorCmd(cmd *cli.Cmd) { // Create peggo and run it peggo, err := orchestrator.NewPeggyOrchestrator( - valAddress, + cosmosKeyring.Addr, cosmosNetwork, ethereumNet, coingeckoFeed, diff --git a/orchestrator/cosmos/keyring.go b/orchestrator/cosmos/keyring.go new file mode 100644 index 00000000..adf18f04 --- /dev/null +++ b/orchestrator/cosmos/keyring.go @@ -0,0 +1,244 @@ +package cosmos + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "github.com/InjectiveLabs/sdk-go/chain/crypto/ethsecp256k1" + "github.com/InjectiveLabs/sdk-go/chain/crypto/hd" + "github.com/cosmos/cosmos-sdk/codec/types" + cosmcrypto "github.com/cosmos/cosmos-sdk/crypto" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + cosmtypes "github.com/cosmos/cosmos-sdk/types" + "github.com/pkg/errors" + "io" + "os" + "path/filepath" + "strings" + + "github.com/InjectiveLabs/sdk-go/chain/codec" + cosmoscdc "github.com/cosmos/cosmos-sdk/codec" +) + +const ( + DefaultKeyName = "validator" +) + +type KeyringConfig struct { + KeyringDir, + KeyringAppName, + KeyringBackend, + KeyFrom, + KeyPassphrase, + PrivateKey string + UseLedger bool +} + +func (cfg KeyringConfig) withPrivateKey() bool { + return len(cfg.PrivateKey) > 9 +} + +type Keyring struct { + keyring.Keyring + + Addr cosmtypes.AccAddress +} + +func NewKeyring(cfg KeyringConfig) (Keyring, error) { + if cfg.withPrivateKey() { + return newInMemoryKeyring(cfg) + } + + return newCosmosKeyring(cfg) + +} + +func newInMemoryKeyring(cfg KeyringConfig) (Keyring, error) { + if cfg.UseLedger { + return Keyring{}, errors.New("cannot use both private key and Ledger") + } + + pk := cfg.PrivateKey + if strings.HasPrefix(pk, "0x") { + pk = pk[2:] + } + + pkRaw, err := hex.DecodeString(pk) + if err != nil { + return Keyring{}, errors.Wrap(err, "invalid private key") + } + + var ( + cosmosPK = ðsecp256k1.PrivKey{Key: pkRaw} + cosmosAddr = cosmtypes.AccAddress(cosmosPK.PubKey().Address()) + keyName = DefaultKeyName + ) + + from, err := cosmtypes.AccAddressFromBech32(cfg.KeyFrom) + if err != nil { + keyName = cfg.KeyFrom // use it as key name + } + + if err == nil && !bytes.Equal(from.Bytes(), cosmosAddr.Bytes()) { + return Keyring{}, errors.Errorf("expected account address %s but got %s from the private key", from.String(), cosmosAddr.String()) + } + + k, err := KeyringForPrivKey(keyName, cosmosPK) + if err != nil { + return Keyring{}, errors.Wrap(err, "failed to initialize cosmos keyring") + } + + kr := Keyring{ + Keyring: k, + Addr: cosmosAddr, + } + + return kr, nil +} + +func newCosmosKeyring(cfg KeyringConfig) (Keyring, error) { + if len(cfg.KeyFrom) == 0 { + return Keyring{}, errors.New("insufficient cosmos details provided") + } + + keyringDir := cfg.KeyringDir + if !filepath.IsAbs(keyringDir) { + dir, err := filepath.Abs(keyringDir) + if err != nil { + return Keyring{}, errors.Wrap(err, "failed to get absolute path of keyring dir") + } + + keyringDir = dir + } + + var reader io.Reader = os.Stdin + if len(cfg.KeyPassphrase) > 9 { + reader = newPassReader(cfg.KeyPassphrase) + } + + kr, err := keyring.New( + cfg.KeyringAppName, + cfg.KeyringBackend, + keyringDir, + reader, + Codec(), + hd.EthSecp256k1Option(), + ) + + if err != nil { + return Keyring{}, errors.Wrap(err, "failed to initialize cosmos keyring") + } + + var keyRecord *keyring.Record + if cosmosAddr, err := cosmtypes.AccAddressFromBech32(cfg.KeyFrom); err != nil { + r, err := kr.KeyByAddress(cosmosAddr) + if err != nil { + return Keyring{}, errors.Wrapf(err, "missing record in keyring: key=%s", cosmosAddr.String()) + } + + keyRecord = r + } else { + r, err := kr.Key(cfg.KeyFrom) + if err != nil { + return Keyring{}, errors.Wrapf(err, "missing record in keyring: key=%s", cosmosAddr.String()) + } + + keyRecord = r + } + + switch keyRecord.GetType() { + case keyring.TypeLocal: + // kb has a key and it's totally usable + addr, err := keyRecord.GetAddress() + if err != nil { + return Keyring{}, errors.Wrap(err, "failed to get address from key record") + } + + k := Keyring{ + Keyring: kr, + Addr: addr, + } + + return k, nil + case keyring.TypeLedger: + // the kb stores references to ledger keys, so we must explicitly + // check that. kb doesn't know how to scan HD keys - they must be added manually before + if !cfg.UseLedger { + return Keyring{}, errors.Errorf("key %s is a Ledger reference, enable Ledger option", keyRecord.Name) + } + + addr, err := keyRecord.GetAddress() + if err != nil { + return Keyring{}, errors.Wrap(err, "failed to get address from key record") + + } + + k := Keyring{ + Keyring: kr, + Addr: addr, + } + + return k, nil + default: + return Keyring{}, errors.Errorf("unsupported key type: %s", keyRecord.GetType()) + } +} + +// KeyringForPrivKey creates a temporary in-mem keyring for a PrivKey. +// Allows to init Context when the key has been provided in plaintext and parsed. +func KeyringForPrivKey(name string, privKey cryptotypes.PrivKey) (keyring.Keyring, error) { + kb := keyring.NewInMemory(Codec(), hd.EthSecp256k1Option()) + tmpPhrase := randPhrase(64) + armored := cosmcrypto.EncryptArmorPrivKey(privKey, tmpPhrase, privKey.Type()) + err := kb.ImportPrivKey(name, armored, tmpPhrase) + if err != nil { + err = errors.Wrap(err, "failed to import privkey") + return nil, err + } + + return kb, nil +} + +func Codec() cosmoscdc.Codec { + interfaceRegistry := types.NewInterfaceRegistry() + codec.RegisterInterfaces(interfaceRegistry) + codec.RegisterLegacyAminoCodec(cosmoscdc.NewLegacyAmino()) + + return cosmoscdc.NewProtoCodec(interfaceRegistry) +} + +func randPhrase(size int) string { + buf := make([]byte, size) + _, err := rand.Read(buf) + if err != nil { + panic("rand failed") + } + + return string(buf) +} + +func newPassReader(pass string) io.Reader { + return &passReader{ + pass: pass, + buf: new(bytes.Buffer), + } +} + +type passReader struct { + pass string + buf *bytes.Buffer +} + +var _ io.Reader = &passReader{} + +func (r *passReader) Read(p []byte) (n int, err error) { + n, err = r.buf.Read(p) + if err == io.EOF || n == 0 { + r.buf.WriteString(r.pass + "\n") + + n, err = r.buf.Read(p) + } + + return +} From aba8b11c942383c640e4d8eaf030f7e5067bdae7 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 18:30:48 +0100 Subject: [PATCH 48/99] fix init --- orchestrator/cosmos/keyring.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/orchestrator/cosmos/keyring.go b/orchestrator/cosmos/keyring.go index adf18f04..bfa049c9 100644 --- a/orchestrator/cosmos/keyring.go +++ b/orchestrator/cosmos/keyring.go @@ -113,7 +113,7 @@ func newCosmosKeyring(cfg KeyringConfig) (Keyring, error) { } var reader io.Reader = os.Stdin - if len(cfg.KeyPassphrase) > 9 { + if len(cfg.KeyPassphrase) > 0 { reader = newPassReader(cfg.KeyPassphrase) } @@ -134,14 +134,14 @@ func newCosmosKeyring(cfg KeyringConfig) (Keyring, error) { if cosmosAddr, err := cosmtypes.AccAddressFromBech32(cfg.KeyFrom); err != nil { r, err := kr.KeyByAddress(cosmosAddr) if err != nil { - return Keyring{}, errors.Wrapf(err, "missing record in keyring: key=%s", cosmosAddr.String()) + return Keyring{}, err } keyRecord = r } else { r, err := kr.Key(cfg.KeyFrom) if err != nil { - return Keyring{}, errors.Wrapf(err, "missing record in keyring: key=%s", cosmosAddr.String()) + return Keyring{}, err } keyRecord = r From f8462e76da4a038e171b4921e5b99048800892c1 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 18:49:20 +0100 Subject: [PATCH 49/99] fix keyring init --- cmd/peggo/orchestrator.go | 19 +++++++++++++++++++ orchestrator/cosmos/keyring.go | 6 +++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index bc63399e..99729e65 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "os" chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" @@ -46,6 +47,15 @@ func orchestratorCmd(cmd *cli.Cmd) { log.Fatalln("cannot use Ledger for orchestrator, since signatures must be realtime") } + println("# Previous cfg #") + fmt.Printf("cosmosKeyringDir: %v\n", *cfg.cosmosKeyringDir) + fmt.Printf("cosmosKeyringAppName: %v\n", *cfg.cosmosKeyringAppName) + fmt.Printf("cosmosKeyringBackend: %v\n", *cfg.cosmosKeyringBackend) + fmt.Printf("cosmosKeyFrom: %v\n", *cfg.cosmosKeyFrom) + fmt.Printf("cosmosKeyPassphrase: %v\n", *cfg.cosmosKeyPassphrase) + fmt.Printf("cosmosPrivKey: %v\n", *cfg.cosmosPrivKey) + fmt.Printf("cosmosUseLedger: %v\n", *cfg.cosmosUseLedger) + keyringCfg := cosmos.KeyringConfig{ KeyringDir: *cfg.cosmosKeyringDir, KeyringAppName: *cfg.cosmosKeyringAppName, @@ -56,6 +66,15 @@ func orchestratorCmd(cmd *cli.Cmd) { UseLedger: *cfg.cosmosUseLedger, } + println("# new cfg ") + fmt.Printf("cosmosKeyringDir: %v\n", keyringCfg.KeyringDir) + fmt.Printf("cosmosKeyringAppName: %v\n", keyringCfg.KeyringAppName) + fmt.Printf("cosmosKeyringBackend: %v\n", keyringCfg.KeyringBackend) + fmt.Printf("cosmosKeyFrom: %v\n", keyringCfg.KeyFrom) + fmt.Printf("cosmosKeyPassphrase: %v\n", keyringCfg.KeyPassphrase) + fmt.Printf("cosmosPrivKey: %v\n", keyringCfg.PrivateKey) + fmt.Printf("cosmosUseLedger: %v\n", keyringCfg.UseLedger) + cosmosKeyring, err := cosmos.NewKeyring(keyringCfg) orShutdown(err) diff --git a/orchestrator/cosmos/keyring.go b/orchestrator/cosmos/keyring.go index bfa049c9..1c0cc40a 100644 --- a/orchestrator/cosmos/keyring.go +++ b/orchestrator/cosmos/keyring.go @@ -36,7 +36,7 @@ type KeyringConfig struct { } func (cfg KeyringConfig) withPrivateKey() bool { - return len(cfg.PrivateKey) > 9 + return len(cfg.PrivateKey) > 0 } type Keyring struct { @@ -132,14 +132,14 @@ func newCosmosKeyring(cfg KeyringConfig) (Keyring, error) { var keyRecord *keyring.Record if cosmosAddr, err := cosmtypes.AccAddressFromBech32(cfg.KeyFrom); err != nil { - r, err := kr.KeyByAddress(cosmosAddr) + r, err := kr.Key(cfg.KeyFrom) if err != nil { return Keyring{}, err } keyRecord = r } else { - r, err := kr.Key(cfg.KeyFrom) + r, err := kr.KeyByAddress(cosmosAddr) if err != nil { return Keyring{}, err } From dd8648d658ce4418eeb1a499dcc7407199ea6114 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 25 Jan 2024 18:55:45 +0100 Subject: [PATCH 50/99] refactor cosmos keyring usage --- cmd/peggo/keys.go | 139 -------------------------------------- cmd/peggo/orchestrator.go | 36 +--------- cmd/peggo/tx.go | 32 ++++----- 3 files changed, 18 insertions(+), 189 deletions(-) diff --git a/cmd/peggo/keys.go b/cmd/peggo/keys.go index 744e3fd2..5ab6ce59 100644 --- a/cmd/peggo/keys.go +++ b/cmd/peggo/keys.go @@ -8,7 +8,6 @@ import ( "log" "math/big" "os" - "path/filepath" "strings" "syscall" @@ -27,7 +26,6 @@ import ( "github.com/pkg/errors" "golang.org/x/crypto/ssh/terminal" - "github.com/InjectiveLabs/sdk-go/chain/crypto/ethsecp256k1" "github.com/InjectiveLabs/sdk-go/chain/crypto/hd" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" @@ -38,143 +36,6 @@ const defaultKeyringKeyName = "validator" var emptyCosmosAddress = cosmtypes.AccAddress{} var cdc = MakeEncodingConfig().Marshaler -func initCosmosKeyring( - cosmosKeyringDir *string, - cosmosKeyringAppName *string, - cosmosKeyringBackend *string, - cosmosKeyFrom *string, - cosmosKeyPassphrase *string, - cosmosPrivKey *string, - cosmosUseLedger *bool, -) (cosmtypes.AccAddress, keyring.Keyring, error) { - - switch { - case len(*cosmosPrivKey) > 0: - if *cosmosUseLedger { - err := errors.New("cannot combine ledger and privkey options") - return emptyCosmosAddress, nil, err - } - - pkBytes, err := hexToBytes(*cosmosPrivKey) - if err != nil { - err = errors.Wrap(err, "failed to hex-decode cosmos account privkey") - return emptyCosmosAddress, nil, err - } - - // Specfic to Injective chain with Ethermint keys - // Should be secp256k1.PrivKey for generic Cosmos chain - cosmosAccPk := ðsecp256k1.PrivKey{ - Key: pkBytes, - } - - addressFromPk := cosmtypes.AccAddress(cosmosAccPk.PubKey().Address().Bytes()) - - var keyName string - - // check that if cosmos 'From' specified separately, it must match the provided privkey, - if len(*cosmosKeyFrom) > 0 { - addressFrom, err := cosmtypes.AccAddressFromBech32(*cosmosKeyFrom) - if err == nil { - if !bytes.Equal(addressFrom.Bytes(), addressFromPk.Bytes()) { - err = errors.Errorf("expected account address %s but got %s from the private key", addressFrom.String(), addressFromPk.String()) - return emptyCosmosAddress, nil, err - } - } else { - // use it as a name then - keyName = *cosmosKeyFrom - } - } - - if len(keyName) == 0 { - keyName = defaultKeyringKeyName - } - - // wrap a PK into a Keyring - kb, err := KeyringForPrivKey(keyName, cosmosAccPk) - return addressFromPk, kb, err - - case len(*cosmosKeyFrom) > 0: - var fromIsAddress bool - addressFrom, err := cosmtypes.AccAddressFromBech32(*cosmosKeyFrom) - if err == nil { - fromIsAddress = true - } - - var passReader io.Reader = os.Stdin - if len(*cosmosKeyPassphrase) > 0 { - passReader = newPassReader(*cosmosKeyPassphrase) - } - - var absoluteKeyringDir string - if filepath.IsAbs(*cosmosKeyringDir) { - absoluteKeyringDir = *cosmosKeyringDir - } else { - absoluteKeyringDir, _ = filepath.Abs(*cosmosKeyringDir) - } - - kb, err := keyring.New( - *cosmosKeyringAppName, - *cosmosKeyringBackend, - absoluteKeyringDir, - passReader, - cdc, - hd.EthSecp256k1Option(), - ) - if err != nil { - err = errors.Wrap(err, "failed to init keyring") - return emptyCosmosAddress, nil, err - } - - var keyInfo *keyring.Record - if fromIsAddress { - if keyInfo, err = kb.KeyByAddress(addressFrom); err != nil { - err = errors.Wrapf(err, "couldn't find an entry for the key %s in keybase", addressFrom.String()) - return emptyCosmosAddress, nil, err - } - } else { - if keyInfo, err = kb.Key(*cosmosKeyFrom); err != nil { - err = errors.Wrapf(err, "could not find an entry for the key '%s' in keybase", *cosmosKeyFrom) - return emptyCosmosAddress, nil, err - } - } - - switch keyType := keyInfo.GetType(); keyType { - case keyring.TypeLocal: - // kb has a key and it's totally usable - addr, err := keyInfo.GetAddress() - if err != nil { - return emptyCosmosAddress, nil, err - } - return addr, kb, nil - case keyring.TypeLedger: - // the kb stores references to ledger keys, so we must explicitly - // check that. kb doesn't know how to scan HD keys - they must be added manually before - if *cosmosUseLedger { - addr, err := keyInfo.GetAddress() - if err != nil { - return emptyCosmosAddress, nil, err - } - return addr, kb, nil - } - err := errors.Errorf("'%s' key is a ledger reference, enable ledger option", keyInfo.Name) - return emptyCosmosAddress, nil, err - case keyring.TypeOffline: - err := errors.Errorf("'%s' key is an offline key, not supported yet", keyInfo.Name) - return emptyCosmosAddress, nil, err - case keyring.TypeMulti: - err := errors.Errorf("'%s' key is an multisig key, not supported yet", keyInfo.Name) - return emptyCosmosAddress, nil, err - default: - err := errors.Errorf("'%s' key has unsupported type: %s", keyInfo.Name, keyType) - return emptyCosmosAddress, nil, err - } - - default: - err := errors.New("insufficient cosmos key details provided") - return emptyCosmosAddress, nil, err - } -} - var emptyEthAddress = ethcmn.Address{} func initEthereumAccountsManager( diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 99729e65..b9ed8249 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -2,7 +2,6 @@ package main import ( "context" - "fmt" "os" chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" @@ -47,15 +46,6 @@ func orchestratorCmd(cmd *cli.Cmd) { log.Fatalln("cannot use Ledger for orchestrator, since signatures must be realtime") } - println("# Previous cfg #") - fmt.Printf("cosmosKeyringDir: %v\n", *cfg.cosmosKeyringDir) - fmt.Printf("cosmosKeyringAppName: %v\n", *cfg.cosmosKeyringAppName) - fmt.Printf("cosmosKeyringBackend: %v\n", *cfg.cosmosKeyringBackend) - fmt.Printf("cosmosKeyFrom: %v\n", *cfg.cosmosKeyFrom) - fmt.Printf("cosmosKeyPassphrase: %v\n", *cfg.cosmosKeyPassphrase) - fmt.Printf("cosmosPrivKey: %v\n", *cfg.cosmosPrivKey) - fmt.Printf("cosmosUseLedger: %v\n", *cfg.cosmosUseLedger) - keyringCfg := cosmos.KeyringConfig{ KeyringDir: *cfg.cosmosKeyringDir, KeyringAppName: *cfg.cosmosKeyringAppName, @@ -66,31 +56,9 @@ func orchestratorCmd(cmd *cli.Cmd) { UseLedger: *cfg.cosmosUseLedger, } - println("# new cfg ") - fmt.Printf("cosmosKeyringDir: %v\n", keyringCfg.KeyringDir) - fmt.Printf("cosmosKeyringAppName: %v\n", keyringCfg.KeyringAppName) - fmt.Printf("cosmosKeyringBackend: %v\n", keyringCfg.KeyringBackend) - fmt.Printf("cosmosKeyFrom: %v\n", keyringCfg.KeyFrom) - fmt.Printf("cosmosKeyPassphrase: %v\n", keyringCfg.KeyPassphrase) - fmt.Printf("cosmosPrivKey: %v\n", keyringCfg.PrivateKey) - fmt.Printf("cosmosUseLedger: %v\n", keyringCfg.UseLedger) - cosmosKeyring, err := cosmos.NewKeyring(keyringCfg) orShutdown(err) - //valAddress, cosmosKeyring, err := initCosmosKeyring( - // cfg.cosmosKeyringDir, - // cfg.cosmosKeyringAppName, - // cfg.cosmosKeyringBackend, - // cfg.cosmosKeyFrom, - // cfg.cosmosKeyPassphrase, - // cfg.cosmosPrivKey, - // cfg.cosmosUseLedger, - //) - //if err != nil { - // log.WithError(err).Fatalln("failed to initialize Injective keyring") - //} - ethKeyFromAddress, signerFn, personalSignFn, err := initEthereumAccountsManager( uint64(*cfg.ethChainID), cfg.ethKeystoreDir, @@ -103,7 +71,7 @@ func orchestratorCmd(cmd *cli.Cmd) { log.WithError(err).Fatalln("failed to initialize Ethereum account") } - cosmosCfg := cosmos.NetworkConfig{ + netCfg := cosmos.NetworkConfig{ ChainID: *cfg.cosmosChainID, ValidatorAddress: cosmosKeyring.Addr.String(), CosmosGRPC: *cfg.cosmosGRPC, @@ -111,7 +79,7 @@ func orchestratorCmd(cmd *cli.Cmd) { GasPrice: *cfg.cosmosGasPrices, } - cosmosNetwork, err := cosmos.NewCosmosNetwork(cosmosKeyring, personalSignFn, cosmosCfg) + cosmosNetwork, err := cosmos.NewCosmosNetwork(cosmosKeyring, personalSignFn, netCfg) orShutdown(err) ctx, cancelFn := context.WithCancel(context.Background()) diff --git a/cmd/peggo/tx.go b/cmd/peggo/tx.go index 3cad7c55..4c1af139 100644 --- a/cmd/peggo/tx.go +++ b/cmd/peggo/tx.go @@ -94,19 +94,19 @@ func registerEthKeyCmd(cmd *cli.Cmd) { log.Warningln("beware: you cannot really use Ledger for orchestrator, so make sure the Ethereum key is accessible outside of it") } - valAddress, cosmosKeyring, err := initCosmosKeyring( - cosmosKeyringDir, - cosmosKeyringAppName, - cosmosKeyringBackend, - cosmosKeyFrom, - cosmosKeyPassphrase, - cosmosPrivKey, - cosmosUseLedger, - ) - if err != nil { - log.WithError(err).Fatalln("failed to init Cosmos keyring") + keyringCfg := cosmos.KeyringConfig{ + KeyringDir: *cosmosKeyringDir, + KeyringAppName: *cosmosKeyringAppName, + KeyringBackend: *cosmosKeyringBackend, + KeyFrom: *cosmosKeyFrom, + KeyPassphrase: *cosmosKeyPassphrase, + PrivateKey: *cosmosPrivKey, + UseLedger: *cosmosUseLedger, } + keyring, err := cosmos.NewKeyring(keyringCfg) + orShutdown(err) + ethKeyFromAddress, _, personalSignFn, err := initEthereumAccountsManager( 0, ethKeystoreDir, @@ -119,7 +119,7 @@ func registerEthKeyCmd(cmd *cli.Cmd) { log.WithError(err).Fatalln("failed to init Ethereum account") } - log.Infoln("Using Cosmos ValAddress", valAddress.String()) + log.Infoln("Using Cosmos ValAddress", keyring.Addr.String()) log.Infoln("Using Ethereum address", ethKeyFromAddress.String()) actionConfirmed := *alwaysAutoConfirm || stdinConfirm("Confirm UpdatePeggyOrchestratorAddresses transaction? [y/N]: ") @@ -127,9 +127,9 @@ func registerEthKeyCmd(cmd *cli.Cmd) { return } - net, err := cosmos.NewCosmosNetwork(cosmosKeyring, personalSignFn, cosmos.NetworkConfig{ + net, err := cosmos.NewCosmosNetwork(keyring, personalSignFn, cosmos.NetworkConfig{ ChainID: *cosmosChainID, - ValidatorAddress: valAddress.String(), + ValidatorAddress: keyring.Addr.String(), CosmosGRPC: *cosmosGRPC, TendermintRPC: *cosmosGasPrices, GasPrice: *tendermintRPC, @@ -142,13 +142,13 @@ func registerEthKeyCmd(cmd *cli.Cmd) { broadcastCtx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second) defer cancelFn() - if err = peggy.BroadcastClient(net).UpdatePeggyOrchestratorAddresses(broadcastCtx, ethKeyFromAddress, valAddress); err != nil { + if err = peggy.BroadcastClient(net).UpdatePeggyOrchestratorAddresses(broadcastCtx, ethKeyFromAddress, keyring.Addr); err != nil { log.WithError(err).Errorln("failed to broadcast Tx") time.Sleep(time.Second) return } log.Infof("Registered Ethereum address %s for validator address %s", - ethKeyFromAddress, valAddress.String()) + ethKeyFromAddress, keyring.Addr.String()) } } From 20bfe635b58337754893a8715f835caa112fa8fb Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 26 Jan 2024 11:24:30 +0100 Subject: [PATCH 51/99] cleanup --- cmd/peggo/keys.go | 42 ---------------- orchestrator/cosmos/config.go | 58 ---------------------- orchestrator/cosmos/keyring.go | 90 ++++++++++++++++------------------ orchestrator/cosmos/network.go | 51 +++++++++++++++++++ 4 files changed, 92 insertions(+), 149 deletions(-) delete mode 100644 orchestrator/cosmos/config.go diff --git a/cmd/peggo/keys.go b/cmd/peggo/keys.go index 5ab6ce59..18cfa754 100644 --- a/cmd/peggo/keys.go +++ b/cmd/peggo/keys.go @@ -2,19 +2,13 @@ package main import ( "bytes" - "crypto/rand" "fmt" "io" - "log" "math/big" "os" "strings" "syscall" - cosmcrypto "github.com/cosmos/cosmos-sdk/crypto" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - cosmtypes "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/usbwallet" @@ -26,16 +20,9 @@ import ( "github.com/pkg/errors" "golang.org/x/crypto/ssh/terminal" - "github.com/InjectiveLabs/sdk-go/chain/crypto/hd" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" ) -const defaultKeyringKeyName = "validator" - -var emptyCosmosAddress = cosmtypes.AccAddress{} -var cdc = MakeEncodingConfig().Marshaler - var emptyEthAddress = ethcmn.Address{} func initEthereumAccountsManager( @@ -253,32 +240,3 @@ func (r *passReader) Read(p []byte) (n int, err error) { return } - -// KeyringForPrivKey creates a temporary in-mem keyring for a PrivKey. -// Allows to init Context when the key has been provided in plaintext and parsed. -func KeyringForPrivKey(name string, privKey cryptotypes.PrivKey) (keyring.Keyring, error) { - kb := keyring.NewInMemory(cdc, hd.EthSecp256k1Option()) - tmpPhrase := randPhrase(64) - armored := cosmcrypto.EncryptArmorPrivKey(privKey, tmpPhrase, privKey.Type()) - err := kb.ImportPrivKey(name, armored, tmpPhrase) - if err != nil { - err = errors.Wrap(err, "failed to import privkey") - return nil, err - } - - return kb, nil -} - -func randPhrase(size int) string { - buf := make([]byte, size) - _, err := rand.Read(buf) - orPanic(err) - - return string(buf) -} - -func orPanic(err error) { - if err != nil { - log.Panicln() - } -} diff --git a/orchestrator/cosmos/config.go b/orchestrator/cosmos/config.go deleted file mode 100644 index a9aaf93b..00000000 --- a/orchestrator/cosmos/config.go +++ /dev/null @@ -1,58 +0,0 @@ -package cosmos - -import ( - "fmt" - - clientcommon "github.com/InjectiveLabs/sdk-go/client/common" - log "github.com/xlab/suplog" -) - -type NetworkConfig struct { - ChainID, - ValidatorAddress, - CosmosGRPC, - TendermintRPC, - GasPrice string -} - -func (cfg NetworkConfig) loadClientConfig() clientcommon.Network { - if custom := cfg.CosmosGRPC != "" && cfg.TendermintRPC != ""; custom { - return customEndpoints(cfg) - } - - return loadBalancedEndpoints(cfg) -} - -func customEndpoints(cfg NetworkConfig) clientcommon.Network { - c := clientcommon.LoadNetwork("devnet", "") - c.Name = "custom" - c.ChainId = cfg.ChainID - c.Fee_denom = "inj" - c.TmEndpoint = cfg.TendermintRPC - c.ChainGrpcEndpoint = cfg.CosmosGRPC - c.ExplorerGrpcEndpoint = "" - c.LcdEndpoint = "" - c.ExplorerGrpcEndpoint = "" - - log.Infoln("using custom endpoints for Injective") - - return c -} - -func loadBalancedEndpoints(cfg NetworkConfig) clientcommon.Network { - var networkName string - switch cfg.ChainID { - case "injective-1": - networkName = "mainnet" - case "injective-777": - networkName = "devnet" - case "injective-888": - networkName = "testnet" - default: - panic(fmt.Errorf("no provider for chain id %s", cfg.ChainID)) - } - - log.Infoln("using load balanced endpoints for Injective") - - return clientcommon.LoadNetwork(networkName, "lb") -} diff --git a/orchestrator/cosmos/keyring.go b/orchestrator/cosmos/keyring.go index 1c0cc40a..e3d5253f 100644 --- a/orchestrator/cosmos/keyring.go +++ b/orchestrator/cosmos/keyring.go @@ -4,21 +4,21 @@ import ( "bytes" "crypto/rand" "encoding/hex" - "github.com/InjectiveLabs/sdk-go/chain/crypto/ethsecp256k1" - "github.com/InjectiveLabs/sdk-go/chain/crypto/hd" - "github.com/cosmos/cosmos-sdk/codec/types" - cosmcrypto "github.com/cosmos/cosmos-sdk/crypto" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - cosmtypes "github.com/cosmos/cosmos-sdk/types" - "github.com/pkg/errors" "io" "os" "path/filepath" "strings" + cosmoscodec "github.com/cosmos/cosmos-sdk/codec" + cosmoscdctypes "github.com/cosmos/cosmos-sdk/codec/types" + cosmoscrypto "github.com/cosmos/cosmos-sdk/crypto" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + "github.com/pkg/errors" + "github.com/InjectiveLabs/sdk-go/chain/codec" - cosmoscdc "github.com/cosmos/cosmos-sdk/codec" + "github.com/InjectiveLabs/sdk-go/chain/crypto/ethsecp256k1" + "github.com/InjectiveLabs/sdk-go/chain/crypto/hd" ) const ( @@ -42,7 +42,7 @@ func (cfg KeyringConfig) withPrivateKey() bool { type Keyring struct { keyring.Keyring - Addr cosmtypes.AccAddress + Addr cosmostypes.AccAddress } func NewKeyring(cfg KeyringConfig) (Keyring, error) { @@ -50,8 +50,7 @@ func NewKeyring(cfg KeyringConfig) (Keyring, error) { return newInMemoryKeyring(cfg) } - return newCosmosKeyring(cfg) - + return newKeyringFromDir(cfg) } func newInMemoryKeyring(cfg KeyringConfig) (Keyring, error) { @@ -71,11 +70,11 @@ func newInMemoryKeyring(cfg KeyringConfig) (Keyring, error) { var ( cosmosPK = ðsecp256k1.PrivKey{Key: pkRaw} - cosmosAddr = cosmtypes.AccAddress(cosmosPK.PubKey().Address()) + cosmosAddr = cosmostypes.AccAddress(cosmosPK.PubKey().Address()) keyName = DefaultKeyName ) - from, err := cosmtypes.AccAddressFromBech32(cfg.KeyFrom) + from, err := cosmostypes.AccAddressFromBech32(cfg.KeyFrom) if err != nil { keyName = cfg.KeyFrom // use it as key name } @@ -84,20 +83,29 @@ func newInMemoryKeyring(cfg KeyringConfig) (Keyring, error) { return Keyring{}, errors.Errorf("expected account address %s but got %s from the private key", from.String(), cosmosAddr.String()) } - k, err := KeyringForPrivKey(keyName, cosmosPK) + // Create a temporary in-mem keyring for cosmosPK. + // Allows to init Context when the key has been provided in plaintext and parsed. + tmpPhrase := randPhrase(64) + armored := cosmoscrypto.EncryptArmorPrivKey(cosmosPK, tmpPhrase, cosmosPK.Type()) + + kr := keyring.NewInMemory(Codec(), hd.EthSecp256k1Option()) + if err := kr.ImportPrivKey(keyName, armored, tmpPhrase); err != nil { + return Keyring{}, errors.Wrap(err, "failed to import private key") + } + if err != nil { return Keyring{}, errors.Wrap(err, "failed to initialize cosmos keyring") } - kr := Keyring{ - Keyring: k, + k := Keyring{ + Keyring: kr, Addr: cosmosAddr, } - return kr, nil + return k, nil } -func newCosmosKeyring(cfg KeyringConfig) (Keyring, error) { +func newKeyringFromDir(cfg KeyringConfig) (Keyring, error) { if len(cfg.KeyFrom) == 0 { return Keyring{}, errors.New("insufficient cosmos details provided") } @@ -131,7 +139,7 @@ func newCosmosKeyring(cfg KeyringConfig) (Keyring, error) { } var keyRecord *keyring.Record - if cosmosAddr, err := cosmtypes.AccAddressFromBech32(cfg.KeyFrom); err != nil { + if cosmosAddr, err := cosmostypes.AccAddressFromBech32(cfg.KeyFrom); err != nil { r, err := kr.Key(cfg.KeyFrom) if err != nil { return Keyring{}, err @@ -185,52 +193,36 @@ func newCosmosKeyring(cfg KeyringConfig) (Keyring, error) { } } -// KeyringForPrivKey creates a temporary in-mem keyring for a PrivKey. -// Allows to init Context when the key has been provided in plaintext and parsed. -func KeyringForPrivKey(name string, privKey cryptotypes.PrivKey) (keyring.Keyring, error) { - kb := keyring.NewInMemory(Codec(), hd.EthSecp256k1Option()) - tmpPhrase := randPhrase(64) - armored := cosmcrypto.EncryptArmorPrivKey(privKey, tmpPhrase, privKey.Type()) - err := kb.ImportPrivKey(name, armored, tmpPhrase) - if err != nil { - err = errors.Wrap(err, "failed to import privkey") - return nil, err - } +func Codec() cosmoscodec.Codec { + iRegistry := cosmoscdctypes.NewInterfaceRegistry() + codec.RegisterInterfaces(iRegistry) + codec.RegisterLegacyAminoCodec(cosmoscodec.NewLegacyAmino()) - return kb, nil -} - -func Codec() cosmoscdc.Codec { - interfaceRegistry := types.NewInterfaceRegistry() - codec.RegisterInterfaces(interfaceRegistry) - codec.RegisterLegacyAminoCodec(cosmoscdc.NewLegacyAmino()) - - return cosmoscdc.NewProtoCodec(interfaceRegistry) + return cosmoscodec.NewProtoCodec(iRegistry) } func randPhrase(size int) string { buf := make([]byte, size) - _, err := rand.Read(buf) - if err != nil { + if _, err := rand.Read(buf); err != nil { panic("rand failed") } return string(buf) } -func newPassReader(pass string) io.Reader { - return &passReader{ - pass: pass, - buf: new(bytes.Buffer), - } -} +var _ io.Reader = &passReader{} type passReader struct { pass string buf *bytes.Buffer } -var _ io.Reader = &passReader{} +func newPassReader(pass string) io.Reader { + return &passReader{ + pass: pass, + buf: new(bytes.Buffer), + } +} func (r *passReader) Read(p []byte) (n int, err error) { n, err = r.buf.Read(p) diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 56dc3f02..9d2b88cf 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -2,6 +2,7 @@ package cosmos import ( "context" + "fmt" "time" comethttp "github.com/cometbft/cometbft/rpc/client/http" @@ -19,6 +20,14 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" ) +type NetworkConfig struct { + ChainID, + ValidatorAddress, + CosmosGRPC, + TendermintRPC, + GasPrice string +} + type Network interface { peggy.QueryClient peggy.BroadcastClient @@ -93,3 +102,45 @@ func awaitConnection(client chain.ChainClient, timeout time.Duration) *grpc.Clie } } } + +func (cfg NetworkConfig) loadClientConfig() clientcommon.Network { + if custom := cfg.CosmosGRPC != "" && cfg.TendermintRPC != ""; custom { + return customEndpoints(cfg) + } + + return loadBalancedEndpoints(cfg) +} + +func customEndpoints(cfg NetworkConfig) clientcommon.Network { + c := clientcommon.LoadNetwork("devnet", "") + c.Name = "custom" + c.ChainId = cfg.ChainID + c.Fee_denom = "inj" + c.TmEndpoint = cfg.TendermintRPC + c.ChainGrpcEndpoint = cfg.CosmosGRPC + c.ExplorerGrpcEndpoint = "" + c.LcdEndpoint = "" + c.ExplorerGrpcEndpoint = "" + + log.Infoln("using custom endpoints for Injective") + + return c +} + +func loadBalancedEndpoints(cfg NetworkConfig) clientcommon.Network { + var networkName string + switch cfg.ChainID { + case "injective-1": + networkName = "mainnet" + case "injective-777": + networkName = "devnet" + case "injective-888": + networkName = "testnet" + default: + panic(fmt.Errorf("no provider for chain id %s", cfg.ChainID)) + } + + log.Infoln("using load balanced endpoints for Injective") + + return clientcommon.LoadNetwork(networkName, "lb") +} From aeced1a927f48a5e31cd1a85f16c6a8086eb42ed Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 26 Jan 2024 13:30:43 +0100 Subject: [PATCH 52/99] refactor configs --- cmd/peggo/orchestrator.go | 16 ++++++++------ orchestrator/orchestrator.go | 41 +++++++++++++++++------------------- orchestrator/relayer.go | 10 ++++----- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index b9ed8249..9ccc93da 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -4,7 +4,6 @@ import ( "context" "os" - chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" gethcommon "github.com/ethereum/go-ethereum/common" cli "github.com/jawher/mow.cli" "github.com/xlab/closer" @@ -15,6 +14,7 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/version" + chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" ) // startOrchestrator action runs an infinite loop, @@ -118,12 +118,14 @@ func orchestratorCmd(cmd *cli.Cmd) { cosmosNetwork, ethereumNet, coingeckoFeed, - erc20ContractMapping, - *cfg.minBatchFeeUSD, - *cfg.relayValsets, - *cfg.relayBatches, - *cfg.relayValsetOffsetDur, - *cfg.relayBatchOffsetDur, + orchestrator.Config{ + MinBatchFeeUSD: *cfg.minBatchFeeUSD, + ERC20ContractMapping: erc20ContractMapping, + RelayValsetOffsetDur: *cfg.relayValsetOffsetDur, + RelayBatchOffsetDur: *cfg.relayBatchOffsetDur, + RelayValsets: *cfg.relayValsets, + RelayBatches: *cfg.relayBatches, + }, ) orShutdown(err) diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 28088b4f..35740136 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -21,25 +21,29 @@ type PriceFeed interface { QueryUSDPrice(address gethcommon.Address) (float64, error) } +type Config struct { + MinBatchFeeUSD float64 + ERC20ContractMapping map[gethcommon.Address]string + RelayValsetOffsetDur string + RelayBatchOffsetDur string + RelayValsets bool + RelayBatches bool +} + type PeggyOrchestrator struct { logger log.Logger svcTags metrics.Tags - inj cosmos.Network - orchestratorAddr cosmostypes.AccAddress - + inj cosmos.Network eth EthereumNetwork pricefeed PriceFeed + orchestratorAddr cosmostypes.AccAddress erc20ContractMapping map[gethcommon.Address]string relayValsetOffsetDur time.Duration relayBatchOffsetDur time.Duration minBatchFeeUSD float64 maxAttempts uint // max number of times a retry func will be called before exiting - - valsetRelayEnabled bool - batchRelayEnabled bool - periodicBatchRequesting bool } func NewPeggyOrchestrator( @@ -47,12 +51,7 @@ func NewPeggyOrchestrator( injective cosmos.Network, ethereum EthereumNetwork, priceFeed PriceFeed, - erc20ContractMapping map[gethcommon.Address]string, - minBatchFeeUSD float64, - valsetRelayingEnabled, - batchRelayingEnabled bool, - valsetRelayingOffset, - batchRelayingOffset string, + cfg Config, ) (*PeggyOrchestrator, error) { o := &PeggyOrchestrator{ logger: log.DefaultLogger, @@ -61,15 +60,13 @@ func NewPeggyOrchestrator( orchestratorAddr: orchestratorAddr, eth: ethereum, pricefeed: priceFeed, - erc20ContractMapping: erc20ContractMapping, - minBatchFeeUSD: minBatchFeeUSD, - valsetRelayEnabled: valsetRelayingEnabled, - batchRelayEnabled: batchRelayingEnabled, - maxAttempts: 10, // default is 10 for retry pkg + erc20ContractMapping: cfg.ERC20ContractMapping, + minBatchFeeUSD: cfg.MinBatchFeeUSD, + maxAttempts: 10, // default for retry pkg } - if valsetRelayingEnabled { - dur, err := time.ParseDuration(valsetRelayingOffset) + if cfg.RelayValsets { + dur, err := time.ParseDuration(cfg.RelayValsetOffsetDur) if err != nil { return nil, errors.Wrapf(err, "valset relaying enabled but offset duration is not properly set") } @@ -77,8 +74,8 @@ func NewPeggyOrchestrator( o.relayValsetOffsetDur = dur } - if batchRelayingEnabled { - dur, err := time.ParseDuration(batchRelayingOffset) + if cfg.RelayBatches { + dur, err := time.ParseDuration(cfg.RelayBatchOffsetDur) if err != nil { return nil, errors.Wrapf(err, "batch relaying enabled but offset duration is not properly set") } diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 837cf8dd..8b69d74e 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -24,7 +24,7 @@ const ( ) func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context) (err error) { - if noRelay := !s.batchRelayEnabled && !s.valsetRelayEnabled; noRelay { + if noRelay := s.relayValsetOffsetDur == 0 && s.relayBatchOffsetDur == 0; noRelay { return nil } @@ -48,8 +48,8 @@ func (l *relayerLoop) Logger() log.Logger { func (l *relayerLoop) Run(ctx context.Context) error { l.Logger().WithFields(log.Fields{ "loop_duration": l.loopDuration.String(), - "relay_batches": l.batchRelayEnabled, - "relay_valsets": l.valsetRelayEnabled, + "relay_batches": l.relayBatchOffsetDur != 0, + "relay_valsets": l.relayValsetOffsetDur != 0, }).Debugln("starting Relayer loop...") return loops.RunLoop(ctx, l.loopDuration, func() error { @@ -75,7 +75,7 @@ func (l *relayerLoop) Run(ctx context.Context) error { var pg loops.ParanoidGroup - if l.valsetRelayEnabled { + if l.relayValsetOffsetDur != 0 { pg.Go(func() error { return retry.Do(func() error { return l.relayValset(ctx, latestEthValset) }, retry.Context(ctx), @@ -87,7 +87,7 @@ func (l *relayerLoop) Run(ctx context.Context) error { }) } - if l.batchRelayEnabled { + if l.relayBatchOffsetDur != 0 { pg.Go(func() error { return retry.Do(func() error { return l.relayBatch(ctx, latestEthValset) }, retry.Context(ctx), From 075d2e0677ba4c0c9fa03688a3f9ead61628dcd1 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 26 Jan 2024 13:35:06 +0100 Subject: [PATCH 53/99] clean orchestrator cmd --- cmd/peggo/orchestrator.go | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 9ccc93da..cc795291 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -22,8 +22,6 @@ import ( // // $ peggo orchestrator func orchestratorCmd(cmd *cli.Cmd) { - // orchestrator-specific CLI options - cmd.Before = func() { initMetrics(cmd) } @@ -46,7 +44,7 @@ func orchestratorCmd(cmd *cli.Cmd) { log.Fatalln("cannot use Ledger for orchestrator, since signatures must be realtime") } - keyringCfg := cosmos.KeyringConfig{ + cosmosKeyring, err := cosmos.NewKeyring(cosmos.KeyringConfig{ KeyringDir: *cfg.cosmosKeyringDir, KeyringAppName: *cfg.cosmosKeyringAppName, KeyringBackend: *cfg.cosmosKeyringBackend, @@ -54,9 +52,7 @@ func orchestratorCmd(cmd *cli.Cmd) { KeyPassphrase: *cfg.cosmosKeyPassphrase, PrivateKey: *cfg.cosmosPrivKey, UseLedger: *cfg.cosmosUseLedger, - } - - cosmosKeyring, err := cosmos.NewKeyring(keyringCfg) + }) orShutdown(err) ethKeyFromAddress, signerFn, personalSignFn, err := initEthereumAccountsManager( @@ -71,15 +67,13 @@ func orchestratorCmd(cmd *cli.Cmd) { log.WithError(err).Fatalln("failed to initialize Ethereum account") } - netCfg := cosmos.NetworkConfig{ + cosmosNetwork, err := cosmos.NewCosmosNetwork(cosmosKeyring, personalSignFn, cosmos.NetworkConfig{ ChainID: *cfg.cosmosChainID, ValidatorAddress: cosmosKeyring.Addr.String(), CosmosGRPC: *cfg.cosmosGRPC, TendermintRPC: *cfg.tendermintRPC, GasPrice: *cfg.cosmosGasPrices, - } - - cosmosNetwork, err := cosmos.NewCosmosNetwork(cosmosKeyring, personalSignFn, netCfg) + }) orShutdown(err) ctx, cancelFn := context.WithCancel(context.Background()) @@ -91,11 +85,13 @@ func orchestratorCmd(cmd *cli.Cmd) { log.WithError(err).Fatalln("failed to query peggy params, is injectived running?") } - peggyContractAddr := gethcommon.HexToAddress(peggyParams.BridgeEthereumAddress) - injTokenAddr := gethcommon.HexToAddress(peggyParams.CosmosCoinErc20Contract) - - erc20ContractMapping := make(map[gethcommon.Address]string) - erc20ContractMapping[injTokenAddr] = chaintypes.InjectiveCoin + var ( + peggyContractAddr = gethcommon.HexToAddress(peggyParams.BridgeEthereumAddress) + injTokenAddr = gethcommon.HexToAddress(peggyParams.CosmosCoinErc20Contract) + erc20ContractMapping = map[gethcommon.Address]string{ + injTokenAddr: chaintypes.InjectiveCoin, + } + ) // Connect to ethereum network ethereumNet, err := ethereum.NewNetwork( From a69eb365242c2df529236fce4eccdf2f877380ae Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Sat, 27 Jan 2024 11:57:22 +0100 Subject: [PATCH 54/99] refactor orch apis --- cmd/peggo/orchestrator.go | 11 +- orchestrator/batch_request.go | 58 +++++----- orchestrator/batch_request_test.go | 20 ++-- orchestrator/coingecko/coingecko.go | 12 +-- orchestrator/coingecko/coingecko_test.go | 4 +- orchestrator/oracle.go | 98 +++++++++-------- orchestrator/oracle_test.go | 50 ++++----- orchestrator/orchestrator.go | 55 +++++----- orchestrator/relayer.go | 131 +++++++++++++---------- orchestrator/relayer_test.go | 104 +++++++++--------- orchestrator/signer.go | 57 +++++----- orchestrator/signer_test.go | 20 ++-- 12 files changed, 321 insertions(+), 299 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index cc795291..47981e93 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -94,7 +94,7 @@ func orchestratorCmd(cmd *cli.Cmd) { ) // Connect to ethereum network - ethereumNet, err := ethereum.NewNetwork( + ethereumNetwork, err := ethereum.NewNetwork( *cfg.ethNodeRPC, peggyContractAddr, ethKeyFromAddress, @@ -106,14 +106,11 @@ func orchestratorCmd(cmd *cli.Cmd) { ) orShutdown(err) - coingeckoFeed := coingecko.NewCoingeckoPriceFeed(100, &coingecko.Config{BaseURL: *cfg.coingeckoApi}) - // Create peggo and run it peggo, err := orchestrator.NewPeggyOrchestrator( cosmosKeyring.Addr, - cosmosNetwork, - ethereumNet, - coingeckoFeed, + ethKeyFromAddress, + coingecko.NewPriceFeed(100, &coingecko.Config{BaseURL: *cfg.coingeckoApi}), orchestrator.Config{ MinBatchFeeUSD: *cfg.minBatchFeeUSD, ERC20ContractMapping: erc20ContractMapping, @@ -126,7 +123,7 @@ func orchestratorCmd(cmd *cli.Cmd) { orShutdown(err) go func() { - if err := peggo.Run(ctx); err != nil { + if err := peggo.Run(ctx, cosmosNetwork, ethereumNetwork); err != nil { log.Errorln(err) os.Exit(1) } diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 84879a0f..8440e8ff 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -4,38 +4,41 @@ import ( "context" "time" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/avast/retry-go" cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/shopspring/decimal" log "github.com/xlab/suplog" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/loops" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) -func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context) (err error) { - loop := batchRequestLoop{ +func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context, inj cosmos.Network) (err error) { + requester := batchRequester{ PeggyOrchestrator: s, - loopDuration: defaultLoopDur, + Injective: inj, + LoopDuration: defaultLoopDur, } - return loop.Run(ctx) + s.logger.WithField("loop_duration", requester.LoopDuration.String()).Debugln("starting BatchRequester...") + + return loops.RunLoop(ctx, requester.LoopDuration, requester.RequestBatchesLoop(ctx)) } -type batchRequestLoop struct { +type batchRequester struct { *PeggyOrchestrator - loopDuration time.Duration + Injective cosmos.Network + LoopDuration time.Duration } -func (l *batchRequestLoop) Logger() log.Logger { +func (l *batchRequester) Logger() log.Logger { return l.logger.WithField("loop", "BatchRequest") } -func (l *batchRequestLoop) Run(ctx context.Context) error { - l.logger.WithField("loop_duration", l.loopDuration.String()).Debugln("starting BatchRequester loop...") - - return loops.RunLoop(ctx, l.loopDuration, func() error { +func (l *batchRequester) RequestBatchesLoop(ctx context.Context) func() error { + return func() error { fees, err := l.getUnbatchedTokenFees(ctx) if err != nil { // non-fatal, just alert @@ -53,13 +56,13 @@ func (l *batchRequestLoop) Run(ctx context.Context) error { } return nil - }) + } } -func (l *batchRequestLoop) getUnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { +func (l *batchRequester) getUnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { var unbatchedFees []*peggytypes.BatchFees getUnbatchedTokenFeesFn := func() (err error) { - unbatchedFees, err = l.inj.UnbatchedTokensWithFees(ctx) + unbatchedFees, err = l.Injective.UnbatchedTokensWithFees(ctx) return err } @@ -76,22 +79,19 @@ func (l *batchRequestLoop) getUnbatchedTokenFees(ctx context.Context) ([]*peggyt return unbatchedFees, nil } -func (l *batchRequestLoop) requestBatch(ctx context.Context, fee *peggytypes.BatchFees) { - var ( - tokenAddr = gethcommon.HexToAddress(fee.Token) - tokenDenom = l.tokenDenom(tokenAddr) - ) - +func (l *batchRequester) requestBatch(ctx context.Context, fee *peggytypes.BatchFees) { + tokenAddr := gethcommon.HexToAddress(fee.Token) if thresholdMet := l.checkFeeThreshold(tokenAddr, fee.TotalFees); !thresholdMet { return } + tokenDenom := l.tokenDenom(tokenAddr) l.Logger().WithFields(log.Fields{"denom": tokenDenom, "token_contract": tokenAddr.String()}).Infoln("requesting batch on Injective") - _ = l.inj.SendRequestBatch(ctx, tokenDenom) + _ = l.Injective.SendRequestBatch(ctx, tokenDenom) } -func (l *batchRequestLoop) tokenDenom(tokenAddr gethcommon.Address) string { +func (l *batchRequester) tokenDenom(tokenAddr gethcommon.Address) string { if cosmosDenom, ok := l.erc20ContractMapping[tokenAddr]; ok { return cosmosDenom } @@ -100,19 +100,21 @@ func (l *batchRequestLoop) tokenDenom(tokenAddr gethcommon.Address) string { return peggytypes.PeggyDenomString(tokenAddr) } -func (l *batchRequestLoop) checkFeeThreshold(tokenAddr gethcommon.Address, fees cosmostypes.Int) bool { +func (l *batchRequester) checkFeeThreshold(tokenAddr gethcommon.Address, fees cosmostypes.Int) bool { if l.minBatchFeeUSD == 0 { return true } - tokenPriceInUSD, err := l.pricefeed.QueryUSDPrice(tokenAddr) + tokenPriceInUSD, err := l.priceFeed.QueryUSDPrice(tokenAddr) if err != nil { return false } - minFeeInUSDDec := decimal.NewFromFloat(l.minBatchFeeUSD) - tokenPriceInUSDDec := decimal.NewFromFloat(tokenPriceInUSD) - totalFeeInUSDDec := decimal.NewFromBigInt(fees.BigInt(), -18).Mul(tokenPriceInUSDDec) + var ( + minFeeInUSDDec = decimal.NewFromFloat(l.minBatchFeeUSD) + tokenPriceInUSDDec = decimal.NewFromFloat(tokenPriceInUSD) + totalFeeInUSDDec = decimal.NewFromBigInt(fees.BigInt(), -18).Mul(tokenPriceInUSDDec) + ) if totalFeeInUSDDec.LessThan(minFeeInUSDDec) { l.Logger().WithFields(log.Fields{"token_contract": tokenAddr.String(), "batch_fee": totalFeeInUSDDec.String(), "min_fee": minFeeInUSDDec.String()}).Debugln("insufficient token batch fee") diff --git a/orchestrator/batch_request_test.go b/orchestrator/batch_request_test.go index 3ae46e36..341870b6 100644 --- a/orchestrator/batch_request_test.go +++ b/orchestrator/batch_request_test.go @@ -31,7 +31,7 @@ func TestRequestBatches(t *testing.T) { maxAttempts: 1, } - loop := batchRequestLoop{ + loop := batchRequester{ PeggyOrchestrator: o, } @@ -53,7 +53,7 @@ func TestRequestBatches(t *testing.T) { maxAttempts: 1, } - loop := batchRequestLoop{ + loop := batchRequester{ PeggyOrchestrator: o, } @@ -84,7 +84,7 @@ func TestRequestBatches(t *testing.T) { o := &PeggyOrchestrator{ logger: suplog.DefaultLogger, inj: inj, - pricefeed: feed, + priceFeed: feed, maxAttempts: 1, minBatchFeeUSD: 51.0, erc20ContractMapping: map[eth.Address]string{ @@ -92,7 +92,7 @@ func TestRequestBatches(t *testing.T) { }, } - loop := batchRequestLoop{ + loop := batchRequester{ PeggyOrchestrator: o, } @@ -123,7 +123,7 @@ func TestRequestBatches(t *testing.T) { o := &PeggyOrchestrator{ logger: suplog.DefaultLogger, inj: inj, - pricefeed: feed, + priceFeed: feed, maxAttempts: 1, minBatchFeeUSD: 49.0, erc20ContractMapping: map[eth.Address]string{ @@ -131,7 +131,7 @@ func TestRequestBatches(t *testing.T) { }, } - loop := batchRequestLoop{ + loop := batchRequester{ PeggyOrchestrator: o, } @@ -157,14 +157,14 @@ func TestCheckFeeThreshold(t *testing.T) { o := &PeggyOrchestrator{ logger: suplog.DefaultLogger, - pricefeed: feed, + priceFeed: feed, minBatchFeeUSD: 21, erc20ContractMapping: map[eth.Address]string{ tokenAddr: "inj", }, } - loop := batchRequestLoop{ + loop := batchRequester{ PeggyOrchestrator: o, } @@ -185,14 +185,14 @@ func TestCheckFeeThreshold(t *testing.T) { o := &PeggyOrchestrator{ logger: suplog.DefaultLogger, - pricefeed: feed, + priceFeed: feed, minBatchFeeUSD: 333.333, erc20ContractMapping: map[eth.Address]string{ tokenAddr: "inj", }, } - loop := batchRequestLoop{ + loop := batchRequester{ PeggyOrchestrator: o, } diff --git a/orchestrator/coingecko/coingecko.go b/orchestrator/coingecko/coingecko.go index f10f69c1..2ba1ebf3 100644 --- a/orchestrator/coingecko/coingecko.go +++ b/orchestrator/coingecko/coingecko.go @@ -27,7 +27,7 @@ const ( var zeroPrice = float64(0) -type CoingeckoPriceFeed struct { +type PriceFeed struct { client *http.Client config *Config @@ -51,7 +51,7 @@ func urlJoin(baseURL string, segments ...string) string { } -func (cp *CoingeckoPriceFeed) QueryUSDPrice(erc20Contract common.Address) (float64, error) { +func (cp *PriceFeed) QueryUSDPrice(erc20Contract common.Address) (float64, error) { metrics.ReportFuncCall(cp.svcTags) doneFn := metrics.ReportFuncTiming(cp.svcTags) defer doneFn() @@ -123,10 +123,10 @@ func (cp *CoingeckoPriceFeed) QueryUSDPrice(erc20Contract common.Address) (float return tokenPriceInUSD, nil } -// NewCoingeckoPriceFeed returns price puller for given symbol. The price will be pulled +// NewPriceFeed returns price puller for given symbol. The price will be pulled // from endpoint and divided by scaleFactor. Symbol name (if reported by endpoint) must match. -func NewCoingeckoPriceFeed(interval time.Duration, endpointConfig *Config) *CoingeckoPriceFeed { - return &CoingeckoPriceFeed{ +func NewPriceFeed(interval time.Duration, endpointConfig *Config) *PriceFeed { + return &PriceFeed{ client: &http.Client{ Transport: &http.Transport{ ResponseHeaderTimeout: maxRespHeadersTime, @@ -159,7 +159,7 @@ func checkCoingeckoConfig(cfg *Config) *Config { return cfg } -func (cp *CoingeckoPriceFeed) CheckFeeThreshold(erc20Contract common.Address, totalFee cosmtypes.Int, minFeeInUSD float64) bool { +func (cp *PriceFeed) CheckFeeThreshold(erc20Contract common.Address, totalFee cosmtypes.Int, minFeeInUSD float64) bool { metrics.ReportFuncCall(cp.svcTags) doneFn := metrics.ReportFuncTiming(cp.svcTags) defer doneFn() diff --git a/orchestrator/coingecko/coingecko_test.go b/orchestrator/coingecko/coingecko_test.go index 1de34652..1ae706b4 100644 --- a/orchestrator/coingecko/coingecko_test.go +++ b/orchestrator/coingecko/coingecko_test.go @@ -13,7 +13,7 @@ func TestFeeThresholdTwoDecimals(t *testing.T) { // https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=0xe28b3b32b6c345a34ff64674606124dd5aceca30&vs_currencies=usd injTokenContract := common.HexToAddress("0xe28b3b32b6c345a34ff64674606124dd5aceca30") - coingeckoFeed := NewCoingeckoPriceFeed(100, &Config{}) + coingeckoFeed := NewPriceFeed(100, &Config{}) currentTokenPrice, _ := coingeckoFeed.QueryUSDPrice(injTokenContract) // "usd":9.35 minFeeInUSD := float64(23.5) // 23.5 USD to submit batch tx @@ -34,7 +34,7 @@ func TestFeeThresholdTwoDecimals(t *testing.T) { func TestFeeThresholdNineDecimals(t *testing.T) { // https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce&vs_currencies=usd shibTokenContract := common.HexToAddress("0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce") - coingeckoFeed := NewCoingeckoPriceFeed(100, &Config{}) + coingeckoFeed := NewPriceFeed(100, &Config{}) currentTokenPrice, _ := coingeckoFeed.QueryUSDPrice(shibTokenContract) // "usd":0.000008853 minFeeInUSD := float64(23.5) // 23.5 USD to submit batch tx diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 287e8971..3fb72ffb 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" log "github.com/xlab/suplog" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/loops" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" ) @@ -25,32 +26,41 @@ const ( // EthOracleMainLoop is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain // and ferried over to Cosmos where they will be used to issue tokens or process batches. -func (s *PeggyOrchestrator) EthOracleMainLoop(ctx context.Context, lastObservedBlock uint64) error { - loop := ethOracleLoop{ +func (s *PeggyOrchestrator) EthOracleMainLoop( + ctx context.Context, + inj cosmos.Network, + eth EthereumNetwork, + lastObservedBlock uint64, +) error { + oracle := ethOracle{ PeggyOrchestrator: s, - loopDuration: defaultLoopDur, - lastCheckedEthHeight: lastObservedBlock, - lastResyncWithInjective: time.Now(), + Injective: inj, + Ethereum: eth, + LoopDuration: defaultLoopDur, + LastObservedEthHeight: lastObservedBlock, + LastResyncWithInjective: time.Now(), } - return loop.Run(ctx) + s.logger.WithField("loop_duration", oracle.LoopDuration.String()).Debugln("starting EthOracle...") + + return loops.RunLoop(ctx, oracle.LoopDuration, oracle.ObserveEthEventsLoop(ctx)) } -type ethOracleLoop struct { +type ethOracle struct { *PeggyOrchestrator - loopDuration time.Duration - lastResyncWithInjective time.Time - lastCheckedEthHeight uint64 + Injective cosmos.Network + Ethereum EthereumNetwork + LoopDuration time.Duration + LastResyncWithInjective time.Time + LastObservedEthHeight uint64 } -func (l *ethOracleLoop) Logger() log.Logger { +func (l *ethOracle) Logger() log.Logger { return l.logger.WithField("loop", "EthOracle") } -func (l *ethOracleLoop) Run(ctx context.Context) error { - l.logger.WithField("loop_duration", l.loopDuration.String()).Debugln("starting EthOracle loop...") - - return loops.RunLoop(ctx, l.loopDuration, func() error { +func (l *ethOracle) ObserveEthEventsLoop(ctx context.Context) func() error { + return func() error { latestHeight, err := l.getLatestEthHeight(ctx) if err != nil { return err @@ -63,16 +73,16 @@ func (l *ethOracleLoop) Run(ctx context.Context) error { // ensure that latest block has minimum confirmations latestHeight = latestHeight - ethBlockConfirmationDelay - if latestHeight <= l.lastCheckedEthHeight { + if latestHeight <= l.LastObservedEthHeight { return nil } // ensure the block range is within defaultBlocksToSearch - if latestHeight > l.lastCheckedEthHeight+defaultBlocksToSearch { - latestHeight = l.lastCheckedEthHeight + defaultBlocksToSearch + if latestHeight > l.LastObservedEthHeight+defaultBlocksToSearch { + latestHeight = l.LastObservedEthHeight + defaultBlocksToSearch } - events, err := l.getEthEvents(ctx, l.lastCheckedEthHeight, latestHeight) + events, err := l.getEthEvents(ctx, l.LastObservedEthHeight, latestHeight) if err != nil { return err } @@ -81,8 +91,8 @@ func (l *ethOracleLoop) Run(ctx context.Context) error { return err } - l.Logger().WithFields(log.Fields{"block_start": l.lastCheckedEthHeight, "block_end": latestHeight}).Debugln("scanned Ethereum blocks") - l.lastCheckedEthHeight = latestHeight + l.Logger().WithFields(log.Fields{"block_start": l.LastObservedEthHeight, "block_end": latestHeight}).Debugln("scanned Ethereum blocks") + l.LastObservedEthHeight = latestHeight /** Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. @@ -90,41 +100,41 @@ func (l *ethOracleLoop) Run(ctx context.Context) error { 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. */ - if time.Since(l.lastResyncWithInjective) >= 48*time.Hour { + if time.Since(l.LastResyncWithInjective) >= 48*time.Hour { if err := l.autoResync(ctx); err != nil { return err } } return nil - }) + } } -func (l *ethOracleLoop) getEthEvents(ctx context.Context, startBlock, endBlock uint64) (ethEvents, error) { +func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint64) (ethEvents, error) { events := ethEvents{} scanEthEventsFn := func() error { - legacyDeposits, err := l.eth.GetSendToCosmosEvents(startBlock, endBlock) + legacyDeposits, err := l.Ethereum.GetSendToCosmosEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get SendToCosmos events") } - deposits, err := l.eth.GetSendToInjectiveEvents(startBlock, endBlock) + deposits, err := l.Ethereum.GetSendToInjectiveEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get SendToInjective events") } - withdrawals, err := l.eth.GetTransactionBatchExecutedEvents(startBlock, endBlock) + withdrawals, err := l.Ethereum.GetTransactionBatchExecutedEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get TransactionBatchExecuted events") } - erc20Deployments, err := l.eth.GetPeggyERC20DeployedEvents(startBlock, endBlock) + erc20Deployments, err := l.Ethereum.GetPeggyERC20DeployedEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get ERC20Deployed events") } - valsetUpdates, err := l.eth.GetValsetUpdatedEvents(startBlock, endBlock) + valsetUpdates, err := l.Ethereum.GetValsetUpdatedEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get ValsetUpdated events") } @@ -152,10 +162,10 @@ func (l *ethOracleLoop) getEthEvents(ctx context.Context, startBlock, endBlock u return events, nil } -func (l *ethOracleLoop) getLatestEthHeight(ctx context.Context) (uint64, error) { +func (l *ethOracle) getLatestEthHeight(ctx context.Context) (uint64, error) { var latestHeight uint64 getLatestEthHeightFn := func() error { - latestHeader, err := l.eth.HeaderByNumber(ctx, nil) + latestHeader, err := l.Ethereum.HeaderByNumber(ctx, nil) if err != nil { return errors.Wrap(err, "failed to get latest ethereum header") } @@ -178,9 +188,9 @@ func (l *ethOracleLoop) getLatestEthHeight(ctx context.Context) (uint64, error) return latestHeight, nil } -func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents) error { +func (l *ethOracle) sendNewEventClaims(ctx context.Context, events ethEvents) error { sendEventsFn := func() error { - lastClaim, err := l.inj.LastClaimEventByAddr(ctx, l.orchestratorAddr) + lastClaim, err := l.Injective.LastClaimEventByAddr(ctx, l.injAddr) if err != nil { return err } @@ -221,10 +231,10 @@ func (l *ethOracleLoop) sendNewEventClaims(ctx context.Context, events ethEvents return nil } -func (l *ethOracleLoop) autoResync(ctx context.Context) error { +func (l *ethOracle) autoResync(ctx context.Context) error { var latestHeight uint64 getLastClaimEventFn := func() (err error) { - latestHeight, err = l.getLastClaimBlockHeight(ctx) + latestHeight, err = l.getLastClaimBlockHeight(ctx, l.Injective) return } @@ -239,26 +249,26 @@ func (l *ethOracleLoop) autoResync(ctx context.Context) error { return err } - l.lastCheckedEthHeight = latestHeight - l.lastResyncWithInjective = time.Now() + l.LastObservedEthHeight = latestHeight + l.LastResyncWithInjective = time.Now() - l.Logger().WithFields(log.Fields{"last_resync_time": l.lastResyncWithInjective.String(), "last_claimed_eth_height": l.lastCheckedEthHeight}).Infoln("auto resync with last claimed event on Injective") + l.Logger().WithFields(log.Fields{"last_resync_time": l.LastResyncWithInjective.String(), "last_claimed_eth_height": l.LastObservedEthHeight}).Infoln("auto resync with last claimed event on Injective") return nil } -func (l *ethOracleLoop) sendEthEventClaim(ctx context.Context, event any) error { +func (l *ethOracle) sendEthEventClaim(ctx context.Context, event any) error { switch e := event.(type) { case *peggyevents.PeggySendToCosmosEvent: - return l.inj.SendOldDepositClaim(ctx, e) + return l.Injective.SendOldDepositClaim(ctx, e) case *peggyevents.PeggySendToInjectiveEvent: - return l.inj.SendDepositClaim(ctx, e) + return l.Injective.SendDepositClaim(ctx, e) case *peggyevents.PeggyValsetUpdatedEvent: - return l.inj.SendValsetClaim(ctx, e) + return l.Injective.SendValsetClaim(ctx, e) case *peggyevents.PeggyTransactionBatchExecutedEvent: - return l.inj.SendWithdrawalClaim(ctx, e) + return l.Injective.SendWithdrawalClaim(ctx, e) case *peggyevents.PeggyERC20DeployedEvent: - return l.inj.SendERC20DeployedClaim(ctx, e) + return l.Injective.SendERC20DeployedClaim(ctx, e) default: panic(errors.Errorf("unknown event type %T", e)) } diff --git a/orchestrator/oracle_test.go b/orchestrator/oracle_test.go index 2d24fa87..eeb1ade1 100644 --- a/orchestrator/oracle_test.go +++ b/orchestrator/oracle_test.go @@ -48,14 +48,14 @@ func TestEthOracle(t *testing.T) { maxAttempts: 1, } - loop := ethOracleLoop{ + loop := ethOracle{ PeggyOrchestrator: o, - lastResyncWithInjective: time.Now(), - lastCheckedEthHeight: 100, + LastResyncWithInjective: time.Now(), + LastObservedEthHeight: 100, } assert.NoError(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.lastCheckedEthHeight, uint64(100)) + assert.Equal(t, loop.LastObservedEthHeight, uint64(100)) }) t.Run("failed to get SendToCosmos events", func(t *testing.T) { @@ -76,14 +76,14 @@ func TestEthOracle(t *testing.T) { maxAttempts: 1, } - loop := ethOracleLoop{ + loop := ethOracle{ PeggyOrchestrator: o, - lastResyncWithInjective: time.Now(), - lastCheckedEthHeight: 100, + LastResyncWithInjective: time.Now(), + LastObservedEthHeight: 100, } assert.Error(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.lastCheckedEthHeight, uint64(100)) + assert.Equal(t, loop.LastObservedEthHeight, uint64(100)) }) t.Run("failed to get last claim event from injective", func(t *testing.T) { @@ -125,14 +125,14 @@ func TestEthOracle(t *testing.T) { maxAttempts: 1, } - loop := ethOracleLoop{ + loop := ethOracle{ PeggyOrchestrator: o, - lastResyncWithInjective: time.Now(), - lastCheckedEthHeight: 100, + LastResyncWithInjective: time.Now(), + LastObservedEthHeight: 100, } assert.Error(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.lastCheckedEthHeight, uint64(100)) + assert.Equal(t, loop.LastObservedEthHeight, uint64(100)) }) t.Run("old events are pruned", func(t *testing.T) { @@ -185,14 +185,14 @@ func TestEthOracle(t *testing.T) { maxAttempts: 1, } - loop := ethOracleLoop{ + loop := ethOracle{ PeggyOrchestrator: o, - lastResyncWithInjective: time.Now(), - lastCheckedEthHeight: 100, + LastResyncWithInjective: time.Now(), + LastObservedEthHeight: 100, } assert.NoError(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.lastCheckedEthHeight, uint64(104)) + assert.Equal(t, loop.LastObservedEthHeight, uint64(104)) assert.Equal(t, inj.sendEthereumClaimsCallCount, 0) }) @@ -246,14 +246,14 @@ func TestEthOracle(t *testing.T) { maxAttempts: 1, } - loop := ethOracleLoop{ + loop := ethOracle{ PeggyOrchestrator: o, - lastResyncWithInjective: time.Now(), - lastCheckedEthHeight: 100, + LastResyncWithInjective: time.Now(), + LastObservedEthHeight: 100, } assert.NoError(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.lastCheckedEthHeight, uint64(104)) + assert.Equal(t, loop.LastObservedEthHeight, uint64(104)) assert.Equal(t, inj.sendEthereumClaimsCallCount, 1) }) @@ -279,14 +279,14 @@ func TestEthOracle(t *testing.T) { maxAttempts: 1, } - loop := ethOracleLoop{ + loop := ethOracle{ PeggyOrchestrator: o, - lastResyncWithInjective: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), - lastCheckedEthHeight: 100, + LastResyncWithInjective: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), + LastObservedEthHeight: 100, } assert.NoError(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.lastCheckedEthHeight, uint64(101)) - assert.True(t, time.Since(loop.lastResyncWithInjective) < 1*time.Second) + assert.Equal(t, loop.LastObservedEthHeight, uint64(101)) + assert.True(t, time.Since(loop.LastResyncWithInjective) < 1*time.Second) }) } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 35740136..5ed2a767 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -34,11 +34,10 @@ type PeggyOrchestrator struct { logger log.Logger svcTags metrics.Tags - inj cosmos.Network - eth EthereumNetwork - pricefeed PriceFeed + injAddr cosmostypes.AccAddress + ethAddr gethcommon.Address - orchestratorAddr cosmostypes.AccAddress + priceFeed PriceFeed erc20ContractMapping map[gethcommon.Address]string relayValsetOffsetDur time.Duration relayBatchOffsetDur time.Duration @@ -48,18 +47,16 @@ type PeggyOrchestrator struct { func NewPeggyOrchestrator( orchestratorAddr cosmostypes.AccAddress, - injective cosmos.Network, - ethereum EthereumNetwork, + ethAddr gethcommon.Address, priceFeed PriceFeed, cfg Config, ) (*PeggyOrchestrator, error) { o := &PeggyOrchestrator{ logger: log.DefaultLogger, svcTags: metrics.Tags{"svc": "peggy_orchestrator"}, - inj: injective, - orchestratorAddr: orchestratorAddr, - eth: ethereum, - pricefeed: priceFeed, + injAddr: orchestratorAddr, + ethAddr: ethAddr, + priceFeed: priceFeed, erc20ContractMapping: cfg.ERC20ContractMapping, minBatchFeeUSD: cfg.MinBatchFeeUSD, maxAttempts: 10, // default for retry pkg @@ -88,19 +85,19 @@ func NewPeggyOrchestrator( // Run starts all major loops required to make // up the Orchestrator, all of these are async loops. -func (s *PeggyOrchestrator) Run(ctx context.Context) error { - if !s.hasDelegateValidator(ctx) { - return s.startRelayerMode(ctx) +func (s *PeggyOrchestrator) Run(ctx context.Context, inj cosmos.Network, eth EthereumNetwork) error { + if !s.hasDelegateValidator(ctx, inj) { + return s.startRelayerMode(ctx, inj, eth) } - return s.startValidatorMode(ctx) + return s.startValidatorMode(ctx, inj, eth) } -func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context) bool { +func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context, inj cosmos.Network) bool { subCtx, cancelFn := context.WithTimeout(ctx, 5*time.Second) defer cancelFn() - validator, err := s.inj.GetValidatorAddress(subCtx, s.eth.FromAddress()) + validator, err := inj.GetValidatorAddress(subCtx, s.ethAddr) if err != nil { s.logger.WithError(err).Debugln("no delegate validator address found") return false @@ -113,13 +110,13 @@ func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context) bool { // startValidatorMode runs all orchestrator processes. This is called // when peggo is run alongside a validator injective node. -func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context) error { +func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.Network, eth EthereumNetwork) error { log.Infoln("running orchestrator in validator mode") // get gethcommon block observed by this validator - lastObservedEthBlock, _ := s.getLastClaimBlockHeight(ctx) + lastObservedEthBlock, _ := s.getLastClaimBlockHeight(ctx, inj) if lastObservedEthBlock == 0 { - peggyParams, err := s.inj.PeggyParams(ctx) + peggyParams, err := inj.PeggyParams(ctx) if err != nil { s.logger.WithError(err).Fatalln("unable to query peggy module params, is injectived running?") } @@ -128,17 +125,17 @@ func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context) error { } // get peggy ID from contract - peggyContractID, err := s.eth.GetPeggyID(ctx) + peggyContractID, err := eth.GetPeggyID(ctx) if err != nil { s.logger.WithError(err).Fatalln("unable to query peggy ID from contract") } var pg loops.ParanoidGroup - pg.Go(func() error { return s.EthOracleMainLoop(ctx, lastObservedEthBlock) }) - pg.Go(func() error { return s.BatchRequesterLoop(ctx) }) - pg.Go(func() error { return s.EthSignerMainLoop(ctx, peggyContractID) }) - pg.Go(func() error { return s.RelayerMainLoop(ctx) }) + pg.Go(func() error { return s.EthOracleMainLoop(ctx, inj, eth, lastObservedEthBlock) }) + pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj) }) + pg.Go(func() error { return s.EthSignerMainLoop(ctx, inj, peggyContractID) }) + pg.Go(func() error { return s.RelayerMainLoop(ctx, inj, eth) }) return pg.Wait() } @@ -146,23 +143,23 @@ func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context) error { // startRelayerMode runs orchestrator processes that only relay specific // messages that do not require a validator's signature. This mode is run // alongside a non-validator injective node -func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context) error { +func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context, inj cosmos.Network, eth EthereumNetwork) error { log.Infoln("running orchestrator in relayer mode") var pg loops.ParanoidGroup - pg.Go(func() error { return s.BatchRequesterLoop(ctx) }) - pg.Go(func() error { return s.RelayerMainLoop(ctx) }) + pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj) }) + pg.Go(func() error { return s.RelayerMainLoop(ctx, inj, eth) }) return pg.Wait() } -func (s *PeggyOrchestrator) getLastClaimBlockHeight(ctx context.Context) (uint64, error) { +func (s *PeggyOrchestrator) getLastClaimBlockHeight(ctx context.Context, inj cosmos.Network) (uint64, error) { metrics.ReportFuncCall(s.svcTags) doneFn := metrics.ReportFuncTiming(s.svcTags) defer doneFn() - claim, err := s.inj.LastClaimEventByAddr(ctx, s.orchestratorAddr) + claim, err := inj.LastClaimEventByAddr(ctx, s.injAddr) if err != nil { return 0, err } diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 8b69d74e..18997b56 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -5,17 +5,18 @@ import ( "sort" "time" - "github.com/InjectiveLabs/metrics" - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/avast/retry-go" cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" log "github.com/xlab/suplog" + "github.com/InjectiveLabs/metrics" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/util" "github.com/InjectiveLabs/peggo/orchestrator/loops" + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) const ( @@ -23,53 +24,43 @@ const ( findValsetBlocksToSearch = 2000 ) -func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context) (err error) { +func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Network, eth EthereumNetwork) (err error) { if noRelay := s.relayValsetOffsetDur == 0 && s.relayBatchOffsetDur == 0; noRelay { return nil } - loop := relayerLoop{ + rel := relayer{ PeggyOrchestrator: s, - loopDuration: defaultRelayerLoopDur, + Injective: inj, + Ethereum: eth, + LoopDuration: defaultRelayerLoopDur, } - return loop.Run(ctx) + s.logger.WithFields(log.Fields{ + "loop_duration": rel.LoopDuration.String(), + "relay_batches": rel.relayBatchOffsetDur != 0, + "relay_valsets": rel.relayValsetOffsetDur != 0, + }).Debugln("starting Relayer...") + + return loops.RunLoop(ctx, rel.LoopDuration, rel.RelayValsetsAndBatchesLoop(ctx)) } -type relayerLoop struct { +type relayer struct { *PeggyOrchestrator - loopDuration time.Duration + Injective cosmos.Network + Ethereum EthereumNetwork + LoopDuration time.Duration } -func (l *relayerLoop) Logger() log.Logger { +func (l *relayer) Logger() log.Logger { return l.logger.WithField("loop", "Relayer") } -func (l *relayerLoop) Run(ctx context.Context) error { - l.Logger().WithFields(log.Fields{ - "loop_duration": l.loopDuration.String(), - "relay_batches": l.relayBatchOffsetDur != 0, - "relay_valsets": l.relayValsetOffsetDur != 0, - }).Debugln("starting Relayer loop...") - - return loops.RunLoop(ctx, l.loopDuration, func() error { - var latestEthValset *peggytypes.Valset - getLatestEthValsetFn := func() error { - vs, err := l.findLatestValsetOnEth(ctx) - if err != nil { - return err - } - - latestEthValset = vs - return nil - } - - if err := retry.Do(getLatestEthValsetFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to find latest valset on Ethereum, will retry (%d)", n) - })); err != nil { +func (l *relayer) RelayValsetsAndBatchesLoop(ctx context.Context) func() error { + return func() error { + // we need the latest vs on Ethereum for sig verification + ethValset, err := l.GetLatestEthValset(ctx) + if err != nil { return err } @@ -77,7 +68,7 @@ func (l *relayerLoop) Run(ctx context.Context) error { if l.relayValsetOffsetDur != 0 { pg.Go(func() error { - return retry.Do(func() error { return l.relayValset(ctx, latestEthValset) }, + return retry.Do(func() error { return l.relayValset(ctx, ethValset) }, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -89,7 +80,7 @@ func (l *relayerLoop) Run(ctx context.Context) error { if l.relayBatchOffsetDur != 0 { pg.Go(func() error { - return retry.Do(func() error { return l.relayBatch(ctx, latestEthValset) }, + return retry.Do(func() error { return l.relayBatch(ctx, ethValset) }, retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { @@ -107,17 +98,43 @@ func (l *relayerLoop) Run(ctx context.Context) error { } return nil - }) + } +} + +func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, error) { + var latestEthValset *peggytypes.Valset + getLatestEthValsetFn := func() error { + vs, err := l.findLatestValsetOnEth(ctx) + if err != nil { + return err + } + + latestEthValset = vs + return nil + } + + if err := retry.Do(getLatestEthValsetFn, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("failed to find latest valset on Ethereum, will retry (%d)", n) + }), + ); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return nil, err + } + + return latestEthValset, nil } -func (l *relayerLoop) relayValset(ctx context.Context, latestEthValset *peggytypes.Valset) error { +func (l *relayer) relayValset(ctx context.Context, latestEthValset *peggytypes.Valset) error { metrics.ReportFuncCall(l.svcTags) doneFn := metrics.ReportFuncTiming(l.svcTags) defer doneFn() // we should determine if we need to relay one // to Ethereum for that we will find the latest confirmed valset and compare it to the ethereum chain - latestValsets, err := l.inj.LatestValsets(ctx) + latestValsets, err := l.Injective.LatestValsets(ctx) if err != nil { return errors.Wrap(err, "failed to get latest valset updates from Injective") } @@ -128,7 +145,7 @@ func (l *relayerLoop) relayValset(ctx context.Context, latestEthValset *peggytyp ) for _, set := range latestValsets { - sigs, err := l.inj.AllValsetConfirms(ctx, set.Nonce) + sigs, err := l.Injective.AllValsetConfirms(ctx, set.Nonce) if err != nil { return errors.Wrapf(err, "failed to get valset confirmations for nonce %d", set.Nonce) } else if len(sigs) == 0 { @@ -149,7 +166,7 @@ func (l *relayerLoop) relayValset(ctx context.Context, latestEthValset *peggytyp return nil } - txHash, err := l.eth.SendEthValsetUpdate(ctx, + txHash, err := l.Ethereum.SendEthValsetUpdate(ctx, latestEthValset, oldestConfirmedValset, oldestConfirmedValsetSigs, @@ -164,8 +181,8 @@ func (l *relayerLoop) relayValset(ctx context.Context, latestEthValset *peggytyp return nil } -func (l *relayerLoop) shouldRelayValset(ctx context.Context, vs *peggytypes.Valset) bool { - latestEthereumValsetNonce, err := l.eth.GetValsetNonce(ctx) +func (l *relayer) shouldRelayValset(ctx context.Context, vs *peggytypes.Valset) bool { + latestEthereumValsetNonce, err := l.Ethereum.GetValsetNonce(ctx) if err != nil { l.Logger().WithError(err).Warningln("failed to get latest valset nonce from Ethereum") return false @@ -178,7 +195,7 @@ func (l *relayerLoop) shouldRelayValset(ctx context.Context, vs *peggytypes.Vals } // Check custom time delay offset - block, err := l.inj.GetBlock(ctx, int64(vs.Height)) + block, err := l.Injective.GetBlock(ctx, int64(vs.Height)) if err != nil { l.Logger().WithError(err).Warningln("unable to get latest block from Injective") return false @@ -195,12 +212,12 @@ func (l *relayerLoop) shouldRelayValset(ctx context.Context, vs *peggytypes.Vals return true } -func (l *relayerLoop) relayBatch(ctx context.Context, latestEthValset *peggytypes.Valset) error { +func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Valset) error { metrics.ReportFuncCall(l.svcTags) doneFn := metrics.ReportFuncTiming(l.svcTags) defer doneFn() - latestBatches, err := l.inj.LatestTransactionBatches(ctx) + latestBatches, err := l.Injective.LatestTransactionBatches(ctx) if err != nil { return err } @@ -211,7 +228,7 @@ func (l *relayerLoop) relayBatch(ctx context.Context, latestEthValset *peggytype ) for _, batch := range latestBatches { - sigs, err := l.inj.TransactionBatchSignatures(ctx, batch.BatchNonce, gethcommon.HexToAddress(batch.TokenContract)) + sigs, err := l.Injective.TransactionBatchSignatures(ctx, batch.BatchNonce, gethcommon.HexToAddress(batch.TokenContract)) if err != nil { return err } else if len(sigs) == 0 { @@ -232,7 +249,7 @@ func (l *relayerLoop) relayBatch(ctx context.Context, latestEthValset *peggytype } // Send SendTransactionBatch to Ethereum - txHash, err := l.eth.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedInjBatch, oldestConfirmedInjBatchSigs) + txHash, err := l.Ethereum.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedInjBatch, oldestConfirmedInjBatchSigs) if err != nil { return err } @@ -242,8 +259,8 @@ func (l *relayerLoop) relayBatch(ctx context.Context, latestEthValset *peggytype return nil } -func (l *relayerLoop) shouldRelayBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) bool { - latestEthBatch, err := l.eth.GetTxBatchNonce(ctx, gethcommon.HexToAddress(batch.TokenContract)) +func (l *relayer) shouldRelayBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) bool { + latestEthBatch, err := l.Ethereum.GetTxBatchNonce(ctx, gethcommon.HexToAddress(batch.TokenContract)) if err != nil { l.Logger().WithError(err).Warningf("unable to get latest batch nonce from Ethereum: token_contract=%s", gethcommon.HexToAddress(batch.TokenContract)) return false @@ -256,7 +273,7 @@ func (l *relayerLoop) shouldRelayBatch(ctx context.Context, batch *peggytypes.Ou } // Check custom time delay offset - blockTime, err := l.inj.GetBlock(ctx, int64(batch.Block)) + blockTime, err := l.Injective.GetBlock(ctx, int64(batch.Block)) if err != nil { l.Logger().WithError(err).Warningln("unable to get latest block from Injective") return false @@ -278,18 +295,18 @@ func (l *relayerLoop) shouldRelayBatch(ctx context.Context, batch *peggytypes.Ou // as the latest update will be in recent blockchain history and the search moves from the present // backwards in time. In the case that the validator set has not been updated for a very long time // this will take longer. -func (l *relayerLoop) findLatestValsetOnEth(ctx context.Context) (*peggytypes.Valset, error) { - latestHeader, err := l.eth.HeaderByNumber(ctx, nil) +func (l *relayer) findLatestValsetOnEth(ctx context.Context) (*peggytypes.Valset, error) { + latestHeader, err := l.Ethereum.HeaderByNumber(ctx, nil) if err != nil { return nil, errors.Wrap(err, "failed to get latest eth header") } - latestEthereumValsetNonce, err := l.eth.GetValsetNonce(ctx) + latestEthereumValsetNonce, err := l.Ethereum.GetValsetNonce(ctx) if err != nil { return nil, errors.Wrap(err, "failed to get latest valset nonce on Ethereum") } - cosmosValset, err := l.inj.ValsetAt(ctx, latestEthereumValsetNonce.Uint64()) + cosmosValset, err := l.Injective.ValsetAt(ctx, latestEthereumValsetNonce.Uint64()) if err != nil { return nil, errors.Wrap(err, "failed to get Injective valset") } @@ -304,7 +321,7 @@ func (l *relayerLoop) findLatestValsetOnEth(ctx context.Context) (*peggytypes.Va startSearchBlock = currentBlock - findValsetBlocksToSearch } - valsetUpdatedEvents, err := l.eth.GetValsetUpdatedEvents(startSearchBlock, currentBlock) + valsetUpdatedEvents, err := l.Ethereum.GetValsetUpdatedEvents(startSearchBlock, currentBlock) if err != nil { return nil, errors.Wrap(err, "failed to filter past ValsetUpdated events from Ethereum") } diff --git a/orchestrator/relayer_test.go b/orchestrator/relayer_test.go index ea0d2f50..1d32408b 100644 --- a/orchestrator/relayer_test.go +++ b/orchestrator/relayer_test.go @@ -35,9 +35,9 @@ func TestValsetRelaying(t *testing.T) { valsetRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -62,9 +62,9 @@ func TestValsetRelaying(t *testing.T) { valsetRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -89,9 +89,9 @@ func TestValsetRelaying(t *testing.T) { valsetRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayValset(context.TODO())) @@ -130,9 +130,9 @@ func TestValsetRelaying(t *testing.T) { valsetRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -171,9 +171,9 @@ func TestValsetRelaying(t *testing.T) { valsetRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -215,9 +215,9 @@ func TestValsetRelaying(t *testing.T) { valsetRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -262,9 +262,9 @@ func TestValsetRelaying(t *testing.T) { valsetRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -312,9 +312,9 @@ func TestValsetRelaying(t *testing.T) { valsetRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -378,9 +378,9 @@ func TestValsetRelaying(t *testing.T) { valsetRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayValset(context.TODO())) @@ -447,9 +447,9 @@ func TestValsetRelaying(t *testing.T) { valsetRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -517,9 +517,9 @@ func TestValsetRelaying(t *testing.T) { relayValsetOffsetDur: time.Second * 5, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayValset(context.TODO())) @@ -590,9 +590,9 @@ func TestValsetRelaying(t *testing.T) { relayValsetOffsetDur: time.Second * 5, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -663,9 +663,9 @@ func TestValsetRelaying(t *testing.T) { relayValsetOffsetDur: time.Second * 5, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayValset(context.TODO())) @@ -691,9 +691,9 @@ func TestBatchRelaying(t *testing.T) { batchRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -718,9 +718,9 @@ func TestBatchRelaying(t *testing.T) { batchRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -745,9 +745,9 @@ func TestBatchRelaying(t *testing.T) { batchRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayBatch(context.TODO())) @@ -779,9 +779,9 @@ func TestBatchRelaying(t *testing.T) { batchRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -821,9 +821,9 @@ func TestBatchRelaying(t *testing.T) { batchRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -867,9 +867,9 @@ func TestBatchRelaying(t *testing.T) { batchRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -916,9 +916,9 @@ func TestBatchRelaying(t *testing.T) { batchRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -968,9 +968,9 @@ func TestBatchRelaying(t *testing.T) { batchRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -1026,9 +1026,9 @@ func TestBatchRelaying(t *testing.T) { batchRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayBatch(context.TODO())) @@ -1087,9 +1087,9 @@ func TestBatchRelaying(t *testing.T) { batchRelayEnabled: true, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -1149,9 +1149,9 @@ func TestBatchRelaying(t *testing.T) { relayBatchOffsetDur: time.Second * 5, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayBatch(context.TODO())) @@ -1214,9 +1214,9 @@ func TestBatchRelaying(t *testing.T) { relayBatchOffsetDur: time.Second * 5, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -1280,9 +1280,9 @@ func TestBatchRelaying(t *testing.T) { relayBatchOffsetDur: time.Second * 5, } - l := relayerLoop{ + l := relayer{ PeggyOrchestrator: o, - loopDuration: defaultRelayerLoopDur, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayBatch(context.TODO())) diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 586c2ec2..5514a8ed 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -4,43 +4,44 @@ import ( "context" "time" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/avast/retry-go" gethcommon "github.com/ethereum/go-ethereum/common" log "github.com/xlab/suplog" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/loops" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) // EthSignerMainLoop simply signs off on any batches or validator sets provided by the validator // since these are provided directly by a trusted Injective node they can simply be assumed to be // valid and signed off on. -func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, peggyID gethcommon.Hash) error { - loop := ethSignerLoop{ +func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, inj cosmos.Network, peggyID gethcommon.Hash) error { + signer := ethSigner{ PeggyOrchestrator: s, - loopDuration: defaultLoopDur, - peggyID: peggyID, - ethFrom: s.eth.FromAddress(), + Injective: inj, + PeggyID: peggyID, + LoopDuration: defaultLoopDur, } - return loop.Run(ctx) + s.logger.WithField("loop_duration", signer.LoopDuration.String()).Debugln("starting EthSigner...") + + return loops.RunLoop(ctx, signer.LoopDuration, signer.SignValsetsAndBatchesLoop(ctx)) } -type ethSignerLoop struct { +type ethSigner struct { *PeggyOrchestrator - loopDuration time.Duration - peggyID gethcommon.Hash - ethFrom gethcommon.Address + Injective cosmos.Network + LoopDuration time.Duration + PeggyID gethcommon.Hash } -func (l *ethSignerLoop) Logger() log.Logger { +func (l *ethSigner) Logger() log.Logger { return l.logger.WithField("loop", "EthSigner") } -func (l *ethSignerLoop) Run(ctx context.Context) error { - l.logger.WithField("loop_duration", l.loopDuration.String()).Debugln("starting EthSigner loop...") - - return loops.RunLoop(ctx, l.loopDuration, func() error { +func (l *ethSigner) SignValsetsAndBatchesLoop(ctx context.Context) func() error { + return func() error { if err := l.signNewValsetUpdates(ctx); err != nil { return err } @@ -50,10 +51,10 @@ func (l *ethSignerLoop) Run(ctx context.Context) error { } return nil - }) + } } -func (l *ethSignerLoop) signNewValsetUpdates(ctx context.Context) error { +func (l *ethSigner) signNewValsetUpdates(ctx context.Context) error { oldestUnsignedValsets, err := l.getUnsignedValsets(ctx) if err != nil { return err @@ -68,14 +69,12 @@ func (l *ethSignerLoop) signNewValsetUpdates(ctx context.Context) error { if err := l.signValset(ctx, vs); err != nil { return err } - - // todo: in case of multiple updates, we should sleep in between tx (non-continuous nonce) } return nil } -func (l *ethSignerLoop) signNewBatch(ctx context.Context) error { +func (l *ethSigner) signNewBatch(ctx context.Context) error { oldestUnsignedTransactionBatch, err := l.getUnsignedBatch(ctx) if err != nil { return err @@ -93,11 +92,11 @@ func (l *ethSignerLoop) signNewBatch(ctx context.Context) error { return nil } -func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { +func (l *ethSigner) getUnsignedBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { var oldestUnsignedBatch *peggytypes.OutgoingTxBatch getOldestUnsignedBatchFn := func() (err error) { // sign the last unsigned batch, TODO check if we already have signed this - oldestUnsignedBatch, err = l.inj.OldestUnsignedTransactionBatch(ctx, l.orchestratorAddr) + oldestUnsignedBatch, err = l.Injective.OldestUnsignedTransactionBatch(ctx, l.injAddr) if oldestUnsignedBatch == nil { return nil } @@ -119,9 +118,9 @@ func (l *ethSignerLoop) getUnsignedBatch(ctx context.Context) (*peggytypes.Outgo return oldestUnsignedBatch, nil } -func (l *ethSignerLoop) signBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) error { +func (l *ethSigner) signBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) error { signFn := func() error { - return l.inj.SendBatchConfirm(ctx, l.ethFrom, l.peggyID, batch) + return l.Injective.SendBatchConfirm(ctx, l.ethAddr, l.PeggyID, batch) } if err := retry.Do(signFn, @@ -140,10 +139,10 @@ func (l *ethSignerLoop) signBatch(ctx context.Context, batch *peggytypes.Outgoin return nil } -func (l *ethSignerLoop) getUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { +func (l *ethSigner) getUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { var oldestUnsignedValsets []*peggytypes.Valset getOldestUnsignedValsetsFn := func() (err error) { - oldestUnsignedValsets, err = l.inj.OldestUnsignedValsets(ctx, l.orchestratorAddr) + oldestUnsignedValsets, err = l.Injective.OldestUnsignedValsets(ctx, l.injAddr) if oldestUnsignedValsets == nil { return nil } @@ -165,9 +164,9 @@ func (l *ethSignerLoop) getUnsignedValsets(ctx context.Context) ([]*peggytypes.V return oldestUnsignedValsets, nil } -func (l *ethSignerLoop) signValset(ctx context.Context, vs *peggytypes.Valset) error { +func (l *ethSigner) signValset(ctx context.Context, vs *peggytypes.Valset) error { signFn := func() error { - return l.inj.SendValsetConfirm(ctx, l.ethFrom, l.peggyID, vs) + return l.Injective.SendValsetConfirm(ctx, l.ethAddr, l.PeggyID, vs) } if err := retry.Do(signFn, diff --git a/orchestrator/signer_test.go b/orchestrator/signer_test.go index 73cd42cb..a98b4d02 100644 --- a/orchestrator/signer_test.go +++ b/orchestrator/signer_test.go @@ -55,9 +55,9 @@ func TestEthSignerLoop(t *testing.T) { maxAttempts: 1, } - l := ethSignerLoop{ + l := ethSigner{ PeggyOrchestrator: o, - loopDuration: defaultLoopDur, + LoopDuration: defaultLoopDur, } assert.NoError(t, l.signNewValsetUpdates(context.TODO())) @@ -94,9 +94,9 @@ func TestEthSignerLoop(t *testing.T) { maxAttempts: 1, } - l := ethSignerLoop{ + l := ethSigner{ PeggyOrchestrator: o, - loopDuration: defaultLoopDur, + LoopDuration: defaultLoopDur, } assert.Error(t, l.signNewValsetUpdates(context.TODO())) @@ -118,9 +118,9 @@ func TestEthSignerLoop(t *testing.T) { maxAttempts: 1, } - l := ethSignerLoop{ + l := ethSigner{ PeggyOrchestrator: o, - loopDuration: defaultLoopDur, + LoopDuration: defaultLoopDur, } assert.NoError(t, l.signNewBatch(context.TODO())) @@ -146,9 +146,9 @@ func TestEthSignerLoop(t *testing.T) { maxAttempts: 1, } - l := ethSignerLoop{ + l := ethSigner{ PeggyOrchestrator: o, - loopDuration: defaultLoopDur, + LoopDuration: defaultLoopDur, } assert.Error(t, l.signNewBatch(context.TODO())) @@ -174,9 +174,9 @@ func TestEthSignerLoop(t *testing.T) { maxAttempts: 1, } - l := ethSignerLoop{ + l := ethSigner{ PeggyOrchestrator: o, - loopDuration: defaultLoopDur, + LoopDuration: defaultLoopDur, } assert.NoError(t, l.signNewBatch(context.TODO())) From 11bfc5eb328f8843f044e92de3c4c346ab41d9b7 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Sat, 27 Jan 2024 12:54:50 +0100 Subject: [PATCH 55/99] fix keyring init --- orchestrator/cosmos/keyring.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/orchestrator/cosmos/keyring.go b/orchestrator/cosmos/keyring.go index e3d5253f..b022b405 100644 --- a/orchestrator/cosmos/keyring.go +++ b/orchestrator/cosmos/keyring.go @@ -74,13 +74,13 @@ func newInMemoryKeyring(cfg KeyringConfig) (Keyring, error) { keyName = DefaultKeyName ) - from, err := cosmostypes.AccAddressFromBech32(cfg.KeyFrom) - if err != nil { - keyName = cfg.KeyFrom // use it as key name - } - - if err == nil && !bytes.Equal(from.Bytes(), cosmosAddr.Bytes()) { - return Keyring{}, errors.Errorf("expected account address %s but got %s from the private key", from.String(), cosmosAddr.String()) + if len(cfg.KeyFrom) > 0 { + from, err := cosmostypes.AccAddressFromBech32(cfg.KeyFrom) + if err != nil { + keyName = cfg.KeyFrom // use it as key name + } else if !bytes.Equal(from.Bytes(), cosmosAddr.Bytes()) { + return Keyring{}, errors.Errorf("expected account address %s but got %s from the private key", from.String(), cosmosAddr.String()) + } } // Create a temporary in-mem keyring for cosmosPK. From 10f2cb177dd6698db7e4182b2e6cf7f3523bb1e8 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Sat, 27 Jan 2024 14:37:55 +0100 Subject: [PATCH 56/99] simplify loop construction --- orchestrator/batch_request.go | 38 ++++++++-------- orchestrator/oracle.go | 84 +++++++++++++++++------------------ orchestrator/relayer.go | 79 ++++++++++++++++---------------- orchestrator/signer.go | 22 ++++----- 4 files changed, 112 insertions(+), 111 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 8440e8ff..d7b6d8b2 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -24,7 +24,9 @@ func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context, inj cosmos.N s.logger.WithField("loop_duration", requester.LoopDuration.String()).Debugln("starting BatchRequester...") - return loops.RunLoop(ctx, requester.LoopDuration, requester.RequestBatchesLoop(ctx)) + return loops.RunLoop(ctx, requester.LoopDuration, func() error { + return requester.RequestBatches(ctx) + }) } type batchRequester struct { @@ -37,26 +39,24 @@ func (l *batchRequester) Logger() log.Logger { return l.logger.WithField("loop", "BatchRequest") } -func (l *batchRequester) RequestBatchesLoop(ctx context.Context) func() error { - return func() error { - fees, err := l.getUnbatchedTokenFees(ctx) - if err != nil { - // non-fatal, just alert - l.Logger().WithError(err).Warningln("unable to get outgoing withdrawal fees") - return nil - } - - if len(fees) == 0 { - l.Logger().Infoln("no withdrawals to batch") - return nil - } - - for _, fee := range fees { - l.requestBatch(ctx, fee) - } +func (l *batchRequester) RequestBatches(ctx context.Context) error { + fees, err := l.getUnbatchedTokenFees(ctx) + if err != nil { + // non-fatal, just alert + l.Logger().WithError(err).Warningln("unable to get outgoing withdrawal fees") + return nil + } + if len(fees) == 0 { + l.Logger().Infoln("no withdrawals to batch") return nil } + + for _, fee := range fees { + l.requestBatch(ctx, fee) + } + + return nil } func (l *batchRequester) getUnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { @@ -70,7 +70,7 @@ func (l *batchRequester) getUnbatchedTokenFees(ctx context.Context) ([]*peggytyp retry.Context(ctx), retry.Attempts(l.maxAttempts), retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Errorf("failed to get outgoing withdrawal fees, will retry (%d)", n) + l.Logger().WithError(err).Errorf("failed to get withdrawal fees, will retry (%d)", n) }), ); err != nil { return nil, err diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 3fb72ffb..8a98a345 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -43,7 +43,9 @@ func (s *PeggyOrchestrator) EthOracleMainLoop( s.logger.WithField("loop_duration", oracle.LoopDuration.String()).Debugln("starting EthOracle...") - return loops.RunLoop(ctx, oracle.LoopDuration, oracle.ObserveEthEventsLoop(ctx)) + return loops.RunLoop(ctx, oracle.LoopDuration, func() error { + return oracle.ObserveEthEvents(ctx) + }) } type ethOracle struct { @@ -59,55 +61,53 @@ func (l *ethOracle) Logger() log.Logger { return l.logger.WithField("loop", "EthOracle") } -func (l *ethOracle) ObserveEthEventsLoop(ctx context.Context) func() error { - return func() error { - latestHeight, err := l.getLatestEthHeight(ctx) - if err != nil { - return err - } +func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { + latestHeight, err := l.getLatestEthHeight(ctx) + if err != nil { + return err + } - // not enough blocks on ethereum yet - if latestHeight <= ethBlockConfirmationDelay { - return nil - } + // not enough blocks on ethereum yet + if latestHeight <= ethBlockConfirmationDelay { + return nil + } - // ensure that latest block has minimum confirmations - latestHeight = latestHeight - ethBlockConfirmationDelay - if latestHeight <= l.LastObservedEthHeight { - return nil - } + // ensure that latest block has minimum confirmations + latestHeight = latestHeight - ethBlockConfirmationDelay + if latestHeight <= l.LastObservedEthHeight { + return nil + } - // ensure the block range is within defaultBlocksToSearch - if latestHeight > l.LastObservedEthHeight+defaultBlocksToSearch { - latestHeight = l.LastObservedEthHeight + defaultBlocksToSearch - } + // ensure the block range is within defaultBlocksToSearch + if latestHeight > l.LastObservedEthHeight+defaultBlocksToSearch { + latestHeight = l.LastObservedEthHeight + defaultBlocksToSearch + } - events, err := l.getEthEvents(ctx, l.LastObservedEthHeight, latestHeight) - if err != nil { - return err - } + events, err := l.getEthEvents(ctx, l.LastObservedEthHeight, latestHeight) + if err != nil { + return err + } - if err := l.sendNewEventClaims(ctx, events); err != nil { - return err - } + if err := l.sendNewEventClaims(ctx, events); err != nil { + return err + } - l.Logger().WithFields(log.Fields{"block_start": l.LastObservedEthHeight, "block_end": latestHeight}).Debugln("scanned Ethereum blocks") - l.LastObservedEthHeight = latestHeight - - /** Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. - 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. - we need to re-scan this block to ensure events are not missed due to indexing delay. - 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. - 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. - */ - if time.Since(l.LastResyncWithInjective) >= 48*time.Hour { - if err := l.autoResync(ctx); err != nil { - return err - } - } + l.Logger().WithFields(log.Fields{"block_start": l.LastObservedEthHeight, "block_end": latestHeight}).Debugln("scanned Ethereum blocks") + l.LastObservedEthHeight = latestHeight - return nil + /** Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. + 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. + we need to re-scan this block to ensure events are not missed due to indexing delay. + 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. + 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. + */ + if time.Since(l.LastResyncWithInjective) >= 48*time.Hour { + if err := l.autoResync(ctx); err != nil { + return err + } } + + return nil } func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint64) (ethEvents, error) { diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 18997b56..b7de7653 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -42,7 +42,9 @@ func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Netw "relay_valsets": rel.relayValsetOffsetDur != 0, }).Debugln("starting Relayer...") - return loops.RunLoop(ctx, rel.LoopDuration, rel.RelayValsetsAndBatchesLoop(ctx)) + return loops.RunLoop(ctx, rel.LoopDuration, func() error { + return rel.RelayValsetsAndBatches(ctx) + }) } type relayer struct { @@ -56,49 +58,48 @@ func (l *relayer) Logger() log.Logger { return l.logger.WithField("loop", "Relayer") } -func (l *relayer) RelayValsetsAndBatchesLoop(ctx context.Context) func() error { - return func() error { - // we need the latest vs on Ethereum for sig verification - ethValset, err := l.GetLatestEthValset(ctx) - if err != nil { - return err - } +func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { + // we need the latest vs on Ethereum for sig verification + ethValset, err := l.GetLatestEthValset(ctx) + if err != nil { + return err + } - var pg loops.ParanoidGroup - - if l.relayValsetOffsetDur != 0 { - pg.Go(func() error { - return retry.Do(func() error { return l.relayValset(ctx, ethValset) }, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay valset, will retry (%d)", n) - }), - ) - }) - } + var pg loops.ParanoidGroup - if l.relayBatchOffsetDur != 0 { - pg.Go(func() error { - return retry.Do(func() error { return l.relayBatch(ctx, ethValset) }, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay batch, will retry (%d)", n) - }), - ) - }) - } + if l.relayValsetOffsetDur != 0 { + pg.Go(func() error { + return retry.Do(func() error { return l.relayValset(ctx, ethValset) }, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("failed to relay valset, will retry (%d)", n) + }), + ) + }) + } - if pg.Initialized() { - if err := pg.Wait(); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return err - } - } + if l.relayBatchOffsetDur != 0 { + pg.Go(func() error { + return retry.Do(func() error { return l.relayBatch(ctx, ethValset) }, + retry.Context(ctx), + retry.Attempts(l.maxAttempts), + retry.OnRetry(func(n uint, err error) { + l.Logger().WithError(err).Warningf("failed to relay batch, will retry (%d)", n) + }), + ) + }) + } - return nil + if pg.Initialized() { + if err := pg.Wait(); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return err + } } + + return nil + } func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, error) { diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 5514a8ed..7f4d1cc1 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -26,7 +26,9 @@ func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, inj cosmos.Ne s.logger.WithField("loop_duration", signer.LoopDuration.String()).Debugln("starting EthSigner...") - return loops.RunLoop(ctx, signer.LoopDuration, signer.SignValsetsAndBatchesLoop(ctx)) + return loops.RunLoop(ctx, signer.LoopDuration, func() error { + return signer.SignValsetsAndBatches(ctx) + }) } type ethSigner struct { @@ -40,18 +42,16 @@ func (l *ethSigner) Logger() log.Logger { return l.logger.WithField("loop", "EthSigner") } -func (l *ethSigner) SignValsetsAndBatchesLoop(ctx context.Context) func() error { - return func() error { - if err := l.signNewValsetUpdates(ctx); err != nil { - return err - } - - if err := l.signNewBatch(ctx); err != nil { - return err - } +func (l *ethSigner) SignValsetsAndBatches(ctx context.Context) error { + if err := l.signNewValsetUpdates(ctx); err != nil { + return err + } - return nil + if err := l.signNewBatch(ctx); err != nil { + return err } + + return nil } func (l *ethSigner) signNewValsetUpdates(ctx context.Context) error { From 922f1f6e41a034bf6d6b9df7d7351ff47b2caa30 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Sat, 27 Jan 2024 16:18:26 +0100 Subject: [PATCH 57/99] extract retry func --- orchestrator/batch_request.go | 13 +----- orchestrator/oracle.go | 41 +++---------------- orchestrator/orchestrator.go | 16 +++++++- orchestrator/relayer.go | 33 ++++------------ orchestrator/signer.go | 74 ++++++++--------------------------- 5 files changed, 47 insertions(+), 130 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index d7b6d8b2..7d2a94ac 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -4,7 +4,6 @@ import ( "context" "time" - "github.com/avast/retry-go" cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/shopspring/decimal" @@ -61,18 +60,10 @@ func (l *batchRequester) RequestBatches(ctx context.Context) error { func (l *batchRequester) getUnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { var unbatchedFees []*peggytypes.BatchFees - getUnbatchedTokenFeesFn := func() (err error) { + if err := retryOnErr(ctx, l.Logger(), func() (err error) { unbatchedFees, err = l.Injective.UnbatchedTokensWithFees(ctx) return err - } - - if err := retry.Do(getUnbatchedTokenFeesFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Errorf("failed to get withdrawal fees, will retry (%d)", n) - }), - ); err != nil { + }); err != nil { return nil, err } diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 8a98a345..5e709225 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -5,7 +5,6 @@ import ( "sort" "time" - "github.com/avast/retry-go" "github.com/pkg/errors" log "github.com/xlab/suplog" @@ -148,13 +147,7 @@ func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint6 return nil } - if err := retry.Do(scanEthEventsFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("error during Ethereum event checking, will retry (%d)", n) - }), - ); err != nil { + if err := retryOnErr(ctx, l.Logger(), scanEthEventsFn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return ethEvents{}, err } @@ -164,7 +157,7 @@ func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint6 func (l *ethOracle) getLatestEthHeight(ctx context.Context) (uint64, error) { var latestHeight uint64 - getLatestEthHeightFn := func() error { + if err := retryOnErr(ctx, l.Logger(), func() error { latestHeader, err := l.Ethereum.HeaderByNumber(ctx, nil) if err != nil { return errors.Wrap(err, "failed to get latest ethereum header") @@ -172,15 +165,7 @@ func (l *ethOracle) getLatestEthHeight(ctx context.Context) (uint64, error) { latestHeight = latestHeader.Number.Uint64() return nil - } - - if err := retry.Do(getLatestEthHeightFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to get latest eth header, will retry (%d)", n) - }), - ); err != nil { + }); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return 0, err } @@ -217,13 +202,7 @@ func (l *ethOracle) sendNewEventClaims(ctx context.Context, events ethEvents) er return nil } - if err := retry.Do(sendEventsFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to send events to Injective, will retry (%d)", n) - }), - ); err != nil { + if err := retryOnErr(ctx, l.Logger(), sendEventsFn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return err } @@ -233,18 +212,10 @@ func (l *ethOracle) sendNewEventClaims(ctx context.Context, events ethEvents) er func (l *ethOracle) autoResync(ctx context.Context) error { var latestHeight uint64 - getLastClaimEventFn := func() (err error) { + if err := retryOnErr(ctx, l.Logger(), func() (err error) { latestHeight, err = l.getLastClaimBlockHeight(ctx, l.Injective) return - } - - if err := retry.Do(getLastClaimEventFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to get last claimed event height, will retry (%d)", n) - }), - ); err != nil { + }); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return err } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 5ed2a767..6ffc2f38 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -4,6 +4,7 @@ import ( "context" "time" + "github.com/avast/retry-go" cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" @@ -14,7 +15,10 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/loops" ) -const defaultLoopDur = 60 * time.Second +const ( + defaultLoopDur = 60 * time.Second + maxRetryAttempts = 10 +) // PriceFeed provides token price for a given contract address type PriceFeed interface { @@ -166,3 +170,13 @@ func (s *PeggyOrchestrator) getLastClaimBlockHeight(ctx context.Context, inj cos return claim.EthereumEventHeight, nil } + +func retryOnErr(ctx context.Context, log log.Logger, fn func() error) error { + return retry.Do(fn, + retry.Context(ctx), + retry.Attempts(maxRetryAttempts), + retry.OnRetry(func(n uint, err error) { + log.WithError(err).Warningf("encountered error, retrying (%d)", n) + }), + ) +} diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index b7de7653..ffd571f4 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -5,7 +5,6 @@ import ( "sort" "time" - "github.com/avast/retry-go" cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" @@ -69,25 +68,17 @@ func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { if l.relayValsetOffsetDur != 0 { pg.Go(func() error { - return retry.Do(func() error { return l.relayValset(ctx, ethValset) }, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay valset, will retry (%d)", n) - }), - ) + return retryOnErr(ctx, l.Logger(), func() error { + return l.relayValset(ctx, ethValset) + }) }) } if l.relayBatchOffsetDur != 0 { pg.Go(func() error { - return retry.Do(func() error { return l.relayBatch(ctx, ethValset) }, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to relay batch, will retry (%d)", n) - }), - ) + return retryOnErr(ctx, l.Logger(), func() error { + return l.relayBatch(ctx, ethValset) + }) }) } @@ -104,7 +95,7 @@ func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, error) { var latestEthValset *peggytypes.Valset - getLatestEthValsetFn := func() error { + if err := retryOnErr(ctx, l.Logger(), func() error { vs, err := l.findLatestValsetOnEth(ctx) if err != nil { return err @@ -112,15 +103,7 @@ func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, e latestEthValset = vs return nil - } - - if err := retry.Do(getLatestEthValsetFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to find latest valset on Ethereum, will retry (%d)", n) - }), - ); err != nil { + }); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return nil, err } diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 7f4d1cc1..435931ea 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -4,7 +4,6 @@ import ( "context" "time" - "github.com/avast/retry-go" gethcommon "github.com/ethereum/go-ethereum/common" log "github.com/xlab/suplog" @@ -80,11 +79,6 @@ func (l *ethSigner) signNewBatch(ctx context.Context) error { return err } - if oldestUnsignedTransactionBatch == nil { - l.Logger().Infoln("no batch to confirm") - return nil - } - if err := l.signBatch(ctx, oldestUnsignedTransactionBatch); err != nil { return err } @@ -94,23 +88,10 @@ func (l *ethSigner) signNewBatch(ctx context.Context) error { func (l *ethSigner) getUnsignedBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { var oldestUnsignedBatch *peggytypes.OutgoingTxBatch - getOldestUnsignedBatchFn := func() (err error) { - // sign the last unsigned batch, TODO check if we already have signed this - oldestUnsignedBatch, err = l.Injective.OldestUnsignedTransactionBatch(ctx, l.injAddr) - if oldestUnsignedBatch == nil { - return nil - } - - return err - } - - if err := retry.Do(getOldestUnsignedBatchFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to get unconfirmed batch, will retry (%d)", n) - }), - ); err != nil { + if err := retryOnErr(ctx, l.Logger(), func() error { + oldestUnsignedBatch, _ = l.Injective.OldestUnsignedTransactionBatch(ctx, l.injAddr) + return nil + }); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return nil, err } @@ -119,17 +100,14 @@ func (l *ethSigner) getUnsignedBatch(ctx context.Context) (*peggytypes.OutgoingT } func (l *ethSigner) signBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) error { - signFn := func() error { - return l.Injective.SendBatchConfirm(ctx, l.ethAddr, l.PeggyID, batch) + if batch == nil { + l.Logger().Infoln("no batch to confirm") + return nil } - if err := retry.Do(signFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to confirm batch on Injective, will retry (%d)", n) - }), - ); err != nil { + if err := retryOnErr(ctx, l.Logger(), func() error { + return l.Injective.SendBatchConfirm(ctx, l.ethAddr, l.PeggyID, batch) + }); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return err } @@ -141,22 +119,10 @@ func (l *ethSigner) signBatch(ctx context.Context, batch *peggytypes.OutgoingTxB func (l *ethSigner) getUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { var oldestUnsignedValsets []*peggytypes.Valset - getOldestUnsignedValsetsFn := func() (err error) { - oldestUnsignedValsets, err = l.Injective.OldestUnsignedValsets(ctx, l.injAddr) - if oldestUnsignedValsets == nil { - return nil - } - - return err - } - - if err := retry.Do(getOldestUnsignedValsetsFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to get unconfirmed valset updates, will retry (%d)", n) - }), - ); err != nil { + if err := retryOnErr(ctx, l.Logger(), func() error { + oldestUnsignedValsets, _ = l.Injective.OldestUnsignedValsets(ctx, l.injAddr) + return nil + }); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return nil, err } @@ -165,17 +131,9 @@ func (l *ethSigner) getUnsignedValsets(ctx context.Context) ([]*peggytypes.Valse } func (l *ethSigner) signValset(ctx context.Context, vs *peggytypes.Valset) error { - signFn := func() error { + if err := retryOnErr(ctx, l.Logger(), func() error { return l.Injective.SendValsetConfirm(ctx, l.ethAddr, l.PeggyID, vs) - } - - if err := retry.Do(signFn, - retry.Context(ctx), - retry.Attempts(l.maxAttempts), - retry.OnRetry(func(n uint, err error) { - l.Logger().WithError(err).Warningf("failed to confirm valset update on Injective, will retry (%d)", n) - }), - ); err != nil { + }); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return err } From 5406bf5a5f9708145ee0f4b12a322c24e951be25 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 29 Jan 2024 11:13:40 +0100 Subject: [PATCH 58/99] remove unused field --- orchestrator/cosmos/peggy/broadcast.go | 5 ++--- orchestrator/cosmos/peggy/query.go | 5 +++-- orchestrator/orchestrator.go | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/orchestrator/cosmos/peggy/broadcast.go b/orchestrator/cosmos/peggy/broadcast.go index 473595f0..4c1f8876 100644 --- a/orchestrator/cosmos/peggy/broadcast.go +++ b/orchestrator/cosmos/peggy/broadcast.go @@ -10,12 +10,11 @@ import ( log "github.com/xlab/suplog" "github.com/InjectiveLabs/metrics" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/InjectiveLabs/sdk-go/client/chain" - - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" ) type BroadcastClient interface { diff --git a/orchestrator/cosmos/peggy/query.go b/orchestrator/cosmos/peggy/query.go index 42bfbfca..22af057e 100644 --- a/orchestrator/cosmos/peggy/query.go +++ b/orchestrator/cosmos/peggy/query.go @@ -3,11 +3,12 @@ package peggy import ( "context" - "github.com/InjectiveLabs/metrics" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + + "github.com/InjectiveLabs/metrics" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) var ErrNotFound = errors.New("not found") diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 6ffc2f38..15ce65c3 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -46,7 +46,6 @@ type PeggyOrchestrator struct { relayValsetOffsetDur time.Duration relayBatchOffsetDur time.Duration minBatchFeeUSD float64 - maxAttempts uint // max number of times a retry func will be called before exiting } func NewPeggyOrchestrator( @@ -63,7 +62,6 @@ func NewPeggyOrchestrator( priceFeed: priceFeed, erc20ContractMapping: cfg.ERC20ContractMapping, minBatchFeeUSD: cfg.MinBatchFeeUSD, - maxAttempts: 10, // default for retry pkg } if cfg.RelayValsets { From fedc82d552d7281b0a95568c4939e15c01177c02 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 29 Jan 2024 17:20:04 +0100 Subject: [PATCH 59/99] move ethereum network interface --- orchestrator/ethereum.go | 39 ---------- orchestrator/ethereum/network.go | 119 +++++++++++++++++-------------- orchestrator/mocks_test.go | 2 +- orchestrator/oracle.go | 7 +- orchestrator/orchestrator.go | 7 +- orchestrator/relayer.go | 9 +-- 6 files changed, 79 insertions(+), 104 deletions(-) delete mode 100644 orchestrator/ethereum.go diff --git a/orchestrator/ethereum.go b/orchestrator/ethereum.go deleted file mode 100644 index 53e396fb..00000000 --- a/orchestrator/ethereum.go +++ /dev/null @@ -1,39 +0,0 @@ -package orchestrator - -import ( - "context" - "math/big" - - gethcommon "github.com/ethereum/go-ethereum/common" - gethtypes "github.com/ethereum/go-ethereum/core/types" - - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" -) - -// EthereumNetwork is the orchestrator's reference endpoint to the Ethereum network -type EthereumNetwork interface { - FromAddress() gethcommon.Address - HeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) - GetPeggyID(ctx context.Context) (gethcommon.Hash, error) - - GetSendToCosmosEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) - GetSendToInjectiveEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) - GetPeggyERC20DeployedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) - GetValsetUpdatedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) - GetTransactionBatchExecutedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) - - GetValsetNonce(ctx context.Context) (*big.Int, error) - SendEthValsetUpdate(ctx context.Context, - oldValset *peggytypes.Valset, - newValset *peggytypes.Valset, - confirms []*peggytypes.MsgValsetConfirm, - ) (*gethcommon.Hash, error) - - GetTxBatchNonce(ctx context.Context, erc20ContractAddress gethcommon.Address) (*big.Int, error) - SendTransactionBatch(ctx context.Context, - currentValset *peggytypes.Valset, - batch *peggytypes.OutgoingTxBatch, - confirms []*peggytypes.MsgConfirmBatch, - ) (*gethcommon.Hash, error) -} diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index cf206442..57240d16 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -7,8 +7,8 @@ import ( "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" - ethcmn "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" "github.com/pkg/errors" log "github.com/xlab/suplog" @@ -16,24 +16,52 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/ethereum/committer" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/provider" - wrappers "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) -type Network struct { +// Network is the orchestrator's reference endpoint to the Ethereum network +type Network interface { + GetHeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) + GetPeggyID(ctx context.Context) (gethcommon.Hash, error) + + GetSendToCosmosEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) + GetSendToInjectiveEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) + GetPeggyERC20DeployedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) + GetValsetUpdatedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) + GetTransactionBatchExecutedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) + + GetValsetNonce(ctx context.Context) (*big.Int, error) + SendEthValsetUpdate(ctx context.Context, + oldValset *peggytypes.Valset, + newValset *peggytypes.Valset, + confirms []*peggytypes.MsgValsetConfirm, + ) (*gethcommon.Hash, error) + + GetTxBatchNonce(ctx context.Context, erc20ContractAddress gethcommon.Address) (*big.Int, error) + SendTransactionBatch(ctx context.Context, + currentValset *peggytypes.Valset, + batch *peggytypes.OutgoingTxBatch, + confirms []*peggytypes.MsgConfirmBatch, + ) (*gethcommon.Hash, error) +} + +type network struct { peggy.PeggyContract + + FromAddr gethcommon.Address } func NewNetwork( ethNodeRPC string, peggyContractAddr, - fromAddr ethcmn.Address, + fromAddr gethcommon.Address, signerFn bind.SignerFn, gasPriceAdjustment float64, maxGasPrice string, pendingTxWaitDuration string, ethNodeAlchemyWS string, -) (*Network, error) { +) (Network, error) { evmRPC, err := rpc.Dial(ethNodeRPC) if err != nil { return nil, errors.Wrapf(err, "failed to connect to ethereum RPC: %s", ethNodeRPC) @@ -74,23 +102,32 @@ func NewNetwork( go peggyContract.SubscribeToPendingTxs(ethNodeAlchemyWS) } - return &Network{PeggyContract: peggyContract}, nil -} + n := &network{ + PeggyContract: peggyContract, + FromAddr: fromAddr, + } -func (n *Network) FromAddress() ethcmn.Address { - return n.PeggyContract.FromAddress() + return n, nil } -func (n *Network) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { +func (n *network) GetHeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) { return n.Provider().HeaderByNumber(ctx, number) } -func (n *Network) GetPeggyID(ctx context.Context) (ethcmn.Hash, error) { - return n.PeggyContract.GetPeggyID(ctx, n.FromAddress()) +func (n *network) GetPeggyID(ctx context.Context) (gethcommon.Hash, error) { + return n.PeggyContract.GetPeggyID(ctx, n.FromAddr) +} + +func (n *network) GetValsetNonce(ctx context.Context) (*big.Int, error) { + return n.PeggyContract.GetValsetNonce(ctx, n.FromAddr) +} + +func (n *network) GetTxBatchNonce(ctx context.Context, erc20ContractAddress gethcommon.Address) (*big.Int, error) { + return n.PeggyContract.GetTxBatchNonce(ctx, erc20ContractAddress, n.FromAddr) } -func (n *Network) GetSendToCosmosEvents(startBlock, endBlock uint64) ([]*wrappers.PeggySendToCosmosEvent, error) { - peggyFilterer, err := wrappers.NewPeggyFilterer(n.Address(), n.Provider()) +func (n *network) GetSendToCosmosEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + peggyFilterer, err := peggyevents.NewPeggyFilterer(n.Address(), n.Provider()) if err != nil { return nil, errors.Wrap(err, "failed to init Peggy events filterer") } @@ -109,7 +146,7 @@ func (n *Network) GetSendToCosmosEvents(startBlock, endBlock uint64) ([]*wrapper defer iter.Close() - var sendToCosmosEvents []*wrappers.PeggySendToCosmosEvent + var sendToCosmosEvents []*peggyevents.PeggySendToCosmosEvent for iter.Next() { sendToCosmosEvents = append(sendToCosmosEvents, iter.Event) } @@ -117,8 +154,8 @@ func (n *Network) GetSendToCosmosEvents(startBlock, endBlock uint64) ([]*wrapper return sendToCosmosEvents, nil } -func (n *Network) GetSendToInjectiveEvents(startBlock, endBlock uint64) ([]*wrappers.PeggySendToInjectiveEvent, error) { - peggyFilterer, err := wrappers.NewPeggyFilterer(n.Address(), n.Provider()) +func (n *network) GetSendToInjectiveEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + peggyFilterer, err := peggyevents.NewPeggyFilterer(n.Address(), n.Provider()) if err != nil { return nil, errors.Wrap(err, "failed to init Peggy events filterer") } @@ -137,7 +174,7 @@ func (n *Network) GetSendToInjectiveEvents(startBlock, endBlock uint64) ([]*wrap defer iter.Close() - var sendToInjectiveEvents []*wrappers.PeggySendToInjectiveEvent + var sendToInjectiveEvents []*peggyevents.PeggySendToInjectiveEvent for iter.Next() { sendToInjectiveEvents = append(sendToInjectiveEvents, iter.Event) } @@ -145,8 +182,8 @@ func (n *Network) GetSendToInjectiveEvents(startBlock, endBlock uint64) ([]*wrap return sendToInjectiveEvents, nil } -func (n *Network) GetPeggyERC20DeployedEvents(startBlock, endBlock uint64) ([]*wrappers.PeggyERC20DeployedEvent, error) { - peggyFilterer, err := wrappers.NewPeggyFilterer(n.Address(), n.Provider()) +func (n *network) GetPeggyERC20DeployedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + peggyFilterer, err := peggyevents.NewPeggyFilterer(n.Address(), n.Provider()) if err != nil { return nil, errors.Wrap(err, "failed to init Peggy events filterer") } @@ -165,7 +202,7 @@ func (n *Network) GetPeggyERC20DeployedEvents(startBlock, endBlock uint64) ([]*w defer iter.Close() - var transactionBatchExecutedEvents []*wrappers.PeggyERC20DeployedEvent + var transactionBatchExecutedEvents []*peggyevents.PeggyERC20DeployedEvent for iter.Next() { transactionBatchExecutedEvents = append(transactionBatchExecutedEvents, iter.Event) } @@ -173,8 +210,8 @@ func (n *Network) GetPeggyERC20DeployedEvents(startBlock, endBlock uint64) ([]*w return transactionBatchExecutedEvents, nil } -func (n *Network) GetValsetUpdatedEvents(startBlock, endBlock uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - peggyFilterer, err := wrappers.NewPeggyFilterer(n.Address(), n.Provider()) +func (n *network) GetValsetUpdatedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + peggyFilterer, err := peggyevents.NewPeggyFilterer(n.Address(), n.Provider()) if err != nil { return nil, errors.Wrap(err, "failed to init Peggy events filterer") } @@ -193,7 +230,7 @@ func (n *Network) GetValsetUpdatedEvents(startBlock, endBlock uint64) ([]*wrappe defer iter.Close() - var valsetUpdatedEvents []*wrappers.PeggyValsetUpdatedEvent + var valsetUpdatedEvents []*peggyevents.PeggyValsetUpdatedEvent for iter.Next() { valsetUpdatedEvents = append(valsetUpdatedEvents, iter.Event) } @@ -201,8 +238,8 @@ func (n *Network) GetValsetUpdatedEvents(startBlock, endBlock uint64) ([]*wrappe return valsetUpdatedEvents, nil } -func (n *Network) GetTransactionBatchExecutedEvents(startBlock, endBlock uint64) ([]*wrappers.PeggyTransactionBatchExecutedEvent, error) { - peggyFilterer, err := wrappers.NewPeggyFilterer(n.Address(), n.Provider()) +func (n *network) GetTransactionBatchExecutedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + peggyFilterer, err := peggyevents.NewPeggyFilterer(n.Address(), n.Provider()) if err != nil { return nil, errors.Wrap(err, "failed to init Peggy events filterer") } @@ -221,7 +258,7 @@ func (n *Network) GetTransactionBatchExecutedEvents(startBlock, endBlock uint64) defer iter.Close() - var transactionBatchExecutedEvents []*wrappers.PeggyTransactionBatchExecutedEvent + var transactionBatchExecutedEvents []*peggyevents.PeggyTransactionBatchExecutedEvent for iter.Next() { transactionBatchExecutedEvents = append(transactionBatchExecutedEvents, iter.Event) } @@ -229,32 +266,6 @@ func (n *Network) GetTransactionBatchExecutedEvents(startBlock, endBlock uint64) return transactionBatchExecutedEvents, nil } -func (n *Network) GetValsetNonce(ctx context.Context) (*big.Int, error) { - return n.PeggyContract.GetValsetNonce(ctx, n.FromAddress()) -} - -func (n *Network) SendEthValsetUpdate( - ctx context.Context, - oldValset *peggytypes.Valset, - newValset *peggytypes.Valset, - confirms []*peggytypes.MsgValsetConfirm, -) (*ethcmn.Hash, error) { - return n.PeggyContract.SendEthValsetUpdate(ctx, oldValset, newValset, confirms) -} - -func (n *Network) GetTxBatchNonce(ctx context.Context, erc20ContractAddress ethcmn.Address) (*big.Int, error) { - return n.PeggyContract.GetTxBatchNonce(ctx, erc20ContractAddress, n.FromAddress()) -} - -func (n *Network) SendTransactionBatch( - ctx context.Context, - currentValset *peggytypes.Valset, - batch *peggytypes.OutgoingTxBatch, - confirms []*peggytypes.MsgConfirmBatch, -) (*ethcmn.Hash, error) { - return n.PeggyContract.SendTransactionBatch(ctx, currentValset, batch, confirms) -} - func isUnknownBlockErr(err error) bool { // Geth error if strings.Contains(err.Error(), "unknown block") { diff --git a/orchestrator/mocks_test.go b/orchestrator/mocks_test.go index 170e920b..ec34d68d 100644 --- a/orchestrator/mocks_test.go +++ b/orchestrator/mocks_test.go @@ -160,7 +160,7 @@ func (e mockEthereum) FromAddress() eth.Address { return e.fromAddressFn() } -func (e mockEthereum) HeaderByNumber(ctx context.Context, number *big.Int) (*ethtypes.Header, error) { +func (e mockEthereum) GetHeaderByNumber(ctx context.Context, number *big.Int) (*ethtypes.Header, error) { return e.headerByNumberFn(ctx, number) } diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 5e709225..8005398d 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -9,6 +9,7 @@ import ( log "github.com/xlab/suplog" "github.com/InjectiveLabs/peggo/orchestrator/cosmos" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/loops" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" ) @@ -28,7 +29,7 @@ const ( func (s *PeggyOrchestrator) EthOracleMainLoop( ctx context.Context, inj cosmos.Network, - eth EthereumNetwork, + eth ethereum.Network, lastObservedBlock uint64, ) error { oracle := ethOracle{ @@ -50,7 +51,7 @@ func (s *PeggyOrchestrator) EthOracleMainLoop( type ethOracle struct { *PeggyOrchestrator Injective cosmos.Network - Ethereum EthereumNetwork + Ethereum ethereum.Network LoopDuration time.Duration LastResyncWithInjective time.Time LastObservedEthHeight uint64 @@ -158,7 +159,7 @@ func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint6 func (l *ethOracle) getLatestEthHeight(ctx context.Context) (uint64, error) { var latestHeight uint64 if err := retryOnErr(ctx, l.Logger(), func() error { - latestHeader, err := l.Ethereum.HeaderByNumber(ctx, nil) + latestHeader, err := l.Ethereum.GetHeaderByNumber(ctx, nil) if err != nil { return errors.Wrap(err, "failed to get latest ethereum header") } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 15ce65c3..15357603 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -12,6 +12,7 @@ import ( "github.com/InjectiveLabs/metrics" "github.com/InjectiveLabs/peggo/orchestrator/cosmos" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/loops" ) @@ -87,7 +88,7 @@ func NewPeggyOrchestrator( // Run starts all major loops required to make // up the Orchestrator, all of these are async loops. -func (s *PeggyOrchestrator) Run(ctx context.Context, inj cosmos.Network, eth EthereumNetwork) error { +func (s *PeggyOrchestrator) Run(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { if !s.hasDelegateValidator(ctx, inj) { return s.startRelayerMode(ctx, inj, eth) } @@ -112,7 +113,7 @@ func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context, inj cosmos // startValidatorMode runs all orchestrator processes. This is called // when peggo is run alongside a validator injective node. -func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.Network, eth EthereumNetwork) error { +func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { log.Infoln("running orchestrator in validator mode") // get gethcommon block observed by this validator @@ -145,7 +146,7 @@ func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.N // startRelayerMode runs orchestrator processes that only relay specific // messages that do not require a validator's signature. This mode is run // alongside a non-validator injective node -func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context, inj cosmos.Network, eth EthereumNetwork) error { +func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { log.Infoln("running orchestrator in relayer mode") var pg loops.ParanoidGroup diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index ffd571f4..02e45c56 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -12,6 +12,7 @@ import ( "github.com/InjectiveLabs/metrics" "github.com/InjectiveLabs/peggo/orchestrator/cosmos" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/util" "github.com/InjectiveLabs/peggo/orchestrator/loops" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" @@ -23,7 +24,7 @@ const ( findValsetBlocksToSearch = 2000 ) -func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Network, eth EthereumNetwork) (err error) { +func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { if noRelay := s.relayValsetOffsetDur == 0 && s.relayBatchOffsetDur == 0; noRelay { return nil } @@ -49,7 +50,7 @@ func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Netw type relayer struct { *PeggyOrchestrator Injective cosmos.Network - Ethereum EthereumNetwork + Ethereum ethereum.Network LoopDuration time.Duration } @@ -58,7 +59,7 @@ func (l *relayer) Logger() log.Logger { } func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { - // we need the latest vs on Ethereum for sig verification + // need latest vs on Ethereum for sig verification ethValset, err := l.GetLatestEthValset(ctx) if err != nil { return err @@ -280,7 +281,7 @@ func (l *relayer) shouldRelayBatch(ctx context.Context, batch *peggytypes.Outgoi // backwards in time. In the case that the validator set has not been updated for a very long time // this will take longer. func (l *relayer) findLatestValsetOnEth(ctx context.Context) (*peggytypes.Valset, error) { - latestHeader, err := l.Ethereum.HeaderByNumber(ctx, nil) + latestHeader, err := l.Ethereum.GetHeaderByNumber(ctx, nil) if err != nil { return nil, errors.Wrap(err, "failed to get latest eth header") } From 8ee9d718d02267988f83b84d5e04f2db0a8cdbbc Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 29 Jan 2024 17:29:59 +0100 Subject: [PATCH 60/99] refactor ethereum network config --- cmd/peggo/orchestrator.go | 14 ++++++++------ cmd/peggo/tx.go | 2 +- orchestrator/cosmos/network.go | 9 ++++----- orchestrator/ethereum/network.go | 32 ++++++++++++++++++-------------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 47981e93..026fa019 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -67,7 +67,7 @@ func orchestratorCmd(cmd *cli.Cmd) { log.WithError(err).Fatalln("failed to initialize Ethereum account") } - cosmosNetwork, err := cosmos.NewCosmosNetwork(cosmosKeyring, personalSignFn, cosmos.NetworkConfig{ + cosmosNetwork, err := cosmos.NewNetwork(cosmosKeyring, personalSignFn, cosmos.NetworkConfig{ ChainID: *cfg.cosmosChainID, ValidatorAddress: cosmosKeyring.Addr.String(), CosmosGRPC: *cfg.cosmosGRPC, @@ -95,14 +95,16 @@ func orchestratorCmd(cmd *cli.Cmd) { // Connect to ethereum network ethereumNetwork, err := ethereum.NewNetwork( - *cfg.ethNodeRPC, peggyContractAddr, ethKeyFromAddress, signerFn, - *cfg.ethGasPriceAdjustment, - *cfg.ethMaxGasPrice, - *cfg.pendingTxWaitDuration, - *cfg.ethNodeAlchemyWS, + ethereum.NetworkConfig{ + EthNodeRPC: *cfg.ethNodeRPC, + GasPriceAdjustment: *cfg.ethGasPriceAdjustment, + MaxGasPrice: *cfg.ethMaxGasPrice, + PendingTxWaitDuration: *cfg.pendingTxWaitDuration, + EthNodeAlchemyWS: *cfg.ethNodeAlchemyWS, + }, ) orShutdown(err) diff --git a/cmd/peggo/tx.go b/cmd/peggo/tx.go index 4c1af139..379e6919 100644 --- a/cmd/peggo/tx.go +++ b/cmd/peggo/tx.go @@ -127,7 +127,7 @@ func registerEthKeyCmd(cmd *cli.Cmd) { return } - net, err := cosmos.NewCosmosNetwork(keyring, personalSignFn, cosmos.NetworkConfig{ + net, err := cosmos.NewNetwork(keyring, personalSignFn, cosmos.NetworkConfig{ ChainID: *cosmosChainID, ValidatorAddress: keyring.Addr.String(), CosmosGRPC: *cosmosGRPC, diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 9d2b88cf..5363240f 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -11,13 +11,12 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/connectivity" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - "github.com/InjectiveLabs/sdk-go/client/chain" - clientcommon "github.com/InjectiveLabs/sdk-go/client/common" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/sdk-go/client/chain" + clientcommon "github.com/InjectiveLabs/sdk-go/client/common" ) type NetworkConfig struct { @@ -34,7 +33,7 @@ type Network interface { tendermint.Client } -func NewCosmosNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg NetworkConfig) (Network, error) { +func NewNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg NetworkConfig) (Network, error) { clientCfg := cfg.loadClientConfig() clientCtx, err := chain.NewClientContext(clientCfg.ChainId, cfg.ValidatorAddress, k) diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index 57240d16..454a1b37 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -20,6 +20,14 @@ import ( peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) +type NetworkConfig struct { + EthNodeRPC string + GasPriceAdjustment float64 + MaxGasPrice string + PendingTxWaitDuration string + EthNodeAlchemyWS string +} + // Network is the orchestrator's reference endpoint to the Ethereum network type Network interface { GetHeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) @@ -53,24 +61,20 @@ type network struct { } func NewNetwork( - ethNodeRPC string, peggyContractAddr, fromAddr gethcommon.Address, signerFn bind.SignerFn, - gasPriceAdjustment float64, - maxGasPrice string, - pendingTxWaitDuration string, - ethNodeAlchemyWS string, + cfg NetworkConfig, ) (Network, error) { - evmRPC, err := rpc.Dial(ethNodeRPC) + evmRPC, err := rpc.Dial(cfg.EthNodeRPC) if err != nil { - return nil, errors.Wrapf(err, "failed to connect to ethereum RPC: %s", ethNodeRPC) + return nil, errors.Wrapf(err, "failed to connect to ethereum RPC: %s", cfg.EthNodeRPC) } ethCommitter, err := committer.NewEthCommitter( fromAddr, - gasPriceAdjustment, - maxGasPrice, + cfg.GasPriceAdjustment, + cfg.MaxGasPrice, signerFn, provider.NewEVMProvider(evmRPC), ) @@ -78,7 +82,7 @@ func NewNetwork( return nil, err } - pendingTxDuration, err := time.ParseDuration(pendingTxWaitDuration) + pendingTxDuration, err := time.ParseDuration(cfg.PendingTxWaitDuration) if err != nil { return nil, err } @@ -89,17 +93,17 @@ func NewNetwork( } log.WithFields(log.Fields{ - "rpc": ethNodeRPC, + "rpc": cfg.EthNodeRPC, "addr": fromAddr.String(), "peggy_contract": peggyContractAddr, }).Infoln("connected to Ethereum network") // If Alchemy Websocket URL is set, then Subscribe to Pending Transaction of Peggy Contract. - if ethNodeAlchemyWS != "" { + if cfg.EthNodeAlchemyWS != "" { log.WithFields(log.Fields{ - "url": ethNodeAlchemyWS, + "url": cfg.EthNodeAlchemyWS, }).Infoln("subscribing to Alchemy websocket") - go peggyContract.SubscribeToPendingTxs(ethNodeAlchemyWS) + go peggyContract.SubscribeToPendingTxs(cfg.EthNodeAlchemyWS) } n := &network{ From 84b7da1f74e2b7190571799db9ad24f0b4b86fcc Mon Sep 17 00:00:00 2001 From: Albert Chon Date: Wed, 21 Feb 2024 00:48:04 -0400 Subject: [PATCH 61/99] chore: format --- cmd/peggo/orchestrator.go | 3 ++- orchestrator/batch_request.go | 3 ++- orchestrator/coingecko/coingecko.go | 1 - orchestrator/cosmos/network.go | 7 ++++--- orchestrator/cosmos/peggy/broadcast.go | 5 +++-- orchestrator/ethereum/committer/eth_committer.go | 4 +++- orchestrator/ethereum/network.go | 3 ++- orchestrator/ethereum/peggy/message_signatures.go | 6 +++--- orchestrator/ethereum/peggy/peggy_contract.go | 3 ++- orchestrator/ethereum/peggy/pending_transactions.go | 1 - orchestrator/ethereum/peggy/send_to_cosmos.go | 1 + orchestrator/loops/paranoid_group.go | 2 +- orchestrator/mocks_test.go | 8 +++++--- orchestrator/oracle_test.go | 3 ++- orchestrator/orchestrator.go | 1 + orchestrator/relayer.go | 3 ++- orchestrator/relayer_test.go | 10 ++++++---- orchestrator/signer.go | 3 ++- 18 files changed, 41 insertions(+), 26 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 026fa019..c0caea6b 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -9,12 +9,13 @@ import ( "github.com/xlab/closer" log "github.com/xlab/suplog" + chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" + "github.com/InjectiveLabs/peggo/orchestrator" "github.com/InjectiveLabs/peggo/orchestrator/coingecko" "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/version" - chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" ) // startOrchestrator action runs an infinite loop, diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 7d2a94ac..4036d4ff 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -9,9 +9,10 @@ import ( "github.com/shopspring/decimal" log "github.com/xlab/suplog" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/loops" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context, inj cosmos.Network) (err error) { diff --git a/orchestrator/coingecko/coingecko.go b/orchestrator/coingecko/coingecko.go index 2ba1ebf3..9f2e7289 100644 --- a/orchestrator/coingecko/coingecko.go +++ b/orchestrator/coingecko/coingecko.go @@ -4,7 +4,6 @@ import ( "encoding/json" "io" "io/ioutil" - "net/http" "net/url" "path" diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 5363240f..e15a027a 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -11,12 +11,13 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/connectivity" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" - "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" "github.com/InjectiveLabs/sdk-go/client/chain" clientcommon "github.com/InjectiveLabs/sdk-go/client/common" + + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/peggy" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos/tendermint" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" ) type NetworkConfig struct { diff --git a/orchestrator/cosmos/peggy/broadcast.go b/orchestrator/cosmos/peggy/broadcast.go index 4c1f8876..152224cf 100644 --- a/orchestrator/cosmos/peggy/broadcast.go +++ b/orchestrator/cosmos/peggy/broadcast.go @@ -10,11 +10,12 @@ import ( log "github.com/xlab/suplog" "github.com/InjectiveLabs/metrics" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/sdk-go/client/chain" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/keystore" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - "github.com/InjectiveLabs/sdk-go/client/chain" ) type BroadcastClient interface { diff --git a/orchestrator/ethereum/committer/eth_committer.go b/orchestrator/ethereum/committer/eth_committer.go index 19444bed..499bc090 100644 --- a/orchestrator/ethereum/committer/eth_committer.go +++ b/orchestrator/ethereum/committer/eth_committer.go @@ -2,10 +2,11 @@ package committer import ( "context" - "github.com/ethereum/go-ethereum" "math/big" "strings" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -13,6 +14,7 @@ import ( log "github.com/xlab/suplog" "github.com/InjectiveLabs/metrics" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/provider" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/util" ) diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index 454a1b37..42e46741 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -13,11 +13,12 @@ import ( "github.com/pkg/errors" log "github.com/xlab/suplog" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/committer" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/provider" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) type NetworkConfig struct { diff --git a/orchestrator/ethereum/peggy/message_signatures.go b/orchestrator/ethereum/peggy/message_signatures.go index 14ca91ff..f44e59d8 100644 --- a/orchestrator/ethereum/peggy/message_signatures.go +++ b/orchestrator/ethereum/peggy/message_signatures.go @@ -14,9 +14,9 @@ import ( "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) -/// EncodeValsetConfirm takes the required input data and produces the required signature to confirm a validator -/// set update on the Peggy Ethereum contract. This value will then be signed before being -/// submitted to Cosmos, verified, and then relayed to Ethereum +// / EncodeValsetConfirm takes the required input data and produces the required signature to confirm a validator +// / set update on the Peggy Ethereum contract. This value will then be signed before being +// / submitted to Cosmos, verified, and then relayed to Ethereum func EncodeValsetConfirm(peggyID common.Hash, valset *types.Valset) common.Hash { // error case here should not occur outside of testing since the above is a constant contractAbi, abiErr := abi.JSON(strings.NewReader(ValsetCheckpointABIJSON)) diff --git a/orchestrator/ethereum/peggy/peggy_contract.go b/orchestrator/ethereum/peggy/peggy_contract.go index a243dd0d..36e92c97 100644 --- a/orchestrator/ethereum/peggy/peggy_contract.go +++ b/orchestrator/ethereum/peggy/peggy_contract.go @@ -14,10 +14,11 @@ import ( "github.com/shopspring/decimal" "github.com/InjectiveLabs/metrics" + "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/committer" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/provider" wrappers "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) type PeggyContract interface { diff --git a/orchestrator/ethereum/peggy/pending_transactions.go b/orchestrator/ethereum/peggy/pending_transactions.go index c9f45f00..60c6fffc 100644 --- a/orchestrator/ethereum/peggy/pending_transactions.go +++ b/orchestrator/ethereum/peggy/pending_transactions.go @@ -3,7 +3,6 @@ package peggy import ( "bytes" "context" - "time" log "github.com/xlab/suplog" diff --git a/orchestrator/ethereum/peggy/send_to_cosmos.go b/orchestrator/ethereum/peggy/send_to_cosmos.go index e780da8d..ea64d8b6 100644 --- a/orchestrator/ethereum/peggy/send_to_cosmos.go +++ b/orchestrator/ethereum/peggy/send_to_cosmos.go @@ -11,6 +11,7 @@ import ( log "github.com/xlab/suplog" "github.com/InjectiveLabs/metrics" + wrappers "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" ) diff --git a/orchestrator/loops/paranoid_group.go b/orchestrator/loops/paranoid_group.go index c0c4f261..d8f50454 100644 --- a/orchestrator/loops/paranoid_group.go +++ b/orchestrator/loops/paranoid_group.go @@ -4,7 +4,7 @@ import ( "sync" "time" - multierror "github.com/hashicorp/go-multierror" + "github.com/hashicorp/go-multierror" ) // ParanoidGroup is a special primitive to run groups of goroutines, e.g. loops. diff --git a/orchestrator/mocks_test.go b/orchestrator/mocks_test.go index ec34d68d..99c1e8db 100644 --- a/orchestrator/mocks_test.go +++ b/orchestrator/mocks_test.go @@ -2,12 +2,14 @@ package orchestrator import ( "context" - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + "math/big" + "time" + sdk "github.com/cosmos/cosmos-sdk/types" eth "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" - "math/big" - "time" + + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) diff --git a/orchestrator/oracle_test.go b/orchestrator/oracle_test.go index eeb1ade1..89d4d461 100644 --- a/orchestrator/oracle_test.go +++ b/orchestrator/oracle_test.go @@ -11,8 +11,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/xlab/suplog" - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" ) func TestEthOracle(t *testing.T) { diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 15357603..579c7e8a 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -11,6 +11,7 @@ import ( log "github.com/xlab/suplog" "github.com/InjectiveLabs/metrics" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/loops" diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 02e45c56..6271bd80 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -11,12 +11,13 @@ import ( log "github.com/xlab/suplog" "github.com/InjectiveLabs/metrics" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/util" "github.com/InjectiveLabs/peggo/orchestrator/loops" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) const ( diff --git a/orchestrator/relayer_test.go b/orchestrator/relayer_test.go index 1d32408b..653d6c20 100644 --- a/orchestrator/relayer_test.go +++ b/orchestrator/relayer_test.go @@ -2,16 +2,18 @@ package orchestrator import ( "context" - wrappers "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + "math/big" + "testing" + "time" + cosmtypes "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" ctypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/xlab/suplog" - "math/big" - "testing" - "time" + + wrappers "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 435931ea..8ea037a4 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -7,9 +7,10 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" log "github.com/xlab/suplog" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/loops" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) // EthSignerMainLoop simply signs off on any batches or validator sets provided by the validator From 1d35acda7600dd0ad5efff03bff1f7e42a5a4222 Mon Sep 17 00:00:00 2001 From: Albert Chon Date: Wed, 21 Feb 2024 01:03:16 -0400 Subject: [PATCH 62/99] fix: comment --- orchestrator/ethereum/peggy/message_signatures.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/orchestrator/ethereum/peggy/message_signatures.go b/orchestrator/ethereum/peggy/message_signatures.go index f44e59d8..a3b85707 100644 --- a/orchestrator/ethereum/peggy/message_signatures.go +++ b/orchestrator/ethereum/peggy/message_signatures.go @@ -14,9 +14,9 @@ import ( "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) -// / EncodeValsetConfirm takes the required input data and produces the required signature to confirm a validator -// / set update on the Peggy Ethereum contract. This value will then be signed before being -// / submitted to Cosmos, verified, and then relayed to Ethereum +// EncodeValsetConfirm takes the required input data and produces the required signature to confirm a validator +// set update on the Peggy Ethereum contract. This value will then be signed before being +// submitted to Cosmos, verified, and then relayed to Ethereum func EncodeValsetConfirm(peggyID common.Hash, valset *types.Valset) common.Hash { // error case here should not occur outside of testing since the above is a constant contractAbi, abiErr := abi.JSON(strings.NewReader(ValsetCheckpointABIJSON)) From 395e78347aa358465d9e660e7ff450c64c4b612b Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 13 Mar 2024 13:28:27 +0100 Subject: [PATCH 63/99] skip batches that timed out when relaying --- orchestrator/relayer.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 17e5b9d2..ee716fd0 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -184,7 +184,16 @@ func (l *relayerLoop) relayBatch(ctx context.Context) error { oldestConfirmedBatchSigs []*types.MsgConfirmBatch ) + latestEthHeight, err := l.eth.HeaderByNumber(ctx, nil) + if err != nil { + return errors.Wrap(err, "failed to get latest ethereum header") + } + for _, batch := range latestBatches { + if batch.BatchTimeout <= latestEthHeight.Number.Uint64() { + continue // skip batches whose timeout has passed + } + sigs, err := l.inj.TransactionBatchSignatures(ctx, batch.BatchNonce, common.HexToAddress(batch.TokenContract)) if err != nil { return err From 740b2a72a33f96307f44b902e243e9d8d643accf Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 14 Mar 2024 17:00:06 +0100 Subject: [PATCH 64/99] start relayer mode when validator is unbonded --- cmd/peggo/orchestrator.go | 3 +++ orchestrator/cosmos/network.go | 19 +++++++++++++++++++ orchestrator/orchestrator.go | 12 +++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 026fa019..f7184219 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -108,6 +108,8 @@ func orchestratorCmd(cmd *cli.Cmd) { ) orShutdown(err) + isValidatorBonded := cosmos.IsBondedValidator(cosmosNetwork, ethKeyFromAddress) + // Create peggo and run it peggo, err := orchestrator.NewPeggyOrchestrator( cosmosKeyring.Addr, @@ -120,6 +122,7 @@ func orchestratorCmd(cmd *cli.Cmd) { RelayBatchOffsetDur: *cfg.relayBatchOffsetDur, RelayValsets: *cfg.relayValsets, RelayBatches: *cfg.relayBatches, + IsBonded: isValidatorBonded, }, ) orShutdown(err) diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index 5363240f..6f14bd4e 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -3,6 +3,7 @@ package cosmos import ( "context" "fmt" + gethcommon "github.com/ethereum/go-ethereum/common" "time" comethttp "github.com/cometbft/cometbft/rpc/client/http" @@ -143,3 +144,21 @@ func loadBalancedEndpoints(cfg NetworkConfig) clientcommon.Network { return clientcommon.LoadNetwork(networkName, "lb") } + +func IsBondedValidator(n Network, ethAddr gethcommon.Address) bool { + ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) + defer cancelFn() + + vs, err := n.CurrentValset(ctx) + if err != nil { + log.Fatalln("failed to query current validator set on Injective") + } + + for _, validator := range vs.Members { + if validator.EthereumAddress == ethAddr.Hex() { + return true + } + } + + return false +} diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 15357603..c2a145cc 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -33,6 +33,7 @@ type Config struct { RelayBatchOffsetDur string RelayValsets bool RelayBatches bool + IsBonded bool } type PeggyOrchestrator struct { @@ -47,6 +48,7 @@ type PeggyOrchestrator struct { relayValsetOffsetDur time.Duration relayBatchOffsetDur time.Duration minBatchFeeUSD float64 + isBonded bool } func NewPeggyOrchestrator( @@ -63,6 +65,7 @@ func NewPeggyOrchestrator( priceFeed: priceFeed, erc20ContractMapping: cfg.ERC20ContractMapping, minBatchFeeUSD: cfg.MinBatchFeeUSD, + isBonded: cfg.IsBonded, } if cfg.RelayValsets { @@ -89,7 +92,7 @@ func NewPeggyOrchestrator( // Run starts all major loops required to make // up the Orchestrator, all of these are async loops. func (s *PeggyOrchestrator) Run(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { - if !s.hasDelegateValidator(ctx, inj) { + if !s.isBonded { return s.startRelayerMode(ctx, inj, eth) } @@ -149,10 +152,17 @@ func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.N func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { log.Infoln("running orchestrator in relayer mode") + // get peggy ID from contract + peggyContractID, err := eth.GetPeggyID(ctx) + if err != nil { + s.logger.WithError(err).Fatalln("unable to query peggy ID from contract") + } + var pg loops.ParanoidGroup pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj) }) pg.Go(func() error { return s.RelayerMainLoop(ctx, inj, eth) }) + pg.Go(func() error { return s.EthSignerMainLoop(ctx, inj, peggyContractID) }) return pg.Wait() } From dead99473af9ed9f17d0b6ca2f930d5c9998593d Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 15 Mar 2024 18:19:29 +0100 Subject: [PATCH 65/99] display token decimals --- orchestrator/batch_request.go | 18 +++++++++++++++++- orchestrator/ethereum/network.go | 22 ++++++++++++++++++++++ orchestrator/orchestrator.go | 4 ++-- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 4036d4ff..413b76f3 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "time" cosmostypes "github.com/cosmos/cosmos-sdk/types" @@ -15,10 +16,11 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/loops" ) -func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context, inj cosmos.Network) (err error) { +func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { requester := batchRequester{ PeggyOrchestrator: s, Injective: inj, + Ethereum: eth, LoopDuration: defaultLoopDur, } @@ -32,6 +34,7 @@ func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context, inj cosmos.N type batchRequester struct { *PeggyOrchestrator Injective cosmos.Network + Ethereum ethereum.Network LoopDuration time.Duration } @@ -40,6 +43,19 @@ func (l *batchRequester) Logger() log.Logger { } func (l *batchRequester) RequestBatches(ctx context.Context) error { + // get inj token decimals + var injAddr gethcommon.Address + for address := range l.erc20ContractMapping { + injAddr = address + } + + decimals, err := l.Ethereum.TokenDecimals(ctx, injAddr) + if err != nil { + println(err.Error()) + } else { + println("inj token decimals", decimals) + } + fees, err := l.getUnbatchedTokenFees(ctx) if err != nil { // non-fatal, just alert diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index 42e46741..67c13d73 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -2,6 +2,7 @@ package ethereum import ( "context" + "github.com/ethereum/go-ethereum" "math/big" "strings" "time" @@ -53,6 +54,8 @@ type Network interface { batch *peggytypes.OutgoingTxBatch, confirms []*peggytypes.MsgConfirmBatch, ) (*gethcommon.Hash, error) + + TokenDecimals(ctx context.Context, tokenContract gethcommon.Address) (uint8, error) } type network struct { @@ -115,6 +118,25 @@ func NewNetwork( return n, nil } +func (n *network) TokenDecimals(ctx context.Context, tokenContract gethcommon.Address) (uint8, error) { + msg := ethereum.CallMsg{ + //From: gethcommon.Address{}, + To: &tokenContract, + Data: gethcommon.Hex2Bytes("313ce567"), // Function signature for decimals(), + } + + res, err := n.Provider().CallContract(ctx, msg, nil) + if err != nil { + return 0, err + } + + if len(res) == 0 { + return 0, errors.New("empty decimals() result") + } + + return res[0], nil +} + func (n *network) GetHeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) { return n.Provider().HeaderByNumber(ctx, number) } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index a18a4bca..0c1fb721 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -140,7 +140,7 @@ func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.N var pg loops.ParanoidGroup pg.Go(func() error { return s.EthOracleMainLoop(ctx, inj, eth, lastObservedEthBlock) }) - pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj) }) + pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj, eth) }) pg.Go(func() error { return s.EthSignerMainLoop(ctx, inj, peggyContractID) }) pg.Go(func() error { return s.RelayerMainLoop(ctx, inj, eth) }) @@ -161,7 +161,7 @@ func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context, inj cosmos.Net var pg loops.ParanoidGroup - pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj) }) + pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj, eth) }) pg.Go(func() error { return s.RelayerMainLoop(ctx, inj, eth) }) pg.Go(func() error { return s.EthSignerMainLoop(ctx, inj, peggyContractID) }) From be6f94fa03b71315ffabc251b142baf6a2a72881 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 15 Mar 2024 18:22:48 +0100 Subject: [PATCH 66/99] debug --- orchestrator/batch_request.go | 2 ++ orchestrator/ethereum/network.go | 3 +++ 2 files changed, 5 insertions(+) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 413b76f3..fc20c30b 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -49,6 +49,8 @@ func (l *batchRequester) RequestBatches(ctx context.Context) error { injAddr = address } + println("inj token contract", injAddr.Hex()) + decimals, err := l.Ethereum.TokenDecimals(ctx, injAddr) if err != nil { println(err.Error()) diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index 67c13d73..76e741c2 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -2,6 +2,7 @@ package ethereum import ( "context" + "fmt" "github.com/ethereum/go-ethereum" "math/big" "strings" @@ -134,6 +135,8 @@ func (n *network) TokenDecimals(ctx context.Context, tokenContract gethcommon.Ad return 0, errors.New("empty decimals() result") } + fmt.Printf("res=%#v\n", res) + return res[0], nil } From 2845da7966b28eaf54aa3dd5ab36220b7b6aa656 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 15 Mar 2024 18:25:45 +0100 Subject: [PATCH 67/99] debug --- orchestrator/ethereum/network.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index 76e741c2..cc52f54f 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -2,7 +2,6 @@ package ethereum import ( "context" - "fmt" "github.com/ethereum/go-ethereum" "math/big" "strings" @@ -135,7 +134,7 @@ func (n *network) TokenDecimals(ctx context.Context, tokenContract gethcommon.Ad return 0, errors.New("empty decimals() result") } - fmt.Printf("res=%#v\n", res) + println("res=", res, "len=", len(res)) return res[0], nil } From 054d945479f444ac1aa4a172fa004085255af715 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 15 Mar 2024 18:28:56 +0100 Subject: [PATCH 68/99] parse decimals result --- orchestrator/ethereum/network.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index cc52f54f..62cd7d40 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -134,9 +134,9 @@ func (n *network) TokenDecimals(ctx context.Context, tokenContract gethcommon.Ad return 0, errors.New("empty decimals() result") } - println("res=", res, "len=", len(res)) + decimals := big.NewInt(0).SetBytes(res).Uint64() - return res[0], nil + return uint8(decimals), nil } func (n *network) GetHeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) { From 4d6c21768d1dcd8c3b3d051f9fd46886b8d172eb Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 15 Mar 2024 18:42:19 +0100 Subject: [PATCH 69/99] support non-standard decimal tokens for batching --- orchestrator/batch_request.go | 43 ++++++++++++++++---------------- orchestrator/ethereum/network.go | 6 ++--- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index fc20c30b..47075b8f 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -43,21 +43,6 @@ func (l *batchRequester) Logger() log.Logger { } func (l *batchRequester) RequestBatches(ctx context.Context) error { - // get inj token decimals - var injAddr gethcommon.Address - for address := range l.erc20ContractMapping { - injAddr = address - } - - println("inj token contract", injAddr.Hex()) - - decimals, err := l.Ethereum.TokenDecimals(ctx, injAddr) - if err != nil { - println(err.Error()) - } else { - println("inj token decimals", decimals) - } - fees, err := l.getUnbatchedTokenFees(ctx) if err != nil { // non-fatal, just alert @@ -79,10 +64,18 @@ func (l *batchRequester) RequestBatches(ctx context.Context) error { func (l *batchRequester) getUnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { var unbatchedFees []*peggytypes.BatchFees - if err := retryOnErr(ctx, l.Logger(), func() (err error) { - unbatchedFees, err = l.Injective.UnbatchedTokensWithFees(ctx) - return err - }); err != nil { + fn := func() error { + fees, err := l.Injective.UnbatchedTokensWithFees(ctx) + if err != nil { + return err + } + + unbatchedFees = fees + + return nil + } + + if err := retryOnErr(ctx, l.Logger(), fn); err != nil { return nil, err } @@ -91,7 +84,13 @@ func (l *batchRequester) getUnbatchedTokenFees(ctx context.Context) ([]*peggytyp func (l *batchRequester) requestBatch(ctx context.Context, fee *peggytypes.BatchFees) { tokenAddr := gethcommon.HexToAddress(fee.Token) - if thresholdMet := l.checkFeeThreshold(tokenAddr, fee.TotalFees); !thresholdMet { + tokenDecimals, err := l.Ethereum.TokenDecimals(ctx, tokenAddr) + if err != nil { + l.Logger().WithError(err).Warningln("failed to get token decimals") + return + } + + if thresholdMet := l.checkFeeThreshold(fee.TotalFees, tokenAddr, tokenDecimals); !thresholdMet { return } @@ -110,7 +109,7 @@ func (l *batchRequester) tokenDenom(tokenAddr gethcommon.Address) string { return peggytypes.PeggyDenomString(tokenAddr) } -func (l *batchRequester) checkFeeThreshold(tokenAddr gethcommon.Address, fees cosmostypes.Int) bool { +func (l *batchRequester) checkFeeThreshold(fees cosmostypes.Int, tokenAddr gethcommon.Address, tokenDecimals uint8) bool { if l.minBatchFeeUSD == 0 { return true } @@ -123,7 +122,7 @@ func (l *batchRequester) checkFeeThreshold(tokenAddr gethcommon.Address, fees co var ( minFeeInUSDDec = decimal.NewFromFloat(l.minBatchFeeUSD) tokenPriceInUSDDec = decimal.NewFromFloat(tokenPriceInUSD) - totalFeeInUSDDec = decimal.NewFromBigInt(fees.BigInt(), -18).Mul(tokenPriceInUSDDec) + totalFeeInUSDDec = decimal.NewFromBigInt(fees.BigInt(), -1*int32(tokenDecimals)).Mul(tokenPriceInUSDDec) ) if totalFeeInUSDDec.LessThan(minFeeInUSDDec) { diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index 62cd7d40..61a6ad2a 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -131,12 +131,10 @@ func (n *network) TokenDecimals(ctx context.Context, tokenContract gethcommon.Ad } if len(res) == 0 { - return 0, errors.New("empty decimals() result") + return 0, errors.Errorf("no decimals found for token contract %s", tokenContract.Hex()) } - decimals := big.NewInt(0).SetBytes(res).Uint64() - - return uint8(decimals), nil + return uint8(big.NewInt(0).SetBytes(res).Uint64()), nil } func (n *network) GetHeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) { From c70fae74b9e5b953b592eb63eb5d436736e03ffd Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 18 Mar 2024 17:02:09 +0100 Subject: [PATCH 70/99] logs in oracle --- orchestrator/oracle.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 8005398d..478421ee 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -69,12 +69,14 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { // not enough blocks on ethereum yet if latestHeight <= ethBlockConfirmationDelay { + l.Logger().Debugln("not enough blocks on Ethereum") return nil } // ensure that latest block has minimum confirmations latestHeight = latestHeight - ethBlockConfirmationDelay if latestHeight <= l.LastObservedEthHeight { + l.Logger().WithFields(log.Fields{"latest": latestHeight, "observed": l.LastObservedEthHeight}).Debugln("latest Ethereum height already observed") return nil } From 69cc74c44d845150d7c432ff9b7f65d989392df4 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 20 Mar 2024 11:57:49 +0100 Subject: [PATCH 71/99] refactor --- orchestrator/batch_request.go | 1 - orchestrator/relayer.go | 23 ++++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 47075b8f..8e48bc5d 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -45,7 +45,6 @@ func (l *batchRequester) Logger() log.Logger { func (l *batchRequester) RequestBatches(ctx context.Context) error { fees, err := l.getUnbatchedTokenFees(ctx) if err != nil { - // non-fatal, just alert l.Logger().WithError(err).Warningln("unable to get outgoing withdrawal fees") return nil } diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index af5d2687..1247f010 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -208,26 +208,27 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va return err } - var ( - oldestConfirmedInjBatch *peggytypes.OutgoingTxBatch - oldestConfirmedInjBatchSigs []*peggytypes.MsgConfirmBatch - ) - latestEthHeight, err := l.Ethereum.GetHeaderByNumber(ctx, nil) if err != nil { return errors.Wrap(err, "failed to get latest Ethereum header") } - for _, batch := range latestBatches { - if batch.BatchTimeout <= latestEthHeight.Number.Uint64() { - println("skipping timed out batch", "nonce=", batch.BatchNonce) - continue // skip timed out batches - } + var ( + oldestConfirmedInjBatch *peggytypes.OutgoingTxBatch + oldestConfirmedInjBatchSigs []*peggytypes.MsgConfirmBatch + ) + for _, batch := range latestBatches { sigs, err := l.Injective.TransactionBatchSignatures(ctx, batch.BatchNonce, gethcommon.HexToAddress(batch.TokenContract)) if err != nil { return err - } else if len(sigs) == 0 { + } + + if len(sigs) == 0 { + continue + } + + if batch.BatchTimeout <= latestEthHeight.Number.Uint64() { continue } From 26b04a06b919ff7a9754daf9e90b980ef9d6e4af Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 22 Mar 2024 13:24:22 +0100 Subject: [PATCH 72/99] remove audit fixes for a future PR --- orchestrator/batch_request.go | 13 +++---------- orchestrator/relayer.go | 11 +---------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 8e48bc5d..9f03dda8 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -83,13 +83,7 @@ func (l *batchRequester) getUnbatchedTokenFees(ctx context.Context) ([]*peggytyp func (l *batchRequester) requestBatch(ctx context.Context, fee *peggytypes.BatchFees) { tokenAddr := gethcommon.HexToAddress(fee.Token) - tokenDecimals, err := l.Ethereum.TokenDecimals(ctx, tokenAddr) - if err != nil { - l.Logger().WithError(err).Warningln("failed to get token decimals") - return - } - - if thresholdMet := l.checkFeeThreshold(fee.TotalFees, tokenAddr, tokenDecimals); !thresholdMet { + if thresholdMet := l.checkFeeThreshold(fee.TotalFees, tokenAddr); !thresholdMet { return } @@ -104,11 +98,10 @@ func (l *batchRequester) tokenDenom(tokenAddr gethcommon.Address) string { return cosmosDenom } - // todo: revisit peggy denom addresses return peggytypes.PeggyDenomString(tokenAddr) } -func (l *batchRequester) checkFeeThreshold(fees cosmostypes.Int, tokenAddr gethcommon.Address, tokenDecimals uint8) bool { +func (l *batchRequester) checkFeeThreshold(fees cosmostypes.Int, tokenAddr gethcommon.Address) bool { if l.minBatchFeeUSD == 0 { return true } @@ -121,7 +114,7 @@ func (l *batchRequester) checkFeeThreshold(fees cosmostypes.Int, tokenAddr gethc var ( minFeeInUSDDec = decimal.NewFromFloat(l.minBatchFeeUSD) tokenPriceInUSDDec = decimal.NewFromFloat(tokenPriceInUSD) - totalFeeInUSDDec = decimal.NewFromBigInt(fees.BigInt(), -1*int32(tokenDecimals)).Mul(tokenPriceInUSDDec) + totalFeeInUSDDec = decimal.NewFromBigInt(fees.BigInt(), -18).Mul(tokenPriceInUSDDec) // todo: fix decimals ) if totalFeeInUSDDec.LessThan(minFeeInUSDDec) { diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index 1247f010..bc90d7ea 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -60,7 +60,6 @@ func (l *relayer) Logger() log.Logger { } func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { - // need latest vs on Ethereum for sig verification ethValset, err := l.GetLatestEthValset(ctx) if err != nil { return err @@ -208,16 +207,12 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va return err } - latestEthHeight, err := l.Ethereum.GetHeaderByNumber(ctx, nil) - if err != nil { - return errors.Wrap(err, "failed to get latest Ethereum header") - } - var ( oldestConfirmedInjBatch *peggytypes.OutgoingTxBatch oldestConfirmedInjBatchSigs []*peggytypes.MsgConfirmBatch ) + // todo: skip timed out batches for _, batch := range latestBatches { sigs, err := l.Injective.TransactionBatchSignatures(ctx, batch.BatchNonce, gethcommon.HexToAddress(batch.TokenContract)) if err != nil { @@ -228,10 +223,6 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va continue } - if batch.BatchTimeout <= latestEthHeight.Number.Uint64() { - continue - } - oldestConfirmedInjBatch = batch oldestConfirmedInjBatchSigs = sigs } From 88d0a5f7e333a8813ec79878ec40d5ecb259795a Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 22 Mar 2024 15:22:21 +0100 Subject: [PATCH 73/99] reduce resync interval and inform user if an event claim was missed --- orchestrator/batch_request.go | 2 +- orchestrator/oracle.go | 176 +++++++++++++++++++++------------- orchestrator/orchestrator.go | 2 +- orchestrator/relayer.go | 6 +- orchestrator/signer.go | 8 +- 5 files changed, 119 insertions(+), 75 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index 9f03dda8..f10389e9 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -74,7 +74,7 @@ func (l *batchRequester) getUnbatchedTokenFees(ctx context.Context) ([]*peggytyp return nil } - if err := retryOnErr(ctx, l.Logger(), fn); err != nil { + if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { return nil, err } diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 478421ee..50e48c13 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -22,6 +22,14 @@ const ( // the oracle loop can potentially run longer than defaultLoopDur due to a surge of events. This usually happens // when there are more than ~50 events to claim in a single run. defaultBlocksToSearch uint64 = 2000 + + // Auto re-sync to catch up the validator's last observed event nonce. Reasons why event nonce fall behind: + // 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. + // We need to re-scan this block to ensure events are not missed due to indexing delay. + // 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. + // 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. + // 4. if an event was sent to Injective successfully but didn't end up in a block, subsequently claimed events will be ignored until a manual restart or auto re-sync occur + resyncInterval = 3 * time.Hour ) // EthOracleMainLoop is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain @@ -97,13 +105,7 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { l.Logger().WithFields(log.Fields{"block_start": l.LastObservedEthHeight, "block_end": latestHeight}).Debugln("scanned Ethereum blocks") l.LastObservedEthHeight = latestHeight - /** Auto re-sync to catch up the nonce. Reasons why event nonce fall behind. - 1. It takes some time for events to be indexed on Ethereum. So if peggo queried events immediately as block produced, there is a chance the event is missed. - we need to re-scan this block to ensure events are not missed due to indexing delay. - 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. - 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. - */ - if time.Since(l.LastResyncWithInjective) >= 48*time.Hour { + if time.Since(l.LastResyncWithInjective) >= resyncInterval { if err := l.autoResync(ctx); err != nil { return err } @@ -114,7 +116,6 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint64) (ethEvents, error) { events := ethEvents{} - scanEthEventsFn := func() error { legacyDeposits, err := l.Ethereum.GetSendToCosmosEvents(startBlock, endBlock) if err != nil { @@ -150,7 +151,7 @@ func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint6 return nil } - if err := retryOnErr(ctx, l.Logger(), scanEthEventsFn); err != nil { + if err := retryFnOnErr(ctx, l.Logger(), scanEthEventsFn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return ethEvents{}, err } @@ -159,16 +160,18 @@ func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint6 } func (l *ethOracle) getLatestEthHeight(ctx context.Context) (uint64, error) { - var latestHeight uint64 - if err := retryOnErr(ctx, l.Logger(), func() error { - latestHeader, err := l.Ethereum.GetHeaderByNumber(ctx, nil) + latestHeight := uint64(0) + fn := func() error { + h, err := l.Ethereum.GetHeaderByNumber(ctx, nil) if err != nil { return errors.Wrap(err, "failed to get latest ethereum header") } - latestHeight = latestHeader.Number.Uint64() + latestHeight = h.Number.Uint64() return nil - }); err != nil { + } + + if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return 0, err } @@ -183,14 +186,23 @@ func (l *ethOracle) sendNewEventClaims(ctx context.Context, events ethEvents) er return err } - newEvents := events.Filter(lastClaim.EthereumEventNonce) - if newEvents.Num() == 0 { + newEvents := events.Filter(lastClaim.EthereumEventNonce).Sort() + if len(newEvents) == 0 { l.Logger().WithField("last_claimed_event_nonce", lastClaim.EthereumEventNonce).Infoln("no new events on Ethereum") return nil } - sortedEvents := newEvents.Sort() - for _, event := range sortedEvents { + if expected, actual := lastClaim.EthereumEventNonce+1, newEvents[0].Nonce(); expected != actual { + l.Logger().WithFields(log.Fields{ + "expected_nonce": expected, + "actual_nonce": newEvents[0].Nonce(), + "time_until_resync": time.Until(l.LastResyncWithInjective.Add(resyncInterval)).String(), + }).Infoln("orchestrator missed an event claim. Restart your peggo or wait until resync") + + return nil + } + + for _, event := range newEvents { if err := l.sendEthEventClaim(ctx, event); err != nil { return err } @@ -200,12 +212,12 @@ func (l *ethOracle) sendNewEventClaims(ctx context.Context, events ethEvents) er time.Sleep(1200 * time.Millisecond) } - l.Logger().WithField("claims", len(sortedEvents)).Infoln("sent new event claims to Injective") + l.Logger().WithField("claims", len(newEvents)).Infoln("sent new event claims to Injective") return nil } - if err := retryOnErr(ctx, l.Logger(), sendEventsFn); err != nil { + if err := retryFnOnErr(ctx, l.Logger(), sendEventsFn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return err } @@ -214,11 +226,18 @@ func (l *ethOracle) sendNewEventClaims(ctx context.Context, events ethEvents) er } func (l *ethOracle) autoResync(ctx context.Context) error { - var latestHeight uint64 - if err := retryOnErr(ctx, l.Logger(), func() (err error) { - latestHeight, err = l.getLastClaimBlockHeight(ctx, l.Injective) - return - }); err != nil { + latestHeight := uint64(0) + fn := func() error { + h, err := l.getLastClaimBlockHeight(ctx, l.Injective) + if err != nil { + return err + } + + latestHeight = h + return nil + } + + if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return err } @@ -231,20 +250,25 @@ func (l *ethOracle) autoResync(ctx context.Context) error { return nil } -func (l *ethOracle) sendEthEventClaim(ctx context.Context, event any) error { - switch e := event.(type) { - case *peggyevents.PeggySendToCosmosEvent: - return l.Injective.SendOldDepositClaim(ctx, e) - case *peggyevents.PeggySendToInjectiveEvent: - return l.Injective.SendDepositClaim(ctx, e) - case *peggyevents.PeggyValsetUpdatedEvent: - return l.Injective.SendValsetClaim(ctx, e) - case *peggyevents.PeggyTransactionBatchExecutedEvent: - return l.Injective.SendWithdrawalClaim(ctx, e) - case *peggyevents.PeggyERC20DeployedEvent: - return l.Injective.SendERC20DeployedClaim(ctx, e) +func (l *ethOracle) sendEthEventClaim(ctx context.Context, ev event) error { + switch e := ev.(type) { + case *oldDeposit: + ev := peggyevents.PeggySendToCosmosEvent(*e) + return l.Injective.SendOldDepositClaim(ctx, &ev) + case *deposit: + ev := peggyevents.PeggySendToInjectiveEvent(*e) + return l.Injective.SendDepositClaim(ctx, &ev) + case *valsetUpdate: + ev := peggyevents.PeggyValsetUpdatedEvent(*e) + return l.Injective.SendValsetClaim(ctx, &ev) + case *withdrawal: + ev := peggyevents.PeggyTransactionBatchExecutedEvent(*e) + return l.Injective.SendWithdrawalClaim(ctx, &ev) + case *erc20Deployment: + ev := peggyevents.PeggyERC20DeployedEvent(*e) + return l.Injective.SendERC20DeployedClaim(ctx, &ev) default: - panic(errors.Errorf("unknown event type %T", e)) + panic(errors.Errorf("unknown ev type %T", e)) } } @@ -305,50 +329,70 @@ func (e ethEvents) Filter(nonce uint64) ethEvents { } } -func (e ethEvents) Sort() []any { - events := make([]any, 0, e.Num()) +func (e ethEvents) Sort() []event { + events := make([]event, 0, e.Num()) - for _, deposit := range e.OldDeposits { - events = append(events, deposit) + for _, d := range e.OldDeposits { + ev := oldDeposit(*d) + events = append(events, &ev) } - for _, deposit := range e.Deposits { - events = append(events, deposit) + for _, d := range e.Deposits { + ev := deposit(*d) + events = append(events, &ev) } - for _, withdrawal := range e.Withdrawals { - events = append(events, withdrawal) + for _, w := range e.Withdrawals { + ev := withdrawal(*w) + events = append(events, &ev) } for _, deployment := range e.ERC20Deployments { - events = append(events, deployment) + ev := erc20Deployment(*deployment) + events = append(events, &ev) } for _, vs := range e.ValsetUpdates { - events = append(events, vs) - } - - eventNonce := func(event any) uint64 { - switch e := event.(type) { - case *peggyevents.PeggySendToCosmosEvent: - return e.EventNonce.Uint64() - case *peggyevents.PeggySendToInjectiveEvent: - return e.EventNonce.Uint64() - case *peggyevents.PeggyValsetUpdatedEvent: - return e.EventNonce.Uint64() - case *peggyevents.PeggyTransactionBatchExecutedEvent: - return e.EventNonce.Uint64() - case *peggyevents.PeggyERC20DeployedEvent: - return e.EventNonce.Uint64() - default: - panic(errors.Errorf("unknown event type %T", e)) - } + ev := valsetUpdate(*vs) + events = append(events, &ev) } // sort by nonce sort.Slice(events, func(i, j int) bool { - return eventNonce(events[i]) < eventNonce(events[j]) + return events[i].Nonce() < events[j].Nonce() }) return events } + +type ( + oldDeposit peggyevents.PeggySendToCosmosEvent + deposit peggyevents.PeggySendToInjectiveEvent + valsetUpdate peggyevents.PeggyValsetUpdatedEvent + withdrawal peggyevents.PeggyTransactionBatchExecutedEvent + erc20Deployment peggyevents.PeggyERC20DeployedEvent + + event interface { + Nonce() uint64 + } +) + +func (o *oldDeposit) Nonce() uint64 { + return o.EventNonce.Uint64() +} + +func (o *deposit) Nonce() uint64 { + return o.EventNonce.Uint64() +} + +func (o *valsetUpdate) Nonce() uint64 { + return o.EventNonce.Uint64() +} + +func (o *withdrawal) Nonce() uint64 { + return o.EventNonce.Uint64() +} + +func (o *erc20Deployment) Nonce() uint64 { + return o.EventNonce.Uint64() +} diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 0c1fb721..963e4299 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -181,7 +181,7 @@ func (s *PeggyOrchestrator) getLastClaimBlockHeight(ctx context.Context, inj cos return claim.EthereumEventHeight, nil } -func retryOnErr(ctx context.Context, log log.Logger, fn func() error) error { +func retryFnOnErr(ctx context.Context, log log.Logger, fn func() error) error { return retry.Do(fn, retry.Context(ctx), retry.Attempts(maxRetryAttempts), diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index bc90d7ea..bde78607 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -69,7 +69,7 @@ func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { if l.relayValsetOffsetDur != 0 { pg.Go(func() error { - return retryOnErr(ctx, l.Logger(), func() error { + return retryFnOnErr(ctx, l.Logger(), func() error { return l.relayValset(ctx, ethValset) }) }) @@ -77,7 +77,7 @@ func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { if l.relayBatchOffsetDur != 0 { pg.Go(func() error { - return retryOnErr(ctx, l.Logger(), func() error { + return retryFnOnErr(ctx, l.Logger(), func() error { return l.relayBatch(ctx, ethValset) }) }) @@ -96,7 +96,7 @@ func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, error) { var latestEthValset *peggytypes.Valset - if err := retryOnErr(ctx, l.Logger(), func() error { + if err := retryFnOnErr(ctx, l.Logger(), func() error { vs, err := l.findLatestValsetOnEth(ctx) if err != nil { return err diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 8ea037a4..8585202d 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -89,7 +89,7 @@ func (l *ethSigner) signNewBatch(ctx context.Context) error { func (l *ethSigner) getUnsignedBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { var oldestUnsignedBatch *peggytypes.OutgoingTxBatch - if err := retryOnErr(ctx, l.Logger(), func() error { + if err := retryFnOnErr(ctx, l.Logger(), func() error { oldestUnsignedBatch, _ = l.Injective.OldestUnsignedTransactionBatch(ctx, l.injAddr) return nil }); err != nil { @@ -106,7 +106,7 @@ func (l *ethSigner) signBatch(ctx context.Context, batch *peggytypes.OutgoingTxB return nil } - if err := retryOnErr(ctx, l.Logger(), func() error { + if err := retryFnOnErr(ctx, l.Logger(), func() error { return l.Injective.SendBatchConfirm(ctx, l.ethAddr, l.PeggyID, batch) }); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") @@ -120,7 +120,7 @@ func (l *ethSigner) signBatch(ctx context.Context, batch *peggytypes.OutgoingTxB func (l *ethSigner) getUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { var oldestUnsignedValsets []*peggytypes.Valset - if err := retryOnErr(ctx, l.Logger(), func() error { + if err := retryFnOnErr(ctx, l.Logger(), func() error { oldestUnsignedValsets, _ = l.Injective.OldestUnsignedValsets(ctx, l.injAddr) return nil }); err != nil { @@ -132,7 +132,7 @@ func (l *ethSigner) getUnsignedValsets(ctx context.Context) ([]*peggytypes.Valse } func (l *ethSigner) signValset(ctx context.Context, vs *peggytypes.Valset) error { - if err := retryOnErr(ctx, l.Logger(), func() error { + if err := retryFnOnErr(ctx, l.Logger(), func() error { return l.Injective.SendValsetConfirm(ctx, l.ethAddr, l.PeggyID, vs) }); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") From 0ed83947457f6f9137c1993df43ea5a5bf8e805f Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 22 Mar 2024 15:39:35 +0100 Subject: [PATCH 74/99] return oracle loop early if validator is not in active set --- orchestrator/batch_request.go | 12 ++++-------- orchestrator/oracle.go | 28 ++++++++++++++++++++++++---- orchestrator/relayer.go | 10 ++++------ orchestrator/signer.go | 12 ++++-------- 4 files changed, 36 insertions(+), 26 deletions(-) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_request.go index f10389e9..8be18251 100644 --- a/orchestrator/batch_request.go +++ b/orchestrator/batch_request.go @@ -3,8 +3,6 @@ package orchestrator import ( "context" "github.com/InjectiveLabs/peggo/orchestrator/ethereum" - "time" - cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/shopspring/decimal" @@ -21,21 +19,19 @@ func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context, inj cosmos.N PeggyOrchestrator: s, Injective: inj, Ethereum: eth, - LoopDuration: defaultLoopDur, } - s.logger.WithField("loop_duration", requester.LoopDuration.String()).Debugln("starting BatchRequester...") + s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting BatchRequester...") - return loops.RunLoop(ctx, requester.LoopDuration, func() error { + return loops.RunLoop(ctx, defaultLoopDur, func() error { return requester.RequestBatches(ctx) }) } type batchRequester struct { *PeggyOrchestrator - Injective cosmos.Network - Ethereum ethereum.Network - LoopDuration time.Duration + Injective cosmos.Network + Ethereum ethereum.Network } func (l *batchRequester) Logger() log.Logger { diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 50e48c13..fd4a48b4 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -44,14 +44,13 @@ func (s *PeggyOrchestrator) EthOracleMainLoop( PeggyOrchestrator: s, Injective: inj, Ethereum: eth, - LoopDuration: defaultLoopDur, LastObservedEthHeight: lastObservedBlock, LastResyncWithInjective: time.Now(), } - s.logger.WithField("loop_duration", oracle.LoopDuration.String()).Debugln("starting EthOracle...") + s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting EthOracle...") - return loops.RunLoop(ctx, oracle.LoopDuration, func() error { + return loops.RunLoop(ctx, defaultLoopDur, func() error { return oracle.ObserveEthEvents(ctx) }) } @@ -60,7 +59,6 @@ type ethOracle struct { *PeggyOrchestrator Injective cosmos.Network Ethereum ethereum.Network - LoopDuration time.Duration LastResyncWithInjective time.Time LastObservedEthHeight uint64 } @@ -70,6 +68,28 @@ func (l *ethOracle) Logger() log.Logger { } func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { + // check if validator is in the active set since claims will fail otherwise + vs, err := l.Injective.CurrentValset(ctx) + if err != nil { + return errors.Wrap(err, "failed to get current valset on Injective") + } + + bonded := false + for _, v := range vs.Members { + if l.ethAddr.Hex() == v.EthereumAddress { + bonded = true + } + } + + if !bonded { + l.Logger().WithFields(log.Fields{ + "orchestrator_addr": l.injAddr.String(), + "eth_addr": l.ethAddr.String(), + "latest_inj_block": vs.Height, + }).Debugln("validator not in active set, returning...") + return nil + } + latestHeight, err := l.getLatestEthHeight(ctx) if err != nil { return err diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go index bde78607..fc4e8679 100644 --- a/orchestrator/relayer.go +++ b/orchestrator/relayer.go @@ -34,25 +34,23 @@ func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Netw PeggyOrchestrator: s, Injective: inj, Ethereum: eth, - LoopDuration: defaultRelayerLoopDur, } s.logger.WithFields(log.Fields{ - "loop_duration": rel.LoopDuration.String(), + "loop_duration": defaultRelayerLoopDur.String(), "relay_batches": rel.relayBatchOffsetDur != 0, "relay_valsets": rel.relayValsetOffsetDur != 0, }).Debugln("starting Relayer...") - return loops.RunLoop(ctx, rel.LoopDuration, func() error { + return loops.RunLoop(ctx, defaultRelayerLoopDur, func() error { return rel.RelayValsetsAndBatches(ctx) }) } type relayer struct { *PeggyOrchestrator - Injective cosmos.Network - Ethereum ethereum.Network - LoopDuration time.Duration + Injective cosmos.Network + Ethereum ethereum.Network } func (l *relayer) Logger() log.Logger { diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 8585202d..4986ffb8 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -2,8 +2,6 @@ package orchestrator import ( "context" - "time" - gethcommon "github.com/ethereum/go-ethereum/common" log "github.com/xlab/suplog" @@ -21,21 +19,19 @@ func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, inj cosmos.Ne PeggyOrchestrator: s, Injective: inj, PeggyID: peggyID, - LoopDuration: defaultLoopDur, } - s.logger.WithField("loop_duration", signer.LoopDuration.String()).Debugln("starting EthSigner...") + s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting EthSigner...") - return loops.RunLoop(ctx, signer.LoopDuration, func() error { + return loops.RunLoop(ctx, defaultLoopDur, func() error { return signer.SignValsetsAndBatches(ctx) }) } type ethSigner struct { *PeggyOrchestrator - Injective cosmos.Network - LoopDuration time.Duration - PeggyID gethcommon.Hash + Injective cosmos.Network + PeggyID gethcommon.Hash } func (l *ethSigner) Logger() log.Logger { From 2815c99a3f656606466205f8269d8e5104f6744a Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 22 Mar 2024 15:49:52 +0100 Subject: [PATCH 75/99] start peggo in relayer mode only if there is no previously registered orch address --- cmd/peggo/orchestrator.go | 4 ++-- orchestrator/cosmos/network.go | 15 ++++----------- orchestrator/orchestrator.go | 17 +++++------------ 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 709f8430..a5bdbddf 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -109,7 +109,7 @@ func orchestratorCmd(cmd *cli.Cmd) { ) orShutdown(err) - isValidatorBonded := cosmos.IsBondedValidator(cosmosNetwork, ethKeyFromAddress) + isValidator := cosmos.HasRegisteredOrchestrator(cosmosNetwork, ethKeyFromAddress) // Create peggo and run it peggo, err := orchestrator.NewPeggyOrchestrator( @@ -123,7 +123,7 @@ func orchestratorCmd(cmd *cli.Cmd) { RelayBatchOffsetDur: *cfg.relayBatchOffsetDur, RelayValsets: *cfg.relayValsets, RelayBatches: *cfg.relayBatches, - IsBonded: isValidatorBonded, + RelayerMode: !isValidator, }, ) orShutdown(err) diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index f8f0d893..db07171f 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -146,20 +146,13 @@ func loadBalancedEndpoints(cfg NetworkConfig) clientcommon.Network { return clientcommon.LoadNetwork(networkName, "lb") } -func IsBondedValidator(n Network, ethAddr gethcommon.Address) bool { +func HasRegisteredOrchestrator(n Network, ethAddr gethcommon.Address) bool { ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) defer cancelFn() - vs, err := n.CurrentValset(ctx) - if err != nil { - log.Fatalln("failed to query current validator set on Injective") - } - - for _, validator := range vs.Members { - if validator.EthereumAddress == ethAddr.Hex() { - return true - } + if _, err := n.GetValidatorAddress(ctx, ethAddr); err != nil { + return false } - return false + return true } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 963e4299..0a033488 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -34,7 +34,7 @@ type Config struct { RelayBatchOffsetDur string RelayValsets bool RelayBatches bool - IsBonded bool + RelayerMode bool } type PeggyOrchestrator struct { @@ -49,7 +49,7 @@ type PeggyOrchestrator struct { relayValsetOffsetDur time.Duration relayBatchOffsetDur time.Duration minBatchFeeUSD float64 - isBonded bool + isRelayer bool } func NewPeggyOrchestrator( @@ -66,7 +66,7 @@ func NewPeggyOrchestrator( priceFeed: priceFeed, erc20ContractMapping: cfg.ERC20ContractMapping, minBatchFeeUSD: cfg.MinBatchFeeUSD, - isBonded: cfg.IsBonded, + isRelayer: cfg.RelayerMode, } if cfg.RelayValsets { @@ -93,7 +93,7 @@ func NewPeggyOrchestrator( // Run starts all major loops required to make // up the Orchestrator, all of these are async loops. func (s *PeggyOrchestrator) Run(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { - if !s.isBonded { + if s.isRelayer { return s.startRelayerMode(ctx, inj, eth) } @@ -140,8 +140,8 @@ func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.N var pg loops.ParanoidGroup pg.Go(func() error { return s.EthOracleMainLoop(ctx, inj, eth, lastObservedEthBlock) }) - pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj, eth) }) pg.Go(func() error { return s.EthSignerMainLoop(ctx, inj, peggyContractID) }) + pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj, eth) }) pg.Go(func() error { return s.RelayerMainLoop(ctx, inj, eth) }) return pg.Wait() @@ -153,17 +153,10 @@ func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.N func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { log.Infoln("running orchestrator in relayer mode") - // get peggy ID from contract - peggyContractID, err := eth.GetPeggyID(ctx) - if err != nil { - s.logger.WithError(err).Fatalln("unable to query peggy ID from contract") - } - var pg loops.ParanoidGroup pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj, eth) }) pg.Go(func() error { return s.RelayerMainLoop(ctx, inj, eth) }) - pg.Go(func() error { return s.EthSignerMainLoop(ctx, inj, peggyContractID) }) return pg.Wait() } From db2d0a440b06bb1d50c783db942d279948ff56dd Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Fri, 22 Mar 2024 15:52:31 +0100 Subject: [PATCH 76/99] fix log --- orchestrator/oracle.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index fd4a48b4..68be55c3 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -83,9 +83,9 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { if !bonded { l.Logger().WithFields(log.Fields{ - "orchestrator_addr": l.injAddr.String(), - "eth_addr": l.ethAddr.String(), - "latest_inj_block": vs.Height, + "orchestrator_addr": l.injAddr.String(), + "eth_addr": l.ethAddr.String(), + "latest_valset_block": vs.Height, }).Debugln("validator not in active set, returning...") return nil } From d0a7e9f7b2205b35d28d081975c252f038a463b6 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 25 Mar 2024 12:11:42 +0100 Subject: [PATCH 77/99] fix log and increase resyncInterval to 1 day --- orchestrator/oracle.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 68be55c3..19219812 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -29,7 +29,7 @@ const ( // 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. // 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. // 4. if an event was sent to Injective successfully but didn't end up in a block, subsequently claimed events will be ignored until a manual restart or auto re-sync occur - resyncInterval = 3 * time.Hour + resyncInterval = 24 * time.Hour ) // EthOracleMainLoop is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain @@ -82,11 +82,7 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { } if !bonded { - l.Logger().WithFields(log.Fields{ - "orchestrator_addr": l.injAddr.String(), - "eth_addr": l.ethAddr.String(), - "latest_valset_block": vs.Height, - }).Debugln("validator not in active set, returning...") + l.Logger().WithFields(log.Fields{"orchestrator_addr": l.injAddr.String(), "eth_addr": l.ethAddr.String(), "latest_inj_block": vs.Height}).Infoln("validator not in active set") return nil } From 54c2fe43ef7d8c863b4e18afae48d8897c645b37 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 25 Mar 2024 12:23:07 +0100 Subject: [PATCH 78/99] fix log --- orchestrator/oracle.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 19219812..0167da56 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -82,7 +82,7 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { } if !bonded { - l.Logger().WithFields(log.Fields{"orchestrator_addr": l.injAddr.String(), "eth_addr": l.ethAddr.String(), "latest_inj_block": vs.Height}).Infoln("validator not in active set") + l.Logger().WithFields(log.Fields{"latest_inj_block": vs.Height}).Infoln("validator not in active set, cannot make claims...") return nil } From 890fc98f8f42fccbf9e9c13375853fdf7aa1f073 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 25 Mar 2024 14:11:29 +0100 Subject: [PATCH 79/99] cleanup oracle --- cmd/peggo/orchestrator.go | 3 + orchestrator/oracle.go | 214 +++++++++++++++-------------------- orchestrator/orchestrator.go | 15 --- orchestrator/signer.go | 6 +- 4 files changed, 99 insertions(+), 139 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index a5bdbddf..dd310c5d 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -110,6 +110,9 @@ func orchestratorCmd(cmd *cli.Cmd) { orShutdown(err) isValidator := cosmos.HasRegisteredOrchestrator(cosmosNetwork, ethKeyFromAddress) + if isValidator { + log.Debugln("provided ETH address is registered with a validator, configuring peggo to run in validator mode") + } // Create peggo and run it peggo, err := orchestrator.NewPeggyOrchestrator( diff --git a/orchestrator/oracle.go b/orchestrator/oracle.go index 0167da56..afc17423 100644 --- a/orchestrator/oracle.go +++ b/orchestrator/oracle.go @@ -12,6 +12,7 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/loops" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) const ( @@ -28,7 +29,6 @@ const ( // We need to re-scan this block to ensure events are not missed due to indexing delay. // 2. if validator was in UnBonding state, the claims broadcasted in last iteration are failed. // 3. if infura call failed while filtering events, the peggo missed to broadcast claim events occured in last iteration. - // 4. if an event was sent to Injective successfully but didn't end up in a block, subsequently claimed events will be ignored until a manual restart or auto re-sync occur resyncInterval = 24 * time.Hour ) @@ -114,7 +114,31 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { return err } - if err := l.sendNewEventClaims(ctx, events); err != nil { + lastClaim, err := l.getLastClaimEvent(ctx) + if err != nil { + return err + } + + newEvents := filterEvents(events, lastClaim.EthereumEventNonce) + sort.Slice(newEvents, func(i, j int) bool { + return newEvents[i].Nonce() < newEvents[j].Nonce() + }) + + if len(newEvents) == 0 { + l.Logger().WithField("last_claimed_event_nonce", lastClaim.EthereumEventNonce).Infoln("no new events on Ethereum") + l.LastObservedEthHeight = latestHeight + + return nil + } + + if expected, actual := lastClaim.EthereumEventNonce+1, newEvents[0].Nonce(); expected != actual { + l.Logger().WithFields(log.Fields{"expected_nonce": expected, "actual_nonce": actual, "last_claim_event_nonce": lastClaim.EthereumEventNonce}).Infoln("orchestrator missed an Ethereum event. Resyncing event nonce with last claimed event...") + l.LastObservedEthHeight = lastClaim.EthereumEventHeight + + return nil + } + + if err := l.sendNewEventClaims(ctx, newEvents); err != nil { return err } @@ -130,46 +154,65 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { return nil } -func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint64) (ethEvents, error) { - events := ethEvents{} +func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint64) ([]event, error) { + var events []event scanEthEventsFn := func() error { - legacyDeposits, err := l.Ethereum.GetSendToCosmosEvents(startBlock, endBlock) + oldDepositEvents, err := l.Ethereum.GetSendToCosmosEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get SendToCosmos events") } - deposits, err := l.Ethereum.GetSendToInjectiveEvents(startBlock, endBlock) + depositEvents, err := l.Ethereum.GetSendToInjectiveEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get SendToInjective events") } - withdrawals, err := l.Ethereum.GetTransactionBatchExecutedEvents(startBlock, endBlock) + withdrawalEvents, err := l.Ethereum.GetTransactionBatchExecutedEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get TransactionBatchExecuted events") } - erc20Deployments, err := l.Ethereum.GetPeggyERC20DeployedEvents(startBlock, endBlock) + erc20DeploymentEvents, err := l.Ethereum.GetPeggyERC20DeployedEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get ERC20Deployed events") } - valsetUpdates, err := l.Ethereum.GetValsetUpdatedEvents(startBlock, endBlock) + valsetUpdateEvents, err := l.Ethereum.GetValsetUpdatedEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get ValsetUpdated events") } - events.OldDeposits = legacyDeposits - events.Deposits = deposits - events.Withdrawals = withdrawals - events.ValsetUpdates = valsetUpdates - events.ERC20Deployments = erc20Deployments + for _, e := range oldDepositEvents { + ev := oldDeposit(*e) + events = append(events, &ev) + } + + for _, e := range depositEvents { + ev := deposit(*e) + events = append(events, &ev) + } + + for _, e := range withdrawalEvents { + ev := withdrawal(*e) + events = append(events, &ev) + } + + for _, e := range valsetUpdateEvents { + ev := valsetUpdate(*e) + events = append(events, &ev) + } + + for _, e := range erc20DeploymentEvents { + ev := erc20Deployment(*e) + events = append(events, &ev) + } return nil } if err := retryFnOnErr(ctx, l.Logger(), scanEthEventsFn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") - return ethEvents{}, err + return nil, err } return events, nil @@ -195,29 +238,39 @@ func (l *ethOracle) getLatestEthHeight(ctx context.Context) (uint64, error) { return latestHeight, nil } -func (l *ethOracle) sendNewEventClaims(ctx context.Context, events ethEvents) error { +func (l *ethOracle) getLastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) { + var claim *peggytypes.LastClaimEvent + fn := func() error { + c, err := l.Injective.LastClaimEventByAddr(ctx, l.injAddr) + if err != nil { + return err + } + + claim = c + return nil + } + + if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return nil, err + } + + return claim, nil +} + +func (l *ethOracle) sendNewEventClaims(ctx context.Context, events []event) error { sendEventsFn := func() error { lastClaim, err := l.Injective.LastClaimEventByAddr(ctx, l.injAddr) if err != nil { return err } - newEvents := events.Filter(lastClaim.EthereumEventNonce).Sort() + newEvents := filterEvents(events, lastClaim.EthereumEventNonce) if len(newEvents) == 0 { l.Logger().WithField("last_claimed_event_nonce", lastClaim.EthereumEventNonce).Infoln("no new events on Ethereum") return nil } - if expected, actual := lastClaim.EthereumEventNonce+1, newEvents[0].Nonce(); expected != actual { - l.Logger().WithFields(log.Fields{ - "expected_nonce": expected, - "actual_nonce": newEvents[0].Nonce(), - "time_until_resync": time.Until(l.LastResyncWithInjective.Add(resyncInterval)).String(), - }).Infoln("orchestrator missed an event claim. Restart your peggo or wait until resync") - - return nil - } - for _, event := range newEvents { if err := l.sendEthEventClaim(ctx, event); err != nil { return err @@ -258,11 +311,11 @@ func (l *ethOracle) autoResync(ctx context.Context) error { return err } + l.Logger().WithFields(log.Fields{"last_resync": l.LastResyncWithInjective.String(), "last_claimed_eth_height": latestHeight}).Infoln("resyncing with last claimed event on Injective") + l.LastObservedEthHeight = latestHeight l.LastResyncWithInjective = time.Now() - l.Logger().WithFields(log.Fields{"last_resync_time": l.LastResyncWithInjective.String(), "last_claimed_eth_height": l.LastObservedEthHeight}).Infoln("auto resync with last claimed event on Injective") - return nil } @@ -288,99 +341,6 @@ func (l *ethOracle) sendEthEventClaim(ctx context.Context, ev event) error { } } -type ethEvents struct { - OldDeposits []*peggyevents.PeggySendToCosmosEvent - Deposits []*peggyevents.PeggySendToInjectiveEvent - Withdrawals []*peggyevents.PeggyTransactionBatchExecutedEvent - ValsetUpdates []*peggyevents.PeggyValsetUpdatedEvent - ERC20Deployments []*peggyevents.PeggyERC20DeployedEvent -} - -func (e ethEvents) Num() int { - return len(e.OldDeposits) + len(e.Deposits) + len(e.Withdrawals) + len(e.ValsetUpdates) + len(e.ERC20Deployments) -} - -func (e ethEvents) Filter(nonce uint64) ethEvents { - var oldDeposits []*peggyevents.PeggySendToCosmosEvent - for _, d := range e.OldDeposits { - if d.EventNonce.Uint64() > nonce { - oldDeposits = append(oldDeposits, d) - } - } - - var deposits []*peggyevents.PeggySendToInjectiveEvent - for _, d := range e.Deposits { - if d.EventNonce.Uint64() > nonce { - deposits = append(deposits, d) - } - } - - var withdrawals []*peggyevents.PeggyTransactionBatchExecutedEvent - for _, w := range e.Withdrawals { - if w.EventNonce.Uint64() > nonce { - withdrawals = append(withdrawals, w) - } - } - - var valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent - for _, vs := range e.ValsetUpdates { - if vs.EventNonce.Uint64() > nonce { - valsetUpdates = append(valsetUpdates, vs) - } - } - - var erc20Deployments []*peggyevents.PeggyERC20DeployedEvent - for _, d := range e.ERC20Deployments { - if d.EventNonce.Uint64() > nonce { - erc20Deployments = append(erc20Deployments, d) - } - } - - return ethEvents{ - OldDeposits: oldDeposits, - Deposits: deposits, - Withdrawals: withdrawals, - ValsetUpdates: valsetUpdates, - ERC20Deployments: erc20Deployments, - } -} - -func (e ethEvents) Sort() []event { - events := make([]event, 0, e.Num()) - - for _, d := range e.OldDeposits { - ev := oldDeposit(*d) - events = append(events, &ev) - } - - for _, d := range e.Deposits { - ev := deposit(*d) - events = append(events, &ev) - } - - for _, w := range e.Withdrawals { - ev := withdrawal(*w) - events = append(events, &ev) - } - - for _, deployment := range e.ERC20Deployments { - ev := erc20Deployment(*deployment) - events = append(events, &ev) - } - - for _, vs := range e.ValsetUpdates { - ev := valsetUpdate(*vs) - events = append(events, &ev) - } - - // sort by nonce - sort.Slice(events, func(i, j int) bool { - return events[i].Nonce() < events[j].Nonce() - }) - - return events -} - type ( oldDeposit peggyevents.PeggySendToCosmosEvent deposit peggyevents.PeggySendToInjectiveEvent @@ -393,6 +353,16 @@ type ( } ) +func filterEvents(events []event, nonce uint64) (filtered []event) { + for _, e := range events { + if e.Nonce() > nonce { + filtered = append(filtered, e) + } + } + + return +} + func (o *oldDeposit) Nonce() uint64 { return o.EventNonce.Uint64() } diff --git a/orchestrator/orchestrator.go b/orchestrator/orchestrator.go index 0a033488..4df1026b 100644 --- a/orchestrator/orchestrator.go +++ b/orchestrator/orchestrator.go @@ -100,21 +100,6 @@ func (s *PeggyOrchestrator) Run(ctx context.Context, inj cosmos.Network, eth eth return s.startValidatorMode(ctx, inj, eth) } -func (s *PeggyOrchestrator) hasDelegateValidator(ctx context.Context, inj cosmos.Network) bool { - subCtx, cancelFn := context.WithTimeout(ctx, 5*time.Second) - defer cancelFn() - - validator, err := inj.GetValidatorAddress(subCtx, s.ethAddr) - if err != nil { - s.logger.WithError(err).Debugln("no delegate validator address found") - return false - } - - s.logger.WithField("addr", validator.String()).Debugln("found delegate validator") - - return true -} - // startValidatorMode runs all orchestrator processes. This is called // when peggo is run alongside a validator injective node. func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { diff --git a/orchestrator/signer.go b/orchestrator/signer.go index 4986ffb8..abd37d66 100644 --- a/orchestrator/signer.go +++ b/orchestrator/signer.go @@ -116,10 +116,12 @@ func (l *ethSigner) signBatch(ctx context.Context, batch *peggytypes.OutgoingTxB func (l *ethSigner) getUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { var oldestUnsignedValsets []*peggytypes.Valset - if err := retryFnOnErr(ctx, l.Logger(), func() error { + fn := func() error { oldestUnsignedValsets, _ = l.Injective.OldestUnsignedValsets(ctx, l.injAddr) return nil - }); err != nil { + } + + if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return nil, err } From 4a7aabe8157f5efd40d51010c649656f46be4b4d Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 25 Mar 2024 14:20:59 +0100 Subject: [PATCH 80/99] add todo --- .../{batch_request.go => batch_requester.go} | 0 orchestrator/ethereum/network.go | 23 ++----------------- 2 files changed, 2 insertions(+), 21 deletions(-) rename orchestrator/{batch_request.go => batch_requester.go} (100%) diff --git a/orchestrator/batch_request.go b/orchestrator/batch_requester.go similarity index 100% rename from orchestrator/batch_request.go rename to orchestrator/batch_requester.go diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index 61a6ad2a..e1406cd2 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -2,7 +2,6 @@ package ethereum import ( "context" - "github.com/ethereum/go-ethereum" "math/big" "strings" "time" @@ -55,7 +54,8 @@ type Network interface { confirms []*peggytypes.MsgConfirmBatch, ) (*gethcommon.Hash, error) - TokenDecimals(ctx context.Context, tokenContract gethcommon.Address) (uint8, error) + // todo: audit fix + //TokenDecimals(ctx context.Context, tokenContract gethcommon.Address) (uint8, error) } type network struct { @@ -118,25 +118,6 @@ func NewNetwork( return n, nil } -func (n *network) TokenDecimals(ctx context.Context, tokenContract gethcommon.Address) (uint8, error) { - msg := ethereum.CallMsg{ - //From: gethcommon.Address{}, - To: &tokenContract, - Data: gethcommon.Hex2Bytes("313ce567"), // Function signature for decimals(), - } - - res, err := n.Provider().CallContract(ctx, msg, nil) - if err != nil { - return 0, err - } - - if len(res) == 0 { - return 0, errors.Errorf("no decimals found for token contract %s", tokenContract.Hex()) - } - - return uint8(big.NewInt(0).SetBytes(res).Uint64()), nil -} - func (n *network) GetHeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) { return n.Provider().HeaderByNumber(ctx, number) } From 1511603aa99a3d3dc0cb008708cd4fdfd9d72ecb Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 25 Mar 2024 14:23:29 +0100 Subject: [PATCH 81/99] rename file --- orchestrator/{oracle.go => eth_oracle.go} | 0 orchestrator/{relayer.go => eth_relayer.go} | 0 orchestrator/{signer.go => inj_signer.go} | 0 orchestrator/{orchestrator.go => peggy_orchestrator.go} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename orchestrator/{oracle.go => eth_oracle.go} (100%) rename orchestrator/{relayer.go => eth_relayer.go} (100%) rename orchestrator/{signer.go => inj_signer.go} (100%) rename orchestrator/{orchestrator.go => peggy_orchestrator.go} (100%) diff --git a/orchestrator/oracle.go b/orchestrator/eth_oracle.go similarity index 100% rename from orchestrator/oracle.go rename to orchestrator/eth_oracle.go diff --git a/orchestrator/relayer.go b/orchestrator/eth_relayer.go similarity index 100% rename from orchestrator/relayer.go rename to orchestrator/eth_relayer.go diff --git a/orchestrator/signer.go b/orchestrator/inj_signer.go similarity index 100% rename from orchestrator/signer.go rename to orchestrator/inj_signer.go diff --git a/orchestrator/orchestrator.go b/orchestrator/peggy_orchestrator.go similarity index 100% rename from orchestrator/orchestrator.go rename to orchestrator/peggy_orchestrator.go From 34b2d74c5c18140c58b03214e384facd04acd805 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 25 Mar 2024 14:47:41 +0100 Subject: [PATCH 82/99] simplify logs --- cmd/peggo/orchestrator.go | 8 ++++++-- orchestrator/cosmos/network.go | 25 ++++++++++++++----------- orchestrator/ethereum/network.go | 14 ++++++++------ 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index dd310c5d..03822cd6 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -77,6 +77,8 @@ func orchestratorCmd(cmd *cli.Cmd) { }) orShutdown(err) + log.Infoln("connected to Injective network") + ctx, cancelFn := context.WithCancel(context.Background()) closer.Bind(cancelFn) @@ -109,9 +111,11 @@ func orchestratorCmd(cmd *cli.Cmd) { ) orShutdown(err) - isValidator := cosmos.HasRegisteredOrchestrator(cosmosNetwork, ethKeyFromAddress) + log.Infoln("connected to Ethereum network") + + addr, isValidator := cosmos.HasRegisteredOrchestrator(cosmosNetwork, ethKeyFromAddress) if isValidator { - log.Debugln("provided ETH address is registered with a validator, configuring peggo to run in validator mode") + log.Debugln("provided ETH address is registered with a validator", addr.String()) } // Create peggo and run it diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index db07171f..b4023052 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -3,6 +3,7 @@ package cosmos import ( "context" "fmt" + cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "time" @@ -36,6 +37,14 @@ type Network interface { } func NewNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg NetworkConfig) (Network, error) { + log.WithFields(log.Fields{ + "chain_id": cfg.ChainID, + "orchestrator_addr": cfg.ValidatorAddress, + "grpc": cfg.CosmosGRPC, + "tendermint_rpc": cfg.TendermintRPC, + "gas_price": cfg.GasPrice, + }).Debugln("Injective network config") + clientCfg := cfg.loadClientConfig() clientCtx, err := chain.NewClientContext(clientCfg.ChainId, cfg.ValidatorAddress, k) @@ -71,13 +80,6 @@ func NewNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg Networ tendermint.NewRPCClient(clientCfg.TmEndpoint), } - log.WithFields(log.Fields{ - "chain_id": cfg.ChainID, - "addr": cfg.ValidatorAddress, - "chain_grpc": clientCfg.ChainGrpcEndpoint, - "tendermint": clientCfg.TmEndpoint, - }).Infoln("connected to Injective network") - return net, nil } @@ -146,13 +148,14 @@ func loadBalancedEndpoints(cfg NetworkConfig) clientcommon.Network { return clientcommon.LoadNetwork(networkName, "lb") } -func HasRegisteredOrchestrator(n Network, ethAddr gethcommon.Address) bool { +func HasRegisteredOrchestrator(n Network, ethAddr gethcommon.Address) (cosmostypes.AccAddress, bool) { ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) defer cancelFn() - if _, err := n.GetValidatorAddress(ctx, ethAddr); err != nil { - return false + validator, err := n.GetValidatorAddress(ctx, ethAddr) + if err != nil { + return nil, false } - return true + return validator, true } diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index e1406cd2..4343f8c5 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -70,6 +70,14 @@ func NewNetwork( signerFn bind.SignerFn, cfg NetworkConfig, ) (Network, error) { + log.WithFields(log.Fields{ + "eth_rpc": cfg.EthNodeRPC, + "eth_addr": fromAddr.String(), + "peggy_contract": peggyContractAddr, + "max_gas_price": cfg.MaxGasPrice, + "gas_price_adjustment": cfg.GasPriceAdjustment, + }).Infoln("Ethereum network config") + evmRPC, err := rpc.Dial(cfg.EthNodeRPC) if err != nil { return nil, errors.Wrapf(err, "failed to connect to ethereum RPC: %s", cfg.EthNodeRPC) @@ -96,12 +104,6 @@ func NewNetwork( return nil, err } - log.WithFields(log.Fields{ - "rpc": cfg.EthNodeRPC, - "addr": fromAddr.String(), - "peggy_contract": peggyContractAddr, - }).Infoln("connected to Ethereum network") - // If Alchemy Websocket URL is set, then Subscribe to Pending Transaction of Peggy Contract. if cfg.EthNodeAlchemyWS != "" { log.WithFields(log.Fields{ From b4f8a17889fa977627d4c70f36964eff14d1f1f0 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 25 Mar 2024 14:57:14 +0100 Subject: [PATCH 83/99] log --- orchestrator/ethereum/network.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index 4343f8c5..b4929a45 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -13,12 +13,11 @@ import ( "github.com/pkg/errors" log "github.com/xlab/suplog" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/committer" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/peggy" "github.com/InjectiveLabs/peggo/orchestrator/ethereum/provider" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) type NetworkConfig struct { @@ -76,7 +75,7 @@ func NewNetwork( "peggy_contract": peggyContractAddr, "max_gas_price": cfg.MaxGasPrice, "gas_price_adjustment": cfg.GasPriceAdjustment, - }).Infoln("Ethereum network config") + }).Debugln("Ethereum network config") evmRPC, err := rpc.Dial(cfg.EthNodeRPC) if err != nil { From 3c9ef1912ed631d82e760ead688f4096130d546a Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 25 Mar 2024 16:32:59 +0100 Subject: [PATCH 84/99] fix logs --- orchestrator/eth_oracle.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/orchestrator/eth_oracle.go b/orchestrator/eth_oracle.go index afc17423..a0384baa 100644 --- a/orchestrator/eth_oracle.go +++ b/orchestrator/eth_oracle.go @@ -125,14 +125,14 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { }) if len(newEvents) == 0 { - l.Logger().WithField("last_claimed_event_nonce", lastClaim.EthereumEventNonce).Infoln("no new events on Ethereum") + l.Logger().WithFields(log.Fields{"last_claimed_event_nonce": lastClaim.EthereumEventNonce, "block_start": l.LastObservedEthHeight, "block_end": latestHeight}).Debugln("no new events on Ethereum") l.LastObservedEthHeight = latestHeight return nil } if expected, actual := lastClaim.EthereumEventNonce+1, newEvents[0].Nonce(); expected != actual { - l.Logger().WithFields(log.Fields{"expected_nonce": expected, "actual_nonce": actual, "last_claim_event_nonce": lastClaim.EthereumEventNonce}).Infoln("orchestrator missed an Ethereum event. Resyncing event nonce with last claimed event...") + l.Logger().WithFields(log.Fields{"expected_nonce": expected, "actual_nonce": actual, "last_claimed_event_nonce": lastClaim.EthereumEventNonce}).Infoln("orchestrator missed an Ethereum event. Resyncing event nonce with last claimed event...") l.LastObservedEthHeight = lastClaim.EthereumEventHeight return nil @@ -142,7 +142,7 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { return err } - l.Logger().WithFields(log.Fields{"block_start": l.LastObservedEthHeight, "block_end": latestHeight}).Debugln("scanned Ethereum blocks") + l.Logger().WithFields(log.Fields{"claims": len(newEvents), "block_start": l.LastObservedEthHeight, "block_end": latestHeight}).Infoln("sent new event claims to Injective") l.LastObservedEthHeight = latestHeight if time.Since(l.LastResyncWithInjective) >= resyncInterval { @@ -260,6 +260,7 @@ func (l *ethOracle) getLastClaimEvent(ctx context.Context) (*peggytypes.LastClai func (l *ethOracle) sendNewEventClaims(ctx context.Context, events []event) error { sendEventsFn := func() error { + // in case sending one of more claims fails, we reload the latest observed nonce to filter processed events lastClaim, err := l.Injective.LastClaimEventByAddr(ctx, l.injAddr) if err != nil { return err @@ -267,7 +268,6 @@ func (l *ethOracle) sendNewEventClaims(ctx context.Context, events []event) erro newEvents := filterEvents(events, lastClaim.EthereumEventNonce) if len(newEvents) == 0 { - l.Logger().WithField("last_claimed_event_nonce", lastClaim.EthereumEventNonce).Infoln("no new events on Ethereum") return nil } @@ -281,8 +281,6 @@ func (l *ethOracle) sendNewEventClaims(ctx context.Context, events []event) erro time.Sleep(1200 * time.Millisecond) } - l.Logger().WithField("claims", len(newEvents)).Infoln("sent new event claims to Injective") - return nil } @@ -311,7 +309,7 @@ func (l *ethOracle) autoResync(ctx context.Context) error { return err } - l.Logger().WithFields(log.Fields{"last_resync": l.LastResyncWithInjective.String(), "last_claimed_eth_height": latestHeight}).Infoln("resyncing with last claimed event on Injective") + l.Logger().WithFields(log.Fields{"last_resync": l.LastResyncWithInjective.String(), "last_claimed_eth_height": latestHeight}).Infoln("auto resyncing with last claimed event on Injective") l.LastObservedEthHeight = latestHeight l.LastResyncWithInjective = time.Now() From 52c739a6a6f1413672484b22210c81c7696ff6bc Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 25 Mar 2024 17:16:28 +0100 Subject: [PATCH 85/99] simplify signer code --- orchestrator/eth_oracle.go | 8 ++-- orchestrator/inj_signer.go | 78 ++++++++++++-------------------------- 2 files changed, 28 insertions(+), 58 deletions(-) diff --git a/orchestrator/eth_oracle.go b/orchestrator/eth_oracle.go index a0384baa..2a89743c 100644 --- a/orchestrator/eth_oracle.go +++ b/orchestrator/eth_oracle.go @@ -125,14 +125,14 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { }) if len(newEvents) == 0 { - l.Logger().WithFields(log.Fields{"last_claimed_event_nonce": lastClaim.EthereumEventNonce, "block_start": l.LastObservedEthHeight, "block_end": latestHeight}).Debugln("no new events on Ethereum") + l.Logger().WithFields(log.Fields{"last_claimed_event_nonce": lastClaim.EthereumEventNonce, "eth_block_start": l.LastObservedEthHeight, "eth_block_end": latestHeight}).Infoln("no new events on Ethereum") l.LastObservedEthHeight = latestHeight return nil } if expected, actual := lastClaim.EthereumEventNonce+1, newEvents[0].Nonce(); expected != actual { - l.Logger().WithFields(log.Fields{"expected_nonce": expected, "actual_nonce": actual, "last_claimed_event_nonce": lastClaim.EthereumEventNonce}).Infoln("orchestrator missed an Ethereum event. Resyncing event nonce with last claimed event...") + l.Logger().WithFields(log.Fields{"expected_nonce": expected, "actual_nonce": actual, "last_claimed_event_nonce": lastClaim.EthereumEventNonce}).Debugln("orchestrator missed an Ethereum event. Resyncing event nonce with last claimed event...") l.LastObservedEthHeight = lastClaim.EthereumEventHeight return nil @@ -142,7 +142,7 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { return err } - l.Logger().WithFields(log.Fields{"claims": len(newEvents), "block_start": l.LastObservedEthHeight, "block_end": latestHeight}).Infoln("sent new event claims to Injective") + l.Logger().WithFields(log.Fields{"claims": len(newEvents), "eth_block_start": l.LastObservedEthHeight, "eth_block_end": latestHeight}).Infoln("sent new event claims to Injective") l.LastObservedEthHeight = latestHeight if time.Since(l.LastResyncWithInjective) >= resyncInterval { @@ -260,7 +260,7 @@ func (l *ethOracle) getLastClaimEvent(ctx context.Context) (*peggytypes.LastClai func (l *ethOracle) sendNewEventClaims(ctx context.Context, events []event) error { sendEventsFn := func() error { - // in case sending one of more claims fails, we reload the latest observed nonce to filter processed events + // in case sending one of more claims fails, we reload the latest claimed nonce to filter processed events lastClaim, err := l.Injective.LastClaimEventByAddr(ctx, l.injAddr) if err != nil { return err diff --git a/orchestrator/inj_signer.go b/orchestrator/inj_signer.go index abd37d66..ed3cfa85 100644 --- a/orchestrator/inj_signer.go +++ b/orchestrator/inj_signer.go @@ -21,7 +21,7 @@ func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, inj cosmos.Ne PeggyID: peggyID, } - s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting EthSigner...") + s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting Signer...") return loops.RunLoop(ctx, defaultLoopDur, func() error { return signer.SignValsetsAndBatches(ctx) @@ -35,7 +35,7 @@ type ethSigner struct { } func (l *ethSigner) Logger() log.Logger { - return l.logger.WithField("loop", "EthSigner") + return l.logger.WithField("loop", "Signer") } func (l *ethSigner) SignValsetsAndBatches(ctx context.Context) error { @@ -51,8 +51,14 @@ func (l *ethSigner) SignValsetsAndBatches(ctx context.Context) error { } func (l *ethSigner) signNewValsetUpdates(ctx context.Context) error { - oldestUnsignedValsets, err := l.getUnsignedValsets(ctx) - if err != nil { + var oldestUnsignedValsets []*peggytypes.Valset + getUnsignedValsetsFn := func() error { + oldestUnsignedValsets, _ = l.Injective.OldestUnsignedValsets(ctx, l.injAddr) + return nil + } + + if err := retryFnOnErr(ctx, l.Logger(), getUnsignedValsetsFn); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") return err } @@ -62,82 +68,46 @@ func (l *ethSigner) signNewValsetUpdates(ctx context.Context) error { } for _, vs := range oldestUnsignedValsets { - if err := l.signValset(ctx, vs); err != nil { + if err := retryFnOnErr(ctx, l.Logger(), func() error { + return l.Injective.SendValsetConfirm(ctx, l.ethAddr, l.PeggyID, vs) + }); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") return err } - } - return nil -} - -func (l *ethSigner) signNewBatch(ctx context.Context) error { - oldestUnsignedTransactionBatch, err := l.getUnsignedBatch(ctx) - if err != nil { - return err - } - - if err := l.signBatch(ctx, oldestUnsignedTransactionBatch); err != nil { - return err + l.Logger().WithFields(log.Fields{"valset_nonce": vs.Nonce, "validators": len(vs.Members)}).Infoln("confirmed valset update on Injective") } return nil } -func (l *ethSigner) getUnsignedBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { +func (l *ethSigner) signNewBatch(ctx context.Context) error { var oldestUnsignedBatch *peggytypes.OutgoingTxBatch - if err := retryFnOnErr(ctx, l.Logger(), func() error { + getBatchFn := func() error { oldestUnsignedBatch, _ = l.Injective.OldestUnsignedTransactionBatch(ctx, l.injAddr) return nil - }); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return nil, err } - return oldestUnsignedBatch, nil -} - -func (l *ethSigner) signBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) error { - if batch == nil { - l.Logger().Infoln("no batch to confirm") - return nil - } - - if err := retryFnOnErr(ctx, l.Logger(), func() error { - return l.Injective.SendBatchConfirm(ctx, l.ethAddr, l.PeggyID, batch) - }); err != nil { + if err := retryFnOnErr(ctx, l.Logger(), getBatchFn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return err } - l.Logger().WithFields(log.Fields{"token_contract": batch.TokenContract, "batch_nonce": batch.BatchNonce, "txs": len(batch.Transactions)}).Infoln("confirmed batch on Injective") - - return nil -} - -func (l *ethSigner) getUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { - var oldestUnsignedValsets []*peggytypes.Valset - fn := func() error { - oldestUnsignedValsets, _ = l.Injective.OldestUnsignedValsets(ctx, l.injAddr) + if oldestUnsignedBatch == nil { + l.Logger().Infoln("no batch to confirm") return nil } - if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return nil, err + confirmBatchFn := func() error { + return l.Injective.SendBatchConfirm(ctx, l.ethAddr, l.PeggyID, oldestUnsignedBatch) } - return oldestUnsignedValsets, nil -} - -func (l *ethSigner) signValset(ctx context.Context, vs *peggytypes.Valset) error { - if err := retryFnOnErr(ctx, l.Logger(), func() error { - return l.Injective.SendValsetConfirm(ctx, l.ethAddr, l.PeggyID, vs) - }); err != nil { + if err := retryFnOnErr(ctx, l.Logger(), confirmBatchFn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return err } - l.Logger().WithFields(log.Fields{"valset_nonce": vs.Nonce, "validators": len(vs.Members)}).Infoln("confirmed valset update on Injective") + l.Logger().WithFields(log.Fields{"token_contract": oldestUnsignedBatch.TokenContract, "batch_nonce": oldestUnsignedBatch.BatchNonce, "txs": len(oldestUnsignedBatch.Transactions)}).Infoln("confirmed batch on Injective") return nil } From 98f3e807af653e5abd94ac261ca533db1018d78c Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Mon, 25 Mar 2024 17:33:23 +0100 Subject: [PATCH 86/99] simplify relayer --- orchestrator/eth_relayer.go | 78 +++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/orchestrator/eth_relayer.go b/orchestrator/eth_relayer.go index fc4e8679..e5153a75 100644 --- a/orchestrator/eth_relayer.go +++ b/orchestrator/eth_relayer.go @@ -26,21 +26,20 @@ const ( ) func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { - if noRelay := s.relayValsetOffsetDur == 0 && s.relayBatchOffsetDur == 0; noRelay { - return nil - } - rel := relayer{ PeggyOrchestrator: s, Injective: inj, Ethereum: eth, } - s.logger.WithFields(log.Fields{ - "loop_duration": defaultRelayerLoopDur.String(), - "relay_batches": rel.relayBatchOffsetDur != 0, - "relay_valsets": rel.relayValsetOffsetDur != 0, - }).Debugln("starting Relayer...") + relayingBatches := rel.IsRelayingValsets() + relayingValsets := rel.IsRelayingValsets() + + if noRelay := !relayingBatches && !relayingValsets; noRelay { + return nil + } + + s.logger.WithFields(log.Fields{"loop_duration": defaultRelayerLoopDur.String(), "relay_batches": relayingBatches, "relay_valsets": relayingValsets}).Debugln("starting Relayer...") return loops.RunLoop(ctx, defaultRelayerLoopDur, func() error { return rel.RelayValsetsAndBatches(ctx) @@ -57,6 +56,14 @@ func (l *relayer) Logger() log.Logger { return l.logger.WithField("loop", "Relayer") } +func (l *relayer) IsRelayingBatches() bool { + return l.relayBatchOffsetDur != 0 +} + +func (l *relayer) IsRelayingValsets() bool { + return l.relayValsetOffsetDur != 0 +} + func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { ethValset, err := l.GetLatestEthValset(ctx) if err != nil { @@ -94,7 +101,7 @@ func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, error) { var latestEthValset *peggytypes.Valset - if err := retryFnOnErr(ctx, l.Logger(), func() error { + fn := func() error { vs, err := l.findLatestValsetOnEth(ctx) if err != nil { return err @@ -102,7 +109,9 @@ func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, e latestEthValset = vs return nil - }); err != nil { + } + + if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { l.Logger().WithError(err).Errorln("got error, loop exits") return nil, err } @@ -115,51 +124,46 @@ func (l *relayer) relayValset(ctx context.Context, latestEthValset *peggytypes.V doneFn := metrics.ReportFuncTiming(l.svcTags) defer doneFn() - // we should determine if we need to relay one - // to Ethereum for that we will find the latest confirmed valset and compare it to the ethereum chain - latestValsets, err := l.Injective.LatestValsets(ctx) + latestInjectiveValsets, err := l.Injective.LatestValsets(ctx) if err != nil { return errors.Wrap(err, "failed to get latest valset updates from Injective") } var ( - oldestConfirmedValset *peggytypes.Valset - oldestConfirmedValsetSigs []*peggytypes.MsgValsetConfirm + latestConfirmedValset *peggytypes.Valset + confirmations []*peggytypes.MsgValsetConfirm ) - for _, set := range latestValsets { + for _, set := range latestInjectiveValsets { sigs, err := l.Injective.AllValsetConfirms(ctx, set.Nonce) if err != nil { return errors.Wrapf(err, "failed to get valset confirmations for nonce %d", set.Nonce) - } else if len(sigs) == 0 { + } + + if len(sigs) == 0 { continue } - oldestConfirmedValsetSigs = sigs - oldestConfirmedValset = set + confirmations = sigs + latestConfirmedValset = set break } - if oldestConfirmedValset == nil { + if latestConfirmedValset == nil { l.Logger().Infoln("no valset to relay") return nil } - if !l.shouldRelayValset(ctx, oldestConfirmedValset) { + if !l.shouldRelayValset(ctx, latestConfirmedValset) { return nil } - txHash, err := l.Ethereum.SendEthValsetUpdate(ctx, - latestEthValset, - oldestConfirmedValset, - oldestConfirmedValsetSigs, - ) - + txHash, err := l.Ethereum.SendEthValsetUpdate(ctx, latestEthValset, latestConfirmedValset, confirmations) if err != nil { return err } - l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent valset tx to Ethereum") + l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent validator set update to Ethereum") return nil } @@ -206,8 +210,8 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va } var ( - oldestConfirmedInjBatch *peggytypes.OutgoingTxBatch - oldestConfirmedInjBatchSigs []*peggytypes.MsgConfirmBatch + oldestConfirmedBatch *peggytypes.OutgoingTxBatch + confirmations []*peggytypes.MsgConfirmBatch ) // todo: skip timed out batches @@ -221,25 +225,25 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va continue } - oldestConfirmedInjBatch = batch - oldestConfirmedInjBatchSigs = sigs + oldestConfirmedBatch = batch + confirmations = sigs } - if oldestConfirmedInjBatch == nil { + if oldestConfirmedBatch == nil { l.Logger().Infoln("no batch to relay") return nil } - if !l.shouldRelayBatch(ctx, oldestConfirmedInjBatch) { + if !l.shouldRelayBatch(ctx, oldestConfirmedBatch) { return nil } - txHash, err := l.Ethereum.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedInjBatch, oldestConfirmedInjBatchSigs) + txHash, err := l.Ethereum.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedBatch, confirmations) if err != nil { return err } - l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent batch tx to Ethereum") + l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent outgoing tx batch to Ethereum") return nil } From 4ce6859eb2684f1f51b931b00a637698fb209bd9 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 26 Mar 2024 11:14:20 +0100 Subject: [PATCH 87/99] rename file --- orchestrator/{eth_relayer.go => inj_eth_relayer.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename orchestrator/{eth_relayer.go => inj_eth_relayer.go} (100%) diff --git a/orchestrator/eth_relayer.go b/orchestrator/inj_eth_relayer.go similarity index 100% rename from orchestrator/eth_relayer.go rename to orchestrator/inj_eth_relayer.go From e6cd0a04e2a45063f6ec2501415da7dee194e7f3 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 26 Mar 2024 11:15:47 +0100 Subject: [PATCH 88/99] replace old file --- orchestrator/{inj_eth_relayer.go => relayer.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename orchestrator/{inj_eth_relayer.go => relayer.go} (100%) diff --git a/orchestrator/inj_eth_relayer.go b/orchestrator/relayer.go similarity index 100% rename from orchestrator/inj_eth_relayer.go rename to orchestrator/relayer.go From 6d08b4030cefa845ad5b8f22ee4053ab49c31940 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 26 Mar 2024 11:18:32 +0100 Subject: [PATCH 89/99] remove file --- orchestrator/relayer.go | 443 ---------------------------------------- 1 file changed, 443 deletions(-) delete mode 100644 orchestrator/relayer.go diff --git a/orchestrator/relayer.go b/orchestrator/relayer.go deleted file mode 100644 index e5153a75..00000000 --- a/orchestrator/relayer.go +++ /dev/null @@ -1,443 +0,0 @@ -package orchestrator - -import ( - "context" - "sort" - "time" - - cosmostypes "github.com/cosmos/cosmos-sdk/types" - gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - log "github.com/xlab/suplog" - - "github.com/InjectiveLabs/metrics" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - - "github.com/InjectiveLabs/peggo/orchestrator/cosmos" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum" - "github.com/InjectiveLabs/peggo/orchestrator/ethereum/util" - "github.com/InjectiveLabs/peggo/orchestrator/loops" - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" -) - -const ( - defaultRelayerLoopDur = 5 * time.Minute - findValsetBlocksToSearch = 2000 -) - -func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { - rel := relayer{ - PeggyOrchestrator: s, - Injective: inj, - Ethereum: eth, - } - - relayingBatches := rel.IsRelayingValsets() - relayingValsets := rel.IsRelayingValsets() - - if noRelay := !relayingBatches && !relayingValsets; noRelay { - return nil - } - - s.logger.WithFields(log.Fields{"loop_duration": defaultRelayerLoopDur.String(), "relay_batches": relayingBatches, "relay_valsets": relayingValsets}).Debugln("starting Relayer...") - - return loops.RunLoop(ctx, defaultRelayerLoopDur, func() error { - return rel.RelayValsetsAndBatches(ctx) - }) -} - -type relayer struct { - *PeggyOrchestrator - Injective cosmos.Network - Ethereum ethereum.Network -} - -func (l *relayer) Logger() log.Logger { - return l.logger.WithField("loop", "Relayer") -} - -func (l *relayer) IsRelayingBatches() bool { - return l.relayBatchOffsetDur != 0 -} - -func (l *relayer) IsRelayingValsets() bool { - return l.relayValsetOffsetDur != 0 -} - -func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { - ethValset, err := l.GetLatestEthValset(ctx) - if err != nil { - return err - } - - var pg loops.ParanoidGroup - - if l.relayValsetOffsetDur != 0 { - pg.Go(func() error { - return retryFnOnErr(ctx, l.Logger(), func() error { - return l.relayValset(ctx, ethValset) - }) - }) - } - - if l.relayBatchOffsetDur != 0 { - pg.Go(func() error { - return retryFnOnErr(ctx, l.Logger(), func() error { - return l.relayBatch(ctx, ethValset) - }) - }) - } - - if pg.Initialized() { - if err := pg.Wait(); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return err - } - } - - return nil - -} - -func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, error) { - var latestEthValset *peggytypes.Valset - fn := func() error { - vs, err := l.findLatestValsetOnEth(ctx) - if err != nil { - return err - } - - latestEthValset = vs - return nil - } - - if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { - l.Logger().WithError(err).Errorln("got error, loop exits") - return nil, err - } - - return latestEthValset, nil -} - -func (l *relayer) relayValset(ctx context.Context, latestEthValset *peggytypes.Valset) error { - metrics.ReportFuncCall(l.svcTags) - doneFn := metrics.ReportFuncTiming(l.svcTags) - defer doneFn() - - latestInjectiveValsets, err := l.Injective.LatestValsets(ctx) - if err != nil { - return errors.Wrap(err, "failed to get latest valset updates from Injective") - } - - var ( - latestConfirmedValset *peggytypes.Valset - confirmations []*peggytypes.MsgValsetConfirm - ) - - for _, set := range latestInjectiveValsets { - sigs, err := l.Injective.AllValsetConfirms(ctx, set.Nonce) - if err != nil { - return errors.Wrapf(err, "failed to get valset confirmations for nonce %d", set.Nonce) - } - - if len(sigs) == 0 { - continue - } - - confirmations = sigs - latestConfirmedValset = set - break - } - - if latestConfirmedValset == nil { - l.Logger().Infoln("no valset to relay") - return nil - } - - if !l.shouldRelayValset(ctx, latestConfirmedValset) { - return nil - } - - txHash, err := l.Ethereum.SendEthValsetUpdate(ctx, latestEthValset, latestConfirmedValset, confirmations) - if err != nil { - return err - } - - l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent validator set update to Ethereum") - - return nil -} - -func (l *relayer) shouldRelayValset(ctx context.Context, vs *peggytypes.Valset) bool { - latestEthereumValsetNonce, err := l.Ethereum.GetValsetNonce(ctx) - if err != nil { - l.Logger().WithError(err).Warningln("failed to get latest valset nonce from Ethereum") - return false - } - - // Check if other validators already updated the valset - if vs.Nonce <= latestEthereumValsetNonce.Uint64() { - l.Logger().WithFields(log.Fields{"eth_nonce": latestEthereumValsetNonce, "inj_nonce": vs.Nonce}).Debugln("valset already updated on Ethereum") - return false - } - - // Check custom time delay offset - block, err := l.Injective.GetBlock(ctx, int64(vs.Height)) - if err != nil { - l.Logger().WithError(err).Warningln("unable to get latest block from Injective") - return false - } - - if timeElapsed := time.Since(block.Block.Time); timeElapsed <= l.relayValsetOffsetDur { - timeRemaining := time.Duration(int64(l.relayValsetOffsetDur) - int64(timeElapsed)) - l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("valset relay offset not reached yet") - return false - } - - l.Logger().WithFields(log.Fields{"inj_nonce": vs.Nonce, "eth_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("new valset update") - - return true -} - -func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Valset) error { - metrics.ReportFuncCall(l.svcTags) - doneFn := metrics.ReportFuncTiming(l.svcTags) - defer doneFn() - - latestBatches, err := l.Injective.LatestTransactionBatches(ctx) - if err != nil { - return err - } - - var ( - oldestConfirmedBatch *peggytypes.OutgoingTxBatch - confirmations []*peggytypes.MsgConfirmBatch - ) - - // todo: skip timed out batches - for _, batch := range latestBatches { - sigs, err := l.Injective.TransactionBatchSignatures(ctx, batch.BatchNonce, gethcommon.HexToAddress(batch.TokenContract)) - if err != nil { - return err - } - - if len(sigs) == 0 { - continue - } - - oldestConfirmedBatch = batch - confirmations = sigs - } - - if oldestConfirmedBatch == nil { - l.Logger().Infoln("no batch to relay") - return nil - } - - if !l.shouldRelayBatch(ctx, oldestConfirmedBatch) { - return nil - } - - txHash, err := l.Ethereum.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedBatch, confirmations) - if err != nil { - return err - } - - l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent outgoing tx batch to Ethereum") - - return nil -} - -func (l *relayer) shouldRelayBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) bool { - latestEthBatch, err := l.Ethereum.GetTxBatchNonce(ctx, gethcommon.HexToAddress(batch.TokenContract)) - if err != nil { - l.Logger().WithError(err).Warningf("unable to get latest batch nonce from Ethereum: token_contract=%s", gethcommon.HexToAddress(batch.TokenContract)) - return false - } - - // Check if ethereum batch was updated by other validators - if batch.BatchNonce <= latestEthBatch.Uint64() { - l.Logger().WithFields(log.Fields{"eth_nonce": latestEthBatch.Uint64(), "inj_nonce": batch.BatchNonce}).Debugln("batch already updated on Ethereum") - return false - } - - // Check custom time delay offset - blockTime, err := l.Injective.GetBlock(ctx, int64(batch.Block)) - if err != nil { - l.Logger().WithError(err).Warningln("unable to get latest block from Injective") - return false - } - - if timeElapsed := time.Since(blockTime.Block.Time); timeElapsed <= l.relayBatchOffsetDur { - timeRemaining := time.Duration(int64(l.relayBatchOffsetDur) - int64(timeElapsed)) - l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("batch relay offset not reached yet") - return false - } - - l.Logger().WithFields(log.Fields{"inj_nonce": batch.BatchNonce, "eth_nonce": latestEthBatch.Uint64()}).Debugln("new batch update") - - return true -} - -// FindLatestValset finds the latest valset on the Peggy contract by looking back through the event -// history and finding the most recent ValsetUpdatedEvent. Most of the time this will be very fast -// as the latest update will be in recent blockchain history and the search moves from the present -// backwards in time. In the case that the validator set has not been updated for a very long time -// this will take longer. -func (l *relayer) findLatestValsetOnEth(ctx context.Context) (*peggytypes.Valset, error) { - latestHeader, err := l.Ethereum.GetHeaderByNumber(ctx, nil) - if err != nil { - return nil, errors.Wrap(err, "failed to get latest eth header") - } - - latestEthereumValsetNonce, err := l.Ethereum.GetValsetNonce(ctx) - if err != nil { - return nil, errors.Wrap(err, "failed to get latest valset nonce on Ethereum") - } - - cosmosValset, err := l.Injective.ValsetAt(ctx, latestEthereumValsetNonce.Uint64()) - if err != nil { - return nil, errors.Wrap(err, "failed to get Injective valset") - } - - currentBlock := latestHeader.Number.Uint64() - - for currentBlock > 0 { - var startSearchBlock uint64 - if currentBlock <= findValsetBlocksToSearch { - startSearchBlock = 0 - } else { - startSearchBlock = currentBlock - findValsetBlocksToSearch - } - - valsetUpdatedEvents, err := l.Ethereum.GetValsetUpdatedEvents(startSearchBlock, currentBlock) - if err != nil { - return nil, errors.Wrap(err, "failed to filter past ValsetUpdated events from Ethereum") - } - - // by default the lowest found valset goes first, we want the highest - // - // TODO(xlab): this follows the original impl, but sort might be skipped there: - // we could access just the latest element later. - sort.Sort(sort.Reverse(PeggyValsetUpdatedEvents(valsetUpdatedEvents))) - - if len(valsetUpdatedEvents) == 0 { - currentBlock = startSearchBlock - continue - } - - // we take only the first event if we find any at all. - event := valsetUpdatedEvents[0] - valset := &peggytypes.Valset{ - Nonce: event.NewValsetNonce.Uint64(), - Members: make([]*peggytypes.BridgeValidator, 0, len(event.Powers)), - RewardAmount: cosmostypes.NewIntFromBigInt(event.RewardAmount), - RewardToken: event.RewardToken.Hex(), - } - - for idx, p := range event.Powers { - valset.Members = append(valset.Members, &peggytypes.BridgeValidator{ - Power: p.Uint64(), - EthereumAddress: event.Validators[idx].Hex(), - }) - } - - checkIfValsetsDiffer(cosmosValset, valset) - - return valset, nil - - } - - return nil, ErrNotFound -} - -var ErrNotFound = errors.New("not found") - -type PeggyValsetUpdatedEvents []*peggyevents.PeggyValsetUpdatedEvent - -func (a PeggyValsetUpdatedEvents) Len() int { return len(a) } -func (a PeggyValsetUpdatedEvents) Less(i, j int) bool { - return a[i].NewValsetNonce.Cmp(a[j].NewValsetNonce) < 0 -} -func (a PeggyValsetUpdatedEvents) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -// This function exists to provide a warning if Cosmos and Ethereum have different validator sets -// for a given nonce. In the mundane version of this warning the validator sets disagree on sorting order -// which can happen if some relayer uses an unstable sort, or in a case of a mild griefing attack. -// The Peggy contract validates signatures in order of highest to lowest power. That way it can exit -// the loop early once a vote has enough power, if a relayer where to submit things in the reverse order -// they could grief users of the contract into paying more in gas. -// The other (and far worse) way a disagreement here could occur is if validators are colluding to steal -// funds from the Peggy contract and have submitted a hijacking update. If slashing for off Cosmos chain -// Ethereum signatures is implemented you would put that handler here. -func checkIfValsetsDiffer(cosmosValset, ethereumValset *peggytypes.Valset) { - if cosmosValset == nil && ethereumValset.Nonce == 0 { - // bootstrapping case - return - } else if cosmosValset == nil { - log.WithField( - "eth_valset_nonce", - ethereumValset.Nonce, - ).Errorln("Cosmos does not have a valset for nonce from Ethereum chain. Possible bridge hijacking!") - return - } - - if cosmosValset.Nonce != ethereumValset.Nonce { - log.WithFields(log.Fields{ - "cosmos_valset_nonce": cosmosValset.Nonce, - "eth_valset_nonce": ethereumValset.Nonce, - }).Errorln("Cosmos does have a wrong valset nonce, differs from Ethereum chain. Possible bridge hijacking!") - return - } - - if len(cosmosValset.Members) != len(ethereumValset.Members) { - log.WithFields(log.Fields{ - "cosmos_valset": len(cosmosValset.Members), - "eth_valset": len(ethereumValset.Members), - }).Errorln("Cosmos and Ethereum Valsets have different length. Possible bridge hijacking!") - return - } - - BridgeValidators(cosmosValset.Members).Sort() - BridgeValidators(ethereumValset.Members).Sort() - - for idx, member := range cosmosValset.Members { - if ethereumValset.Members[idx].EthereumAddress != member.EthereumAddress { - log.Errorln("Valsets are different, a sorting error?") - } - if ethereumValset.Members[idx].Power != member.Power { - log.Errorln("Valsets are different, a sorting error?") - } - } -} - -type BridgeValidators []*peggytypes.BridgeValidator - -// Sort sorts the validators by power -func (b BridgeValidators) Sort() { - sort.Slice(b, func(i, j int) bool { - if b[i].Power == b[j].Power { - // Secondary sort on eth address in case powers are equal - return util.EthAddrLessThan(b[i].EthereumAddress, b[j].EthereumAddress) - } - return b[i].Power > b[j].Power - }) -} - -// HasDuplicates returns true if there are duplicates in the set -func (b BridgeValidators) HasDuplicates() bool { - m := make(map[string]struct{}, len(b)) - for i := range b { - m[b[i].EthereumAddress] = struct{}{} - } - return len(m) != len(b) -} - -// GetPowers returns only the power values for all members -func (b BridgeValidators) GetPowers() []uint64 { - r := make([]uint64, len(b)) - for i := range b { - r[i] = b[i].Power - } - return r -} From 889a7ce9dff2f51e9610115da27ae454895b4498 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 26 Mar 2024 11:18:59 +0100 Subject: [PATCH 90/99] new file --- orchestrator/inj_relayer.go | 443 ++++++++++++++++++++++++++++++++++++ 1 file changed, 443 insertions(+) create mode 100644 orchestrator/inj_relayer.go diff --git a/orchestrator/inj_relayer.go b/orchestrator/inj_relayer.go new file mode 100644 index 00000000..e5153a75 --- /dev/null +++ b/orchestrator/inj_relayer.go @@ -0,0 +1,443 @@ +package orchestrator + +import ( + "context" + "sort" + "time" + + cosmostypes "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + log "github.com/xlab/suplog" + + "github.com/InjectiveLabs/metrics" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum/util" + "github.com/InjectiveLabs/peggo/orchestrator/loops" + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" +) + +const ( + defaultRelayerLoopDur = 5 * time.Minute + findValsetBlocksToSearch = 2000 +) + +func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { + rel := relayer{ + PeggyOrchestrator: s, + Injective: inj, + Ethereum: eth, + } + + relayingBatches := rel.IsRelayingValsets() + relayingValsets := rel.IsRelayingValsets() + + if noRelay := !relayingBatches && !relayingValsets; noRelay { + return nil + } + + s.logger.WithFields(log.Fields{"loop_duration": defaultRelayerLoopDur.String(), "relay_batches": relayingBatches, "relay_valsets": relayingValsets}).Debugln("starting Relayer...") + + return loops.RunLoop(ctx, defaultRelayerLoopDur, func() error { + return rel.RelayValsetsAndBatches(ctx) + }) +} + +type relayer struct { + *PeggyOrchestrator + Injective cosmos.Network + Ethereum ethereum.Network +} + +func (l *relayer) Logger() log.Logger { + return l.logger.WithField("loop", "Relayer") +} + +func (l *relayer) IsRelayingBatches() bool { + return l.relayBatchOffsetDur != 0 +} + +func (l *relayer) IsRelayingValsets() bool { + return l.relayValsetOffsetDur != 0 +} + +func (l *relayer) RelayValsetsAndBatches(ctx context.Context) error { + ethValset, err := l.GetLatestEthValset(ctx) + if err != nil { + return err + } + + var pg loops.ParanoidGroup + + if l.relayValsetOffsetDur != 0 { + pg.Go(func() error { + return retryFnOnErr(ctx, l.Logger(), func() error { + return l.relayValset(ctx, ethValset) + }) + }) + } + + if l.relayBatchOffsetDur != 0 { + pg.Go(func() error { + return retryFnOnErr(ctx, l.Logger(), func() error { + return l.relayBatch(ctx, ethValset) + }) + }) + } + + if pg.Initialized() { + if err := pg.Wait(); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return err + } + } + + return nil + +} + +func (l *relayer) GetLatestEthValset(ctx context.Context) (*peggytypes.Valset, error) { + var latestEthValset *peggytypes.Valset + fn := func() error { + vs, err := l.findLatestValsetOnEth(ctx) + if err != nil { + return err + } + + latestEthValset = vs + return nil + } + + if err := retryFnOnErr(ctx, l.Logger(), fn); err != nil { + l.Logger().WithError(err).Errorln("got error, loop exits") + return nil, err + } + + return latestEthValset, nil +} + +func (l *relayer) relayValset(ctx context.Context, latestEthValset *peggytypes.Valset) error { + metrics.ReportFuncCall(l.svcTags) + doneFn := metrics.ReportFuncTiming(l.svcTags) + defer doneFn() + + latestInjectiveValsets, err := l.Injective.LatestValsets(ctx) + if err != nil { + return errors.Wrap(err, "failed to get latest valset updates from Injective") + } + + var ( + latestConfirmedValset *peggytypes.Valset + confirmations []*peggytypes.MsgValsetConfirm + ) + + for _, set := range latestInjectiveValsets { + sigs, err := l.Injective.AllValsetConfirms(ctx, set.Nonce) + if err != nil { + return errors.Wrapf(err, "failed to get valset confirmations for nonce %d", set.Nonce) + } + + if len(sigs) == 0 { + continue + } + + confirmations = sigs + latestConfirmedValset = set + break + } + + if latestConfirmedValset == nil { + l.Logger().Infoln("no valset to relay") + return nil + } + + if !l.shouldRelayValset(ctx, latestConfirmedValset) { + return nil + } + + txHash, err := l.Ethereum.SendEthValsetUpdate(ctx, latestEthValset, latestConfirmedValset, confirmations) + if err != nil { + return err + } + + l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent validator set update to Ethereum") + + return nil +} + +func (l *relayer) shouldRelayValset(ctx context.Context, vs *peggytypes.Valset) bool { + latestEthereumValsetNonce, err := l.Ethereum.GetValsetNonce(ctx) + if err != nil { + l.Logger().WithError(err).Warningln("failed to get latest valset nonce from Ethereum") + return false + } + + // Check if other validators already updated the valset + if vs.Nonce <= latestEthereumValsetNonce.Uint64() { + l.Logger().WithFields(log.Fields{"eth_nonce": latestEthereumValsetNonce, "inj_nonce": vs.Nonce}).Debugln("valset already updated on Ethereum") + return false + } + + // Check custom time delay offset + block, err := l.Injective.GetBlock(ctx, int64(vs.Height)) + if err != nil { + l.Logger().WithError(err).Warningln("unable to get latest block from Injective") + return false + } + + if timeElapsed := time.Since(block.Block.Time); timeElapsed <= l.relayValsetOffsetDur { + timeRemaining := time.Duration(int64(l.relayValsetOffsetDur) - int64(timeElapsed)) + l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("valset relay offset not reached yet") + return false + } + + l.Logger().WithFields(log.Fields{"inj_nonce": vs.Nonce, "eth_nonce": latestEthereumValsetNonce.Uint64()}).Debugln("new valset update") + + return true +} + +func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Valset) error { + metrics.ReportFuncCall(l.svcTags) + doneFn := metrics.ReportFuncTiming(l.svcTags) + defer doneFn() + + latestBatches, err := l.Injective.LatestTransactionBatches(ctx) + if err != nil { + return err + } + + var ( + oldestConfirmedBatch *peggytypes.OutgoingTxBatch + confirmations []*peggytypes.MsgConfirmBatch + ) + + // todo: skip timed out batches + for _, batch := range latestBatches { + sigs, err := l.Injective.TransactionBatchSignatures(ctx, batch.BatchNonce, gethcommon.HexToAddress(batch.TokenContract)) + if err != nil { + return err + } + + if len(sigs) == 0 { + continue + } + + oldestConfirmedBatch = batch + confirmations = sigs + } + + if oldestConfirmedBatch == nil { + l.Logger().Infoln("no batch to relay") + return nil + } + + if !l.shouldRelayBatch(ctx, oldestConfirmedBatch) { + return nil + } + + txHash, err := l.Ethereum.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedBatch, confirmations) + if err != nil { + return err + } + + l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent outgoing tx batch to Ethereum") + + return nil +} + +func (l *relayer) shouldRelayBatch(ctx context.Context, batch *peggytypes.OutgoingTxBatch) bool { + latestEthBatch, err := l.Ethereum.GetTxBatchNonce(ctx, gethcommon.HexToAddress(batch.TokenContract)) + if err != nil { + l.Logger().WithError(err).Warningf("unable to get latest batch nonce from Ethereum: token_contract=%s", gethcommon.HexToAddress(batch.TokenContract)) + return false + } + + // Check if ethereum batch was updated by other validators + if batch.BatchNonce <= latestEthBatch.Uint64() { + l.Logger().WithFields(log.Fields{"eth_nonce": latestEthBatch.Uint64(), "inj_nonce": batch.BatchNonce}).Debugln("batch already updated on Ethereum") + return false + } + + // Check custom time delay offset + blockTime, err := l.Injective.GetBlock(ctx, int64(batch.Block)) + if err != nil { + l.Logger().WithError(err).Warningln("unable to get latest block from Injective") + return false + } + + if timeElapsed := time.Since(blockTime.Block.Time); timeElapsed <= l.relayBatchOffsetDur { + timeRemaining := time.Duration(int64(l.relayBatchOffsetDur) - int64(timeElapsed)) + l.Logger().WithField("time_remaining", timeRemaining.String()).Debugln("batch relay offset not reached yet") + return false + } + + l.Logger().WithFields(log.Fields{"inj_nonce": batch.BatchNonce, "eth_nonce": latestEthBatch.Uint64()}).Debugln("new batch update") + + return true +} + +// FindLatestValset finds the latest valset on the Peggy contract by looking back through the event +// history and finding the most recent ValsetUpdatedEvent. Most of the time this will be very fast +// as the latest update will be in recent blockchain history and the search moves from the present +// backwards in time. In the case that the validator set has not been updated for a very long time +// this will take longer. +func (l *relayer) findLatestValsetOnEth(ctx context.Context) (*peggytypes.Valset, error) { + latestHeader, err := l.Ethereum.GetHeaderByNumber(ctx, nil) + if err != nil { + return nil, errors.Wrap(err, "failed to get latest eth header") + } + + latestEthereumValsetNonce, err := l.Ethereum.GetValsetNonce(ctx) + if err != nil { + return nil, errors.Wrap(err, "failed to get latest valset nonce on Ethereum") + } + + cosmosValset, err := l.Injective.ValsetAt(ctx, latestEthereumValsetNonce.Uint64()) + if err != nil { + return nil, errors.Wrap(err, "failed to get Injective valset") + } + + currentBlock := latestHeader.Number.Uint64() + + for currentBlock > 0 { + var startSearchBlock uint64 + if currentBlock <= findValsetBlocksToSearch { + startSearchBlock = 0 + } else { + startSearchBlock = currentBlock - findValsetBlocksToSearch + } + + valsetUpdatedEvents, err := l.Ethereum.GetValsetUpdatedEvents(startSearchBlock, currentBlock) + if err != nil { + return nil, errors.Wrap(err, "failed to filter past ValsetUpdated events from Ethereum") + } + + // by default the lowest found valset goes first, we want the highest + // + // TODO(xlab): this follows the original impl, but sort might be skipped there: + // we could access just the latest element later. + sort.Sort(sort.Reverse(PeggyValsetUpdatedEvents(valsetUpdatedEvents))) + + if len(valsetUpdatedEvents) == 0 { + currentBlock = startSearchBlock + continue + } + + // we take only the first event if we find any at all. + event := valsetUpdatedEvents[0] + valset := &peggytypes.Valset{ + Nonce: event.NewValsetNonce.Uint64(), + Members: make([]*peggytypes.BridgeValidator, 0, len(event.Powers)), + RewardAmount: cosmostypes.NewIntFromBigInt(event.RewardAmount), + RewardToken: event.RewardToken.Hex(), + } + + for idx, p := range event.Powers { + valset.Members = append(valset.Members, &peggytypes.BridgeValidator{ + Power: p.Uint64(), + EthereumAddress: event.Validators[idx].Hex(), + }) + } + + checkIfValsetsDiffer(cosmosValset, valset) + + return valset, nil + + } + + return nil, ErrNotFound +} + +var ErrNotFound = errors.New("not found") + +type PeggyValsetUpdatedEvents []*peggyevents.PeggyValsetUpdatedEvent + +func (a PeggyValsetUpdatedEvents) Len() int { return len(a) } +func (a PeggyValsetUpdatedEvents) Less(i, j int) bool { + return a[i].NewValsetNonce.Cmp(a[j].NewValsetNonce) < 0 +} +func (a PeggyValsetUpdatedEvents) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +// This function exists to provide a warning if Cosmos and Ethereum have different validator sets +// for a given nonce. In the mundane version of this warning the validator sets disagree on sorting order +// which can happen if some relayer uses an unstable sort, or in a case of a mild griefing attack. +// The Peggy contract validates signatures in order of highest to lowest power. That way it can exit +// the loop early once a vote has enough power, if a relayer where to submit things in the reverse order +// they could grief users of the contract into paying more in gas. +// The other (and far worse) way a disagreement here could occur is if validators are colluding to steal +// funds from the Peggy contract and have submitted a hijacking update. If slashing for off Cosmos chain +// Ethereum signatures is implemented you would put that handler here. +func checkIfValsetsDiffer(cosmosValset, ethereumValset *peggytypes.Valset) { + if cosmosValset == nil && ethereumValset.Nonce == 0 { + // bootstrapping case + return + } else if cosmosValset == nil { + log.WithField( + "eth_valset_nonce", + ethereumValset.Nonce, + ).Errorln("Cosmos does not have a valset for nonce from Ethereum chain. Possible bridge hijacking!") + return + } + + if cosmosValset.Nonce != ethereumValset.Nonce { + log.WithFields(log.Fields{ + "cosmos_valset_nonce": cosmosValset.Nonce, + "eth_valset_nonce": ethereumValset.Nonce, + }).Errorln("Cosmos does have a wrong valset nonce, differs from Ethereum chain. Possible bridge hijacking!") + return + } + + if len(cosmosValset.Members) != len(ethereumValset.Members) { + log.WithFields(log.Fields{ + "cosmos_valset": len(cosmosValset.Members), + "eth_valset": len(ethereumValset.Members), + }).Errorln("Cosmos and Ethereum Valsets have different length. Possible bridge hijacking!") + return + } + + BridgeValidators(cosmosValset.Members).Sort() + BridgeValidators(ethereumValset.Members).Sort() + + for idx, member := range cosmosValset.Members { + if ethereumValset.Members[idx].EthereumAddress != member.EthereumAddress { + log.Errorln("Valsets are different, a sorting error?") + } + if ethereumValset.Members[idx].Power != member.Power { + log.Errorln("Valsets are different, a sorting error?") + } + } +} + +type BridgeValidators []*peggytypes.BridgeValidator + +// Sort sorts the validators by power +func (b BridgeValidators) Sort() { + sort.Slice(b, func(i, j int) bool { + if b[i].Power == b[j].Power { + // Secondary sort on eth address in case powers are equal + return util.EthAddrLessThan(b[i].EthereumAddress, b[j].EthereumAddress) + } + return b[i].Power > b[j].Power + }) +} + +// HasDuplicates returns true if there are duplicates in the set +func (b BridgeValidators) HasDuplicates() bool { + m := make(map[string]struct{}, len(b)) + for i := range b { + m[b[i].EthereumAddress] = struct{}{} + } + return len(m) != len(b) +} + +// GetPowers returns only the power values for all members +func (b BridgeValidators) GetPowers() []uint64 { + r := make([]uint64, len(b)) + for i := range b { + r[i] = b[i].Power + } + return r +} From ce69bba08e0976e27ed60a4c68c61d0985df89e9 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 26 Mar 2024 12:52:11 +0100 Subject: [PATCH 91/99] use token decimals when calculating batch fees --- orchestrator/batch_request_test.go | 4 +-- orchestrator/batch_requester.go | 42 +++++++++++++++++------------- orchestrator/ethereum/network.go | 23 ++++++++++++++-- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/orchestrator/batch_request_test.go b/orchestrator/batch_request_test.go index 341870b6..200040f2 100644 --- a/orchestrator/batch_request_test.go +++ b/orchestrator/batch_request_test.go @@ -169,7 +169,7 @@ func TestCheckFeeThreshold(t *testing.T) { } // 2.5 * 10 > 21 - assert.True(t, loop.checkFeeThreshold(tokenAddr, totalFees)) + assert.True(t, loop.CheckMinBatchFee(tokenAddr, totalFees)) }) t.Run("fee threshold is met", func(t *testing.T) { @@ -197,6 +197,6 @@ func TestCheckFeeThreshold(t *testing.T) { } // 2.5 * 100 < 333.333 - assert.False(t, loop.checkFeeThreshold(tokenAddr, totalFees)) + assert.False(t, loop.CheckMinBatchFee(tokenAddr, totalFees)) }) } diff --git a/orchestrator/batch_requester.go b/orchestrator/batch_requester.go index 8be18251..9eac5113 100644 --- a/orchestrator/batch_requester.go +++ b/orchestrator/batch_requester.go @@ -3,7 +3,6 @@ package orchestrator import ( "context" "github.com/InjectiveLabs/peggo/orchestrator/ethereum" - cosmostypes "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/shopspring/decimal" log "github.com/xlab/suplog" @@ -39,7 +38,7 @@ func (l *batchRequester) Logger() log.Logger { } func (l *batchRequester) RequestBatches(ctx context.Context) error { - fees, err := l.getUnbatchedTokenFees(ctx) + fees, err := l.GetUnbatchedTokenFees(ctx) if err != nil { l.Logger().WithError(err).Warningln("unable to get outgoing withdrawal fees") return nil @@ -51,13 +50,13 @@ func (l *batchRequester) RequestBatches(ctx context.Context) error { } for _, fee := range fees { - l.requestBatch(ctx, fee) + l.RequestTokenBatch(ctx, fee) } return nil } -func (l *batchRequester) getUnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { +func (l *batchRequester) GetUnbatchedTokenFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { var unbatchedFees []*peggytypes.BatchFees fn := func() error { fees, err := l.Injective.UnbatchedTokensWithFees(ctx) @@ -77,19 +76,31 @@ func (l *batchRequester) getUnbatchedTokenFees(ctx context.Context) ([]*peggytyp return unbatchedFees, nil } -func (l *batchRequester) requestBatch(ctx context.Context, fee *peggytypes.BatchFees) { - tokenAddr := gethcommon.HexToAddress(fee.Token) - if thresholdMet := l.checkFeeThreshold(fee.TotalFees, tokenAddr); !thresholdMet { +func (l *batchRequester) RequestTokenBatch(ctx context.Context, fee *peggytypes.BatchFees) { + tokenContract := gethcommon.HexToAddress(fee.Token) + tokenPriceInUSD, err := l.priceFeed.QueryUSDPrice(tokenContract) + if err != nil { + l.Logger().WithError(err).Warningln("failed to query oracle for token price") + return + } + + tokenDecimals, err := l.Ethereum.TokenDecimals(ctx, tokenContract) + if err != nil { + l.Logger().WithError(err).Warningln("failed to query decimals from token contract") + return + } + + if l.CheckMinBatchFee(fee, tokenPriceInUSD, tokenDecimals) { return } - tokenDenom := l.tokenDenom(tokenAddr) - l.Logger().WithFields(log.Fields{"denom": tokenDenom, "token_contract": tokenAddr.String()}).Infoln("requesting batch on Injective") + tokenDenom := l.GetTokenDenom(tokenContract) + l.Logger().WithFields(log.Fields{"token_denom": tokenDenom, "token_contract": tokenContract.String()}).Infoln("requesting new token batch on Injective") _ = l.Injective.SendRequestBatch(ctx, tokenDenom) } -func (l *batchRequester) tokenDenom(tokenAddr gethcommon.Address) string { +func (l *batchRequester) GetTokenDenom(tokenAddr gethcommon.Address) string { if cosmosDenom, ok := l.erc20ContractMapping[tokenAddr]; ok { return cosmosDenom } @@ -97,24 +108,19 @@ func (l *batchRequester) tokenDenom(tokenAddr gethcommon.Address) string { return peggytypes.PeggyDenomString(tokenAddr) } -func (l *batchRequester) checkFeeThreshold(fees cosmostypes.Int, tokenAddr gethcommon.Address) bool { +func (l *batchRequester) CheckMinBatchFee(fee *peggytypes.BatchFees, tokenPriceInUSD float64, tokenDecimals uint8) bool { if l.minBatchFeeUSD == 0 { return true } - tokenPriceInUSD, err := l.priceFeed.QueryUSDPrice(tokenAddr) - if err != nil { - return false - } - var ( minFeeInUSDDec = decimal.NewFromFloat(l.minBatchFeeUSD) tokenPriceInUSDDec = decimal.NewFromFloat(tokenPriceInUSD) - totalFeeInUSDDec = decimal.NewFromBigInt(fees.BigInt(), -18).Mul(tokenPriceInUSDDec) // todo: fix decimals + totalFeeInUSDDec = decimal.NewFromBigInt(fee.TotalFees.BigInt(), -1*int32(tokenDecimals)).Mul(tokenPriceInUSDDec) ) if totalFeeInUSDDec.LessThan(minFeeInUSDDec) { - l.Logger().WithFields(log.Fields{"token_contract": tokenAddr.String(), "batch_fee": totalFeeInUSDDec.String(), "min_fee": minFeeInUSDDec.String()}).Debugln("insufficient token batch fee") + l.Logger().WithFields(log.Fields{"token_contract": fee.Token, "total_fee": totalFeeInUSDDec.String(), "min_fee": minFeeInUSDDec.String()}).Debugln("insufficient fee for token batch request, skipping...") return false } diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index b4929a45..3dc49ae7 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -2,6 +2,7 @@ package ethereum import ( "context" + "github.com/ethereum/go-ethereum" "math/big" "strings" "time" @@ -53,8 +54,7 @@ type Network interface { confirms []*peggytypes.MsgConfirmBatch, ) (*gethcommon.Hash, error) - // todo: audit fix - //TokenDecimals(ctx context.Context, tokenContract gethcommon.Address) (uint8, error) + TokenDecimals(ctx context.Context, tokenContract gethcommon.Address) (uint8, error) } type network struct { @@ -119,6 +119,25 @@ func NewNetwork( return n, nil } +func (n *network) TokenDecimals(ctx context.Context, tokenContract gethcommon.Address) (uint8, error) { + msg := ethereum.CallMsg{ + //From: gethcommon.Address{}, + To: &tokenContract, + Data: gethcommon.Hex2Bytes("313ce567"), // Function signature for decimals(), + } + + res, err := n.Provider().CallContract(ctx, msg, nil) + if err != nil { + return 0, err + } + + if len(res) == 0 { + return 0, errors.Errorf("no decimals found for token contract %s", tokenContract.Hex()) + } + + return uint8(big.NewInt(0).SetBytes(res).Uint64()), nil +} + func (n *network) GetHeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) { return n.Provider().HeaderByNumber(ctx, number) } From 200aef06570bf437423bf092371edacb722da61f Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 26 Mar 2024 13:08:30 +0100 Subject: [PATCH 92/99] skip timed out batches when relaying --- orchestrator/inj_relayer.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/orchestrator/inj_relayer.go b/orchestrator/inj_relayer.go index e5153a75..ce92f17e 100644 --- a/orchestrator/inj_relayer.go +++ b/orchestrator/inj_relayer.go @@ -209,13 +209,22 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va return err } + latestEthHeight, err := l.Ethereum.GetHeaderByNumber(ctx, nil) + if err != nil { + return err + } + var ( oldestConfirmedBatch *peggytypes.OutgoingTxBatch confirmations []*peggytypes.MsgConfirmBatch ) - // todo: skip timed out batches for _, batch := range latestBatches { + if batch.BatchTimeout <= latestEthHeight.Number.Uint64() { + l.Logger().WithFields(log.Fields{"batch_nonce": batch.BatchNonce, "batch_timeout_height": batch.BatchTimeout, "latest_eth_height": latestEthHeight.Number.Uint64()}).Debugln("skipping timed out batch") + continue + } + sigs, err := l.Injective.TransactionBatchSignatures(ctx, batch.BatchNonce, gethcommon.HexToAddress(batch.TokenContract)) if err != nil { return err From 965645c76e9e5130ea0281d4477431c31f3b1df2 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 28 Mar 2024 12:32:39 +0100 Subject: [PATCH 93/99] init logs --- cmd/peggo/orchestrator.go | 56 +++++------ orchestrator/batch_request_test.go | 24 ++--- orchestrator/batch_requester.go | 10 +- orchestrator/eth_oracle.go | 10 +- orchestrator/ethereum/network.go | 8 -- orchestrator/inj_relayer.go | 13 ++- orchestrator/inj_signer.go | 12 +-- orchestrator/oracle_test.go | 28 +++--- orchestrator/peggy_orchestrator.go | 26 ++--- orchestrator/relayer_test.go | 156 ++++++++++++++--------------- orchestrator/signer_test.go | 34 +++---- 11 files changed, 185 insertions(+), 192 deletions(-) diff --git a/cmd/peggo/orchestrator.go b/cmd/peggo/orchestrator.go index 03822cd6..727b45b4 100644 --- a/cmd/peggo/orchestrator.go +++ b/cmd/peggo/orchestrator.go @@ -6,16 +6,16 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" cli "github.com/jawher/mow.cli" + "github.com/pkg/errors" "github.com/xlab/closer" log "github.com/xlab/suplog" - chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" - "github.com/InjectiveLabs/peggo/orchestrator" "github.com/InjectiveLabs/peggo/orchestrator/coingecko" "github.com/InjectiveLabs/peggo/orchestrator/cosmos" "github.com/InjectiveLabs/peggo/orchestrator/ethereum" "github.com/InjectiveLabs/peggo/orchestrator/version" + chaintypes "github.com/InjectiveLabs/sdk-go/chain/types" ) // startOrchestrator action runs an infinite loop, @@ -39,7 +39,7 @@ func orchestratorCmd(cmd *cli.Cmd) { "build_date": version.BuildDate, "go_version": version.GoVersion, "go_arch": version.GoArch, - }).Infoln("peggo - peggy binary for Ethereum bridge") + }).Infoln("Peggo - Peggy module companion binary used to bridge assets between Injective and Ethereum") if *cfg.cosmosUseLedger || *cfg.ethUseLedger { log.Fatalln("cannot use Ledger for orchestrator, since signatures must be realtime") @@ -54,7 +54,9 @@ func orchestratorCmd(cmd *cli.Cmd) { PrivateKey: *cfg.cosmosPrivKey, UseLedger: *cfg.cosmosUseLedger, }) - orShutdown(err) + orShutdown(errors.Wrap(err, "failed to initialize Injective keyring")) + + log.Infoln("initialized Injective keyring", cosmosKeyring.Addr.String()) ethKeyFromAddress, signerFn, personalSignFn, err := initEthereumAccountsManager( uint64(*cfg.ethChainID), @@ -64,9 +66,9 @@ func orchestratorCmd(cmd *cli.Cmd) { cfg.ethPrivKey, cfg.ethUseLedger, ) - if err != nil { - log.WithError(err).Fatalln("failed to initialize Ethereum account") - } + orShutdown(errors.Wrap(err, "failed to initialize Ethereum keyring")) + + log.Infoln("initialized Ethereum keyring", ethKeyFromAddress.String()) cosmosNetwork, err := cosmos.NewNetwork(cosmosKeyring, personalSignFn, cosmos.NetworkConfig{ ChainID: *cfg.cosmosChainID, @@ -77,45 +79,43 @@ func orchestratorCmd(cmd *cli.Cmd) { }) orShutdown(err) - log.Infoln("connected to Injective network") + log.WithFields(log.Fields{"chain_id": *cfg.cosmosChainID, "gas_price": *cfg.cosmosGasPrices}).Infoln("connected to Injective network") ctx, cancelFn := context.WithCancel(context.Background()) closer.Bind(cancelFn) // Construct erc20 token mapping peggyParams, err := cosmosNetwork.PeggyParams(ctx) - if err != nil { - log.WithError(err).Fatalln("failed to query peggy params, is injectived running?") - } + orShutdown(errors.Wrap(err, "failed to query peggy params, is injectived running?")) var ( peggyContractAddr = gethcommon.HexToAddress(peggyParams.BridgeEthereumAddress) injTokenAddr = gethcommon.HexToAddress(peggyParams.CosmosCoinErc20Contract) - erc20ContractMapping = map[gethcommon.Address]string{ - injTokenAddr: chaintypes.InjectiveCoin, - } + erc20ContractMapping = map[gethcommon.Address]string{injTokenAddr: chaintypes.InjectiveCoin} ) + log.WithFields(log.Fields{"peggy_contract": peggyContractAddr.String(), "inj_token_contract": injTokenAddr.String()}).Debugln("loaded Peggy module params") + // Connect to ethereum network - ethereumNetwork, err := ethereum.NewNetwork( - peggyContractAddr, - ethKeyFromAddress, - signerFn, - ethereum.NetworkConfig{ - EthNodeRPC: *cfg.ethNodeRPC, - GasPriceAdjustment: *cfg.ethGasPriceAdjustment, - MaxGasPrice: *cfg.ethMaxGasPrice, - PendingTxWaitDuration: *cfg.pendingTxWaitDuration, - EthNodeAlchemyWS: *cfg.ethNodeAlchemyWS, - }, - ) + ethereumNetwork, err := ethereum.NewNetwork(peggyContractAddr, ethKeyFromAddress, signerFn, ethereum.NetworkConfig{ + EthNodeRPC: *cfg.ethNodeRPC, + GasPriceAdjustment: *cfg.ethGasPriceAdjustment, + MaxGasPrice: *cfg.ethMaxGasPrice, + PendingTxWaitDuration: *cfg.pendingTxWaitDuration, + EthNodeAlchemyWS: *cfg.ethNodeAlchemyWS, + }) orShutdown(err) - log.Infoln("connected to Ethereum network") + log.WithFields(log.Fields{ + "chain_id": *cfg.ethChainID, + "rpc": *cfg.ethNodeRPC, + "max_gas_price": *cfg.ethMaxGasPrice, + "gas_price_adjustment": *cfg.ethGasPriceAdjustment, + }).Infoln("connected to Ethereum network") addr, isValidator := cosmos.HasRegisteredOrchestrator(cosmosNetwork, ethKeyFromAddress) if isValidator { - log.Debugln("provided ETH address is registered with a validator", addr.String()) + log.Debugln("provided ETH address is registered with an orchestrator", addr.String()) } // Create peggo and run it diff --git a/orchestrator/batch_request_test.go b/orchestrator/batch_request_test.go index 341870b6..d4d48eed 100644 --- a/orchestrator/batch_request_test.go +++ b/orchestrator/batch_request_test.go @@ -25,14 +25,14 @@ func TestRequestBatches(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, maxAttempts: 1, } loop := batchRequester{ - PeggyOrchestrator: o, + Orchestrator: o, } assert.NoError(t, loop.requestBatches(context.TODO())) @@ -47,14 +47,14 @@ func TestRequestBatches(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, maxAttempts: 1, } loop := batchRequester{ - PeggyOrchestrator: o, + Orchestrator: o, } assert.NoError(t, loop.requestBatches(context.TODO())) @@ -81,7 +81,7 @@ func TestRequestBatches(t *testing.T) { feed := mockPriceFeed{queryFn: func(_ eth.Address) (float64, error) { return 1, nil }} - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, priceFeed: feed, @@ -93,7 +93,7 @@ func TestRequestBatches(t *testing.T) { } loop := batchRequester{ - PeggyOrchestrator: o, + Orchestrator: o, } assert.NoError(t, loop.requestBatches(context.TODO())) @@ -120,7 +120,7 @@ func TestRequestBatches(t *testing.T) { feed := mockPriceFeed{queryFn: func(_ eth.Address) (float64, error) { return 1, nil }} - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, priceFeed: feed, @@ -132,7 +132,7 @@ func TestRequestBatches(t *testing.T) { } loop := batchRequester{ - PeggyOrchestrator: o, + Orchestrator: o, } assert.NoError(t, loop.requestBatches(context.TODO())) @@ -155,7 +155,7 @@ func TestCheckFeeThreshold(t *testing.T) { }} ) - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, priceFeed: feed, minBatchFeeUSD: 21, @@ -165,7 +165,7 @@ func TestCheckFeeThreshold(t *testing.T) { } loop := batchRequester{ - PeggyOrchestrator: o, + Orchestrator: o, } // 2.5 * 10 > 21 @@ -183,7 +183,7 @@ func TestCheckFeeThreshold(t *testing.T) { }} ) - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, priceFeed: feed, minBatchFeeUSD: 333.333, @@ -193,7 +193,7 @@ func TestCheckFeeThreshold(t *testing.T) { } loop := batchRequester{ - PeggyOrchestrator: o, + Orchestrator: o, } // 2.5 * 100 < 333.333 diff --git a/orchestrator/batch_requester.go b/orchestrator/batch_requester.go index 8be18251..3b242d35 100644 --- a/orchestrator/batch_requester.go +++ b/orchestrator/batch_requester.go @@ -14,11 +14,11 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/loops" ) -func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { +func (s *Orchestrator) runBatchRequester(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { requester := batchRequester{ - PeggyOrchestrator: s, - Injective: inj, - Ethereum: eth, + Orchestrator: s, + Injective: inj, + Ethereum: eth, } s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting BatchRequester...") @@ -29,7 +29,7 @@ func (s *PeggyOrchestrator) BatchRequesterLoop(ctx context.Context, inj cosmos.N } type batchRequester struct { - *PeggyOrchestrator + *Orchestrator Injective cosmos.Network Ethereum ethereum.Network } diff --git a/orchestrator/eth_oracle.go b/orchestrator/eth_oracle.go index 2a89743c..0e30e636 100644 --- a/orchestrator/eth_oracle.go +++ b/orchestrator/eth_oracle.go @@ -32,16 +32,16 @@ const ( resyncInterval = 24 * time.Hour ) -// EthOracleMainLoop is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain +// runEthOracle is responsible for making sure that Ethereum events are retrieved from the Ethereum blockchain // and ferried over to Cosmos where they will be used to issue tokens or process batches. -func (s *PeggyOrchestrator) EthOracleMainLoop( +func (s *Orchestrator) runEthOracle( ctx context.Context, inj cosmos.Network, eth ethereum.Network, lastObservedBlock uint64, ) error { oracle := ethOracle{ - PeggyOrchestrator: s, + Orchestrator: s, Injective: inj, Ethereum: eth, LastObservedEthHeight: lastObservedBlock, @@ -56,7 +56,7 @@ func (s *PeggyOrchestrator) EthOracleMainLoop( } type ethOracle struct { - *PeggyOrchestrator + *Orchestrator Injective cosmos.Network Ethereum ethereum.Network LastResyncWithInjective time.Time @@ -157,6 +157,8 @@ func (l *ethOracle) ObserveEthEvents(ctx context.Context) error { func (l *ethOracle) getEthEvents(ctx context.Context, startBlock, endBlock uint64) ([]event, error) { var events []event scanEthEventsFn := func() error { + events = nil // clear previous result in case a retry happens + oldDepositEvents, err := l.Ethereum.GetSendToCosmosEvents(startBlock, endBlock) if err != nil { return errors.Wrap(err, "failed to get SendToCosmos events") diff --git a/orchestrator/ethereum/network.go b/orchestrator/ethereum/network.go index b4929a45..3b5828d7 100644 --- a/orchestrator/ethereum/network.go +++ b/orchestrator/ethereum/network.go @@ -69,14 +69,6 @@ func NewNetwork( signerFn bind.SignerFn, cfg NetworkConfig, ) (Network, error) { - log.WithFields(log.Fields{ - "eth_rpc": cfg.EthNodeRPC, - "eth_addr": fromAddr.String(), - "peggy_contract": peggyContractAddr, - "max_gas_price": cfg.MaxGasPrice, - "gas_price_adjustment": cfg.GasPriceAdjustment, - }).Debugln("Ethereum network config") - evmRPC, err := rpc.Dial(cfg.EthNodeRPC) if err != nil { return nil, errors.Wrapf(err, "failed to connect to ethereum RPC: %s", cfg.EthNodeRPC) diff --git a/orchestrator/inj_relayer.go b/orchestrator/inj_relayer.go index e5153a75..2defe3e7 100644 --- a/orchestrator/inj_relayer.go +++ b/orchestrator/inj_relayer.go @@ -25,16 +25,15 @@ const ( findValsetBlocksToSearch = 2000 ) -func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { +func (s *Orchestrator) runRelayer(ctx context.Context, inj cosmos.Network, eth ethereum.Network) (err error) { rel := relayer{ - PeggyOrchestrator: s, - Injective: inj, - Ethereum: eth, + Orchestrator: s, + Injective: inj, + Ethereum: eth, } - relayingBatches := rel.IsRelayingValsets() + relayingBatches := rel.IsRelayingBatches() relayingValsets := rel.IsRelayingValsets() - if noRelay := !relayingBatches && !relayingValsets; noRelay { return nil } @@ -47,7 +46,7 @@ func (s *PeggyOrchestrator) RelayerMainLoop(ctx context.Context, inj cosmos.Netw } type relayer struct { - *PeggyOrchestrator + *Orchestrator Injective cosmos.Network Ethereum ethereum.Network } diff --git a/orchestrator/inj_signer.go b/orchestrator/inj_signer.go index ed3cfa85..d656d5e0 100644 --- a/orchestrator/inj_signer.go +++ b/orchestrator/inj_signer.go @@ -11,14 +11,14 @@ import ( "github.com/InjectiveLabs/peggo/orchestrator/loops" ) -// EthSignerMainLoop simply signs off on any batches or validator sets provided by the validator +// runEthSigner simply signs off on any batches or validator sets provided by the validator // since these are provided directly by a trusted Injective node they can simply be assumed to be // valid and signed off on. -func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, inj cosmos.Network, peggyID gethcommon.Hash) error { +func (s *Orchestrator) runEthSigner(ctx context.Context, inj cosmos.Network, peggyID gethcommon.Hash) error { signer := ethSigner{ - PeggyOrchestrator: s, - Injective: inj, - PeggyID: peggyID, + Orchestrator: s, + Injective: inj, + PeggyID: peggyID, } s.logger.WithField("loop_duration", defaultLoopDur.String()).Debugln("starting Signer...") @@ -29,7 +29,7 @@ func (s *PeggyOrchestrator) EthSignerMainLoop(ctx context.Context, inj cosmos.Ne } type ethSigner struct { - *PeggyOrchestrator + *Orchestrator Injective cosmos.Network PeggyID gethcommon.Hash } diff --git a/orchestrator/oracle_test.go b/orchestrator/oracle_test.go index 89d4d461..e077077b 100644 --- a/orchestrator/oracle_test.go +++ b/orchestrator/oracle_test.go @@ -22,7 +22,7 @@ func TestEthOracle(t *testing.T) { t.Run("failed to get latest header from ethereum", func(t *testing.T) { t.Parallel() - orch := &PeggyOrchestrator{ + orch := &Orchestrator{ logger: suplog.DefaultLogger, eth: mockEthereum{ headerByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { @@ -31,7 +31,7 @@ func TestEthOracle(t *testing.T) { }, } - assert.Error(t, orch.EthOracleMainLoop(context.TODO())) + assert.Error(t, orch.runEthOracle(context.TODO())) }) t.Run("latest ethereum header is old", func(t *testing.T) { @@ -43,14 +43,14 @@ func TestEthOracle(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, eth: ethereum, maxAttempts: 1, } loop := ethOracle{ - PeggyOrchestrator: o, + Orchestrator: o, LastResyncWithInjective: time.Now(), LastObservedEthHeight: 100, } @@ -71,14 +71,14 @@ func TestEthOracle(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, eth: ethereum, maxAttempts: 1, } loop := ethOracle{ - PeggyOrchestrator: o, + Orchestrator: o, LastResyncWithInjective: time.Now(), LastObservedEthHeight: 100, } @@ -119,7 +119,7 @@ func TestEthOracle(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, eth: ethereum, inj: injective, @@ -127,7 +127,7 @@ func TestEthOracle(t *testing.T) { } loop := ethOracle{ - PeggyOrchestrator: o, + Orchestrator: o, LastResyncWithInjective: time.Now(), LastObservedEthHeight: 100, } @@ -179,7 +179,7 @@ func TestEthOracle(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, eth: eth, inj: inj, @@ -187,7 +187,7 @@ func TestEthOracle(t *testing.T) { } loop := ethOracle{ - PeggyOrchestrator: o, + Orchestrator: o, LastResyncWithInjective: time.Now(), LastObservedEthHeight: 100, } @@ -240,7 +240,7 @@ func TestEthOracle(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, eth: eth, inj: inj, @@ -248,7 +248,7 @@ func TestEthOracle(t *testing.T) { } loop := ethOracle{ - PeggyOrchestrator: o, + Orchestrator: o, LastResyncWithInjective: time.Now(), LastObservedEthHeight: 100, } @@ -273,7 +273,7 @@ func TestEthOracle(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, eth: eth, inj: inj, @@ -281,7 +281,7 @@ func TestEthOracle(t *testing.T) { } loop := ethOracle{ - PeggyOrchestrator: o, + Orchestrator: o, LastResyncWithInjective: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), LastObservedEthHeight: 100, } diff --git a/orchestrator/peggy_orchestrator.go b/orchestrator/peggy_orchestrator.go index 4df1026b..109b525b 100644 --- a/orchestrator/peggy_orchestrator.go +++ b/orchestrator/peggy_orchestrator.go @@ -37,7 +37,7 @@ type Config struct { RelayerMode bool } -type PeggyOrchestrator struct { +type Orchestrator struct { logger log.Logger svcTags metrics.Tags @@ -57,8 +57,8 @@ func NewPeggyOrchestrator( ethAddr gethcommon.Address, priceFeed PriceFeed, cfg Config, -) (*PeggyOrchestrator, error) { - o := &PeggyOrchestrator{ +) (*Orchestrator, error) { + o := &Orchestrator{ logger: log.DefaultLogger, svcTags: metrics.Tags{"svc": "peggy_orchestrator"}, injAddr: orchestratorAddr, @@ -92,7 +92,7 @@ func NewPeggyOrchestrator( // Run starts all major loops required to make // up the Orchestrator, all of these are async loops. -func (s *PeggyOrchestrator) Run(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { +func (s *Orchestrator) Run(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { if s.isRelayer { return s.startRelayerMode(ctx, inj, eth) } @@ -102,7 +102,7 @@ func (s *PeggyOrchestrator) Run(ctx context.Context, inj cosmos.Network, eth eth // startValidatorMode runs all orchestrator processes. This is called // when peggo is run alongside a validator injective node. -func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { +func (s *Orchestrator) startValidatorMode(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { log.Infoln("running orchestrator in validator mode") // get gethcommon block observed by this validator @@ -124,10 +124,10 @@ func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.N var pg loops.ParanoidGroup - pg.Go(func() error { return s.EthOracleMainLoop(ctx, inj, eth, lastObservedEthBlock) }) - pg.Go(func() error { return s.EthSignerMainLoop(ctx, inj, peggyContractID) }) - pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj, eth) }) - pg.Go(func() error { return s.RelayerMainLoop(ctx, inj, eth) }) + pg.Go(func() error { return s.runEthOracle(ctx, inj, eth, lastObservedEthBlock) }) + pg.Go(func() error { return s.runEthSigner(ctx, inj, peggyContractID) }) + pg.Go(func() error { return s.runBatchRequester(ctx, inj, eth) }) + pg.Go(func() error { return s.runRelayer(ctx, inj, eth) }) return pg.Wait() } @@ -135,18 +135,18 @@ func (s *PeggyOrchestrator) startValidatorMode(ctx context.Context, inj cosmos.N // startRelayerMode runs orchestrator processes that only relay specific // messages that do not require a validator's signature. This mode is run // alongside a non-validator injective node -func (s *PeggyOrchestrator) startRelayerMode(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { +func (s *Orchestrator) startRelayerMode(ctx context.Context, inj cosmos.Network, eth ethereum.Network) error { log.Infoln("running orchestrator in relayer mode") var pg loops.ParanoidGroup - pg.Go(func() error { return s.BatchRequesterLoop(ctx, inj, eth) }) - pg.Go(func() error { return s.RelayerMainLoop(ctx, inj, eth) }) + pg.Go(func() error { return s.runBatchRequester(ctx, inj, eth) }) + pg.Go(func() error { return s.runRelayer(ctx, inj, eth) }) return pg.Wait() } -func (s *PeggyOrchestrator) getLastClaimBlockHeight(ctx context.Context, inj cosmos.Network) (uint64, error) { +func (s *Orchestrator) getLastClaimBlockHeight(ctx context.Context, inj cosmos.Network) (uint64, error) { metrics.ReportFuncCall(s.svcTags) doneFn := metrics.ReportFuncTiming(s.svcTags) defer doneFn() diff --git a/orchestrator/relayer_test.go b/orchestrator/relayer_test.go index 653d6c20..1b90b401 100644 --- a/orchestrator/relayer_test.go +++ b/orchestrator/relayer_test.go @@ -30,7 +30,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: injective, maxAttempts: 1, @@ -38,8 +38,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -57,7 +57,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, maxAttempts: 1, @@ -65,8 +65,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -84,7 +84,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, maxAttempts: 1, @@ -92,8 +92,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayValset(context.TODO())) @@ -124,7 +124,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -133,8 +133,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -165,7 +165,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -174,8 +174,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -209,7 +209,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -218,8 +218,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -256,7 +256,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -265,8 +265,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -306,7 +306,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -315,8 +315,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -372,7 +372,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -381,8 +381,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayValset(context.TODO())) @@ -441,7 +441,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -450,8 +450,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -510,7 +510,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -520,8 +520,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayValset(context.TODO())) @@ -583,7 +583,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -593,8 +593,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayValset(context.TODO())) @@ -656,7 +656,7 @@ func TestValsetRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -666,8 +666,8 @@ func TestValsetRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayValset(context.TODO())) @@ -686,7 +686,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, maxAttempts: 1, @@ -694,8 +694,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -713,7 +713,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, maxAttempts: 1, @@ -721,8 +721,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -740,7 +740,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, maxAttempts: 1, @@ -748,8 +748,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayBatch(context.TODO())) @@ -773,7 +773,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -782,8 +782,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -815,7 +815,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -824,8 +824,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -861,7 +861,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -870,8 +870,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -910,7 +910,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -919,8 +919,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -962,7 +962,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -971,8 +971,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -1020,7 +1020,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -1029,8 +1029,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayBatch(context.TODO())) @@ -1081,7 +1081,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -1090,8 +1090,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -1142,7 +1142,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -1152,8 +1152,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayBatch(context.TODO())) @@ -1207,7 +1207,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -1217,8 +1217,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.Error(t, l.relayBatch(context.TODO())) @@ -1273,7 +1273,7 @@ func TestBatchRelaying(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: suplog.DefaultLogger, inj: inj, eth: eth, @@ -1283,8 +1283,8 @@ func TestBatchRelaying(t *testing.T) { } l := relayer{ - PeggyOrchestrator: o, - LoopDuration: defaultRelayerLoopDur, + Orchestrator: o, + LoopDuration: defaultRelayerLoopDur, } assert.NoError(t, l.relayBatch(context.TODO())) diff --git a/orchestrator/signer_test.go b/orchestrator/signer_test.go index a98b4d02..a75fd72c 100644 --- a/orchestrator/signer_test.go +++ b/orchestrator/signer_test.go @@ -19,7 +19,7 @@ func TestEthSignerLoop(t *testing.T) { t.Run("failed to fetch peggy id from contract", func(t *testing.T) { t.Parallel() - orch := &PeggyOrchestrator{ + orch := &Orchestrator{ maxAttempts: 1, eth: mockEthereum{ getPeggyIDFn: func(context.Context) (common.Hash, error) { @@ -28,7 +28,7 @@ func TestEthSignerLoop(t *testing.T) { }, } - assert.Error(t, orch.EthSignerMainLoop(context.TODO())) + assert.Error(t, orch.runEthSigner(context.TODO())) }) t.Run("no valset to sign", func(t *testing.T) { @@ -49,15 +49,15 @@ func TestEthSignerLoop(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: log.DefaultLogger, inj: injective, maxAttempts: 1, } l := ethSigner{ - PeggyOrchestrator: o, - LoopDuration: defaultLoopDur, + Orchestrator: o, + LoopDuration: defaultLoopDur, } assert.NoError(t, l.signNewValsetUpdates(context.TODO())) @@ -88,15 +88,15 @@ func TestEthSignerLoop(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: log.DefaultLogger, inj: injective, maxAttempts: 1, } l := ethSigner{ - PeggyOrchestrator: o, - LoopDuration: defaultLoopDur, + Orchestrator: o, + LoopDuration: defaultLoopDur, } assert.Error(t, l.signNewValsetUpdates(context.TODO())) @@ -112,15 +112,15 @@ func TestEthSignerLoop(t *testing.T) { sendBatchConfirmFn: func(context.Context, common.Hash, *types.OutgoingTxBatch, common.Address) error { return nil }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: log.DefaultLogger, inj: injective, maxAttempts: 1, } l := ethSigner{ - PeggyOrchestrator: o, - LoopDuration: defaultLoopDur, + Orchestrator: o, + LoopDuration: defaultLoopDur, } assert.NoError(t, l.signNewBatch(context.TODO())) @@ -140,15 +140,15 @@ func TestEthSignerLoop(t *testing.T) { }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: log.DefaultLogger, inj: injective, maxAttempts: 1, } l := ethSigner{ - PeggyOrchestrator: o, - LoopDuration: defaultLoopDur, + Orchestrator: o, + LoopDuration: defaultLoopDur, } assert.Error(t, l.signNewBatch(context.TODO())) @@ -168,15 +168,15 @@ func TestEthSignerLoop(t *testing.T) { sendBatchConfirmFn: func(context.Context, common.Hash, *types.OutgoingTxBatch, common.Address) error { return nil }, } - o := &PeggyOrchestrator{ + o := &Orchestrator{ logger: log.DefaultLogger, inj: injective, maxAttempts: 1, } l := ethSigner{ - PeggyOrchestrator: o, - LoopDuration: defaultLoopDur, + Orchestrator: o, + LoopDuration: defaultLoopDur, } assert.NoError(t, l.signNewBatch(context.TODO())) From 0cdf0c8c18f4a9e306961383b4fd64ff43ba53e2 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 28 Mar 2024 12:48:19 +0100 Subject: [PATCH 94/99] dont retry on a failed batch submission --- orchestrator/inj_relayer.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/orchestrator/inj_relayer.go b/orchestrator/inj_relayer.go index 2defe3e7..5171cafa 100644 --- a/orchestrator/inj_relayer.go +++ b/orchestrator/inj_relayer.go @@ -239,7 +239,10 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va txHash, err := l.Ethereum.SendTransactionBatch(ctx, latestEthValset, oldestConfirmedBatch, confirmations) if err != nil { - return err + // Returning an error here triggers retries which don't help much except risk a binary crash + // Better to warn the user and try again in the next loop interval + log.WithError(err).Warningln("failed to send outgoing tx batch to Ethereum") + return nil } l.Logger().WithField("tx_hash", txHash.Hex()).Infoln("sent outgoing tx batch to Ethereum") From a144a72ab55d7c240f3c278808e289f501f7b9e4 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 28 Mar 2024 13:28:02 +0100 Subject: [PATCH 95/99] log --- orchestrator/cosmos/network.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index b4023052..f9fc8eed 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -37,14 +37,6 @@ type Network interface { } func NewNetwork(k keyring.Keyring, ethSignFn keystore.PersonalSignFn, cfg NetworkConfig) (Network, error) { - log.WithFields(log.Fields{ - "chain_id": cfg.ChainID, - "orchestrator_addr": cfg.ValidatorAddress, - "grpc": cfg.CosmosGRPC, - "tendermint_rpc": cfg.TendermintRPC, - "gas_price": cfg.GasPrice, - }).Debugln("Injective network config") - clientCfg := cfg.loadClientConfig() clientCtx, err := chain.NewClientContext(clientCfg.ChainId, cfg.ValidatorAddress, k) @@ -108,10 +100,14 @@ func awaitConnection(client chain.ChainClient, timeout time.Duration) *grpc.Clie func (cfg NetworkConfig) loadClientConfig() clientcommon.Network { if custom := cfg.CosmosGRPC != "" && cfg.TendermintRPC != ""; custom { + log.WithFields(log.Fields{"cosmos_grpc": cfg.CosmosGRPC, "tendermint_rpc": cfg.TendermintRPC}).Debugln("using custom endpoints for Injective") return customEndpoints(cfg) } - return loadBalancedEndpoints(cfg) + c := loadBalancedEndpoints(cfg) + log.WithFields(log.Fields{"cosmos_grpc": c.ChainGrpcEndpoint, "tendermint_rpc": c.TmEndpoint}).Debugln("using load balanced endpoints for Injective") + + return c } func customEndpoints(cfg NetworkConfig) clientcommon.Network { From 83b41effae868e90f9b7a739b2c047edf5db579e Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 28 Mar 2024 13:29:53 +0100 Subject: [PATCH 96/99] remove more logs --- orchestrator/cosmos/network.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/orchestrator/cosmos/network.go b/orchestrator/cosmos/network.go index f9fc8eed..43d5f712 100644 --- a/orchestrator/cosmos/network.go +++ b/orchestrator/cosmos/network.go @@ -121,8 +121,6 @@ func customEndpoints(cfg NetworkConfig) clientcommon.Network { c.LcdEndpoint = "" c.ExplorerGrpcEndpoint = "" - log.Infoln("using custom endpoints for Injective") - return c } @@ -139,8 +137,6 @@ func loadBalancedEndpoints(cfg NetworkConfig) clientcommon.Network { panic(fmt.Errorf("no provider for chain id %s", cfg.ChainID)) } - log.Infoln("using load balanced endpoints for Injective") - return clientcommon.LoadNetwork(networkName, "lb") } From 160b714e4c5f8e72759b6c5ee652dd6dd75f40e2 Mon Sep 17 00:00:00 2001 From: Nikola Marcetic Date: Thu, 28 Mar 2024 19:15:06 +0100 Subject: [PATCH 97/99] fix/github actions tags Signed-off-by: Nikola Marcetic --- .github/workflows/docker.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 46eb5b4f..53a92c60 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -51,15 +51,7 @@ jobs: - name: Build image and push run: | cd peggo/ - TAG=$(echo ${GITHUB_REF#refs/heads/} | cut -d '/' -f 2) + TAG=${{ github.ref_name }} echo $TAG [[ $ECR_ENABLED == "false" ]] || docker buildx build --tag $ECR_REPO:$TAG --platform linux/amd64,linux/arm64 --push . [[ $GHCR_ENABLED == "false" ]] || docker buildx build --tag $GHCR_REPO:$TAG --platform linux/amd64,linux/arm64 --push . - - - name: NONROOT Build image and push - run: | - cd peggo/ - TAG=$(echo ${GITHUB_REF#refs/heads/} | cut -d '/' -f 2)-nonroot - echo $TAG - [[ $ECR_ENABLED == "false" ]] || docker buildx build -f Dockerfile.nonroot --tag $ECR_REPO:$TAG --platform linux/amd64,linux/arm64 --push . - [[ $GHCR_ENABLED == "false" ]] || docker buildx build -f Dockerfile.nonroot --tag $GHCR_REPO:$TAG --platform linux/amd64,linux/arm64 --push . From 2d84e9752a8405b7ebcc2335846fbe7fcbed3306 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Thu, 28 Mar 2024 16:20:42 +0100 Subject: [PATCH 98/99] refactor unit tests --- orchestrator/batch_request_test.go | 202 ---- orchestrator/mocks_test.go | 380 ++++--- orchestrator/oracle_test.go | 293 ----- orchestrator/peggy_orchestrator.go | 7 +- orchestrator/peggy_orchestrator_test.go | 1230 +++++++++++++++++++++ orchestrator/relayer_test.go | 1292 ----------------------- orchestrator/signer_test.go | 184 ---- 7 files changed, 1478 insertions(+), 2110 deletions(-) delete mode 100644 orchestrator/batch_request_test.go delete mode 100644 orchestrator/oracle_test.go create mode 100644 orchestrator/peggy_orchestrator_test.go delete mode 100644 orchestrator/relayer_test.go delete mode 100644 orchestrator/signer_test.go diff --git a/orchestrator/batch_request_test.go b/orchestrator/batch_request_test.go deleted file mode 100644 index d4d48eed..00000000 --- a/orchestrator/batch_request_test.go +++ /dev/null @@ -1,202 +0,0 @@ -package orchestrator - -import ( - "context" - "errors" - "testing" - - cosmtypes "github.com/cosmos/cosmos-sdk/types" - eth "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/assert" - "github.com/xlab/suplog" - - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" -) - -func TestRequestBatches(t *testing.T) { - t.Parallel() - - t.Run("failed to get unbatched tokens from injective", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - unbatchedTokenFeesFn: func(context.Context) ([]*peggytypes.BatchFees, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - maxAttempts: 1, - } - - loop := batchRequester{ - Orchestrator: o, - } - - assert.NoError(t, loop.requestBatches(context.TODO())) - }) - - t.Run("no unbatched tokens", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - unbatchedTokenFeesFn: func(context.Context) ([]*peggytypes.BatchFees, error) { - return nil, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - maxAttempts: 1, - } - - loop := batchRequester{ - Orchestrator: o, - } - - assert.NoError(t, loop.requestBatches(context.TODO())) - - }) - - t.Run("batch does not meet fee threshold", func(t *testing.T) { - t.Parallel() - - tokenAddr := "0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30" - - inj := &mockInjective{ - sendRequestBatchFn: func(context.Context, string) error { return nil }, - unbatchedTokenFeesFn: func(context.Context) ([]*peggytypes.BatchFees, error) { - fees, _ := cosmtypes.NewIntFromString("50000000000000000000") - return []*peggytypes.BatchFees{ - { - Token: eth.HexToAddress(tokenAddr).String(), - TotalFees: fees, - }, - }, nil - }, - } - - feed := mockPriceFeed{queryFn: func(_ eth.Address) (float64, error) { return 1, nil }} - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - priceFeed: feed, - maxAttempts: 1, - minBatchFeeUSD: 51.0, - erc20ContractMapping: map[eth.Address]string{ - eth.HexToAddress(tokenAddr): "inj", - }, - } - - loop := batchRequester{ - Orchestrator: o, - } - - assert.NoError(t, loop.requestBatches(context.TODO())) - assert.Equal(t, inj.sendRequestBatchCallCount, 0) - }) - - t.Run("batch meets threshold and a request is sent", func(t *testing.T) { - t.Parallel() - - tokenAddr := "0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30" - - inj := &mockInjective{ - sendRequestBatchFn: func(context.Context, string) error { return nil }, - unbatchedTokenFeesFn: func(_ context.Context) ([]*peggytypes.BatchFees, error) { - fees, _ := cosmtypes.NewIntFromString("50000000000000000000") - return []*peggytypes.BatchFees{ - { - Token: eth.HexToAddress(tokenAddr).String(), - TotalFees: fees, - }, - }, nil - }, - } - - feed := mockPriceFeed{queryFn: func(_ eth.Address) (float64, error) { return 1, nil }} - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - priceFeed: feed, - maxAttempts: 1, - minBatchFeeUSD: 49.0, - erc20ContractMapping: map[eth.Address]string{ - eth.HexToAddress(tokenAddr): "inj", - }, - } - - loop := batchRequester{ - Orchestrator: o, - } - - assert.NoError(t, loop.requestBatches(context.TODO())) - assert.Equal(t, inj.sendRequestBatchCallCount, 1) - }) - -} - -func TestCheckFeeThreshold(t *testing.T) { - t.Parallel() - - t.Run("fee threshold is met", func(t *testing.T) { - t.Parallel() - - var ( - totalFees, _ = cosmtypes.NewIntFromString("10000000000000000000") // 10inj - tokenAddr = eth.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30") - feed = mockPriceFeed{queryFn: func(_ eth.Address) (float64, error) { - return 2.5, nil - }} - ) - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - priceFeed: feed, - minBatchFeeUSD: 21, - erc20ContractMapping: map[eth.Address]string{ - tokenAddr: "inj", - }, - } - - loop := batchRequester{ - Orchestrator: o, - } - - // 2.5 * 10 > 21 - assert.True(t, loop.checkFeeThreshold(tokenAddr, totalFees)) - }) - - t.Run("fee threshold is met", func(t *testing.T) { - t.Parallel() - - var ( - tokenAddr = eth.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30") - totalFees, _ = cosmtypes.NewIntFromString("100000000000000000000") // 10inj - feed = mockPriceFeed{queryFn: func(_ eth.Address) (float64, error) { - return 2.5, nil - }} - ) - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - priceFeed: feed, - minBatchFeeUSD: 333.333, - erc20ContractMapping: map[eth.Address]string{ - tokenAddr: "inj", - }, - } - - loop := batchRequester{ - Orchestrator: o, - } - - // 2.5 * 100 < 333.333 - assert.False(t, loop.checkFeeThreshold(tokenAddr, totalFees)) - }) -} diff --git a/orchestrator/mocks_test.go b/orchestrator/mocks_test.go index 99c1e8db..e549411c 100644 --- a/orchestrator/mocks_test.go +++ b/orchestrator/mocks_test.go @@ -2,219 +2,325 @@ package orchestrator import ( "context" + log "github.com/xlab/suplog" "math/big" "time" - sdk "github.com/cosmos/cosmos-sdk/types" - eth "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" + comettypes "github.com/cometbft/cometbft/rpc/core/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethtypes "github.com/ethereum/go-ethereum/core/types" peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" ) -type mockPriceFeed struct { - queryFn func(eth.Address) (float64, error) +type MockPriceFeed struct { + QueryUSDPriceFn func(gethcommon.Address) (float64, error) +} + +func (p MockPriceFeed) QueryUSDPrice(address gethcommon.Address) (float64, error) { + return p.QueryUSDPriceFn(address) +} + +type MockCosmosNetwork struct { + PeggyParamsFn func(ctx context.Context) (*peggytypes.Params, error) + LastClaimEventByAddrFn func(ctx context.Context, address sdktypes.AccAddress) (*peggytypes.LastClaimEvent, error) + GetValidatorAddressFn func(ctx context.Context, address gethcommon.Address) (sdktypes.AccAddress, error) + CurrentValsetFn func(ctx context.Context) (*peggytypes.Valset, error) + ValsetAtFn func(ctx context.Context, uint642 uint64) (*peggytypes.Valset, error) + OldestUnsignedValsetsFn func(ctx context.Context, address sdktypes.AccAddress) ([]*peggytypes.Valset, error) + LatestValsetsFn func(ctx context.Context) ([]*peggytypes.Valset, error) + AllValsetConfirmsFn func(ctx context.Context, uint642 uint64) ([]*peggytypes.MsgValsetConfirm, error) + OldestUnsignedTransactionBatchFn func(ctx context.Context, address sdktypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) + LatestTransactionBatchesFn func(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) + UnbatchedTokensWithFeesFn func(ctx context.Context) ([]*peggytypes.BatchFees, error) + TransactionBatchSignaturesFn func(ctx context.Context, uint642 uint64, address gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) + UpdatePeggyOrchestratorAddressesFn func(ctx context.Context, address gethcommon.Address, address2 sdktypes.Address) error + SendValsetConfirmFn func(ctx context.Context, address gethcommon.Address, hash gethcommon.Hash, valset *peggytypes.Valset) error + SendBatchConfirmFn func(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch) error + SendRequestBatchFn func(ctx context.Context, denom string) error + SendToEthFn func(ctx context.Context, destination gethcommon.Address, amount, fee sdktypes.Coin) error + SendOldDepositClaimFn func(ctx context.Context, deposit *peggyevents.PeggySendToCosmosEvent) error + SendDepositClaimFn func(ctx context.Context, deposit *peggyevents.PeggySendToInjectiveEvent) error + SendWithdrawalClaimFn func(ctx context.Context, withdrawal *peggyevents.PeggyTransactionBatchExecutedEvent) error + SendValsetClaimFn func(ctx context.Context, vs *peggyevents.PeggyValsetUpdatedEvent) error + SendERC20DeployedClaimFn func(ctx context.Context, erc20 *peggyevents.PeggyERC20DeployedEvent) error + GetBlockFn func(ctx context.Context, height int64) (*comettypes.ResultBlock, error) + GetLatestBlockHeightFn func(ctx context.Context) (int64, error) +} + +func (n MockCosmosNetwork) PeggyParams(ctx context.Context) (*peggytypes.Params, error) { + return n.PeggyParamsFn(ctx) +} + +func (n MockCosmosNetwork) LastClaimEventByAddr(ctx context.Context, validatorAccountAddress sdktypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return n.LastClaimEventByAddrFn(ctx, validatorAccountAddress) +} + +func (n MockCosmosNetwork) GetValidatorAddress(ctx context.Context, addr gethcommon.Address) (sdktypes.AccAddress, error) { + return n.GetValidatorAddressFn(ctx, addr) +} + +func (n MockCosmosNetwork) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) { + return n.ValsetAtFn(ctx, nonce) +} + +func (n MockCosmosNetwork) CurrentValset(ctx context.Context) (*peggytypes.Valset, error) { + return n.CurrentValsetFn(ctx) +} + +func (n MockCosmosNetwork) OldestUnsignedValsets(ctx context.Context, valAccountAddress sdktypes.AccAddress) ([]*peggytypes.Valset, error) { + return n.OldestUnsignedValsetsFn(ctx, valAccountAddress) +} + +func (n MockCosmosNetwork) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) { + return n.LatestValsetsFn(ctx) +} + +func (n MockCosmosNetwork) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return n.AllValsetConfirmsFn(ctx, nonce) +} + +func (n MockCosmosNetwork) OldestUnsignedTransactionBatch(ctx context.Context, valAccountAddress sdktypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { + return n.OldestUnsignedTransactionBatchFn(ctx, valAccountAddress) +} + +func (n MockCosmosNetwork) LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return n.LatestTransactionBatchesFn(ctx) +} + +func (n MockCosmosNetwork) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { + return n.UnbatchedTokensWithFeesFn(ctx) +} + +func (n MockCosmosNetwork) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return n.TransactionBatchSignaturesFn(ctx, nonce, tokenContract) +} + +func (n MockCosmosNetwork) UpdatePeggyOrchestratorAddresses(ctx context.Context, ethFrom gethcommon.Address, orchAddr sdktypes.AccAddress) error { + return n.UpdatePeggyOrchestratorAddressesFn(ctx, ethFrom, orchAddr) +} + +func (n MockCosmosNetwork) SendValsetConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, valset *peggytypes.Valset) error { + return n.SendValsetConfirmFn(ctx, ethFrom, peggyID, valset) +} + +func (n MockCosmosNetwork) SendBatchConfirm(ctx context.Context, ethFrom gethcommon.Address, peggyID gethcommon.Hash, batch *peggytypes.OutgoingTxBatch) error { + return n.SendBatchConfirmFn(ctx, ethFrom, peggyID, batch) +} + +func (n MockCosmosNetwork) SendRequestBatch(ctx context.Context, denom string) error { + return n.SendRequestBatchFn(ctx, denom) +} + +func (n MockCosmosNetwork) SendToEth(ctx context.Context, destination gethcommon.Address, amount, fee sdktypes.Coin) error { + return n.SendToEthFn(ctx, destination, amount, fee) +} + +func (n MockCosmosNetwork) SendOldDepositClaim(ctx context.Context, deposit *peggyevents.PeggySendToCosmosEvent) error { + return n.SendOldDepositClaimFn(ctx, deposit) +} + +func (n MockCosmosNetwork) SendDepositClaim(ctx context.Context, deposit *peggyevents.PeggySendToInjectiveEvent) error { + return n.SendDepositClaimFn(ctx, deposit) +} + +func (n MockCosmosNetwork) SendWithdrawalClaim(ctx context.Context, withdrawal *peggyevents.PeggyTransactionBatchExecutedEvent) error { + return n.SendWithdrawalClaimFn(ctx, withdrawal) +} + +func (n MockCosmosNetwork) SendValsetClaim(ctx context.Context, vs *peggyevents.PeggyValsetUpdatedEvent) error { + return n.SendValsetClaimFn(ctx, vs) +} + +func (n MockCosmosNetwork) SendERC20DeployedClaim(ctx context.Context, erc20 *peggyevents.PeggyERC20DeployedEvent) error { + return n.SendERC20DeployedClaimFn(ctx, erc20) +} + +func (n MockCosmosNetwork) GetBlock(ctx context.Context, height int64) (*comettypes.ResultBlock, error) { + return n.GetBlockFn(ctx, height) +} + +func (n MockCosmosNetwork) GetLatestBlockHeight(ctx context.Context) (int64, error) { + //TODO implement me + panic("implement me") +} + +func (n MockCosmosNetwork) GetTxs(ctx context.Context, block *comettypes.ResultBlock) ([]*comettypes.ResultTx, error) { + //TODO implement me + panic("implement me") +} + +func (n MockCosmosNetwork) GetValidatorSet(ctx context.Context, height int64) (*comettypes.ResultValidators, error) { + //TODO implement me + panic("implement me") +} + +type MockEthereumNetwork struct { + GetHeaderByNumberFn func(ctx context.Context, number *big.Int) (*gethtypes.Header, error) + GetPeggyIDFn func(ctx context.Context) (gethcommon.Hash, error) + GetSendToCosmosEventsFn func(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) + GetSendToInjectiveEventsFn func(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) + GetPeggyERC20DeployedEventsFn func(startBlock, endBlock uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) + GetValsetUpdatedEventsFn func(startBlock, endBlock uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) + GetTransactionBatchExecutedEventsFn func(startBlock, endBlock uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) + GetValsetNonceFn func(ctx context.Context) (*big.Int, error) + SendEthValsetUpdateFn func(ctx context.Context, oldValset *peggytypes.Valset, newValset *peggytypes.Valset, confirms []*peggytypes.MsgValsetConfirm) (*gethcommon.Hash, error) + GetTxBatchNonceFn func(ctx context.Context, erc20ContractAddress gethcommon.Address) (*big.Int, error) + SendTransactionBatchFn func(ctx context.Context, currentValset *peggytypes.Valset, batch *peggytypes.OutgoingTxBatch, confirms []*peggytypes.MsgConfirmBatch) (*gethcommon.Hash, error) +} + +func (n MockEthereumNetwork) GetHeaderByNumber(ctx context.Context, number *big.Int) (*gethtypes.Header, error) { + return n.GetHeaderByNumberFn(ctx, number) +} + +func (n MockEthereumNetwork) GetPeggyID(ctx context.Context) (gethcommon.Hash, error) { + return n.GetPeggyIDFn(ctx) +} + +func (n MockEthereumNetwork) GetSendToCosmosEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return n.GetSendToCosmosEventsFn(startBlock, endBlock) +} + +func (n MockEthereumNetwork) GetSendToInjectiveEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return n.GetSendToInjectiveEventsFn(startBlock, endBlock) +} + +func (n MockEthereumNetwork) GetPeggyERC20DeployedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return n.GetPeggyERC20DeployedEventsFn(startBlock, endBlock) +} + +func (n MockEthereumNetwork) GetValsetUpdatedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return n.GetValsetUpdatedEventsFn(startBlock, endBlock) } -func (p mockPriceFeed) QueryUSDPrice(address eth.Address) (float64, error) { - return p.queryFn(address) +func (n MockEthereumNetwork) GetTransactionBatchExecutedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return n.GetTransactionBatchExecutedEventsFn(startBlock, endBlock) } -type mockInjective struct { - unbatchedTokenFeesFn func(context.Context) ([]*peggytypes.BatchFees, error) - unbatchedTokenFeesCallCount int +func (n MockEthereumNetwork) GetValsetNonce(ctx context.Context) (*big.Int, error) { + return n.GetValsetNonceFn(ctx) +} - sendRequestBatchFn func(context.Context, string) error - sendRequestBatchCallCount int +func (n MockEthereumNetwork) SendEthValsetUpdate(ctx context.Context, oldValset *peggytypes.Valset, newValset *peggytypes.Valset, confirms []*peggytypes.MsgValsetConfirm) (*gethcommon.Hash, error) { + return n.SendEthValsetUpdateFn(ctx, oldValset, newValset, confirms) +} + +func (n MockEthereumNetwork) GetTxBatchNonce(ctx context.Context, erc20ContractAddress gethcommon.Address) (*big.Int, error) { + return n.GetTxBatchNonceFn(ctx, erc20ContractAddress) +} - peggyParamsFn func(context.Context) (*peggytypes.Params, error) - lastClaimEventFn func(context.Context) (*peggytypes.LastClaimEvent, error) - sendEthereumClaimsFn func( - ctx context.Context, - lastClaimEvent uint64, - oldDeposits []*peggyevents.PeggySendToCosmosEvent, - deposits []*peggyevents.PeggySendToInjectiveEvent, - withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, - ) error - sendEthereumClaimsCallCount int +func (n MockEthereumNetwork) SendTransactionBatch(ctx context.Context, currentValset *peggytypes.Valset, batch *peggytypes.OutgoingTxBatch, confirms []*peggytypes.MsgConfirmBatch) (*gethcommon.Hash, error) { + return n.SendTransactionBatchFn(ctx, currentValset, batch, confirms) +} - oldestUnsignedValsetsFn func(context.Context) ([]*peggytypes.Valset, error) - sendValsetConfirmFn func(context.Context, eth.Hash, *peggytypes.Valset, eth.Address) error +var ( + DummyLog = DummyLogger{} +) - oldestUnsignedTransactionBatchFn func(context.Context) (*peggytypes.OutgoingTxBatch, error) - sendBatchConfirmFn func(context.Context, eth.Hash, *peggytypes.OutgoingTxBatch, eth.Address) error +type DummyLogger struct{} - latestValsetsFn func(context.Context) ([]*peggytypes.Valset, error) - getBlockCreationTimeFn func(context.Context, int64) (time.Time, error) +func (l DummyLogger) Success(format string, args ...interface{}) { +} - allValsetConfirmsFn func(context.Context, uint64) ([]*peggytypes.MsgValsetConfirm, error) - valsetAtFn func(context.Context, uint64) (*peggytypes.Valset, error) +func (l DummyLogger) Warning(format string, args ...interface{}) { +} - latestTransactionBatchesFn func(context.Context) ([]*peggytypes.OutgoingTxBatch, error) - transactionBatchSignaturesFn func(context.Context, uint64, eth.Address) ([]*peggytypes.MsgConfirmBatch, error) +func (l DummyLogger) Error(format string, args ...interface{}) { +} - getValidatorAddressFn func(ctx context.Context, address eth.Address) (sdk.AccAddress, error) +func (l DummyLogger) Debug(format string, args ...interface{}) { } -func (i *mockInjective) GetValidatorAddress(ctx context.Context, addr eth.Address) (sdk.AccAddress, error) { - return i.getValidatorAddressFn(ctx, addr) +func (l DummyLogger) WithField(key string, value interface{}) log.Logger { + return l } -func (i *mockInjective) UnbatchedTokensWithFees(ctx context.Context) ([]*peggytypes.BatchFees, error) { - i.unbatchedTokenFeesCallCount++ - return i.unbatchedTokenFeesFn(ctx) +func (l DummyLogger) WithFields(fields log.Fields) log.Logger { + return l } -func (i *mockInjective) SendRequestBatch(ctx context.Context, denom string) error { - i.sendRequestBatchCallCount++ - return i.sendRequestBatchFn(ctx, denom) +func (l DummyLogger) WithError(err error) log.Logger { + return l } -func (i *mockInjective) PeggyParams(ctx context.Context) (*peggytypes.Params, error) { - return i.peggyParamsFn(ctx) +func (l DummyLogger) WithContext(ctx context.Context) log.Logger { + return l } -func (i *mockInjective) LastClaimEvent(ctx context.Context) (*peggytypes.LastClaimEvent, error) { - return i.lastClaimEventFn(ctx) +func (l DummyLogger) WithTime(t time.Time) log.Logger { + return l } -func (i *mockInjective) SendEthereumClaims( - ctx context.Context, - lastClaimEvent uint64, - oldDeposits []*peggyevents.PeggySendToCosmosEvent, - deposits []*peggyevents.PeggySendToInjectiveEvent, - withdraws []*peggyevents.PeggyTransactionBatchExecutedEvent, - erc20Deployed []*peggyevents.PeggyERC20DeployedEvent, - valsetUpdates []*peggyevents.PeggyValsetUpdatedEvent, -) error { - i.sendEthereumClaimsCallCount++ - return i.sendEthereumClaimsFn( - ctx, - lastClaimEvent, - oldDeposits, - deposits, - withdraws, - erc20Deployed, - valsetUpdates, - ) +func (l DummyLogger) Logf(level log.Level, format string, args ...interface{}) { } -func (i *mockInjective) OldestUnsignedValsets(ctx context.Context) ([]*peggytypes.Valset, error) { - return i.oldestUnsignedValsetsFn(ctx) +func (l DummyLogger) Tracef(format string, args ...interface{}) { } -func (i *mockInjective) SendValsetConfirm(ctx context.Context, ethFrom eth.Address, peggyID eth.Hash, valset *peggytypes.Valset) error { - return i.sendValsetConfirmFn(ctx, peggyID, valset, ethFrom) +func (l DummyLogger) Debugf(format string, args ...interface{}) { } -func (i *mockInjective) OldestUnsignedTransactionBatch(ctx context.Context) (*peggytypes.OutgoingTxBatch, error) { - return i.oldestUnsignedTransactionBatchFn(ctx) +func (l DummyLogger) Infof(format string, args ...interface{}) { } -func (i *mockInjective) LatestValsets(ctx context.Context) ([]*peggytypes.Valset, error) { - return i.latestValsetsFn(ctx) +func (l DummyLogger) Printf(format string, args ...interface{}) { } -func (i *mockInjective) AllValsetConfirms(ctx context.Context, nonce uint64) ([]*peggytypes.MsgValsetConfirm, error) { - return i.allValsetConfirmsFn(ctx, nonce) +func (l DummyLogger) Warningf(format string, args ...interface{}) { } -func (i *mockInjective) SendBatchConfirm(ctx context.Context, ethFrom eth.Address, peggyID eth.Hash, batch *peggytypes.OutgoingTxBatch) error { - return i.sendBatchConfirmFn(ctx, peggyID, batch, ethFrom) +func (l DummyLogger) Errorf(format string, args ...interface{}) { } -func (i *mockInjective) ValsetAt(ctx context.Context, nonce uint64) (*peggytypes.Valset, error) { - return i.valsetAtFn(ctx, nonce) +func (l DummyLogger) Fatalf(format string, args ...interface{}) { } -func (i *mockInjective) LatestTransactionBatches(ctx context.Context) ([]*peggytypes.OutgoingTxBatch, error) { - return i.latestTransactionBatchesFn(ctx) +func (l DummyLogger) Panicf(format string, args ...interface{}) { } -func (i *mockInjective) TransactionBatchSignatures(ctx context.Context, nonce uint64, tokenContract eth.Address) ([]*peggytypes.MsgConfirmBatch, error) { - return i.transactionBatchSignaturesFn(ctx, nonce, tokenContract) +func (l DummyLogger) Log(level log.Level, args ...interface{}) { } -func (i *mockInjective) GetBlockCreationTime(ctx context.Context, height int64) (time.Time, error) { - return i.getBlockCreationTimeFn(ctx, height) +func (l DummyLogger) Trace(args ...interface{}) { } -type mockEthereum struct { - fromAddressFn func() eth.Address - headerByNumberFn func(context.Context, *big.Int) (*ethtypes.Header, error) - getSendToCosmosEventsFn func(uint64, uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) - getSendToInjectiveEventsFn func(uint64, uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) - getPeggyERC20DeployedEventsFn func(uint64, uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) - getValsetUpdatedEventsFn func(uint64, uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) - getTransactionBatchExecutedEventsFn func(uint64, uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) - getPeggyIDFn func(context.Context) (eth.Hash, error) - getValsetNonceFn func(context.Context) (*big.Int, error) - sendEthValsetUpdateFn func(context.Context, *peggytypes.Valset, *peggytypes.Valset, []*peggytypes.MsgValsetConfirm) (*eth.Hash, error) - getTxBatchNonceFn func(context.Context, eth.Address) (*big.Int, error) - sendTransactionBatchFn func(context.Context, *peggytypes.Valset, *peggytypes.OutgoingTxBatch, []*peggytypes.MsgConfirmBatch) (*eth.Hash, error) +func (l DummyLogger) Info(args ...interface{}) { } -func (e mockEthereum) FromAddress() eth.Address { - return e.fromAddressFn() +func (l DummyLogger) Print(args ...interface{}) { } -func (e mockEthereum) GetHeaderByNumber(ctx context.Context, number *big.Int) (*ethtypes.Header, error) { - return e.headerByNumberFn(ctx, number) +func (l DummyLogger) Fatal(args ...interface{}) { } -func (e mockEthereum) GetSendToCosmosEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return e.getSendToCosmosEventsFn(startBlock, endBlock) +func (l DummyLogger) Panic(args ...interface{}) { } -func (e mockEthereum) GetSendToInjectiveEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { - return e.getSendToInjectiveEventsFn(startBlock, endBlock) +func (l DummyLogger) Logln(level log.Level, args ...interface{}) { } -func (e mockEthereum) GetPeggyERC20DeployedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { - return e.getPeggyERC20DeployedEventsFn(startBlock, endBlock) +func (l DummyLogger) Traceln(args ...interface{}) { } -func (e mockEthereum) GetValsetUpdatedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { - return e.getValsetUpdatedEventsFn(startBlock, endBlock) +func (l DummyLogger) Debugln(args ...interface{}) { } -func (e mockEthereum) GetTransactionBatchExecutedEvents(startBlock, endBlock uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { - return e.getTransactionBatchExecutedEventsFn(startBlock, endBlock) +func (l DummyLogger) Infoln(args ...interface{}) { } -func (e mockEthereum) GetPeggyID(ctx context.Context) (eth.Hash, error) { - return e.getPeggyIDFn(ctx) +func (l DummyLogger) Println(args ...interface{}) { } -func (e mockEthereum) GetValsetNonce(ctx context.Context) (*big.Int, error) { - return e.getValsetNonceFn(ctx) +func (l DummyLogger) Warningln(args ...interface{}) { } -func (e mockEthereum) SendEthValsetUpdate( - ctx context.Context, - oldValset *peggytypes.Valset, - newValset *peggytypes.Valset, - confirms []*peggytypes.MsgValsetConfirm, -) (*eth.Hash, error) { - return e.sendEthValsetUpdateFn(ctx, oldValset, newValset, confirms) +func (l DummyLogger) Errorln(args ...interface{}) { } -func (e mockEthereum) GetTxBatchNonce( - ctx context.Context, - erc20ContractAddress eth.Address, -) (*big.Int, error) { - return e.getTxBatchNonceFn(ctx, erc20ContractAddress) +func (l DummyLogger) Fatalln(args ...interface{}) { } -func (e mockEthereum) SendTransactionBatch( - ctx context.Context, - currentValset *peggytypes.Valset, - batch *peggytypes.OutgoingTxBatch, - confirms []*peggytypes.MsgConfirmBatch, -) (*eth.Hash, error) { - return e.sendTransactionBatchFn(ctx, currentValset, batch, confirms) +func (l DummyLogger) Panicln(args ...interface{}) { } diff --git a/orchestrator/oracle_test.go b/orchestrator/oracle_test.go deleted file mode 100644 index e077077b..00000000 --- a/orchestrator/oracle_test.go +++ /dev/null @@ -1,293 +0,0 @@ -package orchestrator - -import ( - "context" - "errors" - "math/big" - "testing" - "time" - - "github.com/ethereum/go-ethereum/core/types" - "github.com/stretchr/testify/assert" - "github.com/xlab/suplog" - - peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" - - peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" -) - -func TestEthOracle(t *testing.T) { - t.Parallel() - - t.Run("failed to get latest header from ethereum", func(t *testing.T) { - t.Parallel() - - orch := &Orchestrator{ - logger: suplog.DefaultLogger, - eth: mockEthereum{ - headerByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return nil, errors.New("fail") - }, - }, - } - - assert.Error(t, orch.runEthOracle(context.TODO())) - }) - - t.Run("latest ethereum header is old", func(t *testing.T) { - t.Parallel() - - ethereum := mockEthereum{ - headerByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(50)}, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - eth: ethereum, - maxAttempts: 1, - } - - loop := ethOracle{ - Orchestrator: o, - LastResyncWithInjective: time.Now(), - LastObservedEthHeight: 100, - } - - assert.NoError(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.LastObservedEthHeight, uint64(100)) - }) - - t.Run("failed to get SendToCosmos events", func(t *testing.T) { - t.Parallel() - - ethereum := mockEthereum{ - headerByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(200)}, nil - }, - getSendToCosmosEventsFn: func(uint64, uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - eth: ethereum, - maxAttempts: 1, - } - - loop := ethOracle{ - Orchestrator: o, - LastResyncWithInjective: time.Now(), - LastObservedEthHeight: 100, - } - - assert.Error(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.LastObservedEthHeight, uint64(100)) - }) - - t.Run("failed to get last claim event from injective", func(t *testing.T) { - t.Parallel() - - ethereum := mockEthereum{ - headerByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(200)}, nil - }, - - // no-ops - getSendToCosmosEventsFn: func(uint64, uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return nil, nil - }, - getTransactionBatchExecutedEventsFn: func(uint64, uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { - return nil, nil - }, - getValsetUpdatedEventsFn: func(uint64, uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { - return nil, nil - }, - getPeggyERC20DeployedEventsFn: func(uint64, uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { - return nil, nil - }, - getSendToInjectiveEventsFn: func(uint64, uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { - return nil, nil - }, - } - - injective := &mockInjective{ - lastClaimEventFn: func(context.Context) (*peggytypes.LastClaimEvent, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - eth: ethereum, - inj: injective, - maxAttempts: 1, - } - - loop := ethOracle{ - Orchestrator: o, - LastResyncWithInjective: time.Now(), - LastObservedEthHeight: 100, - } - - assert.Error(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.LastObservedEthHeight, uint64(100)) - }) - - t.Run("old events are pruned", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - lastClaimEventFn: func(context.Context) (*peggytypes.LastClaimEvent, error) { - return &peggytypes.LastClaimEvent{EthereumEventNonce: 6}, nil - }, - sendEthereumClaimsFn: func( - context.Context, - uint64, - []*peggyevents.PeggySendToCosmosEvent, - []*peggyevents.PeggySendToInjectiveEvent, - []*peggyevents.PeggyTransactionBatchExecutedEvent, - []*peggyevents.PeggyERC20DeployedEvent, - []*peggyevents.PeggyValsetUpdatedEvent, - ) error { - return nil - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(200)}, nil - }, - getSendToCosmosEventsFn: func(uint64, uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return []*peggyevents.PeggySendToCosmosEvent{{EventNonce: big.NewInt(5)}}, nil - }, - - // no-ops - getTransactionBatchExecutedEventsFn: func(uint64, uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { - return nil, nil - }, - getValsetUpdatedEventsFn: func(uint64, uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { - return nil, nil - }, - getPeggyERC20DeployedEventsFn: func(uint64, uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { - return nil, nil - }, - getSendToInjectiveEventsFn: func(uint64, uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { - return nil, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - eth: eth, - inj: inj, - maxAttempts: 1, - } - - loop := ethOracle{ - Orchestrator: o, - LastResyncWithInjective: time.Now(), - LastObservedEthHeight: 100, - } - - assert.NoError(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.LastObservedEthHeight, uint64(104)) - assert.Equal(t, inj.sendEthereumClaimsCallCount, 0) - }) - - t.Run("new events are sent to injective", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - lastClaimEventFn: func(context.Context) (*peggytypes.LastClaimEvent, error) { - return &peggytypes.LastClaimEvent{EthereumEventNonce: 6}, nil - }, - sendEthereumClaimsFn: func( - context.Context, - uint64, - []*peggyevents.PeggySendToCosmosEvent, - []*peggyevents.PeggySendToInjectiveEvent, - []*peggyevents.PeggyTransactionBatchExecutedEvent, - []*peggyevents.PeggyERC20DeployedEvent, - []*peggyevents.PeggyValsetUpdatedEvent, - ) error { - return nil - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(200)}, nil - }, - getSendToCosmosEventsFn: func(uint64, uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { - return []*peggyevents.PeggySendToCosmosEvent{{EventNonce: big.NewInt(10)}}, nil - }, - - // no-ops - getTransactionBatchExecutedEventsFn: func(uint64, uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { - return nil, nil - }, - getValsetUpdatedEventsFn: func(uint64, uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { - return nil, nil - }, - getPeggyERC20DeployedEventsFn: func(uint64, uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { - return nil, nil - }, - getSendToInjectiveEventsFn: func(uint64, uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { - return nil, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - eth: eth, - inj: inj, - maxAttempts: 1, - } - - loop := ethOracle{ - Orchestrator: o, - LastResyncWithInjective: time.Now(), - LastObservedEthHeight: 100, - } - - assert.NoError(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.LastObservedEthHeight, uint64(104)) - assert.Equal(t, inj.sendEthereumClaimsCallCount, 1) - }) - - t.Run("auto resync", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - lastClaimEventFn: func(_ context.Context) (*peggytypes.LastClaimEvent, error) { - return &peggytypes.LastClaimEvent{EthereumEventHeight: 101}, nil - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { - return &types.Header{Number: big.NewInt(50)}, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - eth: eth, - inj: inj, - maxAttempts: 1, - } - - loop := ethOracle{ - Orchestrator: o, - LastResyncWithInjective: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), - LastObservedEthHeight: 100, - } - - assert.NoError(t, loop.observeEthEvents(context.TODO())) - assert.Equal(t, loop.LastObservedEthHeight, uint64(101)) - assert.True(t, time.Since(loop.LastResyncWithInjective) < 1*time.Second) - }) -} diff --git a/orchestrator/peggy_orchestrator.go b/orchestrator/peggy_orchestrator.go index 109b525b..2562fa24 100644 --- a/orchestrator/peggy_orchestrator.go +++ b/orchestrator/peggy_orchestrator.go @@ -18,8 +18,11 @@ import ( ) const ( - defaultLoopDur = 60 * time.Second - maxRetryAttempts = 10 + defaultLoopDur = 60 * time.Second +) + +var ( + maxRetryAttempts uint = 10 ) // PriceFeed provides token price for a given contract address diff --git a/orchestrator/peggy_orchestrator_test.go b/orchestrator/peggy_orchestrator_test.go new file mode 100644 index 00000000..5320af6e --- /dev/null +++ b/orchestrator/peggy_orchestrator_test.go @@ -0,0 +1,1230 @@ +package orchestrator + +import ( + "context" + "errors" + "github.com/InjectiveLabs/metrics" + "github.com/InjectiveLabs/peggo/orchestrator/cosmos" + "github.com/InjectiveLabs/peggo/orchestrator/ethereum" + peggyevents "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" + peggytypes "github.com/InjectiveLabs/sdk-go/chain/peggy/types" + comettypes "github.com/cometbft/cometbft/rpc/core/types" + comet "github.com/cometbft/cometbft/types" + cosmtypes "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/assert" + "math/big" + "testing" + "time" +) + +func Test_Orchestrator_Loops(t *testing.T) { + t.Parallel() + + // faster test runs + maxRetryAttempts = 1 + + t.Run("batch requester", func(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + expected error + orch *Orchestrator + inj cosmos.Network + eth ethereum.Network + }{ + { + name: "failed to get token fees", + expected: nil, + orch: &Orchestrator{logger: DummyLog}, + inj: MockCosmosNetwork{ + UnbatchedTokensWithFeesFn: func(_ context.Context) ([]*peggytypes.BatchFees, error) { + return nil, errors.New("oops") + }, + }, + }, + + { + name: "no unbatched tokens", + expected: nil, + orch: &Orchestrator{logger: DummyLog}, + inj: MockCosmosNetwork{ + UnbatchedTokensWithFeesFn: func(context.Context) ([]*peggytypes.BatchFees, error) { + return nil, nil + }, + }, + }, + + { + name: "batch does not meet fee threshold", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + priceFeed: MockPriceFeed{QueryUSDPriceFn: func(_ gethcommon.Address) (float64, error) { return 1, nil }}, + minBatchFeeUSD: 51.0, + erc20ContractMapping: map[gethcommon.Address]string{ + gethcommon.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30"): "inj", + }, + }, + inj: MockCosmosNetwork{ + SendRequestBatchFn: func(context.Context, string) error { return nil }, + UnbatchedTokensWithFeesFn: func(context.Context) ([]*peggytypes.BatchFees, error) { + fees, _ := cosmtypes.NewIntFromString("50000000000000000000") + return []*peggytypes.BatchFees{ + { + Token: gethcommon.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30").String(), + TotalFees: fees, + }, + }, nil + }, + }, + }, + + { + name: "batch meets threshold and a request is sent", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + priceFeed: MockPriceFeed{QueryUSDPriceFn: func(_ gethcommon.Address) (float64, error) { return 1, nil }}, + minBatchFeeUSD: 49.0, + erc20ContractMapping: map[gethcommon.Address]string{ + gethcommon.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30"): "inj", + }, + }, + inj: MockCosmosNetwork{ + SendRequestBatchFn: func(context.Context, string) error { return nil }, + UnbatchedTokensWithFeesFn: func(_ context.Context) ([]*peggytypes.BatchFees, error) { + fees, _ := cosmtypes.NewIntFromString("50000000000000000000") + return []*peggytypes.BatchFees{{ + Token: gethcommon.HexToAddress("0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30").String(), + TotalFees: fees, + }}, nil + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + r := batchRequester{ + Orchestrator: tt.orch, + Injective: tt.inj, + } + + assert.ErrorIs(t, r.RequestBatches(context.Background()), tt.expected) + }) + } + }) + + t.Run("oracle", func(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + expected error + orch *Orchestrator + inj cosmos.Network + eth ethereum.Network + lastResyncWithInjective time.Time + lastObservedEthHeight uint64 + }{ + { + name: "failed to get current valset", + expected: errors.New("oops"), + orch: &Orchestrator{ + logger: DummyLog, + }, + inj: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return nil, errors.New("oops") + }, + }, + }, + + { + name: "orchestrator not bonded", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + ethAddr: gethcommon.HexToAddress("0x76D2dDbb89C36FA39FAa5c5e7C61ee95AC4D76C4"), + }, + inj: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", + }, + }, + }, nil + }, + }, + }, + + { + name: "failed to get latest eth height", + expected: errors.New("oops"), + orch: &Orchestrator{ + logger: DummyLog, + ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), + }, + inj: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", + }, + }, + }, nil + }, + }, + eth: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { + return nil, errors.New("fail") + }, + }, + }, + + { + name: "not enough block on ethereum", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), + }, + inj: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", + }, + }, + }, nil + }, + }, + eth: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { + return &types.Header{Number: big.NewInt(10)}, nil // minimum is 12 + }, + }, + }, + + { + name: "failed to get ethereum events", + expected: errors.New("oops"), + orch: &Orchestrator{ + logger: DummyLog, + ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), + }, + inj: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", + }, + }, + }, nil + }, + }, + eth: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { + return &types.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return nil, errors.New("oops") + }, + }, + lastObservedEthHeight: 100, + }, + + { + name: "failed to get last claim event", + expected: errors.New("oops"), + orch: &Orchestrator{ + logger: DummyLog, + ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), + }, + inj: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", + }, + }, + }, nil + }, + + LastClaimEventByAddrFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return nil, errors.New("oops") + }, + }, + eth: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { + return &types.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return []*peggyevents.PeggySendToCosmosEvent{ + { + EventNonce: big.NewInt(100), + }, + }, nil + }, + + GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return nil, nil + }, + GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return nil, nil + }, + GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return nil, nil + }, + GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return nil, nil + }, + }, + lastObservedEthHeight: 100, + }, + + { + name: "no new events", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), + }, + inj: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", + }, + }, + }, nil + }, + + LastClaimEventByAddrFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return &peggytypes.LastClaimEvent{ + EthereumEventNonce: 101, + }, nil + }, + }, + eth: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { + return &types.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return []*peggyevents.PeggySendToCosmosEvent{ + { + EventNonce: big.NewInt(100), + }, + }, nil + }, + + GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return nil, nil + }, + GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return nil, nil + }, + GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return nil, nil + }, + GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return nil, nil + }, + }, + lastObservedEthHeight: 100, + }, + + { + name: "missed events triggers resync", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), + }, + inj: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", + }, + }, + }, nil + }, + + LastClaimEventByAddrFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return &peggytypes.LastClaimEvent{ + EthereumEventNonce: 102, + EthereumEventHeight: 1000, + }, nil + }, + }, + eth: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { + return &types.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return []*peggyevents.PeggySendToCosmosEvent{ + { + EventNonce: big.NewInt(104), + }, + }, nil + }, + + GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return nil, nil + }, + GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return nil, nil + }, + GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return nil, nil + }, + GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return nil, nil + }, + }, + lastObservedEthHeight: 100, + }, + + { + name: "sent new event claim", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), + }, + inj: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", + }, + }, + }, nil + }, + + LastClaimEventByAddrFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return &peggytypes.LastClaimEvent{ + EthereumEventNonce: 102, + EthereumEventHeight: 1000, + }, nil + }, + + SendOldDepositClaimFn: func(_ context.Context, _ *peggyevents.PeggySendToCosmosEvent) error { + return nil + }, + }, + eth: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { + return &types.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return []*peggyevents.PeggySendToCosmosEvent{ + { + EventNonce: big.NewInt(103), + }, + }, nil + }, + + GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return nil, nil + }, + GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return nil, nil + }, + GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return nil, nil + }, + GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return nil, nil + }, + }, + lastObservedEthHeight: 100, + lastResyncWithInjective: time.Now(), // skip auto resync + }, + + { + name: "auto resync", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + ethAddr: gethcommon.HexToAddress("0x3959f5246c452463279F690301D923D5a75bbD88"), + }, + inj: MockCosmosNetwork{ + CurrentValsetFn: func(_ context.Context) (*peggytypes.Valset, error) { + return &peggytypes.Valset{ + Members: []*peggytypes.BridgeValidator{ + { + EthereumAddress: "0x3959f5246c452463279F690301D923D5a75bbD88", + }, + }, + }, nil + }, + + LastClaimEventByAddrFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.LastClaimEvent, error) { + return &peggytypes.LastClaimEvent{ + EthereumEventNonce: 102, + EthereumEventHeight: 1000, + }, nil + }, + + SendOldDepositClaimFn: func(_ context.Context, _ *peggyevents.PeggySendToCosmosEvent) error { + return nil + }, + }, + eth: MockEthereumNetwork{ + GetHeaderByNumberFn: func(context.Context, *big.Int) (*types.Header, error) { + return &types.Header{Number: big.NewInt(2100)}, nil + }, + GetSendToCosmosEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToCosmosEvent, error) { + return []*peggyevents.PeggySendToCosmosEvent{ + { + EventNonce: big.NewInt(103), + }, + }, nil + }, + + GetValsetUpdatedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyValsetUpdatedEvent, error) { + return nil, nil + }, + GetSendToInjectiveEventsFn: func(_, _ uint64) ([]*peggyevents.PeggySendToInjectiveEvent, error) { + return nil, nil + }, + GetTransactionBatchExecutedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyTransactionBatchExecutedEvent, error) { + return nil, nil + }, + GetPeggyERC20DeployedEventsFn: func(_, _ uint64) ([]*peggyevents.PeggyERC20DeployedEvent, error) { + return nil, nil + }, + }, + lastObservedEthHeight: 100, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + o := ethOracle{ + Orchestrator: tt.orch, + Injective: tt.inj, + Ethereum: tt.eth, + LastResyncWithInjective: tt.lastResyncWithInjective, + LastObservedEthHeight: tt.lastObservedEthHeight, + } + + err := o.ObserveEthEvents(context.Background()) + if tt.expected == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } + }) + + t.Run("relayer valset", func(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + expected error + orch *Orchestrator + inj cosmos.Network + eth ethereum.Network + }{ + { + name: "failed to get latest valset updates", + expected: errors.New("oops"), + orch: &Orchestrator{svcTags: metrics.Tags{"svc": "relayer"}}, + inj: MockCosmosNetwork{ + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return nil, errors.New("oops") + }, + }, + }, + + { + name: "failed to get valset confirmations", + expected: errors.New("oops"), + orch: &Orchestrator{svcTags: metrics.Tags{"svc": "relayer"}}, + inj: MockCosmosNetwork{ + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return nil, errors.New("oops") + }, + }, + }, + + { + name: "no new valset to relay", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + }, + inj: MockCosmosNetwork{ + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return nil, nil + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return nil, nil + }, + }, + }, + + { + name: "no new valset to relay", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + }, + eth: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return nil, errors.New("oops") + }, + }, + + inj: MockCosmosNetwork{ + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + + { + name: "valset already updated", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + }, + eth: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return big.NewInt(101), nil + }, + }, + + inj: MockCosmosNetwork{ + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + + { + name: "failed to get injective block", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + }, + eth: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return big.NewInt(99), nil + }, + }, + + inj: MockCosmosNetwork{ + GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { + return nil, errors.New("oops") + }, + + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + + { + name: "relay valset offser duration not expired", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + relayValsetOffsetDur: 10 * time.Second, + }, + eth: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return big.NewInt(99), nil + }, + }, + + inj: MockCosmosNetwork{ + GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { + return &comettypes.ResultBlock{ + Block: &comet.Block{ + Header: comet.Header{Time: time.Now()}, + }, + }, nil + }, + + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + + { + name: "failed to send valset update", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + relayValsetOffsetDur: 0, + }, + eth: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return big.NewInt(99), nil + }, + + SendEthValsetUpdateFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.Valset, _ []*peggytypes.MsgValsetConfirm) (*gethcommon.Hash, error) { + return nil, errors.New("oops") + }, + }, + + inj: MockCosmosNetwork{ + GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { + return &comettypes.ResultBlock{ + Block: &comet.Block{ + Header: comet.Header{Time: time.Now()}, + }, + }, nil + }, + + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + + { + name: "sent valset update", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + relayValsetOffsetDur: 0, + }, + eth: MockEthereumNetwork{ + GetValsetNonceFn: func(_ context.Context) (*big.Int, error) { + return big.NewInt(99), nil + }, + + SendEthValsetUpdateFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.Valset, _ []*peggytypes.MsgValsetConfirm) (*gethcommon.Hash, error) { + return &gethcommon.Hash{}, nil + }, + }, + + inj: MockCosmosNetwork{ + GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { + return &comettypes.ResultBlock{ + Block: &comet.Block{ + Header: comet.Header{Time: time.Now()}, + }, + }, nil + }, + + LatestValsetsFn: func(_ context.Context) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil // non-empty will do + }, + + AllValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*peggytypes.MsgValsetConfirm, error) { + return []*peggytypes.MsgValsetConfirm{{}}, nil // non-empty will do + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + r := relayer{ + Orchestrator: tt.orch, + Injective: tt.inj, + Ethereum: tt.eth, + } + + latestEthValset := &peggytypes.Valset{ + Nonce: 101, + } + + err := r.relayValset(context.Background(), latestEthValset) + if tt.expected == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } + }) + + t.Run("relayer batches", func(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + expected error + orch *Orchestrator + inj cosmos.Network + eth ethereum.Network + }{ + { + name: "failed to get latest batches", + expected: errors.New("oops"), + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + }, + inj: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return nil, errors.New("oops") + }, + }, + }, + + { + name: "failed to get batch confirmations", + expected: errors.New("oops"), + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + }, + inj: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{}}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return nil, errors.New("oops") + }, + }, + }, + + { + name: "no batch to relay", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + }, + inj: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return nil, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + }, + }, + + { + name: "failed to get latest batch nonce", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + }, + inj: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{}}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + }, + eth: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return nil, errors.New("oops") + }, + }, + }, + + { + name: "batch already updated", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + }, + inj: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchNonce: 100, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + }, + eth: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return big.NewInt(100), nil + }, + }, + }, + + { + name: "failed to get injective block", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + }, + inj: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchNonce: 101, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + + GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { + return nil, errors.New("oops") + }, + }, + eth: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return big.NewInt(100), nil + }, + }, + }, + + { + name: "batch relay offset not expired", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + relayBatchOffsetDur: 10 * time.Second, + }, + inj: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchNonce: 101, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + + GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { + return &comettypes.ResultBlock{ + Block: &comet.Block{ + Header: comet.Header{Time: time.Now()}, + }, + }, nil + }, + }, + eth: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return big.NewInt(100), nil + }, + }, + }, + + { + name: "failed to send batch update", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + relayBatchOffsetDur: 0, + }, + inj: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchNonce: 101, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + + GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { + return &comettypes.ResultBlock{ + Block: &comet.Block{ + Header: comet.Header{Time: time.Now()}, + }, + }, nil + }, + }, + eth: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return big.NewInt(100), nil + }, + + SendTransactionBatchFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.OutgoingTxBatch, _ []*peggytypes.MsgConfirmBatch) (*gethcommon.Hash, error) { + return nil, errors.New("oops") + }, + }, + }, + + { + name: "sent batch update", + expected: nil, + orch: &Orchestrator{ + logger: DummyLog, + svcTags: metrics.Tags{"svc": "relayer"}, + relayBatchOffsetDur: 0, + }, + inj: MockCosmosNetwork{ + LatestTransactionBatchesFn: func(_ context.Context) ([]*peggytypes.OutgoingTxBatch, error) { + return []*peggytypes.OutgoingTxBatch{{ + BatchNonce: 101, + }}, nil + }, + + TransactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ gethcommon.Address) ([]*peggytypes.MsgConfirmBatch, error) { + return []*peggytypes.MsgConfirmBatch{{}}, nil + }, + + GetBlockFn: func(_ context.Context, _ int64) (*comettypes.ResultBlock, error) { + return &comettypes.ResultBlock{ + Block: &comet.Block{ + Header: comet.Header{Time: time.Now()}, + }, + }, nil + }, + }, + eth: MockEthereumNetwork{ + GetTxBatchNonceFn: func(_ context.Context, _ gethcommon.Address) (*big.Int, error) { + return big.NewInt(100), nil + }, + + SendTransactionBatchFn: func(_ context.Context, _ *peggytypes.Valset, _ *peggytypes.OutgoingTxBatch, _ []*peggytypes.MsgConfirmBatch) (*gethcommon.Hash, error) { + return &gethcommon.Hash{}, nil + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + r := relayer{ + Orchestrator: tt.orch, + Injective: tt.inj, + Ethereum: tt.eth, + } + + latestEthValset := &peggytypes.Valset{ + Nonce: 101, + } + + err := r.relayBatch(context.Background(), latestEthValset) + if tt.expected == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } + }) + + t.Run("signer valsets", func(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + expected error + orch *Orchestrator + inj cosmos.Network + }{ + { + name: "failed to get unsigned valsets", + expected: nil, + orch: &Orchestrator{logger: DummyLog}, + inj: MockCosmosNetwork{ + OldestUnsignedValsetsFn: func(_ context.Context, _ cosmtypes.AccAddress) ([]*peggytypes.Valset, error) { + return nil, errors.New("oops") + }, + }, + }, + + { + name: "no valset updates to sign", + expected: nil, + orch: &Orchestrator{logger: DummyLog}, + inj: MockCosmosNetwork{ + OldestUnsignedValsetsFn: func(_ context.Context, _ cosmtypes.AccAddress) ([]*peggytypes.Valset, error) { + return nil, nil + }, + }, + }, + + { + name: "failed to send valset confirm", + expected: errors.New("oops"), + orch: &Orchestrator{logger: DummyLog}, + inj: MockCosmosNetwork{ + OldestUnsignedValsetsFn: func(_ context.Context, _ cosmtypes.AccAddress) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil + }, + + SendValsetConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.Valset) error { + return errors.New("oops") + }, + }, + }, + + { + name: "sent valset confirm", + expected: nil, + orch: &Orchestrator{logger: DummyLog}, + inj: MockCosmosNetwork{ + OldestUnsignedValsetsFn: func(_ context.Context, _ cosmtypes.AccAddress) ([]*peggytypes.Valset, error) { + return []*peggytypes.Valset{{}}, nil + }, + + SendValsetConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.Valset) error { + return nil + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + s := ethSigner{ + Orchestrator: tt.orch, + Injective: tt.inj, + } + + err := s.signNewValsetUpdates(context.Background()) + if tt.expected == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } + }) + + t.Run("signer batches", func(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + expected error + orch *Orchestrator + inj cosmos.Network + }{ + { + name: "failed to get unsigned batches/no batch to confirm", + expected: nil, + orch: &Orchestrator{logger: DummyLog}, + inj: MockCosmosNetwork{ + OldestUnsignedTransactionBatchFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { + return nil, errors.New("ooops") + }, + }, + }, + + { + name: "failed to send batch confirm", + expected: errors.New("oops"), + orch: &Orchestrator{logger: DummyLog}, + inj: MockCosmosNetwork{ + OldestUnsignedTransactionBatchFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { + return &peggytypes.OutgoingTxBatch{}, nil + }, + + SendBatchConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.OutgoingTxBatch) error { + return errors.New("oops") + }, + }, + }, + + { + name: "sent batch confirm", + expected: nil, + orch: &Orchestrator{logger: DummyLog}, + inj: MockCosmosNetwork{ + OldestUnsignedTransactionBatchFn: func(_ context.Context, _ cosmtypes.AccAddress) (*peggytypes.OutgoingTxBatch, error) { + return &peggytypes.OutgoingTxBatch{}, nil + }, + + SendBatchConfirmFn: func(_ context.Context, _ gethcommon.Address, _ gethcommon.Hash, _ *peggytypes.OutgoingTxBatch) error { + return nil + }, + }, + }, + } + + for _, tt := range testTable { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + s := ethSigner{ + Orchestrator: tt.orch, + Injective: tt.inj, + } + + err := s.signNewBatch(context.Background()) + if tt.expected == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } + }) +} diff --git a/orchestrator/relayer_test.go b/orchestrator/relayer_test.go deleted file mode 100644 index 1b90b401..00000000 --- a/orchestrator/relayer_test.go +++ /dev/null @@ -1,1292 +0,0 @@ -package orchestrator - -import ( - "context" - "math/big" - "testing" - "time" - - cosmtypes "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" - ctypes "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/xlab/suplog" - - wrappers "github.com/InjectiveLabs/peggo/solidity/wrappers/Peggy.sol" - - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" -) - -func TestValsetRelaying(t *testing.T) { - t.Parallel() - - t.Run("failed to fetch latest valsets from injective", func(t *testing.T) { - t.Parallel() - - injective := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: injective, - maxAttempts: 1, - valsetRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayValset(context.TODO())) - }) - - t.Run("failed to fetch confirms for a valset", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{{}}, nil // non-empty will do - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - maxAttempts: 1, - valsetRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayValset(context.TODO())) - }) - - t.Run("no confirms for valset", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{{}}, nil // non-empty will do - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return nil, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - maxAttempts: 1, - valsetRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.NoError(t, l.relayValset(context.TODO())) - }) - - t.Run("failed to get latest ethereum header", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{{}}, nil // non-empty will do - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return []*types.MsgValsetConfirm{ - { - Nonce: 5, - Orchestrator: "orch", - EthAddress: "eth", - Signature: "sig", - }, - }, nil - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - valsetRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayValset(context.TODO())) - }) - - t.Run("failed to get latest ethereum header", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{{}}, nil // non-empty will do - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return []*types.MsgValsetConfirm{ - { - Nonce: 5, - Orchestrator: "orch", - EthAddress: "eth", - Signature: "sig", - }, - }, nil - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - valsetRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayValset(context.TODO())) - }) - - t.Run("failed to get valset nonce from peggy contract", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{{}}, nil // non-empty will do - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return []*types.MsgValsetConfirm{ - { - Nonce: 5, - Orchestrator: "orch", - EthAddress: "eth", - Signature: "sig", - }, - }, nil - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(123)}, nil - }, - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - valsetRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayValset(context.TODO())) - }) - - t.Run("failed to get specific valset from injective", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{{}}, nil // non-empty will do - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return []*types.MsgValsetConfirm{ - { - Nonce: 5, - Orchestrator: "orch", - EthAddress: "eth", - Signature: "sig", - }, - }, nil - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return nil, errors.New("fail") - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(123)}, nil - }, - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - valsetRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayValset(context.TODO())) - }) - - t.Run("failed to get valset update events from ethereum", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{{}}, nil // non-empty will do - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return []*types.MsgValsetConfirm{ - { - Nonce: 5, - Orchestrator: "orch", - EthAddress: "eth", - Signature: "sig", - }, - }, nil - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{}, nil // non-empty will do - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(123)}, nil - }, - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - valsetRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayValset(context.TODO())) - }) - - t.Run("ethereum valset is not higher than injective valset", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{ - { - Nonce: 333, - RewardAmount: cosmtypes.NewInt(1000), - RewardToken: "0xfafafafafafafafa", - }, - }, nil - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return []*types.MsgValsetConfirm{ - { - Nonce: 5, - Orchestrator: "orch", - EthAddress: "eth", - Signature: "sig", - }, - }, nil - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{ - Nonce: 333, - RewardAmount: cosmtypes.NewInt(1000), - RewardToken: "0xfafafafafafafafa", - }, nil // non-empty will do - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(123)}, nil - }, - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return []*wrappers.PeggyValsetUpdatedEvent{ - { - NewValsetNonce: big.NewInt(333), - RewardAmount: big.NewInt(1000), - RewardToken: common.HexToAddress("0xfafafafafafafafa"), - }, - }, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - valsetRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.NoError(t, l.relayValset(context.TODO())) - }) - - t.Run("injective valset is higher than ethereum but failed to get block from injective", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{ - { - Nonce: 444, - RewardAmount: cosmtypes.NewInt(1000), - RewardToken: "0xfafafafafafafafa", - }, - }, nil - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return []*types.MsgValsetConfirm{ - { - Nonce: 5, - Orchestrator: "orch", - EthAddress: "eth", - Signature: "sig", - }, - }, nil - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{ - Nonce: 333, - RewardAmount: cosmtypes.NewInt(1000), - RewardToken: "0xfafafafafafafafa", - }, nil // non-empty will do - }, - getBlockCreationTimeFn: func(_ context.Context, _ int64) (time.Time, error) { - return time.Time{}, errors.New("fail") - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(123)}, nil - }, - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return []*wrappers.PeggyValsetUpdatedEvent{ - { - NewValsetNonce: big.NewInt(333), - RewardAmount: big.NewInt(1000), - RewardToken: common.HexToAddress("0xfafafafafafafafa"), - }, - }, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - valsetRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayValset(context.TODO())) - }) - - t.Run("injective valset is higher than ethereum but valsetOffsetDur has not expired", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{ - { - Nonce: 444, - RewardAmount: cosmtypes.NewInt(1000), - RewardToken: "0xfafafafafafafafa", - }, - }, nil - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return []*types.MsgValsetConfirm{ - { - Nonce: 5, - Orchestrator: "orch", - EthAddress: "eth", - Signature: "sig", - }, - }, nil - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{ - Nonce: 333, - RewardAmount: cosmtypes.NewInt(1000), - RewardToken: "0xfafafafafafafafa", - }, nil // non-empty will do - }, - getBlockCreationTimeFn: func(_ context.Context, _ int64) (time.Time, error) { - return time.Now().Add(time.Hour), nil - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(123)}, nil - }, - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return []*wrappers.PeggyValsetUpdatedEvent{ - { - NewValsetNonce: big.NewInt(333), - RewardAmount: big.NewInt(1000), - RewardToken: common.HexToAddress("0xfafafafafafafafa"), - }, - }, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - valsetRelayEnabled: true, - relayValsetOffsetDur: time.Second * 5, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.NoError(t, l.relayValset(context.TODO())) - }) - - t.Run("injective valset is higher than ethereum but failed to send update tx to ethereum", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{ - { - Nonce: 444, - RewardAmount: cosmtypes.NewInt(1000), - RewardToken: "0xfafafafafafafafa", - }, - }, nil - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return []*types.MsgValsetConfirm{ - { - Nonce: 5, - Orchestrator: "orch", - EthAddress: "eth", - Signature: "sig", - }, - }, nil - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{ - Nonce: 333, - RewardAmount: cosmtypes.NewInt(1000), - RewardToken: "0xfafafafafafafafa", - }, nil // non-empty will do - }, - getBlockCreationTimeFn: func(_ context.Context, _ int64) (time.Time, error) { - return time.Date(1970, 1, 0, 0, 0, 0, 0, time.UTC), nil - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(123)}, nil - }, - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return []*wrappers.PeggyValsetUpdatedEvent{ - { - NewValsetNonce: big.NewInt(333), - RewardAmount: big.NewInt(1000), - RewardToken: common.HexToAddress("0xfafafafafafafafa"), - }, - }, nil - }, - sendEthValsetUpdateFn: func(_ context.Context, _ *types.Valset, _ *types.Valset, _ []*types.MsgValsetConfirm) (*common.Hash, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - valsetRelayEnabled: true, - relayValsetOffsetDur: time.Second * 5, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayValset(context.TODO())) - }) - - t.Run("new valset update is sent to ethereum", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{ - { - Nonce: 444, - RewardAmount: cosmtypes.NewInt(1000), - RewardToken: "0xfafafafafafafafa", - }, - }, nil - }, - allValsetConfirmsFn: func(_ context.Context, _ uint64) ([]*types.MsgValsetConfirm, error) { - return []*types.MsgValsetConfirm{ - { - Nonce: 5, - Orchestrator: "orch", - EthAddress: "eth", - Signature: "sig", - }, - }, nil - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{ - Nonce: 333, - RewardAmount: cosmtypes.NewInt(1000), - RewardToken: "0xfafafafafafafafa", - }, nil - }, - getBlockCreationTimeFn: func(_ context.Context, _ int64) (time.Time, error) { - return time.Date(1970, 1, 0, 0, 0, 0, 0, time.UTC), nil - }, - } - - eth := mockEthereum{ - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(123)}, nil - }, - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return []*wrappers.PeggyValsetUpdatedEvent{ - { - NewValsetNonce: big.NewInt(333), - RewardAmount: big.NewInt(1000), - RewardToken: common.HexToAddress("0xfafafafafafafafa"), - }, - }, nil - }, - sendEthValsetUpdateFn: func(_ context.Context, _ *types.Valset, _ *types.Valset, _ []*types.MsgValsetConfirm) (*common.Hash, error) { - return &common.Hash{}, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - valsetRelayEnabled: true, - relayValsetOffsetDur: time.Second * 5, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.NoError(t, l.relayValset(context.TODO())) - }) -} - -func TestBatchRelaying(t *testing.T) { - t.Parallel() - - t.Run("failed to get latest batches from injective", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - maxAttempts: 1, - batchRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayBatch(context.TODO())) - }) - - t.Run("failed to get latest batches from injective", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{{}}, nil // non-empty will do - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - maxAttempts: 1, - batchRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayBatch(context.TODO())) - }) - - t.Run("no batch confirms", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{{}}, nil // non-empty will do - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return nil, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - maxAttempts: 1, - batchRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.NoError(t, l.relayBatch(context.TODO())) - }) - - t.Run("failed to get batch nonce from ethereum", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{{}}, nil // non-empty will do - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return []*types.MsgConfirmBatch{{}}, nil // non-nil will do - }, - } - - eth := mockEthereum{ - getTxBatchNonceFn: func(_ context.Context, _ common.Address) (*big.Int, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - batchRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayBatch(context.TODO())) - }) - - t.Run("failed to get latest ethereum header", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{ - { - TokenContract: "tokenContract", - BatchNonce: 100, - }, - }, nil - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return []*types.MsgConfirmBatch{{}}, nil // non-nil will do - }, - } - - eth := mockEthereum{ - getTxBatchNonceFn: func(_ context.Context, _ common.Address) (*big.Int, error) { - return big.NewInt(99), nil - }, - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - batchRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayBatch(context.TODO())) - }) - - t.Run("failed to get valset nonce from ethereum", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{ - { - TokenContract: "tokenContract", - BatchNonce: 100, - }, - }, nil - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return []*types.MsgConfirmBatch{{}}, nil // non-nil will do - }, - } - - eth := mockEthereum{ - getTxBatchNonceFn: func(_ context.Context, _ common.Address) (*big.Int, error) { - return big.NewInt(99), nil - }, - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(100)}, nil - }, - - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - batchRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayBatch(context.TODO())) - }) - - t.Run("failed to get specific valset from injective", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{ - { - TokenContract: "tokenContract", - BatchNonce: 100, - }, - }, nil - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return []*types.MsgConfirmBatch{{}}, nil // non-nil will do - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return nil, errors.New("fail") - }, - } - - eth := mockEthereum{ - getTxBatchNonceFn: func(_ context.Context, _ common.Address) (*big.Int, error) { - return big.NewInt(99), nil - }, - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(100)}, nil - }, - - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - batchRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayBatch(context.TODO())) - }) - - t.Run("failed to get valset updated events from ethereum", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{ - { - TokenContract: "tokenContract", - BatchNonce: 100, - }, - }, nil - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return []*types.MsgConfirmBatch{{}}, nil // non-nil will do - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{}, nil - }, - } - - eth := mockEthereum{ - getTxBatchNonceFn: func(_ context.Context, _ common.Address) (*big.Int, error) { - return big.NewInt(99), nil - }, - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(100)}, nil - }, - - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - batchRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayBatch(context.TODO())) - }) - - t.Run("ethereum batch is not lower than injective batch", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{ - { - TokenContract: "tokenContract", - BatchNonce: 202, - }, - }, nil - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return []*types.MsgConfirmBatch{{}}, nil // non-nil will do - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{Nonce: 202}, nil - }, - } - - eth := mockEthereum{ - getTxBatchNonceFn: func(_ context.Context, _ common.Address) (*big.Int, error) { - return big.NewInt(202), nil - }, - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(100)}, nil - }, - - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return []*wrappers.PeggyValsetUpdatedEvent{ - { - NewValsetNonce: big.NewInt(202), - RewardAmount: big.NewInt(1000), - RewardToken: common.HexToAddress("0xcafecafecafecafe"), - }, - }, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - batchRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.NoError(t, l.relayBatch(context.TODO())) - }) - - t.Run("ethereum batch is lower than injective batch but failed to get block from injhective", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{ - { - TokenContract: "tokenContract", - BatchNonce: 202, - }, - }, nil - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return []*types.MsgConfirmBatch{{}}, nil // non-nil will do - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{Nonce: 202}, nil - }, - getBlockCreationTimeFn: func(_ context.Context, _ int64) (time.Time, error) { - return time.Time{}, errors.New("fail") - }, - } - - eth := mockEthereum{ - getTxBatchNonceFn: func(_ context.Context, _ common.Address) (*big.Int, error) { - return big.NewInt(201), nil - }, - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(100)}, nil - }, - - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return []*wrappers.PeggyValsetUpdatedEvent{ - { - NewValsetNonce: big.NewInt(202), - RewardAmount: big.NewInt(1000), - RewardToken: common.HexToAddress("0xcafecafecafecafe"), - }, - }, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - batchRelayEnabled: true, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayBatch(context.TODO())) - }) - - t.Run("ethereum batch is lower than injective batch but relayBatchOffsetDur has not expired", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{ - { - TokenContract: "tokenContract", - BatchNonce: 202, - }, - }, nil - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return []*types.MsgConfirmBatch{{}}, nil // non-nil will do - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{Nonce: 202}, nil - }, - getBlockCreationTimeFn: func(_ context.Context, _ int64) (time.Time, error) { - return time.Now().Add(time.Hour), nil - }, - } - - eth := mockEthereum{ - getTxBatchNonceFn: func(_ context.Context, _ common.Address) (*big.Int, error) { - return big.NewInt(201), nil - }, - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(100)}, nil - }, - - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return []*wrappers.PeggyValsetUpdatedEvent{ - { - NewValsetNonce: big.NewInt(202), - RewardAmount: big.NewInt(1000), - RewardToken: common.HexToAddress("0xcafecafecafecafe"), - }, - }, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - batchRelayEnabled: true, - relayBatchOffsetDur: time.Second * 5, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.NoError(t, l.relayBatch(context.TODO())) - }) - - t.Run("ethereum batch is lower than injective batch but failed to send batch update", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{ - { - TokenContract: "tokenContract", - BatchNonce: 202, - }, - }, nil - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return []*types.MsgConfirmBatch{{}}, nil // non-nil will do - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{Nonce: 202}, nil - }, - getBlockCreationTimeFn: func(_ context.Context, _ int64) (time.Time, error) { - return time.Date(1970, 1, 0, 0, 0, 0, 0, time.UTC), nil - }, - } - - eth := mockEthereum{ - getTxBatchNonceFn: func(_ context.Context, _ common.Address) (*big.Int, error) { - return big.NewInt(201), nil - }, - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(100)}, nil - }, - - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return []*wrappers.PeggyValsetUpdatedEvent{ - { - NewValsetNonce: big.NewInt(202), - RewardAmount: big.NewInt(1000), - RewardToken: common.HexToAddress("0xcafecafecafecafe"), - }, - }, nil - }, - sendTransactionBatchFn: func(_ context.Context, _ *types.Valset, _ *types.OutgoingTxBatch, _ []*types.MsgConfirmBatch) (*common.Hash, error) { - return nil, errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - batchRelayEnabled: true, - relayBatchOffsetDur: time.Second * 5, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.Error(t, l.relayBatch(context.TODO())) - - }) - - t.Run("sending a batch update to ethereum", func(t *testing.T) { - t.Parallel() - - inj := &mockInjective{ - latestTransactionBatchesFn: func(_ context.Context) ([]*types.OutgoingTxBatch, error) { - return []*types.OutgoingTxBatch{ - { - TokenContract: "tokenContract", - BatchNonce: 202, - }, - }, nil - }, - transactionBatchSignaturesFn: func(_ context.Context, _ uint64, _ common.Address) ([]*types.MsgConfirmBatch, error) { - return []*types.MsgConfirmBatch{{}}, nil // non-nil will do - }, - valsetAtFn: func(_ context.Context, _ uint64) (*types.Valset, error) { - return &types.Valset{Nonce: 202}, nil - }, - getBlockCreationTimeFn: func(_ context.Context, _ int64) (time.Time, error) { - return time.Date(1970, 1, 0, 0, 0, 0, 0, time.UTC), nil - }, - } - - eth := mockEthereum{ - getTxBatchNonceFn: func(_ context.Context, _ common.Address) (*big.Int, error) { - return big.NewInt(201), nil - }, - headerByNumberFn: func(_ context.Context, _ *big.Int) (*ctypes.Header, error) { - return &ctypes.Header{Number: big.NewInt(100)}, nil - }, - - getValsetNonceFn: func(_ context.Context) (*big.Int, error) { - return big.NewInt(100), nil - }, - getValsetUpdatedEventsFn: func(_ uint64, _ uint64) ([]*wrappers.PeggyValsetUpdatedEvent, error) { - return []*wrappers.PeggyValsetUpdatedEvent{ - { - NewValsetNonce: big.NewInt(202), - RewardAmount: big.NewInt(1000), - RewardToken: common.HexToAddress("0xcafecafecafecafe"), - }, - }, nil - }, - sendTransactionBatchFn: func(_ context.Context, _ *types.Valset, _ *types.OutgoingTxBatch, _ []*types.MsgConfirmBatch) (*common.Hash, error) { - return &common.Hash{}, nil - }, - } - - o := &Orchestrator{ - logger: suplog.DefaultLogger, - inj: inj, - eth: eth, - maxAttempts: 1, - batchRelayEnabled: true, - relayBatchOffsetDur: time.Second * 5, - } - - l := relayer{ - Orchestrator: o, - LoopDuration: defaultRelayerLoopDur, - } - - assert.NoError(t, l.relayBatch(context.TODO())) - }) -} diff --git a/orchestrator/signer_test.go b/orchestrator/signer_test.go deleted file mode 100644 index a75fd72c..00000000 --- a/orchestrator/signer_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package orchestrator - -import ( - "context" - "testing" - - cosmtypes "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - log "github.com/xlab/suplog" - - "github.com/InjectiveLabs/sdk-go/chain/peggy/types" -) - -func TestEthSignerLoop(t *testing.T) { - t.Parallel() - - t.Run("failed to fetch peggy id from contract", func(t *testing.T) { - t.Parallel() - - orch := &Orchestrator{ - maxAttempts: 1, - eth: mockEthereum{ - getPeggyIDFn: func(context.Context) (common.Hash, error) { - return [32]byte{}, errors.New("fail") - }, - }, - } - - assert.Error(t, orch.runEthSigner(context.TODO())) - }) - - t.Run("no valset to sign", func(t *testing.T) { - t.Parallel() - - injective := &mockInjective{ - oldestUnsignedValsetsFn: func(context.Context) ([]*types.Valset, error) { - return nil, errors.New("fail") - }, - sendValsetConfirmFn: func(context.Context, common.Hash, *types.Valset, common.Address) error { - return nil - }, - oldestUnsignedTransactionBatchFn: func(context.Context) (*types.OutgoingTxBatch, error) { - return nil, nil - }, - sendBatchConfirmFn: func(context.Context, common.Hash, *types.OutgoingTxBatch, common.Address) error { - return nil - }, - } - - o := &Orchestrator{ - logger: log.DefaultLogger, - inj: injective, - maxAttempts: 1, - } - - l := ethSigner{ - Orchestrator: o, - LoopDuration: defaultLoopDur, - } - - assert.NoError(t, l.signNewValsetUpdates(context.TODO())) - }) - - t.Run("failed to send valset confirm", func(t *testing.T) { - t.Parallel() - - injective := &mockInjective{ - oldestUnsignedValsetsFn: func(context.Context) ([]*types.Valset, error) { - return []*types.Valset{ - { - Nonce: 5, - Members: []*types.BridgeValidator{ - { - Power: 100, - EthereumAddress: "abcd", - }, - }, - Height: 500, - RewardAmount: cosmtypes.NewInt(123), - RewardToken: "dusanToken", - }, - }, nil - }, - sendValsetConfirmFn: func(context.Context, common.Hash, *types.Valset, common.Address) error { - return errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: log.DefaultLogger, - inj: injective, - maxAttempts: 1, - } - - l := ethSigner{ - Orchestrator: o, - LoopDuration: defaultLoopDur, - } - - assert.Error(t, l.signNewValsetUpdates(context.TODO())) - }) - - t.Run("no transaction batch sign", func(t *testing.T) { - t.Parallel() - - injective := &mockInjective{ - oldestUnsignedValsetsFn: func(_ context.Context) ([]*types.Valset, error) { return nil, nil }, - sendValsetConfirmFn: func(context.Context, common.Hash, *types.Valset, common.Address) error { return nil }, - oldestUnsignedTransactionBatchFn: func(_ context.Context) (*types.OutgoingTxBatch, error) { return nil, errors.New("fail") }, - sendBatchConfirmFn: func(context.Context, common.Hash, *types.OutgoingTxBatch, common.Address) error { return nil }, - } - - o := &Orchestrator{ - logger: log.DefaultLogger, - inj: injective, - maxAttempts: 1, - } - - l := ethSigner{ - Orchestrator: o, - LoopDuration: defaultLoopDur, - } - - assert.NoError(t, l.signNewBatch(context.TODO())) - }) - - t.Run("failed to send batch confirm", func(t *testing.T) { - t.Parallel() - - injective := &mockInjective{ - oldestUnsignedValsetsFn: func(_ context.Context) ([]*types.Valset, error) { return nil, nil }, - sendValsetConfirmFn: func(context.Context, common.Hash, *types.Valset, common.Address) error { return nil }, - oldestUnsignedTransactionBatchFn: func(_ context.Context) (*types.OutgoingTxBatch, error) { - return &types.OutgoingTxBatch{}, nil // non-empty will do - }, - sendBatchConfirmFn: func(context.Context, common.Hash, *types.OutgoingTxBatch, common.Address) error { - return errors.New("fail") - }, - } - - o := &Orchestrator{ - logger: log.DefaultLogger, - inj: injective, - maxAttempts: 1, - } - - l := ethSigner{ - Orchestrator: o, - LoopDuration: defaultLoopDur, - } - - assert.Error(t, l.signNewBatch(context.TODO())) - }) - - t.Run("valset update and transaction batch are confirmed", func(t *testing.T) { - t.Parallel() - - injective := &mockInjective{ - oldestUnsignedValsetsFn: func(_ context.Context) ([]*types.Valset, error) { - return []*types.Valset{}, nil // non-empty will do - }, - oldestUnsignedTransactionBatchFn: func(_ context.Context) (*types.OutgoingTxBatch, error) { - return &types.OutgoingTxBatch{}, nil // non-empty will do - }, - sendValsetConfirmFn: func(context.Context, common.Hash, *types.Valset, common.Address) error { return nil }, - sendBatchConfirmFn: func(context.Context, common.Hash, *types.OutgoingTxBatch, common.Address) error { return nil }, - } - - o := &Orchestrator{ - logger: log.DefaultLogger, - inj: injective, - maxAttempts: 1, - } - - l := ethSigner{ - Orchestrator: o, - LoopDuration: defaultLoopDur, - } - - assert.NoError(t, l.signNewBatch(context.TODO())) - }) -} From 3609994e2ad303d394f6e9f72837949bb826ce71 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Tue, 14 May 2024 11:46:00 +0100 Subject: [PATCH 99/99] fix non existent token --- orchestrator/inj_relayer.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/orchestrator/inj_relayer.go b/orchestrator/inj_relayer.go index 0c94b199..9136b639 100644 --- a/orchestrator/inj_relayer.go +++ b/orchestrator/inj_relayer.go @@ -219,6 +219,12 @@ func (l *relayer) relayBatch(ctx context.Context, latestEthValset *peggytypes.Va ) for _, batch := range latestBatches { + // temporary + if _, err := l.Ethereum.TokenDecimals(ctx, gethcommon.HexToAddress(batch.TokenContract)); err != nil { + l.Logger().Warningln("skipping batch for non existent token contract", batch.TokenContract) + continue + } + if batch.BatchTimeout <= latestEthHeight.Number.Uint64() { l.Logger().WithFields(log.Fields{"batch_nonce": batch.BatchNonce, "batch_timeout_height": batch.BatchTimeout, "latest_eth_height": latestEthHeight.Number.Uint64()}).Debugln("skipping timed out batch") continue