From d884d6061fd1ef3545e7838dd8d58ba2bfda519b Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 24 Jun 2024 17:19:15 -0500 Subject: [PATCH 01/18] adjusted zetaclient initialization flow --- cmd/zetaclientd/keygen_tss.go | 22 +++-- cmd/zetaclientd/start.go | 89 +++++++++---------- zetaclient/chains/bitcoin/observer/inbound.go | 3 +- zetaclient/chains/evm/observer/inbound.go | 6 +- .../chains/evm/observer/inbound_test.go | 15 ++-- .../chains/evm/observer/outbound_test.go | 3 +- zetaclient/chains/interfaces/interfaces.go | 2 + zetaclient/compliance/compliance.go | 23 ++++- zetaclient/compliance/compliance_test.go | 10 +-- zetaclient/config/config.go | 21 ----- zetaclient/context/app_context.go | 12 +++ zetaclient/context/app_context_test.go | 62 +++++++++++++ zetaclient/context/zetacore_context_test.go | 31 +++++-- zetaclient/orchestrator/orchestrator.go | 2 + .../orchestrator}/utils.go | 41 +-------- zetaclient/testutils/mocks/zetacore_client.go | 4 + zetaclient/zetacore/client.go | 35 ++++++++ 17 files changed, 231 insertions(+), 150 deletions(-) rename {cmd/zetaclientd => zetaclient/orchestrator}/utils.go (79%) diff --git a/cmd/zetaclientd/keygen_tss.go b/cmd/zetaclientd/keygen_tss.go index 007ce9ec8c..d9c4f1be80 100644 --- a/cmd/zetaclientd/keygen_tss.go +++ b/cmd/zetaclientd/keygen_tss.go @@ -22,6 +22,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/zetacore" ) +// GenerateTss waits for the keygen block height to arrive and generates a new TSS func GenerateTss( appContext *context.AppContext, logger zerolog.Logger, @@ -31,18 +32,11 @@ func GenerateTss( ts *metrics.TelemetryServer, tssHistoricalList []observertypes.TSS, tssPassword string, - hotkeyPassword string) (*mc.TSS, error) { + hotkeyPassword string, +) (*mc.TSS, error) { keygenLogger := logger.With().Str("module", "keygen").Logger() - // Bitcoin chain ID is currently used for using the correct signature format - // TODO: remove this once we have a better way to determine the signature format - // https://github.com/zeta-chain/node/issues/1397 - bitcoinChainID := chains.BitcoinRegtest.ChainId - btcChain, _, btcEnabled := appContext.GetBTCChainAndConfig() - if btcEnabled { - bitcoinChainID = btcChain.ChainId - } - + bitcoinChainID := appContext.GetBTCChainID() tss, err := mc.NewTSS( appContext, peers, @@ -74,7 +68,11 @@ func GenerateTss( // This loop will try keygen at the keygen block and then wait for keygen to be successfully reported by all nodes before breaking out of the loop. // If keygen is unsuccessful, it will reset the triedKeygenAtBlock flag and try again at a new keygen block. - keyGen := appContext.ZetacoreContext().GetKeygen() + keyGen, err := client.GetKeyGen() + if err != nil { + keygenLogger.Error().Err(err).Msg("GetKeyGen error") + continue + } if keyGen.Status == observertypes.KeygenStatus_KeyGenSuccess { return tss, nil } @@ -107,7 +105,7 @@ func GenerateTss( } // Try keygen only once at a particular block, irrespective of whether it is successful or failure triedKeygenAtBlock = true - err = keygenTss(keyGen, tss, keygenLogger) + err = keygenTss(*keyGen, tss, keygenLogger) if err != nil { keygenLogger.Error().Err(err).Msg("keygenTss error") tssFailedVoteHash, err := client.SetTSS("", keyGen.BlockNumber, chains.ReceiveStatus_failed) diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index c815b616c0..ccd398ffaf 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -10,7 +10,6 @@ import ( "path/filepath" "strings" "syscall" - "time" "github.com/cometbft/cometbft/crypto/secp256k1" ethcommon "github.com/ethereum/go-ethereum/common" @@ -23,12 +22,14 @@ import ( "github.com/zeta-chain/zetacore/pkg/authz" "github.com/zeta-chain/zetacore/pkg/constant" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" + authzclient "github.com/zeta-chain/zetacore/zetaclient/authz" "github.com/zeta-chain/zetacore/zetaclient/chains/base" + "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/orchestrator" + "github.com/zeta-chain/zetacore/zetaclient/zetacore" ) type Multiaddr = core.Multiaddr @@ -57,18 +58,11 @@ func start(_ *cobra.Command, _ []string) error { return err } - //Load Config file given path + // Load Config file from given path cfg, err := config.Load(rootArgs.zetaCoreHome) if err != nil { return err } - logger, err := base.InitLogger(cfg) - if err != nil { - log.Error().Err(err).Msg("InitLogger failed") - return err - } - - //Wait until zetacore has started if len(cfg.Peer) != 0 { err := validatePeer(cfg.Peer) if err != nil { @@ -77,6 +71,15 @@ func start(_ *cobra.Command, _ []string) error { } } + // Load compliance config + compliance.LoadComplianceConfig(cfg) + + // Initialize base logger + logger, err := base.InitLogger(cfg) + if err != nil { + log.Error().Err(err).Msg("InitLogger failed") + return err + } masterLogger := logger.Std startLogger := masterLogger.With().Str("module", "startup").Logger() @@ -84,7 +87,9 @@ func start(_ *cobra.Command, _ []string) error { waitForZetaCore(cfg, startLogger) startLogger.Info().Msgf("Zetacore is ready, trying to connect to %s", cfg.Peer) + // Start telemetry server telemetryServer := metrics.NewTelemetryServer() + telemetryServer.SetIPAddress(cfg.PublicIP) go func() { err := telemetryServer.Start() if err != nil { @@ -93,11 +98,21 @@ func start(_ *cobra.Command, _ []string) error { } }() - // CreateZetacoreClient: zetacore client is used for all communication to zetacore , which this client connects to. - // Zetacore accumulates votes , and provides a centralized source of truth for all clients - zetacoreClient, err := CreateZetacoreClient(cfg, telemetryServer, hotkeyPass) + // Start metrics server + m, err := metrics.NewMetrics() if err != nil { - startLogger.Error().Err(err).Msg("CreateZetacoreClient error") + log.Error().Err(err).Msg("NewMetrics") + return err + } + m.Start() + metrics.Info.WithLabelValues(constant.Version).Set(1) + metrics.LastStartTime.SetToCurrentTime() + + // Create zetacore client to communicate with zetacore. + // Zetacore accumulates votes, and provides a centralized source of truth for all clients + zetacoreClient, err := zetacore.CreateClient(cfg, telemetryServer, hotkeyPass) + if err != nil { + startLogger.Error().Err(err).Msg("Create zetacore client error") return err } @@ -133,17 +148,18 @@ func start(_ *cobra.Command, _ []string) error { } } - // CreateAuthzSigner : which is used to sign all authz messages . All votes broadcast to zetacore are wrapped in authz exec . - // This is to ensure that the user does not need to keep their operator key online , and can use a cold key to sign votes - signerAddress, err := zetacoreClient.GetKeys().GetAddress() + // Set up authz signer to sign all authz messages. All votes broadcast to zetacore are wrapped in authz exec. + // This is to ensure that the user does not need to keep their operator key online, and can use a cold key to sign votes + granter := zetacoreClient.GetKeys().GetOperatorAddress().String() + grantee, err := zetacoreClient.GetKeys().GetAddress() if err != nil { startLogger.Error().Err(err).Msg("error getting signer address") return err } - CreateAuthzSigner(zetacoreClient.GetKeys().GetOperatorAddress().String(), signerAddress) - startLogger.Debug().Msgf("CreateAuthzSigner is ready") + authzclient.SetupAuthZSignerList(granter, grantee) + startLogger.Info().Msgf("Authz signer is ready") - // Initialize core parameters from zetacore + // Initialize app context and zetacore context appContext := context.NewAppContext(context.NewZetacoreContext(cfg), cfg) err = zetacoreClient.UpdateZetacoreContext(appContext.ZetacoreContext(), true, startLogger) if err != nil { @@ -152,8 +168,6 @@ func start(_ *cobra.Command, _ []string) error { } startLogger.Info().Msgf("Config is updated from zetacore %s", maskCfg(cfg)) - go zetacoreClient.ZetacoreContextUpdater(appContext) - // Generate TSS address . The Tss address is generated through Keygen ceremony. The TSS key is used to sign all outbound transactions . // The hotkeyPk is private key for the Hotkey. The Hotkey is used to sign all inbound transactions // Each node processes a portion of the key stored in ~/.tss by default . Custom location can be specified in config file during init. @@ -184,23 +198,12 @@ func start(_ *cobra.Command, _ []string) error { } } - m, err := metrics.NewMetrics() - if err != nil { - log.Error().Err(err).Msg("NewMetrics") - return err - } - m.Start() - - metrics.Info.WithLabelValues(constant.Version).Set(1) - metrics.LastStartTime.SetToCurrentTime() - - var tssHistoricalList []observerTypes.TSS - tssHistoricalList, err = zetacoreClient.GetTssHistory() + // Generate a new TSS key if there is a planned keygen ceremony + tssHistoricalList, err := zetacoreClient.GetTssHistory() if err != nil { startLogger.Error().Err(err).Msg("GetTssHistory error") } - telemetryServer.SetIPAddress(cfg.PublicIP) tss, err := GenerateTss( appContext, masterLogger, @@ -222,18 +225,6 @@ func start(_ *cobra.Command, _ []string) error { } } - // Wait for TSS keygen to be successful before proceeding, This is a blocking thread only for a new keygen. - // For existing keygen, this should directly proceed to the next step - ticker := time.NewTicker(time.Second * 1) - for range ticker.C { - keyGen := appContext.ZetacoreContext().GetKeygen() - if keyGen.Status != observerTypes.KeygenStatus_KeyGenSuccess { - startLogger.Info().Msgf("Waiting for TSS Keygen to be a success, current status %s", keyGen.Status) - continue - } - break - } - // Update Current TSS value from zetacore, if TSS keygen is successful, the TSS address is set on zeta-core // Returns err if the RPC call fails as zeta client needs the current TSS address to be set // This is only needed in case of a new Keygen , as the TSS address is set on zetacore only after the keygen is successful i.e enough votes have been broadcast @@ -268,7 +259,7 @@ func start(_ *cobra.Command, _ []string) error { } // CreateSignerMap: This creates a map of all signers for each chain . Each signer is responsible for signing transactions for a particular chain - signerMap, err := CreateSignerMap(appContext, tss, logger, telemetryServer) + signerMap, err := orchestrator.CreateSignerMap(appContext, tss, logger, telemetryServer) if err != nil { log.Error().Err(err).Msg("CreateSignerMap") return err @@ -282,7 +273,7 @@ func start(_ *cobra.Command, _ []string) error { dbpath := filepath.Join(userDir, ".zetaclient/chainobserver") // Creates a map of all chain observers for each chain. Each chain observer is responsible for observing events on the chain and processing them. - observerMap, err := CreateChainObserverMap(appContext, zetacoreClient, tss, dbpath, logger, telemetryServer) + observerMap, err := orchestrator.CreateChainObserverMap(appContext, zetacoreClient, tss, dbpath, logger, telemetryServer) if err != nil { startLogger.Err(err).Msg("CreateChainObserverMap") return err diff --git a/zetaclient/chains/bitcoin/observer/inbound.go b/zetaclient/chains/bitcoin/observer/inbound.go index 54c5294745..1f6b50c81d 100644 --- a/zetaclient/chains/bitcoin/observer/inbound.go +++ b/zetaclient/chains/bitcoin/observer/inbound.go @@ -19,7 +19,6 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/compliance" - "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/types" "github.com/zeta-chain/zetacore/zetaclient/zetacore" @@ -366,7 +365,7 @@ func (ob *Observer) DoesInboundContainsRestrictedAddress(inTx *BTCInboundEvent) if err == nil && parsedAddress != (ethcommon.Address{}) { receiver = parsedAddress.Hex() } - if config.ContainRestrictedAddress(inTx.FromAddress, receiver) { + if compliance.ContainRestrictedAddress(inTx.FromAddress, receiver) { compliance.PrintComplianceLog(ob.logger.Inbound, ob.logger.Compliance, false, ob.chain.ChainId, inTx.TxHash, inTx.FromAddress, receiver, "BTC") return true diff --git a/zetaclient/chains/evm/observer/inbound.go b/zetaclient/chains/evm/observer/inbound.go index 1e261c4d0a..9391c31431 100644 --- a/zetaclient/chains/evm/observer/inbound.go +++ b/zetaclient/chains/evm/observer/inbound.go @@ -587,7 +587,7 @@ func (ob *Observer) BuildInboundVoteMsgForDepositedEvent( if err == nil && parsedAddress != (ethcommon.Address{}) { maybeReceiver = parsedAddress.Hex() } - if config.ContainRestrictedAddress(sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), maybeReceiver) { + if compliance.ContainRestrictedAddress(sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), maybeReceiver) { compliance.PrintComplianceLog( ob.logger.Inbound, ob.logger.Compliance, @@ -643,7 +643,7 @@ func (ob *Observer) BuildInboundVoteMsgForZetaSentEvent( // compliance check sender := event.ZetaTxSenderAddress.Hex() - if config.ContainRestrictedAddress(sender, destAddr, event.SourceTxOriginAddress.Hex()) { + if compliance.ContainRestrictedAddress(sender, destAddr, event.SourceTxOriginAddress.Hex()) { compliance.PrintComplianceLog(ob.logger.Inbound, ob.logger.Compliance, false, ob.chain.ChainId, event.Raw.TxHash.Hex(), sender, destAddr, "Zeta") return nil @@ -699,7 +699,7 @@ func (ob *Observer) BuildInboundVoteMsgForTokenSentToTSS( if err == nil && parsedAddress != (ethcommon.Address{}) { maybeReceiver = parsedAddress.Hex() } - if config.ContainRestrictedAddress(sender.Hex(), maybeReceiver) { + if compliance.ContainRestrictedAddress(sender.Hex(), maybeReceiver) { compliance.PrintComplianceLog(ob.logger.Inbound, ob.logger.Compliance, false, ob.chain.ChainId, tx.Hash, sender.Hex(), sender.Hex(), "Gas") return nil diff --git a/zetaclient/chains/evm/observer/inbound_test.go b/zetaclient/chains/evm/observer/inbound_test.go index 906634e1ad..582a59ae4b 100644 --- a/zetaclient/chains/evm/observer/inbound_test.go +++ b/zetaclient/chains/evm/observer/inbound_test.go @@ -13,6 +13,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/pkg/constant" "github.com/zeta-chain/zetacore/zetaclient/chains/evm" + "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/testutils" @@ -266,21 +267,21 @@ func Test_BuildInboundVoteMsgForZetaSentEvent(t *testing.T) { t.Run("should return nil msg if sender is restricted", func(t *testing.T) { sender := event.ZetaTxSenderAddress.Hex() cfg.ComplianceConfig.RestrictedAddresses = []string{sender} - config.LoadComplianceConfig(cfg) + compliance.LoadComplianceConfig(cfg) msg := ob.BuildInboundVoteMsgForZetaSentEvent(event) require.Nil(t, msg) }) t.Run("should return nil msg if receiver is restricted", func(t *testing.T) { receiver := clienttypes.BytesToEthHex(event.DestinationAddress) cfg.ComplianceConfig.RestrictedAddresses = []string{receiver} - config.LoadComplianceConfig(cfg) + compliance.LoadComplianceConfig(cfg) msg := ob.BuildInboundVoteMsgForZetaSentEvent(event) require.Nil(t, msg) }) t.Run("should return nil msg if txOrigin is restricted", func(t *testing.T) { txOrigin := event.SourceTxOriginAddress.Hex() cfg.ComplianceConfig.RestrictedAddresses = []string{txOrigin} - config.LoadComplianceConfig(cfg) + compliance.LoadComplianceConfig(cfg) msg := ob.BuildInboundVoteMsgForZetaSentEvent(event) require.Nil(t, msg) }) @@ -313,14 +314,14 @@ func Test_BuildInboundVoteMsgForDepositedEvent(t *testing.T) { }) t.Run("should return nil msg if sender is restricted", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{sender.Hex()} - config.LoadComplianceConfig(cfg) + compliance.LoadComplianceConfig(cfg) msg := ob.BuildInboundVoteMsgForDepositedEvent(event, sender) require.Nil(t, msg) }) t.Run("should return nil msg if receiver is restricted", func(t *testing.T) { receiver := clienttypes.BytesToEthHex(event.Recipient) cfg.ComplianceConfig.RestrictedAddresses = []string{receiver} - config.LoadComplianceConfig(cfg) + compliance.LoadComplianceConfig(cfg) msg := ob.BuildInboundVoteMsgForDepositedEvent(event, sender) require.Nil(t, msg) }) @@ -370,7 +371,7 @@ func Test_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { }) t.Run("should return nil msg if sender is restricted", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{tx.From} - config.LoadComplianceConfig(cfg) + compliance.LoadComplianceConfig(cfg) msg := ob.BuildInboundVoteMsgForTokenSentToTSS( tx, ethcommon.HexToAddress(tx.From), @@ -384,7 +385,7 @@ func Test_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { message := hex.EncodeToString(ethcommon.HexToAddress(testutils.OtherAddress1).Bytes()) txCopy.Input = message // use other address as receiver cfg.ComplianceConfig.RestrictedAddresses = []string{testutils.OtherAddress1} - config.LoadComplianceConfig(cfg) + compliance.LoadComplianceConfig(cfg) msg := ob.BuildInboundVoteMsgForTokenSentToTSS( txCopy, ethcommon.HexToAddress(txCopy.From), diff --git a/zetaclient/chains/evm/observer/outbound_test.go b/zetaclient/chains/evm/observer/outbound_test.go index e0806d6086..ad6ae67f0c 100644 --- a/zetaclient/chains/evm/observer/outbound_test.go +++ b/zetaclient/chains/evm/observer/outbound_test.go @@ -14,6 +14,7 @@ import ( "github.com/zeta-chain/zetacore/testutil/sample" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" + "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/testutils" "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" @@ -82,7 +83,7 @@ func Test_IsOutboundProcessed(t *testing.T) { ComplianceConfig: config.ComplianceConfig{}, } cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.InboundParams.Sender} - config.LoadComplianceConfig(cfg) + compliance.LoadComplianceConfig(cfg) // post outbound vote isIncluded, isConfirmed, err := client.IsOutboundProcessed(cctx, zerolog.Logger{}) diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index 1ef94ec8de..46679cd159 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -23,6 +23,7 @@ import ( crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" + clientcontext "github.com/zeta-chain/zetacore/zetaclient/context" keyinterfaces "github.com/zeta-chain/zetacore/zetaclient/keys/interfaces" "github.com/zeta-chain/zetacore/zetaclient/outboundprocessor" ) @@ -64,6 +65,7 @@ type ChainSigner interface { // ZetacoreClient is the client interface to interact with zetacore type ZetacoreClient interface { + ZetacoreContextUpdater(appContext *clientcontext.AppContext) PostVoteInbound(gasLimit, retryGasLimit uint64, msg *crosschaintypes.MsgVoteInbound) (string, string, error) PostVoteOutbound( sendHash string, diff --git a/zetaclient/compliance/compliance.go b/zetaclient/compliance/compliance.go index e085b6954b..bb8bdbd005 100644 --- a/zetaclient/compliance/compliance.go +++ b/zetaclient/compliance/compliance.go @@ -1,18 +1,39 @@ package compliance import ( + "strings" + "github.com/rs/zerolog" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/config" ) +// restrictedAddressBook is a map of restricted addresses +var restrictedAddressBook = map[string]bool{} + +// LoadComplianceConfig loads compliance config from zetaclient config +func LoadComplianceConfig(cfg config.Config) { + restrictedAddressBook = cfg.GetRestrictedAddressBook() +} + +// ContainRestrictedAddress returns true if any one of the addresses is restricted +// Note: the addrs can contains both ETH and BTC addresses +func ContainRestrictedAddress(addrs ...string) bool { + for _, addr := range addrs { + if addr != "" && restrictedAddressBook[strings.ToLower(addr)] { + return true + } + } + return false +} + // IsCctxRestricted returns true if the cctx involves restricted addresses func IsCctxRestricted(cctx *crosschaintypes.CrossChainTx) bool { sender := cctx.InboundParams.Sender receiver := cctx.GetCurrentOutboundParam().Receiver - return config.ContainRestrictedAddress(sender, receiver) + return ContainRestrictedAddress(sender, receiver) } // PrintComplianceLog prints compliance log with fields [chain, cctx/inbound, chain, sender, receiver, token] diff --git a/zetaclient/compliance/compliance_test.go b/zetaclient/compliance/compliance_test.go index 0f587ceb20..7f31243b9a 100644 --- a/zetaclient/compliance/compliance_test.go +++ b/zetaclient/compliance/compliance_test.go @@ -23,29 +23,29 @@ func TestCctxRestricted(t *testing.T) { t.Run("should return true if sender is restricted", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.InboundParams.Sender} - config.LoadComplianceConfig(cfg) + LoadComplianceConfig(cfg) require.True(t, IsCctxRestricted(cctx)) }) t.Run("should return true if receiver is restricted", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.GetCurrentOutboundParam().Receiver} - config.LoadComplianceConfig(cfg) + LoadComplianceConfig(cfg) require.True(t, IsCctxRestricted(cctx)) }) t.Run("should return false if sender and receiver are not restricted", func(t *testing.T) { // restrict other address cfg.ComplianceConfig.RestrictedAddresses = []string{"0x27104b8dB4aEdDb054fCed87c346C0758Ff5dFB1"} - config.LoadComplianceConfig(cfg) + LoadComplianceConfig(cfg) require.False(t, IsCctxRestricted(cctx)) }) t.Run("should be able to restrict coinbase address", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{ethcommon.Address{}.String()} - config.LoadComplianceConfig(cfg) + LoadComplianceConfig(cfg) cctx.InboundParams.Sender = ethcommon.Address{}.String() require.True(t, IsCctxRestricted(cctx)) }) t.Run("should ignore empty address", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{""} - config.LoadComplianceConfig(cfg) + LoadComplianceConfig(cfg) cctx.InboundParams.Sender = "" require.False(t, IsCctxRestricted(cctx)) }) diff --git a/zetaclient/config/config.go b/zetaclient/config/config.go index 7dcf1b00f3..990f45b4fa 100644 --- a/zetaclient/config/config.go +++ b/zetaclient/config/config.go @@ -8,9 +8,6 @@ import ( "strings" ) -// restrictedAddressBook is a map of restricted addresses -var restrictedAddressBook = map[string]bool{} - const filename string = "zetaclient_config.json" const folder string = "config" @@ -72,16 +69,9 @@ func Load(path string) (Config, error) { cfg.PreParamsPath = GetPath(cfg.PreParamsPath) cfg.ZetaCoreHome = path - // load compliance config - LoadComplianceConfig(cfg) - return cfg, nil } -func LoadComplianceConfig(cfg Config) { - restrictedAddressBook = cfg.GetRestrictedAddressBook() -} - func GetPath(inputPath string) string { path := strings.Split(inputPath, "/") if len(path) > 0 { @@ -96,14 +86,3 @@ func GetPath(inputPath string) string { } return inputPath } - -// ContainRestrictedAddress returns true if any one of the addresses is restricted -// Note: the addrs can contains both ETH and BTC addresses -func ContainRestrictedAddress(addrs ...string) bool { - for _, addr := range addrs { - if addr != "" && restrictedAddressBook[strings.ToLower(addr)] { - return true - } - } - return false -} diff --git a/zetaclient/context/app_context.go b/zetaclient/context/app_context.go index d47cd925e8..b15cbe261d 100644 --- a/zetaclient/context/app_context.go +++ b/zetaclient/context/app_context.go @@ -41,3 +41,15 @@ func (a AppContext) GetBTCChainAndConfig() (chains.Chain, config.BTCConfig, bool return btcChain, btcConfig, true } + +// GetBTCChainID returns btc chain id if enabled or regnet chain id by default +// Bitcoin chain ID is currently needed by TSS to calculate the correct Bitcoin address +// TODO: we might have multiple BTC chains in the future: https://github.com/zeta-chain/node/issues/1397 +func (a AppContext) GetBTCChainID() int64 { + bitcoinChainID := chains.BitcoinRegtest.ChainId + btcChain, _, enabled := a.GetBTCChainAndConfig() + if enabled { + bitcoinChainID = btcChain.ChainId + } + return bitcoinChainID +} diff --git a/zetaclient/context/app_context_test.go b/zetaclient/context/app_context_test.go index 960b2b7c5a..e2f27738cb 100644 --- a/zetaclient/context/app_context_test.go +++ b/zetaclient/context/app_context_test.go @@ -1 +1,63 @@ package context_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "github.com/zeta-chain/zetacore/zetaclient/config" + context "github.com/zeta-chain/zetacore/zetaclient/context" +) + +func Test_GetBTCChainID(t *testing.T) { + // test chains and params + evmChain := chains.Ethereum + btcChain := chains.BitcoinMainnet + evmParams := &observertypes.ChainParams{ + ChainId: evmChain.ChainId, + } + btcParams := &observertypes.ChainParams{ + ChainId: btcChain.ChainId, + } + + t.Run("GetBTCChainID returns regnet chain id if btc chain is not enabled in config", func(t *testing.T) { + // config without btc chain + cfg := config.NewConfig() + + // create app context without BTC chain + coreContext := getTestCoreContext(evmChain, evmParams, nil, nil, nil) + appContext := context.NewAppContext(coreContext, cfg) + + btcChainID := appContext.GetBTCChainID() + require.Equal(t, chains.BitcoinRegtest.ChainId, btcChainID) + }) + t.Run("GetBTCChainID returns regnet chain id if btc chain params are not enabled", func(t *testing.T) { + // config with btc chain + cfg := config.NewConfig() + cfg.BitcoinConfig = config.BTCConfig{ + RPCUsername: "user", + } + + // create app context without BTC chain params + coreContext := getTestCoreContext(evmChain, evmParams, nil, nil, nil) + appContext := context.NewAppContext(coreContext, cfg) + + btcChainID := appContext.GetBTCChainID() + require.Equal(t, chains.BitcoinRegtest.ChainId, btcChainID) + }) + t.Run("GetBTCChainID returns btc chain id if enabled", func(t *testing.T) { + // config with btc chain + cfg := config.NewConfig() + cfg.BitcoinConfig = config.BTCConfig{ + RPCUsername: "user", + } + + // create app context with BTC chain + coreContext := getTestCoreContext(evmChain, evmParams, btcParams, nil, nil) + appContext := context.NewAppContext(coreContext, cfg) + + btcChainID := appContext.GetBTCChainID() + require.Equal(t, btcChain.ChainId, btcChainID) + }) +} diff --git a/zetaclient/context/zetacore_context_test.go b/zetaclient/context/zetacore_context_test.go index d8117e124b..f1b655ae36 100644 --- a/zetaclient/context/zetacore_context_test.go +++ b/zetaclient/context/zetacore_context_test.go @@ -15,10 +15,12 @@ import ( context "github.com/zeta-chain/zetacore/zetaclient/context" ) +// getTestCoreContext creates a test zetacore context with provided chain params and flags func getTestCoreContext( evmChain chains.Chain, evmChainParams *observertypes.ChainParams, - ccFlags observertypes.CrosschainFlags, + btcChainParams *observertypes.ChainParams, + ccFlags *observertypes.CrosschainFlags, headerSupportedChains []lightclienttypes.HeaderSupportedChain, ) *context.ZetacoreContext { // create config @@ -26,19 +28,30 @@ func getTestCoreContext( cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ Chain: evmChain, } + if btcChainParams != nil { + cfg.BitcoinConfig = config.BTCConfig{ + RPCUsername: "test", + } + } + // create zetacore context coreContext := context.NewZetacoreContext(cfg) evmChainParamsMap := make(map[int64]*observertypes.ChainParams) evmChainParamsMap[evmChain.ChainId] = evmChainParams + // create crosschain flags if not provided + if ccFlags == nil { + ccFlags = sample.CrosschainFlags() + } + // feed chain params coreContext.Update( &observertypes.Keygen{}, []chains.Chain{evmChain}, evmChainParamsMap, - nil, + btcChainParams, "", - ccFlags, + *ccFlags, headerSupportedChains, true, zerolog.Logger{}, @@ -344,7 +357,7 @@ func TestIsOutboundObservationEnabled(t *testing.T) { } t.Run("should return true if chain is supported and outbound flag is enabled", func(t *testing.T) { - coreCTX := getTestCoreContext(evmChain, chainParams, ccFlags, verificationFlags) + coreCTX := getTestCoreContext(evmChain, chainParams, nil, &ccFlags, verificationFlags) require.True(t, context.IsOutboundObservationEnabled(coreCTX, *chainParams)) }) t.Run("should return false if chain is not supported yet", func(t *testing.T) { @@ -352,13 +365,13 @@ func TestIsOutboundObservationEnabled(t *testing.T) { ChainId: evmChain.ChainId, IsSupported: false, } - coreCTXUnsupported := getTestCoreContext(evmChain, paramsUnsupported, ccFlags, verificationFlags) + coreCTXUnsupported := getTestCoreContext(evmChain, paramsUnsupported, nil, &ccFlags, verificationFlags) require.False(t, context.IsOutboundObservationEnabled(coreCTXUnsupported, *paramsUnsupported)) }) t.Run("should return false if outbound flag is disabled", func(t *testing.T) { flagsDisabled := ccFlags flagsDisabled.IsOutboundEnabled = false - coreCTXDisabled := getTestCoreContext(evmChain, chainParams, flagsDisabled, verificationFlags) + coreCTXDisabled := getTestCoreContext(evmChain, chainParams, nil, &flagsDisabled, verificationFlags) require.False(t, context.IsOutboundObservationEnabled(coreCTXDisabled, *chainParams)) }) } @@ -374,7 +387,7 @@ func TestIsInboundObservationEnabled(t *testing.T) { } t.Run("should return true if chain is supported and inbound flag is enabled", func(t *testing.T) { - coreCTX := getTestCoreContext(evmChain, chainParams, ccFlags, verificationFlags) + coreCTX := getTestCoreContext(evmChain, chainParams, nil, &ccFlags, verificationFlags) require.True(t, context.IsInboundObservationEnabled(coreCTX, *chainParams)) }) t.Run("should return false if chain is not supported yet", func(t *testing.T) { @@ -382,13 +395,13 @@ func TestIsInboundObservationEnabled(t *testing.T) { ChainId: evmChain.ChainId, IsSupported: false, } - coreCTXUnsupported := getTestCoreContext(evmChain, paramsUnsupported, ccFlags, verificationFlags) + coreCTXUnsupported := getTestCoreContext(evmChain, paramsUnsupported, nil, &ccFlags, verificationFlags) require.False(t, context.IsInboundObservationEnabled(coreCTXUnsupported, *paramsUnsupported)) }) t.Run("should return false if inbound flag is disabled", func(t *testing.T) { flagsDisabled := ccFlags flagsDisabled.IsInboundEnabled = false - coreCTXDisabled := getTestCoreContext(evmChain, chainParams, flagsDisabled, verificationFlags) + coreCTXDisabled := getTestCoreContext(evmChain, chainParams, nil, &flagsDisabled, verificationFlags) require.False(t, context.IsInboundObservationEnabled(coreCTXDisabled, *chainParams)) }) } diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index 1407557d3e..5536875f09 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -95,6 +95,8 @@ func NewOrchestrator( } func (oc *Orchestrator) MonitorCore(appContext *context.AppContext) error { + go oc.zetacoreClient.ZetacoreContextUpdater(appContext) + signerAddress, err := oc.zetacoreClient.GetKeys().GetAddress() if err != nil { return fmt.Errorf("failed to get signer address: %w", err) diff --git a/cmd/zetaclientd/utils.go b/zetaclient/orchestrator/utils.go similarity index 79% rename from cmd/zetaclientd/utils.go rename to zetaclient/orchestrator/utils.go index 43caf04c11..5269bbc998 100644 --- a/cmd/zetaclientd/utils.go +++ b/zetaclient/orchestrator/utils.go @@ -1,10 +1,8 @@ -package main +package orchestrator import ( - sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/zeta-chain/zetacore/zetaclient/authz" "github.com/zeta-chain/zetacore/zetaclient/chains/base" btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" btcsigner "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/signer" @@ -13,47 +11,10 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/context" - "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/zetacore" ) -func CreateAuthzSigner(granter string, grantee sdk.AccAddress) { - authz.SetupAuthZSignerList(granter, grantee) -} - -func CreateZetacoreClient( - cfg config.Config, - telemetry *metrics.TelemetryServer, - hotkeyPassword string, -) (*zetacore.Client, error) { - hotKey := cfg.AuthzHotkey - if cfg.HsmMode { - hotKey = cfg.HsmHotKey - } - - chainIP := cfg.ZetaCoreURL - - kb, _, err := keys.GetKeyringKeybase(cfg, hotkeyPassword) - if err != nil { - return nil, err - } - - granterAddreess, err := sdk.AccAddressFromBech32(cfg.AuthzGranter) - if err != nil { - return nil, err - } - - k := keys.NewKeysWithKeybase(kb, granterAddreess, cfg.AuthzHotkey, hotkeyPassword) - - client, err := zetacore.NewClient(k, chainIP, hotKey, cfg.ChainID, cfg.HsmMode, telemetry) - if err != nil { - return nil, err - } - - return client, nil -} - // CreateSignerMap creates a map of ChainSigners for all chains in the config func CreateSignerMap( appContext *context.AppContext, diff --git a/zetaclient/testutils/mocks/zetacore_client.go b/zetaclient/testutils/mocks/zetacore_client.go index 953d8ce287..f116de0dbc 100644 --- a/zetaclient/testutils/mocks/zetacore_client.go +++ b/zetaclient/testutils/mocks/zetacore_client.go @@ -16,6 +16,7 @@ import ( lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" observerTypes "github.com/zeta-chain/zetacore/x/observer/types" chaininterfaces "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" + clientcontext "github.com/zeta-chain/zetacore/zetaclient/context" keyinterfaces "github.com/zeta-chain/zetacore/zetaclient/keys/interfaces" "github.com/zeta-chain/zetacore/zetaclient/testutils" ) @@ -51,6 +52,9 @@ func NewMockZetacoreClient() *MockZetacoreClient { } } +func (m *MockZetacoreClient) ZetacoreContextUpdater(_ *clientcontext.AppContext) { +} + func (m *MockZetacoreClient) PostVoteInbound(_, _ uint64, _ *crosschaintypes.MsgVoteInbound) (string, string, error) { if m.paused { return "", "", errors.New(ErrMsgPaused) diff --git a/zetaclient/zetacore/client.go b/zetaclient/zetacore/client.go index 3b85296fe1..2dc88c542c 100644 --- a/zetaclient/zetacore/client.go +++ b/zetaclient/zetacore/client.go @@ -7,6 +7,7 @@ import ( "cosmossdk.io/simapp/params" rpcclient "github.com/cometbft/cometbft/rpc/client" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -19,6 +20,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/context" + "github.com/zeta-chain/zetacore/zetaclient/keys" keyinterfaces "github.com/zeta-chain/zetacore/zetaclient/keys/interfaces" "github.com/zeta-chain/zetacore/zetaclient/metrics" ) @@ -48,6 +50,39 @@ type Client struct { mockSDKClient rpcclient.Client } +// CreateClient is a helper function to create a new instance of Client +func CreateClient( + cfg config.Config, + telemetry *metrics.TelemetryServer, + hotkeyPassword string, +) (*Client, error) { + hotKey := cfg.AuthzHotkey + if cfg.HsmMode { + hotKey = cfg.HsmHotKey + } + + chainIP := cfg.ZetaCoreURL + + kb, _, err := keys.GetKeyringKeybase(cfg, hotkeyPassword) + if err != nil { + return nil, err + } + + granterAddreess, err := sdk.AccAddressFromBech32(cfg.AuthzGranter) + if err != nil { + return nil, err + } + + keys := keys.NewKeysWithKeybase(kb, granterAddreess, cfg.AuthzHotkey, hotkeyPassword) + + client, err := NewClient(keys, chainIP, hotKey, cfg.ChainID, cfg.HsmMode, telemetry) + if err != nil { + return nil, err + } + + return client, nil +} + // NewClient create a new instance of Client func NewClient( keys keyinterfaces.ObserverKeys, From fc607d4ac7d384968142110516c074449d8a5f53 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 26 Jun 2024 17:36:26 -0500 Subject: [PATCH 02/18] organize some AppContext logic into orchestrator --- cmd/zetaclientd/start.go | 44 +- zetaclient/chains/base/observer.go | 1 + zetaclient/chains/base/observer_test.go | 3 + zetaclient/chains/evm/constant.go | 3 - zetaclient/chains/evm/signer/signer.go | 3 +- zetaclient/chains/interfaces/interfaces.go | 9 +- zetaclient/common/constant.go | 5 + zetaclient/context/zetacore_context.go | 83 ++-- zetaclient/context/zetacore_context_test.go | 18 +- zetaclient/orchestrator/app_context_update.go | 376 ++++++++++++++++++ .../orchestrator/app_context_update_test.go | 1 + zetaclient/orchestrator/orchestrator.go | 89 +++-- zetaclient/orchestrator/utils.go | 157 -------- zetaclient/testutils/mocks/zetacore_client.go | 49 ++- zetaclient/zetacore/client.go | 90 +++++ zetaclient/zetacore/tx.go | 22 - 16 files changed, 669 insertions(+), 284 deletions(-) create mode 100644 zetaclient/orchestrator/app_context_update.go create mode 100644 zetaclient/orchestrator/app_context_update_test.go delete mode 100644 zetaclient/orchestrator/utils.go diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index ccd398ffaf..2de63c420c 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -6,10 +6,8 @@ import ( "fmt" "io" "os" - "os/signal" "path/filepath" "strings" - "syscall" "github.com/cometbft/cometbft/crypto/secp256k1" ethcommon "github.com/ethereum/go-ethereum/common" @@ -26,7 +24,6 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/base" "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/orchestrator" "github.com/zeta-chain/zetacore/zetaclient/zetacore" @@ -82,10 +79,11 @@ func start(_ *cobra.Command, _ []string) error { } masterLogger := logger.Std startLogger := masterLogger.With().Str("module", "startup").Logger() + startLogger.Info().Msgf("zetaclient config file: \n%s", maskCfg(cfg)) // Wait until zetacore is up waitForZetaCore(cfg, startLogger) - startLogger.Info().Msgf("Zetacore is ready, trying to connect to %s", cfg.Peer) + startLogger.Info().Msgf("zetacore is ready, trying to connect to %s", cfg.Peer) // Start telemetry server telemetryServer := metrics.NewTelemetryServer() @@ -157,16 +155,14 @@ func start(_ *cobra.Command, _ []string) error { return err } authzclient.SetupAuthZSignerList(granter, grantee) - startLogger.Info().Msgf("Authz signer is ready") + startLogger.Info().Msgf("Authz is ready for granter %s grantee %s", granter, grantee) // Initialize app context and zetacore context - appContext := context.NewAppContext(context.NewZetacoreContext(cfg), cfg) - err = zetacoreClient.UpdateZetacoreContext(appContext.ZetacoreContext(), true, startLogger) + appContext, err := orchestrator.CreateAppContext(cfg, zetacoreClient, startLogger) if err != nil { - startLogger.Error().Err(err).Msg("Error getting core parameters") + startLogger.Error().Err(err).Msg("error creating app context") return err } - startLogger.Info().Msgf("Config is updated from zetacore %s", maskCfg(cfg)) // Generate TSS address . The Tss address is generated through Keygen ceremony. The TSS key is used to sign all outbound transactions . // The hotkeyPk is private key for the Hotkey. The Hotkey is used to sign all inbound transactions @@ -241,8 +237,8 @@ func start(_ *cobra.Command, _ []string) error { } startLogger.Info(). Msgf("Current TSS address \n ETH : %s \n BTC : %s \n PubKey : %s ", tss.EVMAddress(), tss.BTCAddress(), tss.CurrentPubkey) - if len(appContext.ZetacoreContext().GetEnabledChains()) == 0 { - startLogger.Error().Msgf("No chains enabled in updated config %s ", cfg.String()) + if len(appContext.ZetacoreContext().GetEnabledExternalChains()) == 0 { + startLogger.Error().Msgf("No external chains enabled in the zetacore %s ", cfg.String()) } observerList, err := zetacoreClient.GetObserverList() @@ -289,14 +285,6 @@ func start(_ *cobra.Command, _ []string) error { } } - // Orchestrator wraps the zetacore client and adds the observers and signer maps to it . This is the high level object used for CCTX interactions - orchestrator := orchestrator.NewOrchestrator(zetacoreClient, signerMap, observerMap, masterLogger, telemetryServer) - err = orchestrator.MonitorCore(appContext) - if err != nil { - startLogger.Error().Err(err).Msg("Orchestrator failed to start") - return err - } - // start zeta supply checker // TODO: enable // https://github.com/zeta-chain/node/issues/1354 @@ -311,17 +299,15 @@ func start(_ *cobra.Command, _ []string) error { // defer zetaSupplyChecker.Stop() //} - startLogger.Info().Msgf("awaiting the os.Interrupt, syscall.SIGTERM signals...") - ch := make(chan os.Signal, 1) - signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) - sig := <-ch - startLogger.Info().Msgf("stop signal received: %s", sig) + // Orchestrator wraps the zetacore client and adds the observers and signer maps to it . This is the high level object used for CCTX interactions + orch := orchestrator.NewOrchestrator(appContext, zetacoreClient, signerMap, observerMap, masterLogger, telemetryServer) + orch.Start() + + // Watch for stop signal + orch.AwaitStopSignals() - // stop chain observers - for _, observer := range observerMap { - observer.Stop() - } - zetacoreClient.Stop() + // Stop orchestrator + orch.Stop() return nil } diff --git a/zetaclient/chains/base/observer.go b/zetaclient/chains/base/observer.go index 1f3fc2d0ca..40492421d4 100644 --- a/zetaclient/chains/base/observer.go +++ b/zetaclient/chains/base/observer.go @@ -138,6 +138,7 @@ func (ob *Observer) Stop() { if err != nil { ob.Logger().Chain.Error().Err(err).Msgf("CloseDB failed for chain %d", ob.Chain().ChainId) } + ob.db = nil } ob.Logger().Chain.Info().Msgf("observer stopped for chain %d", ob.Chain().ChainId) } diff --git a/zetaclient/chains/base/observer_test.go b/zetaclient/chains/base/observer_test.go index a04a48fcc3..9afe3cb9d5 100644 --- a/zetaclient/chains/base/observer_test.go +++ b/zetaclient/chains/base/observer_test.go @@ -141,6 +141,9 @@ func TestStop(t *testing.T) { // stop observer ob.Stop() + + // db should be removed + require.Nil(t, ob.DB()) }) } diff --git a/zetaclient/chains/evm/constant.go b/zetaclient/chains/evm/constant.go index b754d57f30..a00db1b538 100644 --- a/zetaclient/chains/evm/constant.go +++ b/zetaclient/chains/evm/constant.go @@ -3,9 +3,6 @@ package evm import "time" const ( - // ZetaBlockTime is the block time of the Zeta network - ZetaBlockTime = 6500 * time.Millisecond - // OutboundInclusionTimeout is the timeout for waiting for an outbound to be included in a block OutboundInclusionTimeout = 20 * time.Minute diff --git a/zetaclient/chains/evm/signer/signer.go b/zetaclient/chains/evm/signer/signer.go index d50b214b03..0b879314fc 100644 --- a/zetaclient/chains/evm/signer/signer.go +++ b/zetaclient/chains/evm/signer/signer.go @@ -29,6 +29,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/evm" "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/compliance" clientcontext "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" @@ -793,7 +794,7 @@ func (signer *Signer) reportToOutboundTracker( break } // retry otherwise - time.Sleep(evm.ZetaBlockTime * 3) + time.Sleep(common.ZetaBlockTime * 3) } } }() diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index 46679cd159..f4385ca1c1 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -65,7 +66,13 @@ type ChainSigner interface { // ZetacoreClient is the client interface to interact with zetacore type ZetacoreClient interface { - ZetacoreContextUpdater(appContext *clientcontext.AppContext) + GetLatestZetacoreContext() (*clientcontext.ZetacoreContext, error) + UpdateZetacoreContext(coreContext *clientcontext.ZetacoreContext, init bool, sampledLogger zerolog.Logger) error + GetUpgradePlan() (*upgradetypes.Plan, error) + GetChainParams() ([]*observertypes.ChainParams, error) + GetSupportedChains() ([]*chains.Chain, error) + GetCurrentTss() (observertypes.TSS, error) + GetBlockHeaderEnabledChains() ([]lightclienttypes.HeaderSupportedChain, error) PostVoteInbound(gasLimit, retryGasLimit uint64, msg *crosschaintypes.MsgVoteInbound) (string, string, error) PostVoteOutbound( sendHash string, diff --git a/zetaclient/common/constant.go b/zetaclient/common/constant.go index 5e410d4886..16438efe4d 100644 --- a/zetaclient/common/constant.go +++ b/zetaclient/common/constant.go @@ -1,6 +1,11 @@ package common +import "time" + const ( + // ZetaBlockTime is the block time of the ZetaChain + ZetaBlockTime = 6000 * time.Millisecond + // EVMOutboundGasPriceMultiplier is the default gas price multiplier for EVM-chain outbond txs EVMOutboundGasPriceMultiplier = 1.2 diff --git a/zetaclient/context/zetacore_context.go b/zetaclient/context/zetacore_context.go index 73ad83ff29..82f5b28838 100644 --- a/zetaclient/context/zetacore_context.go +++ b/zetaclient/context/zetacore_context.go @@ -15,9 +15,10 @@ import ( // ZetacoreContext contains zetacore context params // these are initialized and updated at runtime at every height type ZetacoreContext struct { - coreContextLock *sync.RWMutex + mu *sync.RWMutex keygen observertypes.Keygen chainsEnabled []chains.Chain + chainParamMap map[int64]*observertypes.ChainParams evmChainParams map[int64]*observertypes.ChainParams bitcoinChainParams *observertypes.ChainParams currentTssPubkey string @@ -32,8 +33,10 @@ type ZetacoreContext struct { // it is initializing chain params from provided config func NewZetacoreContext(cfg config.Config) *ZetacoreContext { evmChainParams := make(map[int64]*observertypes.ChainParams) + chainParamsMap := make(map[int64]*observertypes.ChainParams) for _, e := range cfg.EVMChainConfigs { evmChainParams[e.Chain.ChainId] = &observertypes.ChainParams{} + chainParamsMap[e.Chain.ChainId] = &observertypes.ChainParams{} } var bitcoinChainParams *observertypes.ChainParams @@ -43,7 +46,7 @@ func NewZetacoreContext(cfg config.Config) *ZetacoreContext { } return &ZetacoreContext{ - coreContextLock: new(sync.RWMutex), + mu: new(sync.RWMutex), chainsEnabled: []chains.Chain{}, evmChainParams: evmChainParams, bitcoinChainParams: bitcoinChainParams, @@ -52,9 +55,33 @@ func NewZetacoreContext(cfg config.Config) *ZetacoreContext { } } +// CreateZetacoreContext creates and returns new ZetacoreContext +func CreateZetacoreContext( + keygen *observertypes.Keygen, + chainsEnabled []chains.Chain, + chainParamMap map[int64]*observertypes.ChainParams, + evmChainParams map[int64]*observertypes.ChainParams, + bitcoinChainParams *observertypes.ChainParams, + tssPubKey string, + crosschainFlags observertypes.CrosschainFlags, + blockHeaderEnabledChains []lightclienttypes.HeaderSupportedChain, +) *ZetacoreContext { + return &ZetacoreContext{ + mu: new(sync.RWMutex), + keygen: *keygen, + chainsEnabled: chainsEnabled, + chainParamMap: chainParamMap, + evmChainParams: evmChainParams, + bitcoinChainParams: bitcoinChainParams, + currentTssPubkey: tssPubKey, + crosschainFlags: crosschainFlags, + blockHeaderEnabledChains: blockHeaderEnabledChains, + } +} + func (c *ZetacoreContext) GetKeygen() observertypes.Keygen { - c.coreContextLock.RLock() - defer c.coreContextLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() var copiedPubkeys []string if c.keygen.GranteePubkeys != nil { @@ -70,25 +97,15 @@ func (c *ZetacoreContext) GetKeygen() observertypes.Keygen { } func (c *ZetacoreContext) GetCurrentTssPubkey() string { - c.coreContextLock.RLock() - defer c.coreContextLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() return c.currentTssPubkey } -// GetEnabledChains returns all enabled chains including zetachain -func (c *ZetacoreContext) GetEnabledChains() []chains.Chain { - c.coreContextLock.RLock() - defer c.coreContextLock.RUnlock() - - copiedChains := make([]chains.Chain, len(c.chainsEnabled)) - copy(copiedChains, c.chainsEnabled) - return copiedChains -} - -// GetEnabledExternalChains returns all enabled external chains +// GetEnabledExternalChains returns all enabled external chains (excluding zetachain) func (c *ZetacoreContext) GetEnabledExternalChains() []chains.Chain { - c.coreContextLock.RLock() - defer c.coreContextLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() externalChains := make([]chains.Chain, 0) for _, chain := range c.chainsEnabled { @@ -100,16 +117,16 @@ func (c *ZetacoreContext) GetEnabledExternalChains() []chains.Chain { } func (c *ZetacoreContext) GetEVMChainParams(chainID int64) (*observertypes.ChainParams, bool) { - c.coreContextLock.RLock() - defer c.coreContextLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() evmChainParams, found := c.evmChainParams[chainID] return evmChainParams, found } func (c *ZetacoreContext) GetAllEVMChainParams() map[int64]*observertypes.ChainParams { - c.coreContextLock.RLock() - defer c.coreContextLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() // deep copy evm chain params copied := make(map[int64]*observertypes.ChainParams, len(c.evmChainParams)) @@ -122,8 +139,8 @@ func (c *ZetacoreContext) GetAllEVMChainParams() map[int64]*observertypes.ChainP // GetBTCChainParams returns (chain, chain params, found) for bitcoin chain func (c *ZetacoreContext) GetBTCChainParams() (chains.Chain, *observertypes.ChainParams, bool) { - c.coreContextLock.RLock() - defer c.coreContextLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() if c.bitcoinChainParams == nil { // bitcoin is not enabled return chains.Chain{}, nil, false @@ -138,22 +155,22 @@ func (c *ZetacoreContext) GetBTCChainParams() (chains.Chain, *observertypes.Chai } func (c *ZetacoreContext) GetCrossChainFlags() observertypes.CrosschainFlags { - c.coreContextLock.RLock() - defer c.coreContextLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() return c.crosschainFlags } // GetAllHeaderEnabledChains returns all verification flags func (c *ZetacoreContext) GetAllHeaderEnabledChains() []lightclienttypes.HeaderSupportedChain { - c.coreContextLock.RLock() - defer c.coreContextLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() return c.blockHeaderEnabledChains } // GetBlockHeaderEnabledChains checks if block header verification is enabled for a specific chain func (c *ZetacoreContext) GetBlockHeaderEnabledChains(chainID int64) (lightclienttypes.HeaderSupportedChain, bool) { - c.coreContextLock.RLock() - defer c.coreContextLock.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() for _, flags := range c.blockHeaderEnabledChains { if flags.ChainId == chainID { return flags, true @@ -175,8 +192,8 @@ func (c *ZetacoreContext) Update( init bool, logger zerolog.Logger, ) { - c.coreContextLock.Lock() - defer c.coreContextLock.Unlock() + c.mu.Lock() + defer c.mu.Unlock() // Ignore whatever order zetacore organizes chain list in state sort.SliceStable(newChains, func(i, j int) bool { diff --git a/zetaclient/context/zetacore_context_test.go b/zetaclient/context/zetacore_context_test.go index f1b655ae36..f84c05205e 100644 --- a/zetaclient/context/zetacore_context_test.go +++ b/zetaclient/context/zetacore_context_test.go @@ -70,9 +70,6 @@ func TestNewZetaCoreContext(t *testing.T) { keyGen := zetaContext.GetKeygen() require.Equal(t, observertypes.Keygen{}, keyGen) - // assert enabled chains - require.Empty(t, len(zetaContext.GetEnabledChains())) - // assert external chains require.Empty(t, len(zetaContext.GetEnabledExternalChains())) @@ -212,9 +209,6 @@ func TestUpdateZetacoreContext(t *testing.T) { keyGen := zetaContext.GetKeygen() require.Equal(t, keyGenToUpdate, keyGen) - // assert enabled chains updated - require.Equal(t, enabledChainsToUpdate, zetaContext.GetEnabledChains()) - // assert enabled external chains require.Equal(t, enabledChainsToUpdate[0:2], zetaContext.GetEnabledExternalChains()) @@ -272,12 +266,14 @@ func TestUpdateZetacoreContext(t *testing.T) { } enabledChainsToUpdate := []chains.Chain{ { - ChainName: 1, - ChainId: 1, + ChainName: 1, + ChainId: 1, + IsExternal: true, }, { - ChainName: 2, - ChainId: 2, + ChainName: 2, + ChainId: 2, + IsExternal: true, }, } evmChainParamsToUpdate := map[int64]*observertypes.ChainParams{ @@ -314,7 +310,7 @@ func TestUpdateZetacoreContext(t *testing.T) { require.Equal(t, keyGenToUpdate, keyGen) // assert enabled chains updated - require.Equal(t, enabledChainsToUpdate, zetaContext.GetEnabledChains()) + require.Equal(t, enabledChainsToUpdate, zetaContext.GetEnabledExternalChains()) // assert current tss pubkey updated require.Equal(t, tssPubKeyToUpdate, zetaContext.GetCurrentTssPubkey()) diff --git a/zetaclient/orchestrator/app_context_update.go b/zetaclient/orchestrator/app_context_update.go new file mode 100644 index 0000000000..b4ac879449 --- /dev/null +++ b/zetaclient/orchestrator/app_context_update.go @@ -0,0 +1,376 @@ +package orchestrator + +import ( + "fmt" + "time" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/pkg/errors" + "github.com/rs/zerolog" + + "github.com/zeta-chain/zetacore/pkg/chains" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "github.com/zeta-chain/zetacore/zetaclient/chains/base" + btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" + btcrpc "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/rpc" + btcsigner "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/signer" + evmobserver "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" + evmsigner "github.com/zeta-chain/zetacore/zetaclient/chains/evm/signer" + "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/common" + "github.com/zeta-chain/zetacore/zetaclient/config" + "github.com/zeta-chain/zetacore/zetaclient/context" + "github.com/zeta-chain/zetacore/zetaclient/metrics" + "github.com/zeta-chain/zetacore/zetaclient/zetacore" +) + +// WatchUpgradePlan watches for upgrade plan and stops orchestrator if upgrade height is reached +func (oc *Orchestrator) WatchUpgradePlan() { + oc.logger.Std.Info().Msg("WatchUpgradePlan started") + + // detect upgrade plan every half Zeta block + ticker := time.NewTicker(common.ZetaBlockTime / 2) + for range ticker.C { + reached, err := oc.UpgradeHeightReached() + if err != nil { + oc.logger.Sampled.Error().Err(err).Msg("error detecting upgrade plan") + } else if reached { + oc.Stop() + oc.logger.Std.Info().Msg("WatchUpgradePlan stopped") + + return + } + } +} + +// UpdateAppContext is a polling goroutine that checks and updates app context periodically +func (oc *Orchestrator) UpdateAppContext() { + oc.logger.Std.Info().Msg("UpdateAppContext started") + + ticker := time.NewTicker(time.Duration(oc.appContext.Config().ConfigUpdateTicker) * time.Second) + for { + select { + case <-ticker.C: + err := UpdateZetacoreContext(oc.zetacoreClient, oc.appContext.ZetacoreContext(), false, oc.logger.Std) + if err != nil { + oc.logger.Std.Err(err).Msg("error updating zetaclient app context") + } + case <-oc.stop: + oc.logger.Std.Info().Msg("UpdateAppContext stopped") + return + } + } +} + +// CreateAppContext creates new app context from config and zetacore client +func CreateAppContext( + cfg config.Config, + zetacoreClient interfaces.ZetacoreClient, + logger zerolog.Logger, +) (*context.AppContext, error) { + // create app context from config + appContext := context.NewAppContext(context.NewZetacoreContext(cfg), cfg) + + // update zetacore context from zetacore + err := UpdateZetacoreContext(zetacoreClient, appContext.ZetacoreContext(), true, logger) + if err != nil { + return nil, errors.Wrap(err, "error updating zetacore context") + } + + return appContext, nil +} + +// UpdateZetacoreContext updates zetacore context +// zetacore stores zetacore context for all clients +func UpdateZetacoreContext( + zetacoreClient interfaces.ZetacoreClient, + coreContext *context.ZetacoreContext, + init bool, + logger zerolog.Logger, +) error { + // create latest zetacore context from zetacore + zetacoreContext, err := zetacoreClient.GetLatestZetacoreContext() + if err != nil { + return errors.Wrap(err, "error getting latest zetacore context") + } + + // get keygen + keygen := zetacoreContext.GetKeygen() + + // get btc chain params + _, newBTCParams, _ := zetacoreContext.GetBTCChainParams() + + coreContext.Update( + &keygen, + zetacoreContext.GetEnabledExternalChains(), + zetacoreContext.GetAllEVMChainParams(), + newBTCParams, + zetacoreContext.GetCurrentTssPubkey(), + zetacoreContext.GetCrossChainFlags(), + zetacoreContext.GetAllHeaderEnabledChains(), + init, + logger, + ) + + return nil +} + +// UpgradeHeightReached returns true if upgrade height is reached +func (oc *Orchestrator) UpgradeHeightReached() (bool, error) { + // query for active upgrade plan + plan, err := oc.zetacoreClient.GetUpgradePlan() + if err != nil { + return false, fmt.Errorf("failed to get upgrade plan: %w", err) + } + + // if there is no active upgrade plan, plan will be nil. + if plan == nil { + return false, nil + } + + // get ZetaChain block height + height, err := oc.zetacoreClient.GetBlockHeight() + if err != nil { + return false, fmt.Errorf("failed to get block height: %w", err) + } + + // if upgrade height is not reached, do nothing + if height != plan.Height-1 { + return false, nil + } + + // stop zetaclients if upgrade height is reached; notify operator to upgrade and restart + oc.logger.Std.Warn(). + Msgf("Active upgrade plan detected and upgrade height reached: %s at height %d; ZetaClient is stopped;"+ + "please kill this process, replace zetaclientd binary with upgraded version, and restart zetaclientd", plan.Name, plan.Height) + + return true, nil +} + +// GetLatestZetacoreContext queries zetacore to build the latest zetacore context +func (oc *Orchestrator) GetLatestZetacoreContext(client interfaces.ZetacoreClient) (*context.ZetacoreContext, error) { + // get latest supported chains + supportedChains, err := client.GetSupportedChains() + if err != nil { + return nil, errors.Wrap(err, "GetSupportedChains failed") + } + supportedChainsMap := make(map[int64]chains.Chain) + for _, chain := range supportedChains { + supportedChainsMap[chain.ChainId] = *chain + } + + // get latest chain parameters + chainParams, err := client.GetChainParams() + if err != nil { + return nil, errors.Wrap(err, "GetChainParams failed") + } + + chainsEnabled := make([]chains.Chain, 0) + chainParamMap := make(map[int64]*observertypes.ChainParams) + + newEVMParams := make(map[int64]*observertypes.ChainParams) + var newBTCParams *observertypes.ChainParams + + for _, chainParam := range chainParams { + // skip unsupported chain + if !chainParam.IsSupported { + continue + } + + // chain should exist in chain list + chain, found := supportedChainsMap[chainParam.ChainId] + if !found { + continue + } + + // skip ZetaChain + if !chain.IsExternalChain() { + continue + } + + // add chain param to map + chainParamMap[chainParam.ChainId] = chainParam + + // keep this chain + chainsEnabled = append(chainsEnabled, chain) + if chains.IsBitcoinChain(chainParam.ChainId) { + newBTCParams = chainParam + } else if chains.IsEVMChain(chainParam.ChainId) { + newEVMParams[chainParam.ChainId] = chainParam + } + } + + // get latest keygen + keyGen, err := client.GetKeyGen() + if err != nil { + return nil, errors.Wrap(err, "GetKeyGen failed") + } + + // get latest TSS public key + tss, err := client.GetCurrentTss() + if err != nil { + return nil, errors.Wrap(err, "GetCurrentTss failed") + } + tssPubKey := tss.GetTssPubkey() + + // get latest crosschain flags + crosschainFlags, err := client.GetCrosschainFlags() + if err != nil { + return nil, errors.Wrap(err, "GetCrosschainFlags failed") + } + + // get latest block header enabled chains + blockHeaderEnabledChains, err := client.GetBlockHeaderEnabledChains() + if err != nil { + return nil, errors.Wrap(err, "GetBlockHeaderEnabledChains failed") + } + + return context.CreateZetacoreContext( + keyGen, + chainsEnabled, + chainParamMap, + newEVMParams, + newBTCParams, + tssPubKey, + crosschainFlags, + blockHeaderEnabledChains, + ), nil +} + +// CreateSignerMap creates a map of ChainSigners for all chains in the config +func CreateSignerMap( + appContext *context.AppContext, + tss interfaces.TSSSigner, + logger base.Logger, + ts *metrics.TelemetryServer, +) (map[int64]interfaces.ChainSigner, error) { + zetacoreContext := appContext.ZetacoreContext() + signerMap := make(map[int64]interfaces.ChainSigner) + + // EVM signers + for _, evmConfig := range appContext.Config().GetAllEVMConfigs() { + if evmConfig.Chain.IsZetaChain() { + continue + } + evmChainParams, found := zetacoreContext.GetEVMChainParams(evmConfig.Chain.ChainId) + if !found { + logger.Std.Error().Msgf("ChainParam not found for chain %s", evmConfig.Chain.String()) + continue + } + mpiAddress := ethcommon.HexToAddress(evmChainParams.ConnectorContractAddress) + erc20CustodyAddress := ethcommon.HexToAddress(evmChainParams.Erc20CustodyContractAddress) + signer, err := evmsigner.NewSigner( + evmConfig.Chain, + zetacoreContext, + tss, + ts, + logger, + evmConfig.Endpoint, + config.GetConnectorABI(), + config.GetERC20CustodyABI(), + mpiAddress, + erc20CustodyAddress) + if err != nil { + logger.Std.Error().Err(err).Msgf("NewEVMSigner error for chain %s", evmConfig.Chain.String()) + continue + } + signerMap[evmConfig.Chain.ChainId] = signer + } + // BTC signer + btcChain, btcConfig, enabled := appContext.GetBTCChainAndConfig() + if enabled { + signer, err := btcsigner.NewSigner(btcChain, zetacoreContext, tss, ts, logger, btcConfig) + if err != nil { + logger.Std.Error().Err(err).Msgf("NewBTCSigner error for chain %s", btcChain.String()) + } else { + signerMap[btcChain.ChainId] = signer + } + } + + return signerMap, nil +} + +// CreateChainObserverMap creates a map of ChainObservers for all chains in the config +func CreateChainObserverMap( + appContext *context.AppContext, + zetacoreClient *zetacore.Client, + tss interfaces.TSSSigner, + dbpath string, + logger base.Logger, + ts *metrics.TelemetryServer, +) (map[int64]interfaces.ChainObserver, error) { + zetacoreContext := appContext.ZetacoreContext() + observerMap := make(map[int64]interfaces.ChainObserver) + // EVM observers + for _, evmConfig := range appContext.Config().GetAllEVMConfigs() { + if evmConfig.Chain.IsZetaChain() { + continue + } + chainParams, found := zetacoreContext.GetEVMChainParams(evmConfig.Chain.ChainId) + if !found { + logger.Std.Error().Msgf("ChainParam not found for chain %s", evmConfig.Chain.String()) + continue + } + + // create EVM client + evmClient, err := ethclient.Dial(evmConfig.Endpoint) + if err != nil { + logger.Std.Error().Err(err).Msgf("error dailing endpoint %s", evmConfig.Endpoint) + continue + } + + // create EVM chain observer + observer, err := evmobserver.NewObserver( + evmConfig, + evmClient, + *chainParams, + zetacoreContext, + zetacoreClient, + tss, + dbpath, + logger, + ts, + ) + if err != nil { + logger.Std.Error().Err(err).Msgf("NewObserver error for evm chain %s", evmConfig.Chain.String()) + continue + } + observerMap[evmConfig.Chain.ChainId] = observer + } + + // BTC observer + _, chainParams, found := zetacoreContext.GetBTCChainParams() + if !found { + return nil, fmt.Errorf("bitcoin chains params not found") + } + + // create BTC chain observer + btcChain, btcConfig, enabled := appContext.GetBTCChainAndConfig() + if enabled { + btcClient, err := btcrpc.NewRPCClient(btcConfig) + if err != nil { + logger.Std.Error().Err(err).Msgf("error creating rpc client for bitcoin chain %s", btcChain.String()) + } else { + // create BTC chain observer + observer, err := btcobserver.NewObserver( + btcChain, + btcClient, + *chainParams, + zetacoreContext, + zetacoreClient, + tss, + dbpath, + logger, + ts, + ) + if err != nil { + logger.Std.Error().Err(err).Msgf("NewObserver error for bitcoin chain %s", btcChain.String()) + } else { + observerMap[btcChain.ChainId] = observer + } + } + } + + return observerMap, nil +} diff --git a/zetaclient/orchestrator/app_context_update_test.go b/zetaclient/orchestrator/app_context_update_test.go new file mode 100644 index 0000000000..503f45c907 --- /dev/null +++ b/zetaclient/orchestrator/app_context_update_test.go @@ -0,0 +1 @@ +package orchestrator_test diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index 5536875f09..1ccae1e805 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -3,6 +3,10 @@ package orchestrator import ( "fmt" "math" + "os" + "os/signal" + "sync" + "syscall" "time" sdkmath "cosmossdk.io/math" @@ -39,6 +43,9 @@ type Log struct { // Orchestrator wraps the zetacore client, chain observers and signers. This is the high level object used for CCTX scheduling type Orchestrator struct { + // appContext contains the config and zetacore context + appContext *context.AppContext + // zetacore client zetacoreClient interfaces.ZetacoreClient @@ -52,14 +59,23 @@ type Orchestrator struct { // last operator balance lastOperatorBalance sdkmath.Int - // misc + // logger contains the loggers used by the orchestrator logger Log - stop chan struct{} - ts *metrics.TelemetryServer + + // ts is the telemetry server for metrics + ts *metrics.TelemetryServer + + // mu protects fields from concurrent access + mu sync.Mutex + + // stop channel and flag to avoid closing twice + stop chan struct{} + stopped bool } // NewOrchestrator creates a new orchestrator func NewOrchestrator( + appContext *context.AppContext, zetacoreClient interfaces.ZetacoreClient, signerMap map[int64]interfaces.ChainSigner, observerMap map[int64]interfaces.ChainObserver, @@ -67,13 +83,16 @@ func NewOrchestrator( ts *metrics.TelemetryServer, ) *Orchestrator { oc := Orchestrator{ - ts: ts, - stop: make(chan struct{}), + appContext: appContext, + ts: ts, + mu: sync.Mutex{}, + stop: make(chan struct{}), + stopped: false, } // create loggers oc.logger = Log{ - Std: logger.With().Str("module", "Orchestrator").Logger(), + Std: logger.With().Str("module", "orchestrator").Logger(), } oc.logger.Sampled = oc.logger.Std.Sample(&zerolog.BasicSampler{N: loggerSamplingRate}) @@ -85,6 +104,7 @@ func NewOrchestrator( // create outbound processor oc.outboundProc = outboundprocessor.NewProcessor(logger) + // initialize hot key balance balance, err := zetacoreClient.GetZetaHotKeyBalance() if err != nil { oc.logger.Std.Error().Err(err).Msg("error getting last balance of the hot key") @@ -94,31 +114,48 @@ func NewOrchestrator( return &oc } -func (oc *Orchestrator) MonitorCore(appContext *context.AppContext) error { - go oc.zetacoreClient.ZetacoreContextUpdater(appContext) +// Start all orchestrator routines +func (oc *Orchestrator) Start() { + // watch for upgrade plan in zetacore + go oc.WatchUpgradePlan() - signerAddress, err := oc.zetacoreClient.GetKeys().GetAddress() - if err != nil { - return fmt.Errorf("failed to get signer address: %w", err) - } - oc.logger.Std.Info().Msgf("Starting orchestrator for signer: %s", signerAddress) + // watch for zetaclient app context (config file and chain params) updates + go oc.UpdateAppContext() + + // schedule pending cctxs across all enabled chains + go oc.SchedulePendingCctxs() +} + +// AwaitStopSignals waits for stop signals +func (oc *Orchestrator) AwaitStopSignals() { + oc.logger.Std.Info().Msgf("awaiting the os.Interrupt, syscall.SIGTERM signals...") - // start cctx scheduler - go oc.StartCctxScheduler(appContext) + // subscribe to stop signals + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) + sig := <-ch + + oc.logger.Std.Info().Msgf("stop signal received: %s", sig) +} - // watch for upgrade plan from zetacore - go func() { - // wait for upgrade plan signal to arrive - oc.zetacoreClient.Pause() +// Stop notifies all zetaclient goroutines to stop +func (oc *Orchestrator) Stop() { + oc.mu.Lock() + defer oc.mu.Unlock() - // now stop orchestrator and all observers + // stop orchestrator only once. + // both WatchUpgradePlan and system signals can trigger Stop() + if !oc.stopped { + // notify app context updater and CCTX scheduler to stop close(oc.stop) + + // notify all chain observers to stop for _, c := range oc.observerMap { c.Stop() } - }() - - return nil + // set stopped flag + oc.stopped = true + } } // GetUpdatedSigner returns signer with updated chain parameters @@ -232,8 +269,8 @@ func (oc *Orchestrator) GetPendingCctxsWithinRatelimit( return output.CctxsMap, nil } -// StartCctxScheduler schedules keysigns for cctxs on each ZetaChain block (the ticker) -func (oc *Orchestrator) StartCctxScheduler(appContext *context.AppContext) { +// SchedulePendingCctxs schedules keysigns for pending cctxs across all chains +func (oc *Orchestrator) SchedulePendingCctxs() { observeTicker := time.NewTicker(3 * time.Second) var lastBlockNum int64 for { @@ -276,7 +313,7 @@ func (oc *Orchestrator) StartCctxScheduler(appContext *context.AppContext) { metrics.HotKeyBurnRate.Set(float64(oc.ts.HotKeyBurnRate.GetBurnRate().Int64())) // get supported external chains - coreContext := appContext.ZetacoreContext() + coreContext := oc.appContext.ZetacoreContext() externalChains := coreContext.GetEnabledExternalChains() // query pending cctxs across all external chains within rate limit diff --git a/zetaclient/orchestrator/utils.go b/zetaclient/orchestrator/utils.go deleted file mode 100644 index 2aa2bf0e52..0000000000 --- a/zetaclient/orchestrator/utils.go +++ /dev/null @@ -1,157 +0,0 @@ -package orchestrator - -import ( - "fmt" - - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethclient" - - "github.com/zeta-chain/zetacore/zetaclient/chains/base" - btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" - btcrpc "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/rpc" - btcsigner "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/signer" - evmobserver "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" - evmsigner "github.com/zeta-chain/zetacore/zetaclient/chains/evm/signer" - "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/context" - "github.com/zeta-chain/zetacore/zetaclient/metrics" - "github.com/zeta-chain/zetacore/zetaclient/zetacore" -) - -// CreateSignerMap creates a map of ChainSigners for all chains in the config -func CreateSignerMap( - appContext *context.AppContext, - tss interfaces.TSSSigner, - logger base.Logger, - ts *metrics.TelemetryServer, -) (map[int64]interfaces.ChainSigner, error) { - zetacoreContext := appContext.ZetacoreContext() - signerMap := make(map[int64]interfaces.ChainSigner) - - // EVM signers - for _, evmConfig := range appContext.Config().GetAllEVMConfigs() { - if evmConfig.Chain.IsZetaChain() { - continue - } - evmChainParams, found := zetacoreContext.GetEVMChainParams(evmConfig.Chain.ChainId) - if !found { - logger.Std.Error().Msgf("ChainParam not found for chain %s", evmConfig.Chain.String()) - continue - } - mpiAddress := ethcommon.HexToAddress(evmChainParams.ConnectorContractAddress) - erc20CustodyAddress := ethcommon.HexToAddress(evmChainParams.Erc20CustodyContractAddress) - signer, err := evmsigner.NewSigner( - evmConfig.Chain, - zetacoreContext, - tss, - ts, - logger, - evmConfig.Endpoint, - config.GetConnectorABI(), - config.GetERC20CustodyABI(), - mpiAddress, - erc20CustodyAddress) - if err != nil { - logger.Std.Error().Err(err).Msgf("NewEVMSigner error for chain %s", evmConfig.Chain.String()) - continue - } - signerMap[evmConfig.Chain.ChainId] = signer - } - // BTC signer - btcChain, btcConfig, enabled := appContext.GetBTCChainAndConfig() - if enabled { - signer, err := btcsigner.NewSigner(btcChain, zetacoreContext, tss, ts, logger, btcConfig) - if err != nil { - logger.Std.Error().Err(err).Msgf("NewBTCSigner error for chain %s", btcChain.String()) - } else { - signerMap[btcChain.ChainId] = signer - } - } - - return signerMap, nil -} - -// CreateChainObserverMap creates a map of ChainObservers for all chains in the config -func CreateChainObserverMap( - appContext *context.AppContext, - zetacoreClient *zetacore.Client, - tss interfaces.TSSSigner, - dbpath string, - logger base.Logger, - ts *metrics.TelemetryServer, -) (map[int64]interfaces.ChainObserver, error) { - zetacoreContext := appContext.ZetacoreContext() - observerMap := make(map[int64]interfaces.ChainObserver) - // EVM observers - for _, evmConfig := range appContext.Config().GetAllEVMConfigs() { - if evmConfig.Chain.IsZetaChain() { - continue - } - chainParams, found := zetacoreContext.GetEVMChainParams(evmConfig.Chain.ChainId) - if !found { - logger.Std.Error().Msgf("ChainParam not found for chain %s", evmConfig.Chain.String()) - continue - } - - // create EVM client - evmClient, err := ethclient.Dial(evmConfig.Endpoint) - if err != nil { - logger.Std.Error().Err(err).Msgf("error dailing endpoint %s", evmConfig.Endpoint) - continue - } - - // create EVM chain observer - observer, err := evmobserver.NewObserver( - evmConfig, - evmClient, - *chainParams, - zetacoreContext, - zetacoreClient, - tss, - dbpath, - logger, - ts, - ) - if err != nil { - logger.Std.Error().Err(err).Msgf("NewObserver error for evm chain %s", evmConfig.Chain.String()) - continue - } - observerMap[evmConfig.Chain.ChainId] = observer - } - - // BTC observer - _, chainParams, found := zetacoreContext.GetBTCChainParams() - if !found { - return nil, fmt.Errorf("bitcoin chains params not found") - } - - // create BTC chain observer - btcChain, btcConfig, enabled := appContext.GetBTCChainAndConfig() - if enabled { - btcClient, err := btcrpc.NewRPCClient(btcConfig) - if err != nil { - logger.Std.Error().Err(err).Msgf("error creating rpc client for bitcoin chain %s", btcChain.String()) - } else { - // create BTC chain observer - observer, err := btcobserver.NewObserver( - btcChain, - btcClient, - *chainParams, - zetacoreContext, - zetacoreClient, - tss, - dbpath, - logger, - ts, - ) - if err != nil { - logger.Std.Error().Err(err).Msgf("NewObserver error for bitcoin chain %s", btcChain.String()) - } else { - observerMap[btcChain.ChainId] = observer - } - } - } - - return observerMap, nil -} diff --git a/zetaclient/testutils/mocks/zetacore_client.go b/zetaclient/testutils/mocks/zetacore_client.go index f116de0dbc..bfc06f6b14 100644 --- a/zetaclient/testutils/mocks/zetacore_client.go +++ b/zetaclient/testutils/mocks/zetacore_client.go @@ -5,6 +5,7 @@ import ( "math/big" "cosmossdk.io/math" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/rs/zerolog" "github.com/zeta-chain/go-tss/blame" @@ -52,7 +53,53 @@ func NewMockZetacoreClient() *MockZetacoreClient { } } -func (m *MockZetacoreClient) ZetacoreContextUpdater(_ *clientcontext.AppContext) { +func (m *MockZetacoreClient) GetLatestZetacoreContext() (*clientcontext.ZetacoreContext, error) { + if m.paused { + return nil, errors.New(ErrMsgPaused) + } + return nil, nil +} + +func (m *MockZetacoreClient) UpdateZetacoreContext(_ *clientcontext.ZetacoreContext, _ bool, _ zerolog.Logger) error { + if m.paused { + return errors.New(ErrMsgPaused) + } + return nil +} + +func (m *MockZetacoreClient) GetUpgradePlan() (*upgradetypes.Plan, error) { + if m.paused { + return nil, errors.New(ErrMsgPaused) + } + return nil, nil +} + +func (m *MockZetacoreClient) GetChainParams() ([]*observerTypes.ChainParams, error) { + if m.paused { + return nil, errors.New(ErrMsgPaused) + } + return nil, nil +} + +func (m *MockZetacoreClient) GetSupportedChains() ([]*chains.Chain, error) { + if m.paused { + return nil, errors.New(ErrMsgPaused) + } + return nil, nil +} + +func (m *MockZetacoreClient) GetCurrentTss() (observerTypes.TSS, error) { + if m.paused { + return observerTypes.TSS{}, errors.New(ErrMsgPaused) + } + return observerTypes.TSS{}, nil +} + +func (m *MockZetacoreClient) GetBlockHeaderEnabledChains() ([]lightclienttypes.HeaderSupportedChain, error) { + if m.paused { + return nil, errors.New(ErrMsgPaused) + } + return nil, nil } func (m *MockZetacoreClient) PostVoteInbound(_, _ uint64, _ *crosschaintypes.MsgVoteInbound) (string, string, error) { diff --git a/zetaclient/zetacore/client.go b/zetaclient/zetacore/client.go index 2dc88c542c..64fb655a8b 100644 --- a/zetaclient/zetacore/client.go +++ b/zetaclient/zetacore/client.go @@ -318,6 +318,96 @@ func (c *Client) UpdateZetacoreContext( return nil } +// GetLatestZetacoreContext queries zetacore to build the latest zetacore context +func (c *Client) GetLatestZetacoreContext() (*context.ZetacoreContext, error) { + // get latest supported chains + supportedChains, err := c.GetSupportedChains() + if err != nil { + return nil, errors.Wrap(err, "GetSupportedChains failed") + } + supportedChainsMap := make(map[int64]chains.Chain) + for _, chain := range supportedChains { + supportedChainsMap[chain.ChainId] = *chain + } + + // get latest chain parameters + chainParams, err := c.GetChainParams() + if err != nil { + return nil, errors.Wrap(err, "GetChainParams failed") + } + + chainsEnabled := make([]chains.Chain, 0) + chainParamMap := make(map[int64]*observertypes.ChainParams) + + newEVMParams := make(map[int64]*observertypes.ChainParams) + var newBTCParams *observertypes.ChainParams + + for _, chainParam := range chainParams { + // skip unsupported chain + if !chainParam.IsSupported { + continue + } + + // chain should exist in chain list + chain, found := supportedChainsMap[chainParam.ChainId] + if !found { + continue + } + + // skip ZetaChain + if !chain.IsExternalChain() { + continue + } + + // add chain param to map + chainParamMap[chainParam.ChainId] = chainParam + + // keep this chain + chainsEnabled = append(chainsEnabled, chain) + if chains.IsBitcoinChain(chainParam.ChainId) { + newBTCParams = chainParam + } else if chains.IsEVMChain(chainParam.ChainId) { + newEVMParams[chainParam.ChainId] = chainParam + } + } + + // get latest keygen + keyGen, err := c.GetKeyGen() + if err != nil { + return nil, errors.Wrap(err, "GetKeyGen failed") + } + + // get latest TSS public key + tss, err := c.GetCurrentTss() + if err != nil { + return nil, errors.Wrap(err, "GetCurrentTss failed") + } + tssPubKey := tss.GetTssPubkey() + + // get latest crosschain flags + crosschainFlags, err := c.GetCrosschainFlags() + if err != nil { + return nil, errors.Wrap(err, "GetCrosschainFlags failed") + } + + // get latest block header enabled chains + blockHeaderEnabledChains, err := c.GetBlockHeaderEnabledChains() + if err != nil { + return nil, errors.Wrap(err, "GetBlockHeaderEnabledChains failed") + } + + return context.CreateZetacoreContext( + keyGen, + chainsEnabled, + chainParamMap, + newEVMParams, + newBTCParams, + tssPubKey, + crosschainFlags, + blockHeaderEnabledChains, + ), nil +} + func (c *Client) Pause() { <-c.pause } diff --git a/zetaclient/zetacore/tx.go b/zetaclient/zetacore/tx.go index 798654856f..7903b49195 100644 --- a/zetaclient/zetacore/tx.go +++ b/zetaclient/zetacore/tx.go @@ -10,7 +10,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz" "github.com/pkg/errors" - "github.com/rs/zerolog" "github.com/zeta-chain/go-tss/blame" "github.com/zeta-chain/zetacore/pkg/chains" @@ -20,7 +19,6 @@ import ( observertypes "github.com/zeta-chain/zetacore/x/observer/types" clientauthz "github.com/zeta-chain/zetacore/zetaclient/authz" clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" - appcontext "github.com/zeta-chain/zetacore/zetaclient/context" ) // GetInboundVoteMessage returns a new MsgVoteInbound @@ -165,26 +163,6 @@ func (c *Client) SetTSS(tssPubkey string, keyGenZetaHeight int64, status chains. return "", fmt.Errorf("set tss failed | err %s", err.Error()) } -// ZetacoreContextUpdater is a polling goroutine that checks and updates zetacore context at every height -func (c *Client) ZetacoreContextUpdater(appContext *appcontext.AppContext) { - c.logger.Info().Msg("ZetacoreContextUpdater started") - ticker := time.NewTicker(time.Duration(appContext.Config().ConfigUpdateTicker) * time.Second) - sampledLogger := c.logger.Sample(&zerolog.BasicSampler{N: 10}) - for { - select { - case <-ticker.C: - c.logger.Debug().Msg("Running Updater") - err := c.UpdateZetacoreContext(appContext.ZetacoreContext(), false, sampledLogger) - if err != nil { - c.logger.Err(err).Msg("ZetacoreContextUpdater failed to update config") - } - case <-c.stop: - c.logger.Info().Msg("ZetacoreContextUpdater stopped") - return - } - } -} - func (c *Client) PostBlameData(blame *blame.Blame, chainID int64, index string) (string, error) { signerAddress := c.keys.GetOperatorAddress().String() zetaBlame := observertypes.Blame{ From 672a461520a43b09b1d1ba1704116fe2226c3b59 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 28 Jun 2024 18:55:02 -0500 Subject: [PATCH 03/18] remove zetacore context and use app context only; remove BitcoinID from TSS and use BTC network params instead; cleanup and simplified methods --- changelog.md | 2 +- cmd/zetaclientd/debug.go | 4 +- cmd/zetaclientd/keygen_tss.go | 2 - cmd/zetaclientd/start.go | 57 +-- zetaclient/chains/base/observer.go | 20 +- zetaclient/chains/base/observer_test.go | 26 +- zetaclient/chains/base/signer.go | 26 +- zetaclient/chains/base/signer_test.go | 14 +- zetaclient/chains/bitcoin/observer/inbound.go | 6 +- .../chains/bitcoin/observer/observer.go | 7 +- .../chains/bitcoin/observer/observer_test.go | 10 +- .../chains/bitcoin/observer/outbound.go | 2 +- zetaclient/chains/bitcoin/signer/signer.go | 6 +- zetaclient/chains/evm/observer/inbound.go | 10 +- zetaclient/chains/evm/observer/observer.go | 9 +- .../chains/evm/observer/observer_test.go | 38 +- zetaclient/chains/evm/observer/outbound.go | 4 +- zetaclient/chains/evm/signer/signer.go | 6 +- zetaclient/chains/evm/signer/signer_test.go | 6 +- zetaclient/chains/interfaces/interfaces.go | 4 +- zetaclient/context/app_context.go | 272 ++++++++++-- zetaclient/context/app_context_test.go | 392 +++++++++++++++-- zetaclient/context/zetacore_context.go | 265 ------------ zetaclient/context/zetacore_context_test.go | 403 ------------------ zetaclient/orchestrator/app_context_update.go | 299 ++----------- zetaclient/orchestrator/chain_activate.go | 205 +++++++++ .../orchestrator/chain_activate_test.go | 1 + zetaclient/orchestrator/orchestrator.go | 148 ++++--- zetaclient/orchestrator/orchestrator_test.go | 74 ++-- .../supplychecker/zeta_supply_checker.go | 12 +- zetaclient/testutils/mocks/zetacore_client.go | 4 +- zetaclient/tss/tss_signer.go | 42 +- zetaclient/zetacore/client.go | 61 ++- zetaclient/zetacore/tx_test.go | 6 +- 34 files changed, 1136 insertions(+), 1307 deletions(-) delete mode 100644 zetaclient/context/zetacore_context.go delete mode 100644 zetaclient/context/zetacore_context_test.go create mode 100644 zetaclient/orchestrator/chain_activate.go create mode 100644 zetaclient/orchestrator/chain_activate_test.go diff --git a/changelog.md b/changelog.md index 373c0bafb3..623128fdb8 100644 --- a/changelog.md +++ b/changelog.md @@ -137,7 +137,7 @@ * [1848](https://github.com/zeta-chain/node/issues/1848) - create a method to observe deposits to tss address in one evm block * [1885](https://github.com/zeta-chain/node/pull/1885) - change important metrics on port 8123 to be prometheus compatible * [1863](https://github.com/zeta-chain/node/pull/1863) - remove duplicate ValidateChainParams function -* [1914](https://github.com/zeta-chain/node/pull/1914) - move crosschain flags to core context in zetaclient +* [1914](https://github.com/zeta-chain/node/pull/1914) - move crosschain flags to app context in zetaclient * [1948](https://github.com/zeta-chain/node/pull/1948) - remove deprecated GetTSSAddress query in crosschain module * [1936](https://github.com/zeta-chain/node/pull/1936) - refactor common package into subpackages and rename to pkg * [1966](https://github.com/zeta-chain/node/pull/1966) - move TSS vote message from crosschain to observer diff --git a/cmd/zetaclientd/debug.go b/cmd/zetaclientd/debug.go index 8aaeb6384e..50022b9c20 100644 --- a/cmd/zetaclientd/debug.go +++ b/cmd/zetaclientd/debug.go @@ -51,7 +51,7 @@ func DebugCmd() *cobra.Command { if err != nil { return err } - coreContext := clientcontext.NewZetacoreContext(cfg) + appContext := clientcontext.NewAppContext(cfg) chainID, err := strconv.ParseInt(args[1], 10, 64) if err != nil { return err @@ -123,7 +123,7 @@ func DebugCmd() *cobra.Command { ZetaTokenContractAddress: chainParams.ZetaTokenContractAddress, Erc20CustodyContractAddress: chainParams.Erc20CustodyContractAddress, }) - evmChainParams, found := coreContext.GetEVMChainParams(chainID) + evmChainParams, found := appContext.GetExternalChainParams(chainID) if !found { return fmt.Errorf("missing chain params for chain %d", chainID) } diff --git a/cmd/zetaclientd/keygen_tss.go b/cmd/zetaclientd/keygen_tss.go index d9c4f1be80..349d5b15e8 100644 --- a/cmd/zetaclientd/keygen_tss.go +++ b/cmd/zetaclientd/keygen_tss.go @@ -36,7 +36,6 @@ func GenerateTss( ) (*mc.TSS, error) { keygenLogger := logger.With().Str("module", "keygen").Logger() - bitcoinChainID := appContext.GetBTCChainID() tss, err := mc.NewTSS( appContext, peers, @@ -44,7 +43,6 @@ func GenerateTss( preParams, client, tssHistoricalList, - bitcoinChainID, tssPassword, hotkeyPassword, ) diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 2de63c420c..1be64c3eaa 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -31,6 +31,11 @@ import ( type Multiaddr = core.Multiaddr +const ( + // ObserverDBPath is the path (relative to user's home) to the observer database. + ObserverDBPath = ".zetaclient/chainobserver" +) + var StartCmd = &cobra.Command{ Use: "start", Short: "Start ZetaClient Observer", @@ -157,7 +162,7 @@ func start(_ *cobra.Command, _ []string) error { authzclient.SetupAuthZSignerList(granter, grantee) startLogger.Info().Msgf("Authz is ready for granter %s grantee %s", granter, grantee) - // Initialize app context and zetacore context + // Initialize zetaclient app context appContext, err := orchestrator.CreateAppContext(cfg, zetacoreClient, startLogger) if err != nil { startLogger.Error().Err(err).Msg("error creating app context") @@ -237,10 +242,11 @@ func start(_ *cobra.Command, _ []string) error { } startLogger.Info(). Msgf("Current TSS address \n ETH : %s \n BTC : %s \n PubKey : %s ", tss.EVMAddress(), tss.BTCAddress(), tss.CurrentPubkey) - if len(appContext.ZetacoreContext().GetEnabledExternalChains()) == 0 { + if len(appContext.GetEnabledExternalChains()) == 0 { startLogger.Error().Msgf("No external chains enabled in the zetacore %s ", cfg.String()) } + // Stop zetaclient if this node is not an active observer observerList, err := zetacoreClient.GetObserverList() if err != nil { startLogger.Error().Err(err).Msg("GetObserverList error") @@ -248,42 +254,24 @@ func start(_ *cobra.Command, _ []string) error { } isNodeActive := false for _, observer := range observerList { - if observer == zetacoreClient.GetKeys().GetOperatorAddress().String() { + if observer == granter { isNodeActive = true break } } - - // CreateSignerMap: This creates a map of all signers for each chain . Each signer is responsible for signing transactions for a particular chain - signerMap, err := orchestrator.CreateSignerMap(appContext, tss, logger, telemetryServer) - if err != nil { - log.Error().Err(err).Msg("CreateSignerMap") - return err + if !isNodeActive { + startLogger.Error().Msgf("Node %s is not an active observer, zetaclient stopped", granter) + return nil } + startLogger.Info().Msgf("Node %s is an active observer, starting orchestrator", granter) + // use the user's home path to store observer database userDir, err := os.UserHomeDir() if err != nil { log.Error().Err(err).Msg("os.UserHomeDir") return err } - dbpath := filepath.Join(userDir, ".zetaclient/chainobserver") - - // Creates a map of all chain observers for each chain. Each chain observer is responsible for observing events on the chain and processing them. - observerMap, err := orchestrator.CreateChainObserverMap(appContext, zetacoreClient, tss, dbpath, logger, telemetryServer) - if err != nil { - startLogger.Err(err).Msg("CreateChainObserverMap") - return err - } - - if !isNodeActive { - startLogger.Error(). - Msgf("Node %s is not an active observer external chain observers will not be started", zetacoreClient.GetKeys().GetOperatorAddress().String()) - } else { - startLogger.Debug().Msgf("Node %s is an active observer starting external chain observers", zetacoreClient.GetKeys().GetOperatorAddress().String()) - for _, observer := range observerMap { - observer.Start() - } - } + dbPath := filepath.Join(userDir, ObserverDBPath) // start zeta supply checker // TODO: enable @@ -300,15 +288,16 @@ func start(_ *cobra.Command, _ []string) error { //} // Orchestrator wraps the zetacore client and adds the observers and signer maps to it . This is the high level object used for CCTX interactions - orch := orchestrator.NewOrchestrator(appContext, zetacoreClient, signerMap, observerMap, masterLogger, telemetryServer) + orch := orchestrator.NewOrchestrator( + appContext, + zetacoreClient, + tss, + logger, + dbPath, + telemetryServer, + ) orch.Start() - // Watch for stop signal - orch.AwaitStopSignals() - - // Stop orchestrator - orch.Stop() - return nil } diff --git a/zetaclient/chains/base/observer.go b/zetaclient/chains/base/observer.go index 40492421d4..890b68e09f 100644 --- a/zetaclient/chains/base/observer.go +++ b/zetaclient/chains/base/observer.go @@ -45,8 +45,8 @@ type Observer struct { // chainParams contains the dynamic chain parameters of the observed chain chainParams observertypes.ChainParams - // coreContext contains context data of ZetaChain - zetacoreContext *context.ZetacoreContext + // appContext contains context data of zetaclient + appContext *context.AppContext // zetacoreClient is the client to interact with ZetaChain zetacoreClient interfaces.ZetacoreClient @@ -87,7 +87,7 @@ type Observer struct { func NewObserver( chain chains.Chain, chainParams observertypes.ChainParams, - zetacoreContext *context.ZetacoreContext, + appContext *context.AppContext, zetacoreClient interfaces.ZetacoreClient, tss interfaces.TSSSigner, blockCacheSize int, @@ -98,7 +98,7 @@ func NewObserver( ob := Observer{ chain: chain, chainParams: chainParams, - zetacoreContext: zetacoreContext, + appContext: appContext, zetacoreClient: zetacoreClient, tss: tss, lastBlock: 0, @@ -165,14 +165,14 @@ func (ob *Observer) WithChainParams(params observertypes.ChainParams) *Observer return ob } -// ZetacoreContext returns the zetacore context for the observer. -func (ob *Observer) ZetacoreContext() *context.ZetacoreContext { - return ob.zetacoreContext +// AppContext returns the app context for the observer. +func (ob *Observer) AppContext() *context.AppContext { + return ob.appContext } -// WithZetacoreContext attaches a new zetacore context to the observer. -func (ob *Observer) WithZetacoreContext(context *context.ZetacoreContext) *Observer { - ob.zetacoreContext = context +// WithAppContext attaches a new app context to the observer. +func (ob *Observer) WithAppContext(context *context.AppContext) *Observer { + ob.appContext = context return ob } diff --git a/zetaclient/chains/base/observer_test.go b/zetaclient/chains/base/observer_test.go index 9afe3cb9d5..2be07d2a1f 100644 --- a/zetaclient/chains/base/observer_test.go +++ b/zetaclient/chains/base/observer_test.go @@ -25,7 +25,7 @@ func createObserver(t *testing.T) *base.Observer { // constructor parameters chain := chains.Ethereum chainParams := *sample.ChainParams(chain.ChainId) - zetacoreContext := context.NewZetacoreContext(config.NewConfig()) + appContext := context.NewAppContext(config.NewConfig()) zetacoreClient := mocks.NewMockZetacoreClient() tss := mocks.NewTSSMainnet() @@ -34,7 +34,7 @@ func createObserver(t *testing.T) *base.Observer { ob, err := base.NewObserver( chain, chainParams, - zetacoreContext, + appContext, zetacoreClient, tss, base.DefaultBlockCacheSize, @@ -51,7 +51,7 @@ func TestNewObserver(t *testing.T) { // constructor parameters chain := chains.Ethereum chainParams := *sample.ChainParams(chain.ChainId) - zetacoreContext := context.NewZetacoreContext(config.NewConfig()) + appContext := context.NewAppContext(config.NewConfig()) zetacoreClient := mocks.NewMockZetacoreClient() tss := mocks.NewTSSMainnet() blockCacheSize := base.DefaultBlockCacheSize @@ -62,7 +62,7 @@ func TestNewObserver(t *testing.T) { name string chain chains.Chain chainParams observertypes.ChainParams - zetacoreContext *context.ZetacoreContext + appContext *context.AppContext zetacoreClient interfaces.ZetacoreClient tss interfaces.TSSSigner blockCacheSize int @@ -74,7 +74,7 @@ func TestNewObserver(t *testing.T) { name: "should be able to create new observer", chain: chain, chainParams: chainParams, - zetacoreContext: zetacoreContext, + appContext: appContext, zetacoreClient: zetacoreClient, tss: tss, blockCacheSize: blockCacheSize, @@ -85,7 +85,7 @@ func TestNewObserver(t *testing.T) { name: "should return error on invalid block cache size", chain: chain, chainParams: chainParams, - zetacoreContext: zetacoreContext, + appContext: appContext, zetacoreClient: zetacoreClient, tss: tss, blockCacheSize: 0, @@ -97,7 +97,7 @@ func TestNewObserver(t *testing.T) { name: "should return error on invalid header cache size", chain: chain, chainParams: chainParams, - zetacoreContext: zetacoreContext, + appContext: appContext, zetacoreClient: zetacoreClient, tss: tss, blockCacheSize: blockCacheSize, @@ -113,7 +113,7 @@ func TestNewObserver(t *testing.T) { ob, err := base.NewObserver( tt.chain, tt.chainParams, - tt.zetacoreContext, + tt.appContext, tt.zetacoreClient, tt.tss, tt.blockCacheSize, @@ -164,13 +164,13 @@ func TestObserverGetterAndSetter(t *testing.T) { ob = ob.WithChainParams(newChainParams) require.True(t, observertypes.ChainParamsEqual(newChainParams, ob.ChainParams())) }) - t.Run("should be able to update zetacore context", func(t *testing.T) { + t.Run("should be able to update app context", func(t *testing.T) { ob := createObserver(t) - // update zetacore context - newZetacoreContext := context.NewZetacoreContext(config.NewConfig()) - ob = ob.WithZetacoreContext(newZetacoreContext) - require.Equal(t, newZetacoreContext, ob.ZetacoreContext()) + // update app context + newAppContext := context.NewAppContext(config.NewConfig()) + ob = ob.WithAppContext(newAppContext) + require.Equal(t, newAppContext, ob.AppContext()) }) t.Run("should be able to update zetacore client", func(t *testing.T) { ob := createObserver(t) diff --git a/zetaclient/chains/base/signer.go b/zetaclient/chains/base/signer.go index bc5ad7934f..b4dde840d3 100644 --- a/zetaclient/chains/base/signer.go +++ b/zetaclient/chains/base/signer.go @@ -15,8 +15,8 @@ type Signer struct { // chain contains static information about the external chain chain chains.Chain - // zetacoreContext is the Zetacore client to interact with ZetaChain - zetacoreContext *context.ZetacoreContext + // appContext contains context data of zetaclient + appContext *context.AppContext // tss is the TSS signer tss interfaces.TSSSigner @@ -35,16 +35,16 @@ type Signer struct { // NewSigner creates a new base signer func NewSigner( chain chains.Chain, - zetacoreContext *context.ZetacoreContext, + appContext *context.AppContext, tss interfaces.TSSSigner, ts *metrics.TelemetryServer, logger Logger, ) *Signer { return &Signer{ - chain: chain, - zetacoreContext: zetacoreContext, - tss: tss, - ts: ts, + chain: chain, + appContext: appContext, + tss: tss, + ts: ts, logger: Logger{ Std: logger.Std.With().Int64("chain", chain.ChainId).Str("module", "signer").Logger(), Compliance: logger.Compliance, @@ -63,14 +63,14 @@ func (s *Signer) WithChain(chain chains.Chain) *Signer { return s } -// ZetacoreContext returns the zetacore context for the signer -func (s *Signer) ZetacoreContext() *context.ZetacoreContext { - return s.zetacoreContext +// AppContext returns the app context for the signer +func (s *Signer) AppContext() *context.AppContext { + return s.appContext } -// WithZetacoreContext attaches a new zetacore context to the signer -func (s *Signer) WithZetacoreContext(context *context.ZetacoreContext) *Signer { - s.zetacoreContext = context +// WithAppContext attaches a new app context to the signer +func (s *Signer) WithAppContext(context *context.AppContext) *Signer { + s.appContext = context return s } diff --git a/zetaclient/chains/base/signer_test.go b/zetaclient/chains/base/signer_test.go index 960c508d6e..1cd25cec7a 100644 --- a/zetaclient/chains/base/signer_test.go +++ b/zetaclient/chains/base/signer_test.go @@ -17,12 +17,12 @@ import ( func createSigner(_ *testing.T) *base.Signer { // constructor parameters chain := chains.Ethereum - zetacoreContext := context.NewZetacoreContext(config.NewConfig()) + appContext := context.NewAppContext(config.NewConfig()) tss := mocks.NewTSSMainnet() logger := base.DefaultLogger() // create signer - return base.NewSigner(chain, zetacoreContext, tss, nil, logger) + return base.NewSigner(chain, appContext, tss, nil, logger) } func TestNewSigner(t *testing.T) { @@ -39,13 +39,13 @@ func TestSignerGetterAndSetter(t *testing.T) { signer = signer.WithChain(chains.BscMainnet) require.Equal(t, newChain, signer.Chain()) }) - t.Run("should be able to update zetacore context", func(t *testing.T) { + t.Run("should be able to update app context", func(t *testing.T) { signer := createSigner(t) - // update zetacore context - newZetacoreContext := context.NewZetacoreContext(config.NewConfig()) - signer = signer.WithZetacoreContext(newZetacoreContext) - require.Equal(t, newZetacoreContext, signer.ZetacoreContext()) + // update app context + newAppContext := context.NewAppContext(config.NewConfig()) + signer = signer.WithAppContext(newAppContext) + require.Equal(t, newAppContext, signer.AppContext()) }) t.Run("should be able to update tss", func(t *testing.T) { signer := createSigner(t) diff --git a/zetaclient/chains/bitcoin/observer/inbound.go b/zetaclient/chains/bitcoin/observer/inbound.go index 6218f65327..1c7dbed33c 100644 --- a/zetaclient/chains/bitcoin/observer/inbound.go +++ b/zetaclient/chains/bitcoin/observer/inbound.go @@ -39,7 +39,7 @@ func (ob *Observer) WatchInbound() { for { select { case <-ticker.C(): - if !context.IsInboundObservationEnabled(ob.ZetacoreContext(), ob.GetChainParams()) { + if !context.IsInboundObservationEnabled(ob.AppContext(), ob.GetChainParams()) { sampledLogger.Info(). Msgf("WatchInbound: inbound observation is disabled for chain %d", ob.Chain().ChainId) continue @@ -104,7 +104,7 @@ func (ob *Observer) ObserveInbound() error { // https://github.com/zeta-chain/node/issues/1847 // TODO: move this logic in its own routine // https://github.com/zeta-chain/node/issues/2204 - blockHeaderVerification, found := ob.ZetacoreContext().GetBlockHeaderEnabledChains(ob.Chain().ChainId) + blockHeaderVerification, found := ob.AppContext().GetBlockHeaderEnabledChains(ob.Chain().ChainId) if found && blockHeaderVerification.Enabled { // #nosec G701 always in range err = ob.postBlockHeader(int64(blockNumber)) @@ -181,7 +181,7 @@ func (ob *Observer) WatchInboundTracker() { for { select { case <-ticker.C(): - if !context.IsInboundObservationEnabled(ob.ZetacoreContext(), ob.GetChainParams()) { + if !context.IsInboundObservationEnabled(ob.AppContext(), ob.GetChainParams()) { continue } err := ob.ProcessInboundTrackers() diff --git a/zetaclient/chains/bitcoin/observer/observer.go b/zetaclient/chains/bitcoin/observer/observer.go index 5d6e308dfe..c7e11ae6be 100644 --- a/zetaclient/chains/bitcoin/observer/observer.go +++ b/zetaclient/chains/bitcoin/observer/observer.go @@ -110,7 +110,7 @@ func NewObserver( chain chains.Chain, btcClient interfaces.BTCRPCClient, chainParams observertypes.ChainParams, - zetacoreContext *context.ZetacoreContext, + appContext *context.AppContext, zetacoreClient interfaces.ZetacoreClient, tss interfaces.TSSSigner, dbpath string, @@ -121,7 +121,7 @@ func NewObserver( baseObserver, err := base.NewObserver( chain, chainParams, - zetacoreContext, + appContext, zetacoreClient, tss, btcBlocksPerDay, @@ -427,8 +427,9 @@ func (ob *Observer) WatchUTXOs() { ob.logger.UTXOs.Error().Err(err).Msg("error creating ticker") return } - defer ticker.Stop() + ob.logger.Outbound.Info().Msgf("WatchUTXOs started for chain %d", ob.Chain().ChainId) + for { select { case <-ticker.C(): diff --git a/zetaclient/chains/bitcoin/observer/observer_test.go b/zetaclient/chains/bitcoin/observer/observer_test.go index c79209e3fc..8fb8838ae3 100644 --- a/zetaclient/chains/bitcoin/observer/observer_test.go +++ b/zetaclient/chains/bitcoin/observer/observer_test.go @@ -113,7 +113,7 @@ func Test_NewObserver(t *testing.T) { chain chains.Chain btcClient interfaces.BTCRPCClient chainParams observertypes.ChainParams - coreContext *context.ZetacoreContext + appContext *context.AppContext coreClient interfaces.ZetacoreClient tss interfaces.TSSSigner dbpath string @@ -127,7 +127,7 @@ func Test_NewObserver(t *testing.T) { chain: chain, btcClient: mocks.NewMockBTCRPCClient().WithBlockCount(100), chainParams: params, - coreContext: nil, + appContext: nil, coreClient: nil, tss: mocks.NewTSSMainnet(), dbpath: sample.CreateTempDir(t), @@ -140,7 +140,7 @@ func Test_NewObserver(t *testing.T) { chain: chains.Chain{ChainId: 111}, // invalid chain id btcClient: mocks.NewMockBTCRPCClient().WithBlockCount(100), chainParams: params, - coreContext: nil, + appContext: nil, coreClient: nil, tss: mocks.NewTSSMainnet(), dbpath: sample.CreateTempDir(t), @@ -153,7 +153,7 @@ func Test_NewObserver(t *testing.T) { name: "should fail on invalid dbpath", chain: chain, chainParams: params, - coreContext: nil, + appContext: nil, coreClient: nil, btcClient: mocks.NewMockBTCRPCClient().WithBlockCount(100), tss: mocks.NewTSSMainnet(), @@ -173,7 +173,7 @@ func Test_NewObserver(t *testing.T) { tt.chain, tt.btcClient, tt.chainParams, - tt.coreContext, + tt.appContext, tt.coreClient, tt.tss, tt.dbpath, diff --git a/zetaclient/chains/bitcoin/observer/outbound.go b/zetaclient/chains/bitcoin/observer/outbound.go index 5bf20c8e7d..4b481240eb 100644 --- a/zetaclient/chains/bitcoin/observer/outbound.go +++ b/zetaclient/chains/bitcoin/observer/outbound.go @@ -42,7 +42,7 @@ func (ob *Observer) WatchOutbound() { for { select { case <-ticker.C(): - if !context.IsOutboundObservationEnabled(ob.ZetacoreContext(), ob.GetChainParams()) { + if !context.IsOutboundObservationEnabled(ob.AppContext(), ob.GetChainParams()) { sampledLogger.Info(). Msgf("WatchOutbound: outbound observation is disabled for chain %d", chainID) continue diff --git a/zetaclient/chains/bitcoin/signer/signer.go b/zetaclient/chains/bitcoin/signer/signer.go index 6ad9bfc2a6..d1be79a4cb 100644 --- a/zetaclient/chains/bitcoin/signer/signer.go +++ b/zetaclient/chains/bitcoin/signer/signer.go @@ -57,13 +57,13 @@ type Signer struct { // NewSigner creates a new Bitcoin signer func NewSigner( chain chains.Chain, - zetacoreContext *context.ZetacoreContext, + appContext *context.AppContext, tss interfaces.TSSSigner, ts *metrics.TelemetryServer, logger base.Logger, cfg config.BTCConfig) (*Signer, error) { // create base signer - baseSigner := base.NewSigner(chain, zetacoreContext, tss, ts, logger) + baseSigner := base.NewSigner(chain, appContext, tss, ts, logger) // create the bitcoin rpc client using the provided config connCfg := &rpcclient.ConnConfig{ @@ -345,7 +345,7 @@ func (signer *Signer) TryProcessOutbound( logger.Error().Msgf("chain observer is not a bitcoin observer") return } - flags := signer.ZetacoreContext().GetCrossChainFlags() + flags := signer.AppContext().GetCrossChainFlags() if !flags.IsOutboundEnabled { logger.Info().Msgf("outbound is disabled") return diff --git a/zetaclient/chains/evm/observer/inbound.go b/zetaclient/chains/evm/observer/inbound.go index 37cc2e6134..d9411f1375 100644 --- a/zetaclient/chains/evm/observer/inbound.go +++ b/zetaclient/chains/evm/observer/inbound.go @@ -51,7 +51,7 @@ func (ob *Observer) WatchInbound() { for { select { case <-ticker.C(): - if !clientcontext.IsInboundObservationEnabled(ob.ZetacoreContext(), ob.GetChainParams()) { + if !clientcontext.IsInboundObservationEnabled(ob.AppContext(), ob.GetChainParams()) { sampledLogger.Info(). Msgf("WatchInbound: inbound observation is disabled for chain %d", ob.Chain().ChainId) continue @@ -81,11 +81,11 @@ func (ob *Observer) WatchInboundTracker() { } defer ticker.Stop() - ob.Logger().Inbound.Info().Msgf("Inbound tracker watcher started for chain %d", ob.Chain().ChainId) + ob.Logger().Inbound.Info().Msgf("WatchInboundTracker started for chain %d", ob.Chain().ChainId) for { select { case <-ticker.C(): - if !clientcontext.IsInboundObservationEnabled(ob.ZetacoreContext(), ob.GetChainParams()) { + if !clientcontext.IsInboundObservationEnabled(ob.AppContext(), ob.GetChainParams()) { continue } err := ob.ProcessInboundTrackers() @@ -391,7 +391,7 @@ func (ob *Observer) ObserverTSSReceive(startBlock, toBlock uint64) uint64 { // post new block header (if any) to zetacore and ignore error // TODO: consider having a independent ticker(from TSS scaning) for posting block headers // https://github.com/zeta-chain/node/issues/1847 - blockHeaderVerification, found := ob.ZetacoreContext().GetBlockHeaderEnabledChains(ob.Chain().ChainId) + blockHeaderVerification, found := ob.AppContext().GetBlockHeaderEnabledChains(ob.Chain().ChainId) if found && blockHeaderVerification.Enabled { // post block header for supported chains // TODO: move this logic in its own routine @@ -655,7 +655,7 @@ func (ob *Observer) BuildInboundVoteMsgForZetaSentEvent( } if !destChain.IsZetaChain() { - paramsDest, found := ob.ZetacoreContext().GetEVMChainParams(destChain.ChainId) + paramsDest, found := ob.AppContext().GetExternalChainParams(destChain.ChainId) if !found { ob.Logger().Inbound.Warn(). Msgf("chain id not present in EVMChainParams %d", event.DestinationChainId.Int64()) diff --git a/zetaclient/chains/evm/observer/observer.go b/zetaclient/chains/evm/observer/observer.go index 3db0538870..61633d2b33 100644 --- a/zetaclient/chains/evm/observer/observer.go +++ b/zetaclient/chains/evm/observer/observer.go @@ -57,7 +57,7 @@ func NewObserver( evmCfg config.EVMConfig, evmClient interfaces.EVMRPCClient, chainParams observertypes.ChainParams, - zetacoreContext *clientcontext.ZetacoreContext, + appContext *clientcontext.AppContext, zetacoreClient interfaces.ZetacoreClient, tss interfaces.TSSSigner, dbpath string, @@ -68,7 +68,7 @@ func NewObserver( baseObserver, err := base.NewObserver( evmCfg.Chain, chainParams, - zetacoreContext, + appContext, zetacoreClient, tss, base.DefaultBlockCacheSize, @@ -184,7 +184,7 @@ func (ob *Observer) Start() { // WatchRPCStatus watches the RPC status of the evm chain func (ob *Observer) WatchRPCStatus() { - ob.Logger().Chain.Info().Msgf("Starting RPC status check for chain %d", ob.Chain().ChainId) + ob.Logger().Chain.Info().Msgf("WatchRPCStatus started for chain %d", ob.Chain().ChainId) ticker := time.NewTicker(60 * time.Second) for { select { @@ -218,6 +218,7 @@ func (ob *Observer) WatchRPCStatus() { ob.Logger().Chain.Info(). Msgf("[OK] RPC status: latest block num %d, timestamp %s ( %.0fs ago), suggested gas price %d", header.Number, blockTime.String(), elapsedSeconds, gasPrice.Uint64()) case <-ob.StopChannel(): + ob.Logger().Chain.Info().Msgf("WatchRPCStatus stopped for chain %d", ob.Chain().ChainId) return } } @@ -320,7 +321,7 @@ func (ob *Observer) WatchGasPrice() { } ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.Logger().GasPrice) case <-ob.StopChannel(): - ob.Logger().GasPrice.Info().Msg("WatchGasPrice stopped") + ob.Logger().GasPrice.Info().Msgf("WatchGasPrice stopped for chain %d", ob.Chain().ChainId) return } } diff --git a/zetaclient/chains/evm/observer/observer_test.go b/zetaclient/chains/evm/observer/observer_test.go index f149d1bae2..f84d1567f9 100644 --- a/zetaclient/chains/evm/observer/observer_test.go +++ b/zetaclient/chains/evm/observer/observer_test.go @@ -7,6 +7,7 @@ import ( "testing" "cosmossdk.io/math" + "github.com/btcsuite/btcd/chaincfg" ethtypes "github.com/ethereum/go-ethereum/core/types" lru "github.com/hashicorp/golang-lru" "github.com/onrik/ethrpc" @@ -32,12 +33,12 @@ import ( // the relative path to the testdata directory var TestDataDir = "../../../" -// getZetacoreContext creates a zetacore context for unit tests -func getZetacoreContext( +// getAppContext creates a app context for unit tests +func getAppContext( evmChain chains.Chain, endpoint string, evmChainParams *observertypes.ChainParams, -) (*context.ZetacoreContext, config.EVMConfig) { +) (*context.AppContext, config.EVMConfig) { // use default endpoint if not provided if endpoint == "" { endpoint = "http://localhost:8545" @@ -50,17 +51,18 @@ func getZetacoreContext( Endpoint: endpoint, } - // create zetacore context - coreCtx := context.NewZetacoreContext(cfg) - evmChainParamsMap := make(map[int64]*observertypes.ChainParams) - evmChainParamsMap[evmChain.ChainId] = evmChainParams + // create app context + appCtx := context.NewAppContext(cfg) + newChainParams := make(map[int64]*observertypes.ChainParams) + newChainParams[evmChain.ChainId] = evmChainParams // feed chain params - coreCtx.Update( - &observertypes.Keygen{}, + appCtx.Update( + cfg, + observertypes.Keygen{}, []chains.Chain{evmChain}, - evmChainParamsMap, - nil, + newChainParams, + &chaincfg.RegressionNetParams, "", *sample.CrosschainFlags(), sample.HeaderSupportedChains(), @@ -68,7 +70,7 @@ func getZetacoreContext( zerolog.Logger{}, ) // create app context - return coreCtx, cfg.EVMChainConfigs[evmChain.ChainId] + return appCtx, cfg.EVMChainConfigs[evmChain.ChainId] } // MockEVMObserver creates a mock ChainObserver with custom chain, TSS, params etc @@ -96,11 +98,11 @@ func MockEVMObserver( if tss == nil { tss = mocks.NewTSSMainnet() } - // create zetacore context - coreCtx, evmCfg := getZetacoreContext(chain, "", ¶ms) + // create app context + appCtx, evmCfg := getAppContext(chain, "", ¶ms) // create observer - ob, err := observer.NewObserver(evmCfg, evmClient, params, coreCtx, zetacoreClient, tss, dbpath, base.Logger{}, nil) + ob, err := observer.NewObserver(evmCfg, evmClient, params, appCtx, zetacoreClient, tss, dbpath, base.Logger{}, nil) require.NoError(t, err) ob.WithEvmJSONRPC(evmJSONRPC) ob.WithLastBlock(lastBlock) @@ -175,8 +177,8 @@ func Test_NewObserver(t *testing.T) { // run tests for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // create zetacore context, client and tss - zetacoreCtx, _ := getZetacoreContext(tt.evmCfg.Chain, tt.evmCfg.Endpoint, ¶ms) + // create app context, client and tss + appCtx, _ := getAppContext(tt.evmCfg.Chain, tt.evmCfg.Endpoint, ¶ms) zetacoreClient := mocks.NewMockZetacoreClient().WithKeys(&keys.Keys{}) // create observer @@ -184,7 +186,7 @@ func Test_NewObserver(t *testing.T) { tt.evmCfg, tt.evmClient, tt.chainParams, - zetacoreCtx, + appCtx, zetacoreClient, tt.tss, tt.dbpath, diff --git a/zetaclient/chains/evm/observer/outbound.go b/zetaclient/chains/evm/observer/outbound.go index 9c3bd1c66b..6825893cd4 100644 --- a/zetaclient/chains/evm/observer/outbound.go +++ b/zetaclient/chains/evm/observer/outbound.go @@ -50,7 +50,7 @@ func (ob *Observer) WatchOutbound() { for { select { case <-ticker.C(): - if !clientcontext.IsOutboundObservationEnabled(ob.ZetacoreContext(), ob.GetChainParams()) { + if !clientcontext.IsOutboundObservationEnabled(ob.AppContext(), ob.GetChainParams()) { sampledLogger.Info(). Msgf("WatchOutbound: outbound observation is disabled for chain %d", ob.Chain().ChainId) continue @@ -88,7 +88,7 @@ func (ob *Observer) WatchOutbound() { } ticker.UpdateInterval(ob.GetChainParams().OutboundTicker, ob.Logger().Outbound) case <-ob.StopChannel(): - ob.Logger().Outbound.Info().Msg("WatchOutbound: stopped") + ob.Logger().Outbound.Info().Msgf("WatchOutbound: stopped for chain %d", ob.Chain().ChainId) return } } diff --git a/zetaclient/chains/evm/signer/signer.go b/zetaclient/chains/evm/signer/signer.go index 0b879314fc..cf1048c7a5 100644 --- a/zetaclient/chains/evm/signer/signer.go +++ b/zetaclient/chains/evm/signer/signer.go @@ -82,7 +82,7 @@ type Signer struct { // NewSigner creates a new EVM signer func NewSigner( chain chains.Chain, - zetacoreContext *clientcontext.ZetacoreContext, + appContext *clientcontext.AppContext, tss interfaces.TSSSigner, ts *metrics.TelemetryServer, logger base.Logger, @@ -93,7 +93,7 @@ func NewSigner( erc20CustodyAddress ethcommon.Address, ) (*Signer, error) { // create base signer - baseSigner := base.NewSigner(chain, zetacoreContext, tss, ts, logger) + baseSigner := base.NewSigner(chain, appContext, tss, ts, logger) // create EVM client client, ethSigner, err := getEVMRPC(endpoint) @@ -375,7 +375,7 @@ func (signer *Signer) TryProcessOutbound( toChain := chains.GetChainFromChainID(txData.toChainID.Int64()) // Get cross-chain flags - crossChainflags := signer.ZetacoreContext().GetCrossChainFlags() + crossChainflags := signer.AppContext().GetCrossChainFlags() // https://github.com/zeta-chain/node/issues/2050 var tx *ethtypes.Transaction // compliance check goes first diff --git a/zetaclient/chains/evm/signer/signer_test.go b/zetaclient/chains/evm/signer/signer_test.go index ea27152b97..de69dc6068 100644 --- a/zetaclient/chains/evm/signer/signer_test.go +++ b/zetaclient/chains/evm/signer/signer_test.go @@ -47,7 +47,7 @@ func getNewEvmSigner(tss interfaces.TSSSigner) (*Signer, error) { return NewSigner( chains.BscMainnet, - context.NewZetacoreContext(cfg), + context.NewAppContext(cfg), tss, nil, logger, @@ -71,7 +71,7 @@ func getNewEvmChainObserver(t *testing.T, tss interfaces.TSSSigner) (*observer.O evmClient := mocks.NewMockEvmClient().WithBlockNumber(1000) params := mocks.MockChainParams(evmcfg.Chain.ChainId, 10) cfg.EVMChainConfigs[chains.BscMainnet.ChainId] = evmcfg - coreCTX := context.NewZetacoreContext(cfg) + appCTX := context.NewAppContext(cfg) dbpath := sample.CreateTempDir(t) logger := base.Logger{} ts := &metrics.TelemetryServer{} @@ -80,7 +80,7 @@ func getNewEvmChainObserver(t *testing.T, tss interfaces.TSSSigner) (*observer.O evmcfg, evmClient, params, - coreCTX, + appCTX, mocks.NewMockZetacoreClient(), tss, dbpath, diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index f4385ca1c1..ffe5dfca97 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -66,8 +66,8 @@ type ChainSigner interface { // ZetacoreClient is the client interface to interact with zetacore type ZetacoreClient interface { - GetLatestZetacoreContext() (*clientcontext.ZetacoreContext, error) - UpdateZetacoreContext(coreContext *clientcontext.ZetacoreContext, init bool, sampledLogger zerolog.Logger) error + GetLatestAppContext() (*clientcontext.AppContext, error) + UpdateAppContext(appContext *clientcontext.AppContext, init bool, sampledLogger zerolog.Logger) error GetUpgradePlan() (*upgradetypes.Plan, error) GetChainParams() ([]*observertypes.ChainParams, error) GetSupportedChains() ([]*chains.Chain, error) diff --git a/zetaclient/context/app_context.go b/zetaclient/context/app_context.go index b15cbe261d..1ac888c992 100644 --- a/zetaclient/context/app_context.go +++ b/zetaclient/context/app_context.go @@ -1,55 +1,271 @@ package context import ( + "sort" + "sync" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/rs/zerolog" + "github.com/zeta-chain/zetacore/pkg/chains" + lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/config" ) -// AppContext contains global app structs like config, zetacore context and logger +// AppContext contains zetaclient application context +// these are initialized and updated at runtime periodically type AppContext struct { - coreContext *ZetacoreContext - config config.Config + config config.Config + keygen observertypes.Keygen + chainsEnabled []chains.Chain + chainParamMap map[int64]*observertypes.ChainParams + btcNetParams *chaincfg.Params + currentTssPubkey string + crosschainFlags observertypes.CrosschainFlags + + // blockHeaderEnabledChains is used to store the list of chains that have block header verification enabled + // All chains in this list will have Enabled flag set to true + blockHeaderEnabledChains []lightclienttypes.HeaderSupportedChain + + // mu is to protect the app context from concurrent access + mu *sync.RWMutex } // NewAppContext creates and returns new AppContext -func NewAppContext( - coreContext *ZetacoreContext, - config config.Config, +// it is initializing chain params from provided config +func NewAppContext(cfg config.Config) *AppContext { + return &AppContext{ + config: cfg, + chainsEnabled: []chains.Chain{}, + chainParamMap: make(map[int64]*observertypes.ChainParams), + crosschainFlags: observertypes.CrosschainFlags{}, + blockHeaderEnabledChains: []lightclienttypes.HeaderSupportedChain{}, + mu: new(sync.RWMutex), + } +} + +// CreateAppContext creates a new AppContext +func CreateAppContext( + keygen observertypes.Keygen, + chainsEnabled []chains.Chain, + chainParamMap map[int64]*observertypes.ChainParams, + btcNetParams *chaincfg.Params, + tssPubKey string, + crosschainFlags observertypes.CrosschainFlags, + blockHeaderEnabledChains []lightclienttypes.HeaderSupportedChain, ) *AppContext { return &AppContext{ - coreContext: coreContext, - config: config, + mu: new(sync.RWMutex), + keygen: keygen, + chainsEnabled: chainsEnabled, + chainParamMap: chainParamMap, + btcNetParams: btcNetParams, + currentTssPubkey: tssPubKey, + crosschainFlags: crosschainFlags, + blockHeaderEnabledChains: blockHeaderEnabledChains, + } +} + +// Config returns the app context config +func (c *AppContext) Config() config.Config { + c.mu.RLock() + defer c.mu.RUnlock() + return c.config +} + +func (c *AppContext) GetKeygen() observertypes.Keygen { + c.mu.RLock() + defer c.mu.RUnlock() + + var copiedPubkeys []string + if c.keygen.GranteePubkeys != nil { + copiedPubkeys = make([]string, len(c.keygen.GranteePubkeys)) + copy(copiedPubkeys, c.keygen.GranteePubkeys) + } + + return observertypes.Keygen{ + Status: c.keygen.Status, + GranteePubkeys: copiedPubkeys, + BlockNumber: c.keygen.BlockNumber, + } +} + +func (c *AppContext) GetCurrentTssPubkey() string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.currentTssPubkey +} + +// GetEnabledExternalChains returns all enabled external chains (excluding zetachain) +func (c *AppContext) GetEnabledExternalChains() []chains.Chain { + c.mu.RLock() + defer c.mu.RUnlock() + + externalChains := make([]chains.Chain, 0) + for _, chain := range c.chainsEnabled { + if chain.IsExternal { + externalChains = append(externalChains, chain) + } + } + return externalChains +} + +// GetEnabledBTCChains returns the enabled bitcoin chains +func (c *AppContext) GetEnabledBTCChains() []chains.Chain { + c.mu.RLock() + defer c.mu.RUnlock() + + btcChains := make([]chains.Chain, 0) + for _, chain := range c.chainsEnabled { + if chain.Consensus == chains.Consensus_bitcoin { + btcChains = append(btcChains, chain) + } + } + return btcChains +} + +// GetEnabledExternalChainParams returns all enabled chain params +func (c *AppContext) GetEnabledExternalChainParams() map[int64]*observertypes.ChainParams { + c.mu.RLock() + defer c.mu.RUnlock() + + // deep copy chain params + copied := make(map[int64]*observertypes.ChainParams, len(c.chainParamMap)) + for chainID, chainParams := range c.chainParamMap { + copied[chainID] = &observertypes.ChainParams{} + *copied[chainID] = *chainParams } + return copied +} + +func (c *AppContext) GetExternalChainParams(chainID int64) (*observertypes.ChainParams, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + + chainParams, found := c.chainParamMap[chainID] + return chainParams, found } -func (a AppContext) Config() config.Config { - return a.config +// GetBTCNetParams returns bitcoin network params +func (c *AppContext) GetBTCNetParams() *chaincfg.Params { + c.mu.RLock() + defer c.mu.RUnlock() + return c.btcNetParams } -func (a AppContext) ZetacoreContext() *ZetacoreContext { - return a.coreContext +// GetCrossChainFlags returns crosschain flags +func (c *AppContext) GetCrossChainFlags() observertypes.CrosschainFlags { + c.mu.RLock() + defer c.mu.RUnlock() + return c.crosschainFlags } -// GetBTCChainAndConfig returns btc chain and config if enabled -func (a AppContext) GetBTCChainAndConfig() (chains.Chain, config.BTCConfig, bool) { - btcConfig, configEnabled := a.Config().GetBTCConfig() - btcChain, _, paramsEnabled := a.coreContext.GetBTCChainParams() +// GetAllHeaderEnabledChains returns all verification flags +func (c *AppContext) GetAllHeaderEnabledChains() []lightclienttypes.HeaderSupportedChain { + c.mu.RLock() + defer c.mu.RUnlock() + return c.blockHeaderEnabledChains +} - if !configEnabled || !paramsEnabled { - return chains.Chain{}, config.BTCConfig{}, false +// GetBlockHeaderEnabledChains checks if block header verification is enabled for a specific chain +func (c *AppContext) GetBlockHeaderEnabledChains(chainID int64) (lightclienttypes.HeaderSupportedChain, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + for _, flags := range c.blockHeaderEnabledChains { + if flags.ChainId == chainID { + return flags, true + } } + return lightclienttypes.HeaderSupportedChain{}, false +} - return btcChain, btcConfig, true +// Update updates the inner config and app context +func (c *AppContext) UpdateContext(config config.Config, newContext *AppContext, logger zerolog.Logger) { + c.Update( + config, + newContext.GetKeygen(), + newContext.GetEnabledExternalChains(), + newContext.GetEnabledExternalChainParams(), + newContext.GetBTCNetParams(), + newContext.GetCurrentTssPubkey(), + newContext.GetCrossChainFlags(), + newContext.GetAllHeaderEnabledChains(), + false, + logger, + ) } -// GetBTCChainID returns btc chain id if enabled or regnet chain id by default -// Bitcoin chain ID is currently needed by TSS to calculate the correct Bitcoin address -// TODO: we might have multiple BTC chains in the future: https://github.com/zeta-chain/node/issues/1397 -func (a AppContext) GetBTCChainID() int64 { - bitcoinChainID := chains.BitcoinRegtest.ChainId - btcChain, _, enabled := a.GetBTCChainAndConfig() - if enabled { - bitcoinChainID = btcChain.ChainId +// Update updates app context and params for all chains +// this must be the ONLY function that writes to app context +func (c *AppContext) Update( + config config.Config, + keygen observertypes.Keygen, + newChains []chains.Chain, + newChainParams map[int64]*observertypes.ChainParams, + btcNetParams *chaincfg.Params, + tssPubKey string, + crosschainFlags observertypes.CrosschainFlags, + blockHeaderEnabledChains []lightclienttypes.HeaderSupportedChain, + init bool, + logger zerolog.Logger, +) { + c.mu.Lock() + defer c.mu.Unlock() + + // Ignore whatever order zetacore organizes chain list in state + sort.SliceStable(newChains, func(i, j int) bool { + return newChains[i].ChainId < newChains[j].ChainId + }) + + if len(newChains) == 0 { + logger.Warn().Msg("UpdateChainParams: No chains enabled in ZeroCore") + } + + // Add some warnings if chain list changes at runtime + if !init { + if len(c.chainsEnabled) != len(newChains) { + logger.Warn().Msgf( + "UpdateChainParams: ChainsEnabled changed at runtime!! current: %v, new: %v", + c.chainsEnabled, + newChains, + ) + } else { + for i, chain := range newChains { + if chain != c.chainsEnabled[i] { + logger.Warn().Msgf( + "UpdateChainParams: ChainsEnabled changed at runtime!! current: %v, new: %v", + c.chainsEnabled, + newChains, + ) + } + } + } + } + + // btcNetParams points one of [mainnet, testnet, regnet] + // btcNetParams initialize only once and should never change + if c.btcNetParams == nil { + c.btcNetParams = btcNetParams } - return bitcoinChainID + + c.config = config + c.keygen = keygen + c.chainsEnabled = newChains + c.chainParamMap = newChainParams + c.currentTssPubkey = tssPubKey + c.crosschainFlags = crosschainFlags + c.blockHeaderEnabledChains = blockHeaderEnabledChains +} + +// IsOutboundObservationEnabled returns true if the chain is supported and outbound flag is enabled +func IsOutboundObservationEnabled(c *AppContext, chainParams observertypes.ChainParams) bool { + flags := c.GetCrossChainFlags() + return chainParams.IsSupported && flags.IsOutboundEnabled +} + +// IsInboundObservationEnabled returns true if the chain is supported and inbound flag is enabled +func IsInboundObservationEnabled(c *AppContext, chainParams observertypes.ChainParams) bool { + flags := c.GetCrossChainFlags() + return chainParams.IsSupported && flags.IsInboundEnabled } diff --git a/zetaclient/context/app_context_test.go b/zetaclient/context/app_context_test.go index e2f27738cb..9f1ff6ca97 100644 --- a/zetaclient/context/app_context_test.go +++ b/zetaclient/context/app_context_test.go @@ -3,61 +3,381 @@ package context_test import ( "testing" + "github.com/btcsuite/btcd/chaincfg" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/testutil/sample" + lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/config" context "github.com/zeta-chain/zetacore/zetaclient/context" ) -func Test_GetBTCChainID(t *testing.T) { - // test chains and params - evmChain := chains.Ethereum - btcChain := chains.BitcoinMainnet - evmParams := &observertypes.ChainParams{ - ChainId: evmChain.ChainId, +// getTestAppContext creates a test app context with provided chain params and flags +func getTestAppContext( + evmChain chains.Chain, + evmChainParams *observertypes.ChainParams, + btcChainParams *observertypes.ChainParams, + ccFlags *observertypes.CrosschainFlags, + headerSupportedChains []lightclienttypes.HeaderSupportedChain, +) *context.AppContext { + // create config + cfg := config.NewConfig() + cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ + Chain: evmChain, + } + if btcChainParams != nil { + cfg.BitcoinConfig = config.BTCConfig{ + RPCUsername: "test", + } } - btcParams := &observertypes.ChainParams{ - ChainId: btcChain.ChainId, + + // create app context + appContext := context.NewAppContext(cfg) + newChainParams := make(map[int64]*observertypes.ChainParams) + newChainParams[evmChain.ChainId] = evmChainParams + newChainParams[btcChainParams.ChainId] = btcChainParams + + // create crosschain flags if not provided + if ccFlags == nil { + ccFlags = sample.CrosschainFlags() } - t.Run("GetBTCChainID returns regnet chain id if btc chain is not enabled in config", func(t *testing.T) { - // config without btc chain - cfg := config.NewConfig() + // feed chain params + appContext.Update( + cfg, + observertypes.Keygen{}, + []chains.Chain{evmChain}, + newChainParams, + &chaincfg.RegressionNetParams, + "", + *ccFlags, + headerSupportedChains, + true, + zerolog.Logger{}, + ) + return appContext +} + +func TestNewAppContext(t *testing.T) { + t.Run("should create new app context with empty config", func(t *testing.T) { + testCfg := config.NewConfig() + + zetaContext := context.NewAppContext(testCfg) + require.NotNil(t, zetaContext) + + // assert keygen + keyGen := zetaContext.GetKeygen() + require.Equal(t, observertypes.Keygen{}, keyGen) + + // assert external chains + require.Empty(t, len(zetaContext.GetEnabledExternalChains())) - // create app context without BTC chain - coreContext := getTestCoreContext(evmChain, evmParams, nil, nil, nil) - appContext := context.NewAppContext(coreContext, cfg) + // assert current tss pubkey + require.Equal(t, "", zetaContext.GetCurrentTssPubkey()) - btcChainID := appContext.GetBTCChainID() - require.Equal(t, chains.BitcoinRegtest.ChainId, btcChainID) + // assert external chain params + externalChainParams := zetaContext.GetEnabledExternalChainParams() + require.Empty(t, externalChainParams) }) - t.Run("GetBTCChainID returns regnet chain id if btc chain params are not enabled", func(t *testing.T) { - // config with btc chain - cfg := config.NewConfig() - cfg.BitcoinConfig = config.BTCConfig{ - RPCUsername: "user", + + t.Run("should return nil chain params if chain id is not found", func(t *testing.T) { + // create config with btc config + testCfg := config.NewConfig() + testCfg.BitcoinConfig = config.BTCConfig{ + RPCUsername: "test_user", + RPCPassword: "test_password", + } + + // create app context with 0 chain id + zetaContext := context.NewAppContext(testCfg) + require.NotNil(t, zetaContext) + }) + + t.Run("should create new app context with config containing evm chain params", func(t *testing.T) { + testCfg := config.NewConfig() + testCfg.EVMChainConfigs = map[int64]config.EVMConfig{ + 1: { + Chain: chains.Chain{ + ChainName: 1, + ChainId: 1, + }, + }, + 2: { + Chain: chains.Chain{ + ChainName: 2, + ChainId: 2, + }, + }, } + zetaContext := context.NewAppContext(testCfg) + require.NotNil(t, zetaContext) - // create app context without BTC chain params - coreContext := getTestCoreContext(evmChain, evmParams, nil, nil, nil) - appContext := context.NewAppContext(coreContext, cfg) + // assert external chain params + externalChainParams := zetaContext.GetEnabledExternalChainParams() + require.Equal(t, 2, len(externalChainParams)) + require.Equal(t, &observertypes.ChainParams{}, externalChainParams[1]) + require.Equal(t, &observertypes.ChainParams{}, externalChainParams[2]) - btcChainID := appContext.GetBTCChainID() - require.Equal(t, chains.BitcoinRegtest.ChainId, btcChainID) + chainParams1, found := zetaContext.GetExternalChainParams(1) + require.True(t, found) + require.Equal(t, &observertypes.ChainParams{}, chainParams1) + + chainParams2, found := zetaContext.GetExternalChainParams(2) + require.True(t, found) + require.Equal(t, &observertypes.ChainParams{}, chainParams2) }) - t.Run("GetBTCChainID returns btc chain id if enabled", func(t *testing.T) { - // config with btc chain - cfg := config.NewConfig() - cfg.BitcoinConfig = config.BTCConfig{ - RPCUsername: "user", + + t.Run("should create new app context with config containing btc config", func(t *testing.T) { + testCfg := config.NewConfig() + testCfg.BitcoinConfig = config.BTCConfig{ + RPCUsername: "test username", + RPCPassword: "test password", + RPCHost: "test host", + RPCParams: "test params", + } + zetaContext := context.NewAppContext(testCfg) + require.NotNil(t, zetaContext) + }) +} + +func TestUpdateAppContext(t *testing.T) { + t.Run("should update app context after being created from empty config", func(t *testing.T) { + testCfg := config.NewConfig() + + zetaContext := context.NewAppContext(testCfg) + require.NotNil(t, zetaContext) + + keyGenToUpdate := observertypes.Keygen{ + Status: observertypes.KeygenStatus_KeyGenSuccess, + GranteePubkeys: []string{"testpubkey1"}, + } + enabledChainsToUpdate := []chains.Chain{ + { + ChainName: 1, + ChainId: 1, + IsExternal: true, + }, + { + ChainName: 2, + ChainId: 2, + IsExternal: true, + }, + chains.ZetaChainTestnet, + } + newChainParamsToUpdate := map[int64]*observertypes.ChainParams{ + 1: { + ChainId: 1, + }, + 2: { + ChainId: 2, + }, + 3: { + ChainId: 3, + }, + } + tssPubKeyToUpdate := "tsspubkeytest" + crosschainFlags := sample.CrosschainFlags() + verificationFlags := sample.HeaderSupportedChains() + + require.NotNil(t, crosschainFlags) + zetaContext.Update( + testCfg, + keyGenToUpdate, + enabledChainsToUpdate, + newChainParamsToUpdate, + &chaincfg.RegressionNetParams, + tssPubKeyToUpdate, + *crosschainFlags, + verificationFlags, + false, + log.Logger, + ) + + // assert keygen updated + keyGen := zetaContext.GetKeygen() + require.Equal(t, keyGenToUpdate, keyGen) + + // assert enabled external chains + require.Equal(t, enabledChainsToUpdate[0:2], zetaContext.GetEnabledExternalChains()) + + // assert current tss pubkey updated + require.Equal(t, tssPubKeyToUpdate, zetaContext.GetCurrentTssPubkey()) + + // assert evm chain params still empty because they were not specified in config + externalChainParams := zetaContext.GetEnabledExternalChainParams() + require.Empty(t, externalChainParams) + + ccFlags := zetaContext.GetCrossChainFlags() + require.Equal(t, *crosschainFlags, ccFlags) + + verFlags := zetaContext.GetAllHeaderEnabledChains() + require.Equal(t, verificationFlags, verFlags) + }) + + t.Run( + "should update app context after being created from config with evm and btc chain params", + func(t *testing.T) { + testCfg := config.NewConfig() + testCfg.EVMChainConfigs = map[int64]config.EVMConfig{ + 1: { + Chain: chains.Chain{ + ChainName: 1, + ChainId: 1, + }, + }, + 2: { + Chain: chains.Chain{ + ChainName: 2, + ChainId: 2, + }, + }, + } + testCfg.BitcoinConfig = config.BTCConfig{ + RPCUsername: "test username", + RPCPassword: "test password", + RPCHost: "test host", + RPCParams: "test params", + } + + zetaContext := context.NewAppContext(testCfg) + require.NotNil(t, zetaContext) + + keyGenToUpdate := observertypes.Keygen{ + Status: observertypes.KeygenStatus_KeyGenSuccess, + GranteePubkeys: []string{"testpubkey1"}, + } + enabledChainsToUpdate := []chains.Chain{ + { + ChainName: 1, + ChainId: 1, + IsExternal: true, + }, + { + ChainName: 2, + ChainId: 2, + IsExternal: true, + }, + } + newChainParamsToUpdate := map[int64]*observertypes.ChainParams{ + 1: { + ChainId: 1, + }, + 2: { + ChainId: 2, + }, + chains.BitcoinTestnet.ChainId: { + ChainId: chains.BitcoinTestnet.ChainId, + }, + } + + tssPubKeyToUpdate := "tsspubkeytest" + crosschainFlags := sample.CrosschainFlags() + verificationFlags := sample.HeaderSupportedChains() + require.NotNil(t, crosschainFlags) + zetaContext.Update( + testCfg, + keyGenToUpdate, + enabledChainsToUpdate, + newChainParamsToUpdate, + &chaincfg.RegressionNetParams, + tssPubKeyToUpdate, + *crosschainFlags, + verificationFlags, + false, + log.Logger, + ) + + // assert keygen updated + keyGen := zetaContext.GetKeygen() + require.Equal(t, keyGenToUpdate, keyGen) + + // assert enabled chains updated + require.Equal(t, enabledChainsToUpdate, zetaContext.GetEnabledExternalChains()) + + // assert current tss pubkey updated + require.Equal(t, tssPubKeyToUpdate, zetaContext.GetCurrentTssPubkey()) + + // assert external chain params + externalChainParams := zetaContext.GetEnabledExternalChainParams() + require.Equal(t, newChainParamsToUpdate, externalChainParams) + + chainParams1, found := zetaContext.GetExternalChainParams(1) + require.True(t, found) + require.Equal(t, newChainParamsToUpdate[1], chainParams1) + + chainParams2, found := zetaContext.GetExternalChainParams(2) + require.True(t, found) + require.Equal(t, newChainParamsToUpdate[2], chainParams2) + + ccFlags := zetaContext.GetCrossChainFlags() + require.Equal(t, ccFlags, *crosschainFlags) + + verFlags := zetaContext.GetAllHeaderEnabledChains() + require.Equal(t, verFlags, verificationFlags) + }, + ) +} + +func TestIsOutboundObservationEnabled(t *testing.T) { + // create test chain params and flags + evmChain := chains.Ethereum + ccFlags := *sample.CrosschainFlags() + verificationFlags := sample.HeaderSupportedChains() + chainParams := &observertypes.ChainParams{ + ChainId: evmChain.ChainId, + IsSupported: true, + } + + t.Run("should return true if chain is supported and outbound flag is enabled", func(t *testing.T) { + appCTX := getTestAppContext(evmChain, chainParams, nil, &ccFlags, verificationFlags) + require.True(t, context.IsOutboundObservationEnabled(appCTX, *chainParams)) + }) + t.Run("should return false if chain is not supported yet", func(t *testing.T) { + paramsUnsupported := &observertypes.ChainParams{ + ChainId: evmChain.ChainId, + IsSupported: false, } + appCTXUnsupported := getTestAppContext(evmChain, paramsUnsupported, nil, &ccFlags, verificationFlags) + require.False(t, context.IsOutboundObservationEnabled(appCTXUnsupported, *paramsUnsupported)) + }) + t.Run("should return false if outbound flag is disabled", func(t *testing.T) { + flagsDisabled := ccFlags + flagsDisabled.IsOutboundEnabled = false + appCTXDisabled := getTestAppContext(evmChain, chainParams, nil, &flagsDisabled, verificationFlags) + require.False(t, context.IsOutboundObservationEnabled(appCTXDisabled, *chainParams)) + }) +} - // create app context with BTC chain - coreContext := getTestCoreContext(evmChain, evmParams, btcParams, nil, nil) - appContext := context.NewAppContext(coreContext, cfg) +func TestIsInboundObservationEnabled(t *testing.T) { + // create test chain params and flags + evmChain := chains.Ethereum + ccFlags := *sample.CrosschainFlags() + verificationFlags := sample.HeaderSupportedChains() + chainParams := &observertypes.ChainParams{ + ChainId: evmChain.ChainId, + IsSupported: true, + } - btcChainID := appContext.GetBTCChainID() - require.Equal(t, btcChain.ChainId, btcChainID) + t.Run("should return true if chain is supported and inbound flag is enabled", func(t *testing.T) { + appCTX := getTestAppContext(evmChain, chainParams, nil, &ccFlags, verificationFlags) + require.True(t, context.IsInboundObservationEnabled(appCTX, *chainParams)) + }) + t.Run("should return false if chain is not supported yet", func(t *testing.T) { + paramsUnsupported := &observertypes.ChainParams{ + ChainId: evmChain.ChainId, + IsSupported: false, + } + appCTXUnsupported := getTestAppContext(evmChain, paramsUnsupported, nil, &ccFlags, verificationFlags) + require.False(t, context.IsInboundObservationEnabled(appCTXUnsupported, *paramsUnsupported)) + }) + t.Run("should return false if inbound flag is disabled", func(t *testing.T) { + flagsDisabled := ccFlags + flagsDisabled.IsInboundEnabled = false + appCTXDisabled := getTestAppContext(evmChain, chainParams, nil, &flagsDisabled, verificationFlags) + require.False(t, context.IsInboundObservationEnabled(appCTXDisabled, *chainParams)) }) } diff --git a/zetaclient/context/zetacore_context.go b/zetaclient/context/zetacore_context.go deleted file mode 100644 index 82f5b28838..0000000000 --- a/zetaclient/context/zetacore_context.go +++ /dev/null @@ -1,265 +0,0 @@ -package context - -import ( - "sort" - "sync" - - "github.com/rs/zerolog" - - "github.com/zeta-chain/zetacore/pkg/chains" - lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient/config" -) - -// ZetacoreContext contains zetacore context params -// these are initialized and updated at runtime at every height -type ZetacoreContext struct { - mu *sync.RWMutex - keygen observertypes.Keygen - chainsEnabled []chains.Chain - chainParamMap map[int64]*observertypes.ChainParams - evmChainParams map[int64]*observertypes.ChainParams - bitcoinChainParams *observertypes.ChainParams - currentTssPubkey string - crosschainFlags observertypes.CrosschainFlags - - // blockHeaderEnabledChains is used to store the list of chains that have block header verification enabled - // All chains in this list will have Enabled flag set to true - blockHeaderEnabledChains []lightclienttypes.HeaderSupportedChain -} - -// NewZetacoreContext creates and returns new ZetacoreContext -// it is initializing chain params from provided config -func NewZetacoreContext(cfg config.Config) *ZetacoreContext { - evmChainParams := make(map[int64]*observertypes.ChainParams) - chainParamsMap := make(map[int64]*observertypes.ChainParams) - for _, e := range cfg.EVMChainConfigs { - evmChainParams[e.Chain.ChainId] = &observertypes.ChainParams{} - chainParamsMap[e.Chain.ChainId] = &observertypes.ChainParams{} - } - - var bitcoinChainParams *observertypes.ChainParams - _, found := cfg.GetBTCConfig() - if found { - bitcoinChainParams = &observertypes.ChainParams{} - } - - return &ZetacoreContext{ - mu: new(sync.RWMutex), - chainsEnabled: []chains.Chain{}, - evmChainParams: evmChainParams, - bitcoinChainParams: bitcoinChainParams, - crosschainFlags: observertypes.CrosschainFlags{}, - blockHeaderEnabledChains: []lightclienttypes.HeaderSupportedChain{}, - } -} - -// CreateZetacoreContext creates and returns new ZetacoreContext -func CreateZetacoreContext( - keygen *observertypes.Keygen, - chainsEnabled []chains.Chain, - chainParamMap map[int64]*observertypes.ChainParams, - evmChainParams map[int64]*observertypes.ChainParams, - bitcoinChainParams *observertypes.ChainParams, - tssPubKey string, - crosschainFlags observertypes.CrosschainFlags, - blockHeaderEnabledChains []lightclienttypes.HeaderSupportedChain, -) *ZetacoreContext { - return &ZetacoreContext{ - mu: new(sync.RWMutex), - keygen: *keygen, - chainsEnabled: chainsEnabled, - chainParamMap: chainParamMap, - evmChainParams: evmChainParams, - bitcoinChainParams: bitcoinChainParams, - currentTssPubkey: tssPubKey, - crosschainFlags: crosschainFlags, - blockHeaderEnabledChains: blockHeaderEnabledChains, - } -} - -func (c *ZetacoreContext) GetKeygen() observertypes.Keygen { - c.mu.RLock() - defer c.mu.RUnlock() - - var copiedPubkeys []string - if c.keygen.GranteePubkeys != nil { - copiedPubkeys = make([]string, len(c.keygen.GranteePubkeys)) - copy(copiedPubkeys, c.keygen.GranteePubkeys) - } - - return observertypes.Keygen{ - Status: c.keygen.Status, - GranteePubkeys: copiedPubkeys, - BlockNumber: c.keygen.BlockNumber, - } -} - -func (c *ZetacoreContext) GetCurrentTssPubkey() string { - c.mu.RLock() - defer c.mu.RUnlock() - return c.currentTssPubkey -} - -// GetEnabledExternalChains returns all enabled external chains (excluding zetachain) -func (c *ZetacoreContext) GetEnabledExternalChains() []chains.Chain { - c.mu.RLock() - defer c.mu.RUnlock() - - externalChains := make([]chains.Chain, 0) - for _, chain := range c.chainsEnabled { - if chain.IsExternal { - externalChains = append(externalChains, chain) - } - } - return externalChains -} - -func (c *ZetacoreContext) GetEVMChainParams(chainID int64) (*observertypes.ChainParams, bool) { - c.mu.RLock() - defer c.mu.RUnlock() - - evmChainParams, found := c.evmChainParams[chainID] - return evmChainParams, found -} - -func (c *ZetacoreContext) GetAllEVMChainParams() map[int64]*observertypes.ChainParams { - c.mu.RLock() - defer c.mu.RUnlock() - - // deep copy evm chain params - copied := make(map[int64]*observertypes.ChainParams, len(c.evmChainParams)) - for chainID, evmConfig := range c.evmChainParams { - copied[chainID] = &observertypes.ChainParams{} - *copied[chainID] = *evmConfig - } - return copied -} - -// GetBTCChainParams returns (chain, chain params, found) for bitcoin chain -func (c *ZetacoreContext) GetBTCChainParams() (chains.Chain, *observertypes.ChainParams, bool) { - c.mu.RLock() - defer c.mu.RUnlock() - - if c.bitcoinChainParams == nil { // bitcoin is not enabled - return chains.Chain{}, nil, false - } - - chain := chains.GetChainFromChainID(c.bitcoinChainParams.ChainId) - if chain == nil { - return chains.Chain{}, nil, false - } - - return *chain, c.bitcoinChainParams, true -} - -func (c *ZetacoreContext) GetCrossChainFlags() observertypes.CrosschainFlags { - c.mu.RLock() - defer c.mu.RUnlock() - return c.crosschainFlags -} - -// GetAllHeaderEnabledChains returns all verification flags -func (c *ZetacoreContext) GetAllHeaderEnabledChains() []lightclienttypes.HeaderSupportedChain { - c.mu.RLock() - defer c.mu.RUnlock() - return c.blockHeaderEnabledChains -} - -// GetBlockHeaderEnabledChains checks if block header verification is enabled for a specific chain -func (c *ZetacoreContext) GetBlockHeaderEnabledChains(chainID int64) (lightclienttypes.HeaderSupportedChain, bool) { - c.mu.RLock() - defer c.mu.RUnlock() - for _, flags := range c.blockHeaderEnabledChains { - if flags.ChainId == chainID { - return flags, true - } - } - return lightclienttypes.HeaderSupportedChain{}, false -} - -// Update updates zetacore context and params for all chains -// this must be the ONLY function that writes to zetacore context -func (c *ZetacoreContext) Update( - keygen *observertypes.Keygen, - newChains []chains.Chain, - evmChainParams map[int64]*observertypes.ChainParams, - btcChainParams *observertypes.ChainParams, - tssPubKey string, - crosschainFlags observertypes.CrosschainFlags, - blockHeaderEnabledChains []lightclienttypes.HeaderSupportedChain, - init bool, - logger zerolog.Logger, -) { - c.mu.Lock() - defer c.mu.Unlock() - - // Ignore whatever order zetacore organizes chain list in state - sort.SliceStable(newChains, func(i, j int) bool { - return newChains[i].ChainId < newChains[j].ChainId - }) - - if len(newChains) == 0 { - logger.Warn().Msg("UpdateChainParams: No chains enabled in ZeroCore") - } - - // Add some warnings if chain list changes at runtime - if !init { - if len(c.chainsEnabled) != len(newChains) { - logger.Warn().Msgf( - "UpdateChainParams: ChainsEnabled changed at runtime!! current: %v, new: %v", - c.chainsEnabled, - newChains, - ) - } else { - for i, chain := range newChains { - if chain != c.chainsEnabled[i] { - logger.Warn().Msgf( - "UpdateChainParams: ChainsEnabled changed at runtime!! current: %v, new: %v", - c.chainsEnabled, - newChains, - ) - } - } - } - } - - if keygen != nil { - c.keygen = *keygen - } - - c.chainsEnabled = newChains - c.crosschainFlags = crosschainFlags - c.blockHeaderEnabledChains = blockHeaderEnabledChains - - // update chain params for bitcoin if it has config in file - if c.bitcoinChainParams != nil && btcChainParams != nil { - c.bitcoinChainParams = btcChainParams - } - - // update core params for evm chains we have configs in file - for _, params := range evmChainParams { - _, found := c.evmChainParams[params.ChainId] - if !found { - continue - } - c.evmChainParams[params.ChainId] = params - } - - if tssPubKey != "" { - c.currentTssPubkey = tssPubKey - } -} - -// IsOutboundObservationEnabled returns true if the chain is supported and outbound flag is enabled -func IsOutboundObservationEnabled(c *ZetacoreContext, chainParams observertypes.ChainParams) bool { - flags := c.GetCrossChainFlags() - return chainParams.IsSupported && flags.IsOutboundEnabled -} - -// IsInboundObservationEnabled returns true if the chain is supported and inbound flag is enabled -func IsInboundObservationEnabled(c *ZetacoreContext, chainParams observertypes.ChainParams) bool { - flags := c.GetCrossChainFlags() - return chainParams.IsSupported && flags.IsInboundEnabled -} diff --git a/zetaclient/context/zetacore_context_test.go b/zetaclient/context/zetacore_context_test.go deleted file mode 100644 index f84c05205e..0000000000 --- a/zetaclient/context/zetacore_context_test.go +++ /dev/null @@ -1,403 +0,0 @@ -package context_test - -import ( - "testing" - - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "github.com/stretchr/testify/require" - - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/testutil/sample" - lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient/config" - context "github.com/zeta-chain/zetacore/zetaclient/context" -) - -// getTestCoreContext creates a test zetacore context with provided chain params and flags -func getTestCoreContext( - evmChain chains.Chain, - evmChainParams *observertypes.ChainParams, - btcChainParams *observertypes.ChainParams, - ccFlags *observertypes.CrosschainFlags, - headerSupportedChains []lightclienttypes.HeaderSupportedChain, -) *context.ZetacoreContext { - // create config - cfg := config.NewConfig() - cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ - Chain: evmChain, - } - if btcChainParams != nil { - cfg.BitcoinConfig = config.BTCConfig{ - RPCUsername: "test", - } - } - - // create zetacore context - coreContext := context.NewZetacoreContext(cfg) - evmChainParamsMap := make(map[int64]*observertypes.ChainParams) - evmChainParamsMap[evmChain.ChainId] = evmChainParams - - // create crosschain flags if not provided - if ccFlags == nil { - ccFlags = sample.CrosschainFlags() - } - - // feed chain params - coreContext.Update( - &observertypes.Keygen{}, - []chains.Chain{evmChain}, - evmChainParamsMap, - btcChainParams, - "", - *ccFlags, - headerSupportedChains, - true, - zerolog.Logger{}, - ) - return coreContext -} - -func TestNewZetaCoreContext(t *testing.T) { - t.Run("should create new zetacore context with empty config", func(t *testing.T) { - testCfg := config.NewConfig() - - zetaContext := context.NewZetacoreContext(testCfg) - require.NotNil(t, zetaContext) - - // assert keygen - keyGen := zetaContext.GetKeygen() - require.Equal(t, observertypes.Keygen{}, keyGen) - - // assert external chains - require.Empty(t, len(zetaContext.GetEnabledExternalChains())) - - // assert current tss pubkey - require.Equal(t, "", zetaContext.GetCurrentTssPubkey()) - - // assert btc chain params - chain, btcChainParams, btcChainParamsFound := zetaContext.GetBTCChainParams() - require.Equal(t, chains.Chain{}, chain) - require.False(t, btcChainParamsFound) - require.Nil(t, btcChainParams) - - // assert evm chain params - allEVMChainParams := zetaContext.GetAllEVMChainParams() - require.Empty(t, allEVMChainParams) - }) - - t.Run("should return nil chain params if chain id is not found", func(t *testing.T) { - // create config with btc config - testCfg := config.NewConfig() - testCfg.BitcoinConfig = config.BTCConfig{ - RPCUsername: "test_user", - RPCPassword: "test_password", - } - - // create zetacore context with 0 chain id - zetaContext := context.NewZetacoreContext(testCfg) - require.NotNil(t, zetaContext) - - // assert btc chain params - chain, btcChainParams, btcChainParamsFound := zetaContext.GetBTCChainParams() - require.Equal(t, chains.Chain{}, chain) - require.False(t, btcChainParamsFound) - require.Nil(t, btcChainParams) - }) - - t.Run("should create new zetacore context with config containing evm chain params", func(t *testing.T) { - testCfg := config.NewConfig() - testCfg.EVMChainConfigs = map[int64]config.EVMConfig{ - 1: { - Chain: chains.Chain{ - ChainName: 1, - ChainId: 1, - }, - }, - 2: { - Chain: chains.Chain{ - ChainName: 2, - ChainId: 2, - }, - }, - } - zetaContext := context.NewZetacoreContext(testCfg) - require.NotNil(t, zetaContext) - - // assert evm chain params - allEVMChainParams := zetaContext.GetAllEVMChainParams() - require.Equal(t, 2, len(allEVMChainParams)) - require.Equal(t, &observertypes.ChainParams{}, allEVMChainParams[1]) - require.Equal(t, &observertypes.ChainParams{}, allEVMChainParams[2]) - - evmChainParams1, found := zetaContext.GetEVMChainParams(1) - require.True(t, found) - require.Equal(t, &observertypes.ChainParams{}, evmChainParams1) - - evmChainParams2, found := zetaContext.GetEVMChainParams(2) - require.True(t, found) - require.Equal(t, &observertypes.ChainParams{}, evmChainParams2) - }) - - t.Run("should create new zetacore context with config containing btc config", func(t *testing.T) { - testCfg := config.NewConfig() - testCfg.BitcoinConfig = config.BTCConfig{ - RPCUsername: "test username", - RPCPassword: "test password", - RPCHost: "test host", - RPCParams: "test params", - } - zetaContext := context.NewZetacoreContext(testCfg) - require.NotNil(t, zetaContext) - }) -} - -func TestUpdateZetacoreContext(t *testing.T) { - t.Run("should update zetacore context after being created from empty config", func(t *testing.T) { - testCfg := config.NewConfig() - - zetaContext := context.NewZetacoreContext(testCfg) - require.NotNil(t, zetaContext) - - keyGenToUpdate := observertypes.Keygen{ - Status: observertypes.KeygenStatus_KeyGenSuccess, - GranteePubkeys: []string{"testpubkey1"}, - } - enabledChainsToUpdate := []chains.Chain{ - { - ChainName: 1, - ChainId: 1, - IsExternal: true, - }, - { - ChainName: 2, - ChainId: 2, - IsExternal: true, - }, - chains.ZetaChainTestnet, - } - evmChainParamsToUpdate := map[int64]*observertypes.ChainParams{ - 1: { - ChainId: 1, - }, - 2: { - ChainId: 2, - }, - } - btcChainParamsToUpdate := &observertypes.ChainParams{ - ChainId: 3, - } - tssPubKeyToUpdate := "tsspubkeytest" - crosschainFlags := sample.CrosschainFlags() - verificationFlags := sample.HeaderSupportedChains() - - require.NotNil(t, crosschainFlags) - zetaContext.Update( - &keyGenToUpdate, - enabledChainsToUpdate, - evmChainParamsToUpdate, - btcChainParamsToUpdate, - tssPubKeyToUpdate, - *crosschainFlags, - verificationFlags, - false, - log.Logger, - ) - - // assert keygen updated - keyGen := zetaContext.GetKeygen() - require.Equal(t, keyGenToUpdate, keyGen) - - // assert enabled external chains - require.Equal(t, enabledChainsToUpdate[0:2], zetaContext.GetEnabledExternalChains()) - - // assert current tss pubkey updated - require.Equal(t, tssPubKeyToUpdate, zetaContext.GetCurrentTssPubkey()) - - // assert btc chain params still empty because they were not specified in config - chain, btcChainParams, btcChainParamsFound := zetaContext.GetBTCChainParams() - require.Equal(t, chains.Chain{}, chain) - require.False(t, btcChainParamsFound) - require.Nil(t, btcChainParams) - - // assert evm chain params still empty because they were not specified in config - allEVMChainParams := zetaContext.GetAllEVMChainParams() - require.Empty(t, allEVMChainParams) - - ccFlags := zetaContext.GetCrossChainFlags() - require.Equal(t, *crosschainFlags, ccFlags) - - verFlags := zetaContext.GetAllHeaderEnabledChains() - require.Equal(t, verificationFlags, verFlags) - }) - - t.Run( - "should update zetacore context after being created from config with evm and btc chain params", - func(t *testing.T) { - testCfg := config.NewConfig() - testCfg.EVMChainConfigs = map[int64]config.EVMConfig{ - 1: { - Chain: chains.Chain{ - ChainName: 1, - ChainId: 1, - }, - }, - 2: { - Chain: chains.Chain{ - ChainName: 2, - ChainId: 2, - }, - }, - } - testCfg.BitcoinConfig = config.BTCConfig{ - RPCUsername: "test username", - RPCPassword: "test password", - RPCHost: "test host", - RPCParams: "test params", - } - - zetaContext := context.NewZetacoreContext(testCfg) - require.NotNil(t, zetaContext) - - keyGenToUpdate := observertypes.Keygen{ - Status: observertypes.KeygenStatus_KeyGenSuccess, - GranteePubkeys: []string{"testpubkey1"}, - } - enabledChainsToUpdate := []chains.Chain{ - { - ChainName: 1, - ChainId: 1, - IsExternal: true, - }, - { - ChainName: 2, - ChainId: 2, - IsExternal: true, - }, - } - evmChainParamsToUpdate := map[int64]*observertypes.ChainParams{ - 1: { - ChainId: 1, - }, - 2: { - ChainId: 2, - }, - } - - testBtcChain := chains.BitcoinTestnet - btcChainParamsToUpdate := &observertypes.ChainParams{ - ChainId: testBtcChain.ChainId, - } - tssPubKeyToUpdate := "tsspubkeytest" - crosschainFlags := sample.CrosschainFlags() - verificationFlags := sample.HeaderSupportedChains() - require.NotNil(t, crosschainFlags) - zetaContext.Update( - &keyGenToUpdate, - enabledChainsToUpdate, - evmChainParamsToUpdate, - btcChainParamsToUpdate, - tssPubKeyToUpdate, - *crosschainFlags, - verificationFlags, - false, - log.Logger, - ) - - // assert keygen updated - keyGen := zetaContext.GetKeygen() - require.Equal(t, keyGenToUpdate, keyGen) - - // assert enabled chains updated - require.Equal(t, enabledChainsToUpdate, zetaContext.GetEnabledExternalChains()) - - // assert current tss pubkey updated - require.Equal(t, tssPubKeyToUpdate, zetaContext.GetCurrentTssPubkey()) - - // assert btc chain params - chain, btcChainParams, btcChainParamsFound := zetaContext.GetBTCChainParams() - require.Equal(t, testBtcChain, chain) - require.True(t, btcChainParamsFound) - require.Equal(t, btcChainParamsToUpdate, btcChainParams) - - // assert evm chain params - allEVMChainParams := zetaContext.GetAllEVMChainParams() - require.Equal(t, evmChainParamsToUpdate, allEVMChainParams) - - evmChainParams1, found := zetaContext.GetEVMChainParams(1) - require.True(t, found) - require.Equal(t, evmChainParamsToUpdate[1], evmChainParams1) - - evmChainParams2, found := zetaContext.GetEVMChainParams(2) - require.True(t, found) - require.Equal(t, evmChainParamsToUpdate[2], evmChainParams2) - - ccFlags := zetaContext.GetCrossChainFlags() - require.Equal(t, ccFlags, *crosschainFlags) - - verFlags := zetaContext.GetAllHeaderEnabledChains() - require.Equal(t, verFlags, verificationFlags) - }, - ) -} - -func TestIsOutboundObservationEnabled(t *testing.T) { - // create test chain params and flags - evmChain := chains.Ethereum - ccFlags := *sample.CrosschainFlags() - verificationFlags := sample.HeaderSupportedChains() - chainParams := &observertypes.ChainParams{ - ChainId: evmChain.ChainId, - IsSupported: true, - } - - t.Run("should return true if chain is supported and outbound flag is enabled", func(t *testing.T) { - coreCTX := getTestCoreContext(evmChain, chainParams, nil, &ccFlags, verificationFlags) - require.True(t, context.IsOutboundObservationEnabled(coreCTX, *chainParams)) - }) - t.Run("should return false if chain is not supported yet", func(t *testing.T) { - paramsUnsupported := &observertypes.ChainParams{ - ChainId: evmChain.ChainId, - IsSupported: false, - } - coreCTXUnsupported := getTestCoreContext(evmChain, paramsUnsupported, nil, &ccFlags, verificationFlags) - require.False(t, context.IsOutboundObservationEnabled(coreCTXUnsupported, *paramsUnsupported)) - }) - t.Run("should return false if outbound flag is disabled", func(t *testing.T) { - flagsDisabled := ccFlags - flagsDisabled.IsOutboundEnabled = false - coreCTXDisabled := getTestCoreContext(evmChain, chainParams, nil, &flagsDisabled, verificationFlags) - require.False(t, context.IsOutboundObservationEnabled(coreCTXDisabled, *chainParams)) - }) -} - -func TestIsInboundObservationEnabled(t *testing.T) { - // create test chain params and flags - evmChain := chains.Ethereum - ccFlags := *sample.CrosschainFlags() - verificationFlags := sample.HeaderSupportedChains() - chainParams := &observertypes.ChainParams{ - ChainId: evmChain.ChainId, - IsSupported: true, - } - - t.Run("should return true if chain is supported and inbound flag is enabled", func(t *testing.T) { - coreCTX := getTestCoreContext(evmChain, chainParams, nil, &ccFlags, verificationFlags) - require.True(t, context.IsInboundObservationEnabled(coreCTX, *chainParams)) - }) - t.Run("should return false if chain is not supported yet", func(t *testing.T) { - paramsUnsupported := &observertypes.ChainParams{ - ChainId: evmChain.ChainId, - IsSupported: false, - } - coreCTXUnsupported := getTestCoreContext(evmChain, paramsUnsupported, nil, &ccFlags, verificationFlags) - require.False(t, context.IsInboundObservationEnabled(coreCTXUnsupported, *paramsUnsupported)) - }) - t.Run("should return false if inbound flag is disabled", func(t *testing.T) { - flagsDisabled := ccFlags - flagsDisabled.IsInboundEnabled = false - coreCTXDisabled := getTestCoreContext(evmChain, chainParams, nil, &flagsDisabled, verificationFlags) - require.False(t, context.IsInboundObservationEnabled(coreCTXDisabled, *chainParams)) - }) -} diff --git a/zetaclient/orchestrator/app_context_update.go b/zetaclient/orchestrator/app_context_update.go index b4ac879449..29e549312f 100644 --- a/zetaclient/orchestrator/app_context_update.go +++ b/zetaclient/orchestrator/app_context_update.go @@ -4,32 +4,20 @@ import ( "fmt" "time" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethclient" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/zeta-chain/zetacore/pkg/chains" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient/chains/base" - btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" - btcrpc "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/rpc" - btcsigner "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/signer" - evmobserver "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" - evmsigner "github.com/zeta-chain/zetacore/zetaclient/chains/evm/signer" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/context" - "github.com/zeta-chain/zetacore/zetaclient/metrics" - "github.com/zeta-chain/zetacore/zetaclient/zetacore" ) // WatchUpgradePlan watches for upgrade plan and stops orchestrator if upgrade height is reached func (oc *Orchestrator) WatchUpgradePlan() { oc.logger.Std.Info().Msg("WatchUpgradePlan started") - // detect upgrade plan every half Zeta block + // detect upgrade plan every half Zeta block in order to hit every height ticker := time.NewTicker(common.ZetaBlockTime / 2) for range ticker.C { reached, err := oc.UpgradeHeightReached() @@ -38,21 +26,20 @@ func (oc *Orchestrator) WatchUpgradePlan() { } else if reached { oc.Stop() oc.logger.Std.Info().Msg("WatchUpgradePlan stopped") - return } } } -// UpdateAppContext is a polling goroutine that checks and updates app context periodically -func (oc *Orchestrator) UpdateAppContext() { +// WatchAppContext watches for app context changes and updates app context +func (oc *Orchestrator) WatchAppContext() { oc.logger.Std.Info().Msg("UpdateAppContext started") ticker := time.NewTicker(time.Duration(oc.appContext.Config().ConfigUpdateTicker) * time.Second) for { select { case <-ticker.C: - err := UpdateZetacoreContext(oc.zetacoreClient, oc.appContext.ZetacoreContext(), false, oc.logger.Std) + err := UpdateAppContext(oc.appContext, oc.zetacoreClient, oc.logger.Std) if err != nil { oc.logger.Std.Err(err).Msg("error updating zetaclient app context") } @@ -65,53 +52,46 @@ func (oc *Orchestrator) UpdateAppContext() { // CreateAppContext creates new app context from config and zetacore client func CreateAppContext( - cfg config.Config, + config config.Config, zetacoreClient interfaces.ZetacoreClient, logger zerolog.Logger, ) (*context.AppContext, error) { // create app context from config - appContext := context.NewAppContext(context.NewZetacoreContext(cfg), cfg) + appContext := context.NewAppContext(config) - // update zetacore context from zetacore - err := UpdateZetacoreContext(zetacoreClient, appContext.ZetacoreContext(), true, logger) + // update app context from zetacore + err := UpdateAppContext(appContext, zetacoreClient, logger) if err != nil { - return nil, errors.Wrap(err, "error updating zetacore context") + return nil, errors.Wrap(err, "error updating app context") } return appContext, nil } -// UpdateZetacoreContext updates zetacore context -// zetacore stores zetacore context for all clients -func UpdateZetacoreContext( +// UpdateAppContext updates zetaclient app context +func UpdateAppContext( + appContext *context.AppContext, zetacoreClient interfaces.ZetacoreClient, - coreContext *context.ZetacoreContext, - init bool, logger zerolog.Logger, ) error { - // create latest zetacore context from zetacore - zetacoreContext, err := zetacoreClient.GetLatestZetacoreContext() + // reload config from file + newConfig, err := config.Load(appContext.Config().ZetaCoreHome) if err != nil { - return errors.Wrap(err, "error getting latest zetacore context") + return errors.Wrapf( + err, + "UpdateAppContext: error loading config from path %s", + appContext.Config().ZetaCoreHome, + ) } - // get keygen - keygen := zetacoreContext.GetKeygen() - - // get btc chain params - _, newBTCParams, _ := zetacoreContext.GetBTCChainParams() + // fetch latest app context from zetacore + newContext, err := zetacoreClient.GetLatestAppContext() + if err != nil { + return errors.Wrap(err, "UpdateAppContext: error getting latest app context") + } - coreContext.Update( - &keygen, - zetacoreContext.GetEnabledExternalChains(), - zetacoreContext.GetAllEVMChainParams(), - newBTCParams, - zetacoreContext.GetCurrentTssPubkey(), - zetacoreContext.GetCrossChainFlags(), - zetacoreContext.GetAllHeaderEnabledChains(), - init, - logger, - ) + // update inner config and app context + appContext.UpdateContext(newConfig, newContext, logger) return nil } @@ -147,230 +127,3 @@ func (oc *Orchestrator) UpgradeHeightReached() (bool, error) { return true, nil } - -// GetLatestZetacoreContext queries zetacore to build the latest zetacore context -func (oc *Orchestrator) GetLatestZetacoreContext(client interfaces.ZetacoreClient) (*context.ZetacoreContext, error) { - // get latest supported chains - supportedChains, err := client.GetSupportedChains() - if err != nil { - return nil, errors.Wrap(err, "GetSupportedChains failed") - } - supportedChainsMap := make(map[int64]chains.Chain) - for _, chain := range supportedChains { - supportedChainsMap[chain.ChainId] = *chain - } - - // get latest chain parameters - chainParams, err := client.GetChainParams() - if err != nil { - return nil, errors.Wrap(err, "GetChainParams failed") - } - - chainsEnabled := make([]chains.Chain, 0) - chainParamMap := make(map[int64]*observertypes.ChainParams) - - newEVMParams := make(map[int64]*observertypes.ChainParams) - var newBTCParams *observertypes.ChainParams - - for _, chainParam := range chainParams { - // skip unsupported chain - if !chainParam.IsSupported { - continue - } - - // chain should exist in chain list - chain, found := supportedChainsMap[chainParam.ChainId] - if !found { - continue - } - - // skip ZetaChain - if !chain.IsExternalChain() { - continue - } - - // add chain param to map - chainParamMap[chainParam.ChainId] = chainParam - - // keep this chain - chainsEnabled = append(chainsEnabled, chain) - if chains.IsBitcoinChain(chainParam.ChainId) { - newBTCParams = chainParam - } else if chains.IsEVMChain(chainParam.ChainId) { - newEVMParams[chainParam.ChainId] = chainParam - } - } - - // get latest keygen - keyGen, err := client.GetKeyGen() - if err != nil { - return nil, errors.Wrap(err, "GetKeyGen failed") - } - - // get latest TSS public key - tss, err := client.GetCurrentTss() - if err != nil { - return nil, errors.Wrap(err, "GetCurrentTss failed") - } - tssPubKey := tss.GetTssPubkey() - - // get latest crosschain flags - crosschainFlags, err := client.GetCrosschainFlags() - if err != nil { - return nil, errors.Wrap(err, "GetCrosschainFlags failed") - } - - // get latest block header enabled chains - blockHeaderEnabledChains, err := client.GetBlockHeaderEnabledChains() - if err != nil { - return nil, errors.Wrap(err, "GetBlockHeaderEnabledChains failed") - } - - return context.CreateZetacoreContext( - keyGen, - chainsEnabled, - chainParamMap, - newEVMParams, - newBTCParams, - tssPubKey, - crosschainFlags, - blockHeaderEnabledChains, - ), nil -} - -// CreateSignerMap creates a map of ChainSigners for all chains in the config -func CreateSignerMap( - appContext *context.AppContext, - tss interfaces.TSSSigner, - logger base.Logger, - ts *metrics.TelemetryServer, -) (map[int64]interfaces.ChainSigner, error) { - zetacoreContext := appContext.ZetacoreContext() - signerMap := make(map[int64]interfaces.ChainSigner) - - // EVM signers - for _, evmConfig := range appContext.Config().GetAllEVMConfigs() { - if evmConfig.Chain.IsZetaChain() { - continue - } - evmChainParams, found := zetacoreContext.GetEVMChainParams(evmConfig.Chain.ChainId) - if !found { - logger.Std.Error().Msgf("ChainParam not found for chain %s", evmConfig.Chain.String()) - continue - } - mpiAddress := ethcommon.HexToAddress(evmChainParams.ConnectorContractAddress) - erc20CustodyAddress := ethcommon.HexToAddress(evmChainParams.Erc20CustodyContractAddress) - signer, err := evmsigner.NewSigner( - evmConfig.Chain, - zetacoreContext, - tss, - ts, - logger, - evmConfig.Endpoint, - config.GetConnectorABI(), - config.GetERC20CustodyABI(), - mpiAddress, - erc20CustodyAddress) - if err != nil { - logger.Std.Error().Err(err).Msgf("NewEVMSigner error for chain %s", evmConfig.Chain.String()) - continue - } - signerMap[evmConfig.Chain.ChainId] = signer - } - // BTC signer - btcChain, btcConfig, enabled := appContext.GetBTCChainAndConfig() - if enabled { - signer, err := btcsigner.NewSigner(btcChain, zetacoreContext, tss, ts, logger, btcConfig) - if err != nil { - logger.Std.Error().Err(err).Msgf("NewBTCSigner error for chain %s", btcChain.String()) - } else { - signerMap[btcChain.ChainId] = signer - } - } - - return signerMap, nil -} - -// CreateChainObserverMap creates a map of ChainObservers for all chains in the config -func CreateChainObserverMap( - appContext *context.AppContext, - zetacoreClient *zetacore.Client, - tss interfaces.TSSSigner, - dbpath string, - logger base.Logger, - ts *metrics.TelemetryServer, -) (map[int64]interfaces.ChainObserver, error) { - zetacoreContext := appContext.ZetacoreContext() - observerMap := make(map[int64]interfaces.ChainObserver) - // EVM observers - for _, evmConfig := range appContext.Config().GetAllEVMConfigs() { - if evmConfig.Chain.IsZetaChain() { - continue - } - chainParams, found := zetacoreContext.GetEVMChainParams(evmConfig.Chain.ChainId) - if !found { - logger.Std.Error().Msgf("ChainParam not found for chain %s", evmConfig.Chain.String()) - continue - } - - // create EVM client - evmClient, err := ethclient.Dial(evmConfig.Endpoint) - if err != nil { - logger.Std.Error().Err(err).Msgf("error dailing endpoint %s", evmConfig.Endpoint) - continue - } - - // create EVM chain observer - observer, err := evmobserver.NewObserver( - evmConfig, - evmClient, - *chainParams, - zetacoreContext, - zetacoreClient, - tss, - dbpath, - logger, - ts, - ) - if err != nil { - logger.Std.Error().Err(err).Msgf("NewObserver error for evm chain %s", evmConfig.Chain.String()) - continue - } - observerMap[evmConfig.Chain.ChainId] = observer - } - - // BTC observer - _, chainParams, found := zetacoreContext.GetBTCChainParams() - if !found { - return nil, fmt.Errorf("bitcoin chains params not found") - } - - // create BTC chain observer - btcChain, btcConfig, enabled := appContext.GetBTCChainAndConfig() - if enabled { - btcClient, err := btcrpc.NewRPCClient(btcConfig) - if err != nil { - logger.Std.Error().Err(err).Msgf("error creating rpc client for bitcoin chain %s", btcChain.String()) - } else { - // create BTC chain observer - observer, err := btcobserver.NewObserver( - btcChain, - btcClient, - *chainParams, - zetacoreContext, - zetacoreClient, - tss, - dbpath, - logger, - ts, - ) - if err != nil { - logger.Std.Error().Err(err).Msgf("NewObserver error for bitcoin chain %s", btcChain.String()) - } else { - observerMap[btcChain.ChainId] = observer - } - } - } - - return observerMap, nil -} diff --git a/zetaclient/orchestrator/chain_activate.go b/zetaclient/orchestrator/chain_activate.go new file mode 100644 index 0000000000..f1f8bd5aba --- /dev/null +++ b/zetaclient/orchestrator/chain_activate.go @@ -0,0 +1,205 @@ +package orchestrator + +import ( + "time" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + + btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" + btcrpc "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/rpc" + btcsigner "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/signer" + evmobserver "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" + evmsigner "github.com/zeta-chain/zetacore/zetaclient/chains/evm/signer" + "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/common" + "github.com/zeta-chain/zetacore/zetaclient/config" +) + +// WatchEnabledChains watches for run-time chain activation and deactivation +func (oc *Orchestrator) WatchEnabledChains() { + oc.logger.Std.Info().Msg("WatchChainActivation started") + + ticker := time.NewTicker(common.ZetaBlockTime) + for { + select { + case <-ticker.C: + oc.ActivateDeactivateChains() + case <-oc.stop: + oc.logger.Std.Info().Msg("WatchChainActivation stopped") + return + } + } +} + +// ActivateDeactivateChains activates or deactivates chain observers and signers +func (oc *Orchestrator) ActivateDeactivateChains() { + // create new signer and observer maps + // Note: the keys of the two maps are chain IDs and they are always exactly matched + newSignerMap := make(map[int64]interfaces.ChainSigner) + newObserverMap := make(map[int64]interfaces.ChainObserver) + + // create new signers and observers + oc.CreateObserversEVM(newSignerMap, newObserverMap) + oc.CreateObserversBTC(newSignerMap, newObserverMap) + + // loop through existing observer map to deactivate chains that are not in new observer map + for chainID, observer := range oc.observerMap { + _, found := newObserverMap[chainID] + if !found { + oc.logger.Std.Info().Msgf("orchestrator deactivating chain %d", chainID) + + observer.Stop() + delete(oc.signerMap, chainID) + delete(oc.observerMap, chainID) + } + } + + // loop through new observer map to activate chains that are not in existing observer map + for chainID, observer := range newObserverMap { + _, found := oc.observerMap[chainID] + if !found { + oc.logger.Std.Info().Msgf("orchestrator activating chain %d", chainID) + + observer.Start() + oc.signerMap[chainID] = newSignerMap[chainID] + oc.observerMap[chainID] = observer + } + } +} + +// CreateObserversEVM creates signer and observer maps for all enabled EVM chains +func (oc *Orchestrator) CreateObserversEVM( + resultSignerMap map[int64]interfaces.ChainSigner, + resultObserverMap map[int64]interfaces.ChainObserver, +) { + // create EVM-chain signers + for _, evmConfig := range oc.appContext.Config().GetAllEVMConfigs() { + chainParams, found := oc.appContext.GetExternalChainParams(evmConfig.Chain.ChainId) + if !found { + oc.logger.Sampled.Warn(). + Msgf("CreateObserversEVM: chain parameter not found for chain %d", evmConfig.Chain.ChainId) + continue + } + connectorAddress := ethcommon.HexToAddress(chainParams.ConnectorContractAddress) + erc20CustodyAddress := ethcommon.HexToAddress(chainParams.Erc20CustodyContractAddress) + + // create signer + signer, err := evmsigner.NewSigner( + evmConfig.Chain, + oc.appContext, + oc.tss, + oc.ts, + oc.logger.Base, + evmConfig.Endpoint, + config.GetConnectorABI(), + config.GetERC20CustodyABI(), + connectorAddress, + erc20CustodyAddress) + if err != nil { + oc.logger.Std.Error(). + Err(err). + Msgf("CreateObserversEVM: error NewSigner for chain %d", evmConfig.Chain.ChainId) + continue + } + + // create RPC client + evmClient, err := ethclient.Dial(evmConfig.Endpoint) + if err != nil { + oc.logger.Std.Error(). + Err(err). + Msgf("CreateObserversEVM: error dailing endpoint %s for chain %d", evmConfig.Endpoint, evmConfig.Chain.ChainId) + continue + } + + // create observer + observer, err := evmobserver.NewObserver( + evmConfig, + evmClient, + *chainParams, + oc.appContext, + oc.zetacoreClient, + oc.tss, + oc.dbPath, + oc.logger.Base, + oc.ts, + ) + if err != nil { + oc.logger.Std.Error(). + Err(err). + Msgf("CreateObserversEVM: error NewObserver for chain %d", evmConfig.Chain.ChainId) + continue + } + + // add signer and observer to result maps + resultSignerMap[evmConfig.Chain.ChainId] = signer + resultObserverMap[evmConfig.Chain.ChainId] = observer + } +} + +// CreateObserversBTC creates signer and observer maps for all enabled BTC chains +func (oc *Orchestrator) CreateObserversBTC( + resultSignerMap map[int64]interfaces.ChainSigner, + resultObserverMap map[int64]interfaces.ChainObserver, +) { + // get enabled BTC chains and config + btcChains := oc.appContext.GetEnabledBTCChains() + btcConfig, found := oc.appContext.Config().GetBTCConfig() + + // currently only one single BTC chain is supported + if !found { + oc.logger.Sampled.Warn().Msg("CreateObserversBTC: BTC config not found") + return + } + if len(btcChains) != 1 { + oc.logger.Std.Error().Msgf("CreateObserversBTC: want single BTC chain, got %d", len(btcChains)) + return + } + + // create BTC-chain signers and observers + // loop is used here in case we have multiple btc chains in the future + for _, btcChain := range btcChains { + chainParams, found := oc.appContext.GetExternalChainParams(btcChain.ChainId) + if !found { + oc.logger.Sampled.Warn(). + Msgf("CreateObserversBTC: chain parameter not found for chain %d", btcChain.ChainId) + continue + } + + // create RPC client + btcClient, err := btcrpc.NewRPCClient(btcConfig) + if err != nil { + oc.logger.Std.Error(). + Err(err). + Msgf("CreateObserversBTC: error NewRPCClient for chain %s", btcChain.String()) + continue + } + + // create signer + signer, err := btcsigner.NewSigner(btcChain, oc.appContext, oc.tss, oc.ts, oc.logger.Base, btcConfig) + if err != nil { + oc.logger.Std.Error().Err(err).Msgf("CreateObserversBTC: error NewSigner for chain %d", btcChain.ChainId) + continue + } + + // create observer + observer, err := btcobserver.NewObserver( + btcChain, + btcClient, + *chainParams, + oc.appContext, + oc.zetacoreClient, + oc.tss, + oc.dbPath, + oc.logger.Base, + oc.ts, + ) + if err != nil { + oc.logger.Std.Error().Err(err).Msgf("NewObserver error for bitcoin chain %s", btcChain.String()) + } + + // add signer and observer to result maps + resultSignerMap[btcChain.ChainId] = signer + resultObserverMap[btcChain.ChainId] = observer + } +} diff --git a/zetaclient/orchestrator/chain_activate_test.go b/zetaclient/orchestrator/chain_activate_test.go new file mode 100644 index 0000000000..503f45c907 --- /dev/null +++ b/zetaclient/orchestrator/chain_activate_test.go @@ -0,0 +1 @@ +package orchestrator_test diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index 1ccae1e805..403401dad9 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -17,6 +17,7 @@ import ( zetamath "github.com/zeta-chain/zetacore/pkg/math" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "github.com/zeta-chain/zetacore/zetaclient/chains/base" btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/context" @@ -37,20 +38,31 @@ const ( ) type Log struct { - Std zerolog.Logger + // Base are the original base loggers used by orchestrator to create observers + Base base.Logger + + // Std is the standard logger for orchestrator module + Std zerolog.Logger + + // Sampled is the sampled logger for orchestrator module Sampled zerolog.Logger } // Orchestrator wraps the zetacore client, chain observers and signers. This is the high level object used for CCTX scheduling type Orchestrator struct { - // appContext contains the config and zetacore context + // appContext contains the zetaclient application context appContext *context.AppContext // zetacore client zetacoreClient interfaces.ZetacoreClient - // chain signers and observers - signerMap map[int64]interfaces.ChainSigner + // tss is the TSS signer + tss interfaces.TSSSigner + + // signerMap contains all external chain signers + signerMap map[int64]interfaces.ChainSigner + + // observerMap contains all external chain observers observerMap map[int64]interfaces.ChainObserver // outbound processor @@ -62,6 +74,9 @@ type Orchestrator struct { // logger contains the loggers used by the orchestrator logger Log + // dbPath is the path observer database + dbPath string + // ts is the telemetry server for metrics ts *metrics.TelemetryServer @@ -77,35 +92,36 @@ type Orchestrator struct { func NewOrchestrator( appContext *context.AppContext, zetacoreClient interfaces.ZetacoreClient, - signerMap map[int64]interfaces.ChainSigner, - observerMap map[int64]interfaces.ChainObserver, - logger zerolog.Logger, + tss interfaces.TSSSigner, + logger base.Logger, + dbPath string, ts *metrics.TelemetryServer, ) *Orchestrator { oc := Orchestrator{ - appContext: appContext, - ts: ts, - mu: sync.Mutex{}, - stop: make(chan struct{}), - stopped: false, + appContext: appContext, + zetacoreClient: zetacoreClient, + tss: tss, + signerMap: make(map[int64]interfaces.ChainSigner), + observerMap: make(map[int64]interfaces.ChainObserver), + dbPath: dbPath, + ts: ts, + mu: sync.Mutex{}, + stop: make(chan struct{}), + stopped: false, } // create loggers oc.logger = Log{ - Std: logger.With().Str("module", "orchestrator").Logger(), + Base: logger, + Std: logger.Std.With().Str("module", "orchestrator").Logger(), } oc.logger.Sampled = oc.logger.Std.Sample(&zerolog.BasicSampler{N: loggerSamplingRate}) - // set zetacore client, signers and chain observers - oc.zetacoreClient = zetacoreClient - oc.signerMap = signerMap - oc.observerMap = observerMap - // create outbound processor - oc.outboundProc = outboundprocessor.NewProcessor(logger) + oc.outboundProc = outboundprocessor.NewProcessor(logger.Std) // initialize hot key balance - balance, err := zetacoreClient.GetZetaHotKeyBalance() + balance, err := oc.zetacoreClient.GetZetaHotKeyBalance() if err != nil { oc.logger.Std.Error().Err(err).Msg("error getting last balance of the hot key") } @@ -119,23 +135,31 @@ func (oc *Orchestrator) Start() { // watch for upgrade plan in zetacore go oc.WatchUpgradePlan() - // watch for zetaclient app context (config file and chain params) updates - go oc.UpdateAppContext() + // watch for zetaclient app context changes + go oc.WatchAppContext() + + // watch for enabling/disabling chains + go oc.WatchEnabledChains() // schedule pending cctxs across all enabled chains go oc.SchedulePendingCctxs() + + // watch for stop signals + oc.AwaitStopSignals() } // AwaitStopSignals waits for stop signals func (oc *Orchestrator) AwaitStopSignals() { - oc.logger.Std.Info().Msgf("awaiting the os.Interrupt, syscall.SIGTERM signals...") + oc.logger.Std.Info().Msgf("orchestrator awaiting the os.Interrupt, syscall.SIGTERM signals...") // subscribe to stop signals ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) sig := <-ch - oc.logger.Std.Info().Msgf("stop signal received: %s", sig) + // stop orchestrator + oc.Stop() + oc.logger.Std.Info().Msgf("orchestrator stopped on signal: %s", sig) } // Stop notifies all zetaclient goroutines to stop @@ -159,62 +183,47 @@ func (oc *Orchestrator) Stop() { } // GetUpdatedSigner returns signer with updated chain parameters -func (oc *Orchestrator) GetUpdatedSigner( - coreContext *context.ZetacoreContext, - chainID int64, -) (interfaces.ChainSigner, error) { +func (oc *Orchestrator) GetUpdatedSigner(chainID int64) (interfaces.ChainSigner, error) { signer, found := oc.signerMap[chainID] if !found { return nil, fmt.Errorf("signer not found for chainID %d", chainID) } - // update EVM signer parameters only. BTC signer doesn't use chain parameters for now. - if chains.IsEVMChain(chainID) { - evmParams, found := coreContext.GetEVMChainParams(chainID) - if found { - // update zeta connector and ERC20 custody addresses - zetaConnectorAddress := ethcommon.HexToAddress(evmParams.GetConnectorContractAddress()) - erc20CustodyAddress := ethcommon.HexToAddress(evmParams.GetErc20CustodyContractAddress()) - if zetaConnectorAddress != signer.GetZetaConnectorAddress() { - signer.SetZetaConnectorAddress(zetaConnectorAddress) - oc.logger.Std.Info().Msgf( - "updated zeta connector address for chainID %d, new address: %s", chainID, zetaConnectorAddress) - } - if erc20CustodyAddress != signer.GetERC20CustodyAddress() { - signer.SetERC20CustodyAddress(erc20CustodyAddress) - oc.logger.Std.Info().Msgf( - "updated ERC20 custody address for chainID %d, new address: %s", chainID, erc20CustodyAddress) - } + + // update signer parameters for the chain. + // the logic is consistent for all chains, even if BTC chain doesn't have zetaConnector/erc20Custody. + evmParams, found := oc.appContext.GetExternalChainParams(chainID) + if found { + // update zeta connector and ERC20 custody addresses + zetaConnectorAddress := ethcommon.HexToAddress(evmParams.GetConnectorContractAddress()) + erc20CustodyAddress := ethcommon.HexToAddress(evmParams.GetErc20CustodyContractAddress()) + if zetaConnectorAddress != signer.GetZetaConnectorAddress() { + signer.SetZetaConnectorAddress(zetaConnectorAddress) + oc.logger.Std.Info().Msgf( + "updated zeta connector address for chainID %d, new address: %s", chainID, zetaConnectorAddress) + } + if erc20CustodyAddress != signer.GetERC20CustodyAddress() { + signer.SetERC20CustodyAddress(erc20CustodyAddress) + oc.logger.Std.Info().Msgf( + "updated ERC20 custody address for chainID %d, new address: %s", chainID, erc20CustodyAddress) } } return signer, nil } // GetUpdatedChainObserver returns chain observer with updated chain parameters -func (oc *Orchestrator) GetUpdatedChainObserver( - coreContext *context.ZetacoreContext, - chainID int64, -) (interfaces.ChainObserver, error) { +func (oc *Orchestrator) GetUpdatedChainObserver(chainID int64) (interfaces.ChainObserver, error) { observer, found := oc.observerMap[chainID] if !found { return nil, fmt.Errorf("chain observer not found for chainID %d", chainID) } - // update chain observer chain parameters - curParams := observer.GetChainParams() - if chains.IsEVMChain(chainID) { - evmParams, found := coreContext.GetEVMChainParams(chainID) - if found && !observertypes.ChainParamsEqual(curParams, *evmParams) { - observer.SetChainParams(*evmParams) - oc.logger.Std.Info().Msgf( - "updated chain params for chainID %d, new params: %v", chainID, *evmParams) - } - } else if chains.IsBitcoinChain(chainID) { - _, btcParams, found := coreContext.GetBTCChainParams() - if found && !observertypes.ChainParamsEqual(curParams, *btcParams) { - observer.SetChainParams(*btcParams) - oc.logger.Std.Info().Msgf( - "updated chain params for Bitcoin, new params: %v", *btcParams) - } + // update chain observer chain parameters + oldParams := observer.GetChainParams() + newParams, found := oc.appContext.GetExternalChainParams(chainID) + if found && !observertypes.ChainParamsEqual(oldParams, *newParams) { + observer.SetChainParams(*newParams) + oc.logger.Std.Info().Msgf( + "updated chain params for chainID %d, new params: %v", chainID, *newParams) } return observer, nil } @@ -313,8 +322,7 @@ func (oc *Orchestrator) SchedulePendingCctxs() { metrics.HotKeyBurnRate.Set(float64(oc.ts.HotKeyBurnRate.GetBurnRate().Int64())) // get supported external chains - coreContext := oc.appContext.ZetacoreContext() - externalChains := coreContext.GetEnabledExternalChains() + externalChains := oc.appContext.GetEnabledExternalChains() // query pending cctxs across all external chains within rate limit cctxMap, err := oc.GetPendingCctxsWithinRatelimit(externalChains) @@ -332,21 +340,21 @@ func (oc *Orchestrator) SchedulePendingCctxs() { } // update chain parameters for signer and chain observer - signer, err := oc.GetUpdatedSigner(coreContext, c.ChainId) + signer, err := oc.GetUpdatedSigner(c.ChainId) if err != nil { oc.logger.Std.Error(). Err(err). Msgf("StartCctxScheduler: GetUpdatedSigner failed for chain %d", c.ChainId) continue } - ob, err := oc.GetUpdatedChainObserver(coreContext, c.ChainId) + ob, err := oc.GetUpdatedChainObserver(c.ChainId) if err != nil { oc.logger.Std.Error(). Err(err). Msgf("StartCctxScheduler: GetUpdatedChainObserver failed for chain %d", c.ChainId) continue } - if !context.IsOutboundObservationEnabled(coreContext, ob.GetChainParams()) { + if !context.IsOutboundObservationEnabled(oc.appContext, ob.GetChainParams()) { continue } diff --git a/zetaclient/orchestrator/orchestrator_test.go b/zetaclient/orchestrator/orchestrator_test.go index 131dc29656..d7530f3f27 100644 --- a/zetaclient/orchestrator/orchestrator_test.go +++ b/zetaclient/orchestrator/orchestrator_test.go @@ -3,6 +3,7 @@ package orchestrator import ( "testing" + "github.com/btcsuite/btcd/chaincfg" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" @@ -24,6 +25,7 @@ import ( // MockOrchestrator creates a mock orchestrator for testing func MockOrchestrator( t *testing.T, + appContext *context.AppContext, zetacoreClient interfaces.ZetacoreClient, evmChain, btcChain chains.Chain, evmChainParams, btcChainParams *observertypes.ChainParams, @@ -40,6 +42,7 @@ func MockOrchestrator( // create orchestrator orchestrator := &Orchestrator{ + appContext: appContext, zetacoreClient: zetacoreClient, signerMap: map[int64]interfaces.ChainSigner{ evmChain.ChainId: evmSigner, @@ -53,10 +56,10 @@ func MockOrchestrator( return orchestrator } -func CreateCoreContext( +func CreateTestAppContext( evmChain, btcChain chains.Chain, evmChainParams, btcChainParams *observertypes.ChainParams, -) *context.ZetacoreContext { +) *context.AppContext { // new config cfg := config.NewConfig() cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ @@ -65,26 +68,28 @@ func CreateCoreContext( cfg.BitcoinConfig = config.BTCConfig{ RPCHost: "localhost", } - // new zetacore context - coreContext := context.NewZetacoreContext(cfg) - evmChainParamsMap := make(map[int64]*observertypes.ChainParams) - evmChainParamsMap[evmChain.ChainId] = evmChainParams + // new app context + appContext := context.NewAppContext(cfg) + chainParamsMap := make(map[int64]*observertypes.ChainParams) + chainParamsMap[evmChain.ChainId] = evmChainParams + chainParamsMap[btcChain.ChainId] = btcChainParams ccFlags := sample.CrosschainFlags() verificationFlags := sample.HeaderSupportedChains() // feed chain params - coreContext.Update( - &observertypes.Keygen{}, + appContext.Update( + cfg, + observertypes.Keygen{}, []chains.Chain{evmChain, btcChain}, - evmChainParamsMap, - btcChainParams, + chainParamsMap, + &chaincfg.RegressionNetParams, "", *ccFlags, verificationFlags, true, zerolog.Logger{}, ) - return coreContext + return appContext } func Test_GetUpdatedSigner(t *testing.T) { @@ -98,7 +103,7 @@ func Test_GetUpdatedSigner(t *testing.T) { } btcChainParams := &observertypes.ChainParams{} - // new chain params in zetacore context + // new chain params in app context evmChainParamsNew := &observertypes.ChainParams{ ChainId: evmChain.ChainId, ConnectorContractAddress: testutils.OtherAddress1, @@ -106,17 +111,18 @@ func Test_GetUpdatedSigner(t *testing.T) { } t.Run("signer should not be found", func(t *testing.T) { - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - context := CreateCoreContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) + appCtx := CreateTestAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) + orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) // BSC signer should not be found - _, err := orchestrator.GetUpdatedSigner(context, chains.BscMainnet.ChainId) + _, err := orchestrator.GetUpdatedSigner(chains.BscMainnet.ChainId) require.ErrorContains(t, err, "signer not found") }) t.Run("should be able to update connector and erc20 custody address", func(t *testing.T) { - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - context := CreateCoreContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) + appCtx := CreateTestAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) + orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) + // update signer with new connector and erc20 custody address - signer, err := orchestrator.GetUpdatedSigner(context, evmChain.ChainId) + signer, err := orchestrator.GetUpdatedSigner(evmChain.ChainId) require.NoError(t, err) require.Equal(t, testutils.OtherAddress1, signer.GetZetaConnectorAddress().Hex()) require.Equal(t, testutils.OtherAddress2, signer.GetERC20CustodyAddress().Hex()) @@ -136,7 +142,7 @@ func Test_GetUpdatedChainObserver(t *testing.T) { ChainId: btcChain.ChainId, } - // new chain params in zetacore context + // new chain params in app context evmChainParamsNew := &observertypes.ChainParams{ ChainId: evmChain.ChainId, ConfirmationCount: 10, @@ -171,33 +177,37 @@ func Test_GetUpdatedChainObserver(t *testing.T) { } t.Run("evm chain observer should not be found", func(t *testing.T) { - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - coreContext := CreateCoreContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) + appCtx := CreateTestAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) + orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) + // BSC chain observer should not be found - _, err := orchestrator.GetUpdatedChainObserver(coreContext, chains.BscMainnet.ChainId) + _, err := orchestrator.GetUpdatedChainObserver(chains.BscMainnet.ChainId) require.ErrorContains(t, err, "chain observer not found") }) t.Run("chain params in evm chain observer should be updated successfully", func(t *testing.T) { - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - coreContext := CreateCoreContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) + appCtx := CreateTestAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) + orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) + // update evm chain observer with new chain params - chainOb, err := orchestrator.GetUpdatedChainObserver(coreContext, evmChain.ChainId) + chainOb, err := orchestrator.GetUpdatedChainObserver(evmChain.ChainId) require.NoError(t, err) require.NotNil(t, chainOb) require.True(t, observertypes.ChainParamsEqual(*evmChainParamsNew, chainOb.GetChainParams())) }) t.Run("btc chain observer should not be found", func(t *testing.T) { - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - coreContext := CreateCoreContext(btcChain, btcChain, evmChainParams, btcChainParamsNew) + appCtx := CreateTestAppContext(btcChain, btcChain, evmChainParams, btcChainParamsNew) + orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) + // BTC testnet chain observer should not be found - _, err := orchestrator.GetUpdatedChainObserver(coreContext, chains.BitcoinTestnet.ChainId) + _, err := orchestrator.GetUpdatedChainObserver(chains.BitcoinTestnet.ChainId) require.ErrorContains(t, err, "chain observer not found") }) t.Run("chain params in btc chain observer should be updated successfully", func(t *testing.T) { - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - coreContext := CreateCoreContext(btcChain, btcChain, evmChainParams, btcChainParamsNew) + appCtx := CreateTestAppContext(btcChain, btcChain, evmChainParams, btcChainParamsNew) + orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) + // update btc chain observer with new chain params - chainOb, err := orchestrator.GetUpdatedChainObserver(coreContext, btcChain.ChainId) + chainOb, err := orchestrator.GetUpdatedChainObserver(btcChain.ChainId) require.NoError(t, err) require.NotNil(t, chainOb) require.True(t, observertypes.ChainParamsEqual(*btcChainParamsNew, chainOb.GetChainParams())) @@ -362,7 +372,7 @@ func Test_GetPendingCctxsWithinRatelimit(t *testing.T) { client.WithRateLimiterInput(tt.response) // create orchestrator - orchestrator := MockOrchestrator(t, client, ethChain, btcChain, ethChainParams, btcChainParams) + orchestrator := MockOrchestrator(t, nil, client, ethChain, btcChain, ethChainParams, btcChainParams) // run the test cctxsMap, err := orchestrator.GetPendingCctxsWithinRatelimit(foreignChains) diff --git a/zetaclient/supplychecker/zeta_supply_checker.go b/zetaclient/supplychecker/zeta_supply_checker.go index 952c0df16f..1aae6bf01f 100644 --- a/zetaclient/supplychecker/zeta_supply_checker.go +++ b/zetaclient/supplychecker/zeta_supply_checker.go @@ -21,7 +21,7 @@ import ( // ZetaSupplyChecker is a utility to check the total supply of Zeta tokens type ZetaSupplyChecker struct { - coreContext *context.ZetacoreContext + appContext *context.AppContext evmClient map[int64]*ethclient.Client zetaClient *zetacore.Client ticker *clienttypes.DynamicTicker @@ -50,8 +50,8 @@ func NewZetaSupplyChecker( logger: logger.With(). Str("module", "ZetaSupplyChecker"). Logger(), - coreContext: appContext.ZetacoreContext(), - zetaClient: zetaClient, + appContext: appContext, + zetaClient: zetaClient, } for _, evmConfig := range appContext.Config().GetAllEVMConfigs() { @@ -115,7 +115,7 @@ func (zs *ZetaSupplyChecker) Stop() { func (zs *ZetaSupplyChecker) CheckZetaTokenSupply() error { externalChainTotalSupply := sdkmath.ZeroInt() for _, chain := range zs.externalEvmChain { - externalEvmChainParams, ok := zs.coreContext.GetEVMChainParams(chain.ChainId) + externalEvmChainParams, ok := zs.appContext.GetExternalChainParams(chain.ChainId) if !ok { return fmt.Errorf("externalEvmChainParams not found for chain id %d", chain.ChainId) } @@ -141,12 +141,12 @@ func (zs *ZetaSupplyChecker) CheckZetaTokenSupply() error { externalChainTotalSupply = externalChainTotalSupply.Add(totalSupplyInt) } - evmChainParams, ok := zs.coreContext.GetEVMChainParams(zs.ethereumChain.ChainId) + ethChainParams, ok := zs.appContext.GetExternalChainParams(zs.ethereumChain.ChainId) if !ok { return fmt.Errorf("eth config not found for chain id %d", zs.ethereumChain.ChainId) } - ethConnectorAddressString := evmChainParams.ConnectorContractAddress + ethConnectorAddressString := ethChainParams.ConnectorContractAddress ethConnectorAddress := ethcommon.HexToAddress(ethConnectorAddressString) ethConnectorContract, err := observer.FetchConnectorContractEth( ethConnectorAddress, diff --git a/zetaclient/testutils/mocks/zetacore_client.go b/zetaclient/testutils/mocks/zetacore_client.go index bfc06f6b14..e9b8b6f93d 100644 --- a/zetaclient/testutils/mocks/zetacore_client.go +++ b/zetaclient/testutils/mocks/zetacore_client.go @@ -53,14 +53,14 @@ func NewMockZetacoreClient() *MockZetacoreClient { } } -func (m *MockZetacoreClient) GetLatestZetacoreContext() (*clientcontext.ZetacoreContext, error) { +func (m *MockZetacoreClient) GetLatestAppContext() (*clientcontext.AppContext, error) { if m.paused { return nil, errors.New(ErrMsgPaused) } return nil, nil } -func (m *MockZetacoreClient) UpdateZetacoreContext(_ *clientcontext.ZetacoreContext, _ bool, _ zerolog.Logger) error { +func (m *MockZetacoreClient) UpdateAppContext(_ *clientcontext.AppContext, _ bool, _ zerolog.Logger) error { if m.paused { return errors.New(ErrMsgPaused) } diff --git a/zetaclient/tss/tss_signer.go b/zetaclient/tss/tss_signer.go index 8fdd0384ee..2774132b5e 100644 --- a/zetaclient/tss/tss_signer.go +++ b/zetaclient/tss/tss_signer.go @@ -13,6 +13,7 @@ import ( "time" "github.com/binance-chain/tss-lib/ecdsa/keygen" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcutil" tmcrypto "github.com/cometbft/cometbft/crypto" @@ -26,7 +27,6 @@ import ( "github.com/zeta-chain/go-tss/p2p" "github.com/zeta-chain/go-tss/tss" - "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/cosmos" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" @@ -79,9 +79,8 @@ type TSS struct { ZetacoreClient interfaces.ZetacoreClient KeysignsTracker *ConcurrentKeysignsTracker - // TODO: support multiple Bitcoin network, not just one network - // https://github.com/zeta-chain/node/issues/1397 - BitcoinChainID int64 + // BitcoinNetParams is the Bitcoin network parameters for the TSS to create BTC addresses + BitcoinNetParams *chaincfg.Params } // NewTSS creates a new TSS instance @@ -92,7 +91,6 @@ func NewTSS( preParams *keygen.LocalPreParams, client interfaces.ZetacoreClient, tssHistoricalList []observertypes.TSS, - bitcoinChainID int64, tssPassword string, hotkeyPassword string, ) (*TSS, error) { @@ -103,13 +101,13 @@ func NewTSS( } newTss := TSS{ - Server: server, - Keys: make(map[string]*Key), - CurrentPubkey: appContext.ZetacoreContext().GetCurrentTssPubkey(), - logger: logger, - ZetacoreClient: client, - KeysignsTracker: NewKeysignsTracker(logger), - BitcoinChainID: bitcoinChainID, + Server: server, + Keys: make(map[string]*Key), + CurrentPubkey: appContext.GetCurrentTssPubkey(), + logger: logger, + ZetacoreClient: client, + KeysignsTracker: NewKeysignsTracker(logger), + BitcoinNetParams: appContext.GetBTCNetParams(), } err = newTss.LoadTssFilesFromDirectory(appContext.Config().TssPath) @@ -430,7 +428,7 @@ func (tss *TSS) EVMAddress() ethcommon.Address { // BTCAddress generates a bech32 p2wpkh address from pubkey func (tss *TSS) BTCAddress() string { - addr, err := GetTssAddrBTC(tss.CurrentPubkey, tss.BitcoinChainID) + addr, err := GetTssAddrBTC(tss.CurrentPubkey, tss.BitcoinNetParams) if err != nil { log.Error().Err(err).Msg("getKeyAddr error") return "" @@ -439,7 +437,7 @@ func (tss *TSS) BTCAddress() string { } func (tss *TSS) BTCAddressWitnessPubkeyHash() *btcutil.AddressWitnessPubKeyHash { - addrWPKH, err := getKeyAddrBTCWitnessPubkeyHash(tss.CurrentPubkey, tss.BitcoinChainID) + addrWPKH, err := getKeyAddrBTCWitnessPubkeyHash(tss.CurrentPubkey, tss.BitcoinNetParams) if err != nil { log.Error().Err(err).Msg("BTCAddressPubkeyHash error") return nil @@ -528,8 +526,8 @@ func (tss *TSS) LoadTssFilesFromDirectory(tssPath string) error { return nil } -func GetTssAddrBTC(tssPubkey string, bitcoinChainID int64) (string, error) { - addrWPKH, err := getKeyAddrBTCWitnessPubkeyHash(tssPubkey, bitcoinChainID) +func GetTssAddrBTC(tssPubkey string, btcNetParams *chaincfg.Params) (string, error) { + addrWPKH, err := getKeyAddrBTCWitnessPubkeyHash(tssPubkey, btcNetParams) if err != nil { log.Fatal().Err(err) return "", err @@ -655,18 +653,16 @@ func wasNodePartOfTss(granteePubKey32 string, granteeList []string) bool { return false } -func getKeyAddrBTCWitnessPubkeyHash(tssPubkey string, chainID int64) (*btcutil.AddressWitnessPubKeyHash, error) { +func getKeyAddrBTCWitnessPubkeyHash( + tssPubkey string, + btcNetParams *chaincfg.Params, +) (*btcutil.AddressWitnessPubKeyHash, error) { pubk, err := cosmos.GetPubKeyFromBech32(cosmos.Bech32PubKeyTypeAccPub, tssPubkey) if err != nil { return nil, err } - bitcoinNetParams, err := chains.BitcoinNetParamsFromChainID(chainID) - if err != nil { - return nil, err - } - - addr, err := btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(pubk.Bytes()), bitcoinNetParams) + addr, err := btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(pubk.Bytes()), btcNetParams) if err != nil { return nil, err } diff --git a/zetaclient/zetacore/client.go b/zetaclient/zetacore/client.go index 64fb655a8b..836876c23a 100644 --- a/zetaclient/zetacore/client.go +++ b/zetaclient/zetacore/client.go @@ -6,6 +6,7 @@ import ( "time" "cosmossdk.io/simapp/params" + "github.com/btcsuite/btcd/chaincfg" rpcclient "github.com/cometbft/cometbft/rpc/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/pkg/errors" @@ -225,10 +226,10 @@ func (c *Client) WaitForZetacoreToCreateBlocks() error { return nil } -// UpdateZetacoreContext updates zetacore context -// zetacore stores zetacore context for all clients -func (c *Client) UpdateZetacoreContext( - coreContext *context.ZetacoreContext, +// UpdateAppContext updates app context +// zetacore stores app context for all clients +func (c *Client) UpdateAppContext( + appContext *context.AppContext, init bool, sampledLogger zerolog.Logger, ) error { @@ -253,8 +254,7 @@ func (c *Client) UpdateZetacoreContext( return fmt.Errorf("failed to get chain params: %w", err) } - newEVMParams := make(map[int64]*observertypes.ChainParams) - var newBTCParams *observertypes.ChainParams + newChainParams := make(map[int64]*observertypes.ChainParams) // check and update chain params for each chain for _, chainParam := range chainParams { @@ -263,11 +263,7 @@ func (c *Client) UpdateZetacoreContext( sampledLogger.Warn().Err(err).Msgf("Invalid chain params for chain %d", chainParam.ChainId) continue } - if chains.IsBitcoinChain(chainParam.ChainId) { - newBTCParams = chainParam - } else if chains.IsEVMChain(chainParam.ChainId) { - newEVMParams[chainParam.ChainId] = chainParam - } + newChainParams[chainParam.ChainId] = chainParam } supportedChains, err := c.GetSupportedChains() @@ -284,6 +280,7 @@ func (c *Client) UpdateZetacoreContext( return fmt.Errorf("failed to get keygen: %w", err) } + // get latest TSS public key tss, err := c.GetCurrentTss() if err != nil { c.logger.Info().Err(err).Msg("Unable to fetch TSS from zetacore") @@ -303,11 +300,12 @@ func (c *Client) UpdateZetacoreContext( return err } - coreContext.Update( - keyGen, + appContext.Update( + config.NewConfig(), + *keyGen, newChains, - newEVMParams, - newBTCParams, + newChainParams, + &chaincfg.RegressionNetParams, tssPubKey, crosschainFlags, blockHeaderEnabledChains, @@ -318,8 +316,8 @@ func (c *Client) UpdateZetacoreContext( return nil } -// GetLatestZetacoreContext queries zetacore to build the latest zetacore context -func (c *Client) GetLatestZetacoreContext() (*context.ZetacoreContext, error) { +// GetLatestAppContext queries zetacore to build the latest app context +func (c *Client) GetLatestAppContext() (*context.AppContext, error) { // get latest supported chains supportedChains, err := c.GetSupportedChains() if err != nil { @@ -336,12 +334,10 @@ func (c *Client) GetLatestZetacoreContext() (*context.ZetacoreContext, error) { return nil, errors.Wrap(err, "GetChainParams failed") } + var btcNetParams *chaincfg.Params chainsEnabled := make([]chains.Chain, 0) chainParamMap := make(map[int64]*observertypes.ChainParams) - newEVMParams := make(map[int64]*observertypes.ChainParams) - var newBTCParams *observertypes.ChainParams - for _, chainParam := range chainParams { // skip unsupported chain if !chainParam.IsSupported { @@ -359,16 +355,18 @@ func (c *Client) GetLatestZetacoreContext() (*context.ZetacoreContext, error) { continue } - // add chain param to map - chainParamMap[chainParam.ChainId] = chainParam - - // keep this chain - chainsEnabled = append(chainsEnabled, chain) + // zetaclient detects Bitcoin network (regnet, testnet, mainnet) from chain params in zetacore + // The network params will be used by TSS to calculate the correct TSS address. if chains.IsBitcoinChain(chainParam.ChainId) { - newBTCParams = chainParam - } else if chains.IsEVMChain(chainParam.ChainId) { - newEVMParams[chainParam.ChainId] = chainParam + btcNetParams, err = chains.BitcoinNetParamsFromChainID(chainParam.ChainId) + if err != nil { + return nil, errors.Wrapf(err, "failed to get Bitcoin network params for chain %d", chainParam.ChainId) + } } + + // zetaclient should observe this chain + chainsEnabled = append(chainsEnabled, chain) + chainParamMap[chainParam.ChainId] = chainParam } // get latest keygen @@ -396,12 +394,11 @@ func (c *Client) GetLatestZetacoreContext() (*context.ZetacoreContext, error) { return nil, errors.Wrap(err, "GetBlockHeaderEnabledChains failed") } - return context.CreateZetacoreContext( - keyGen, + return context.CreateAppContext( + *keyGen, chainsEnabled, chainParamMap, - newEVMParams, - newBTCParams, + btcNetParams, tssPubKey, crosschainFlags, blockHeaderEnabledChains, diff --git a/zetaclient/zetacore/tx_test.go b/zetaclient/zetacore/tx_test.go index 1d00f33d1a..3521ee2382 100644 --- a/zetaclient/zetacore/tx_test.go +++ b/zetaclient/zetacore/tx_test.go @@ -199,7 +199,7 @@ func TestZetacore_SetTSS(t *testing.T) { }) } -func TestZetacore_UpdateZetacoreContext(t *testing.T) { +func TestZetacore_UpdateAppContext(t *testing.T) { //Setup server for multiple grpc calls listener, err := net.Listen("tcp", "127.0.0.1:9090") require.NoError(t, err) @@ -333,9 +333,9 @@ func TestZetacore_UpdateZetacoreContext(t *testing.T) { t.Run("zetacore update success", func(t *testing.T) { cfg := config.NewConfig() - coreCtx := context.NewZetacoreContext(cfg) + appCTX := context.NewAppContext(cfg) zetacoreBroadcast = MockBroadcast - err := client.UpdateZetacoreContext(coreCtx, false, zerolog.Logger{}) + err := client.UpdateAppContext(appCTX, false, zerolog.Logger{}) require.NoError(t, err) }) } From cef707a948f81be7b9b1d53b0ca709ace13d0a72 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Sat, 29 Jun 2024 00:36:22 -0500 Subject: [PATCH 04/18] simplified app context create and update; cleanup unused code --- cmd/zetaclientd/start.go | 6 +- .../chains/evm/observer/observer_test.go | 1 - zetaclient/chains/interfaces/interfaces.go | 5 +- zetaclient/context/app_context.go | 43 +----- zetaclient/context/app_context_test.go | 3 - zetaclient/orchestrator/app_context_update.go | 83 ++++------- zetaclient/orchestrator/orchestrator.go | 6 +- zetaclient/orchestrator/orchestrator_test.go | 1 - zetaclient/testutils/mocks/zetacore_client.go | 9 +- zetaclient/zetacore/client.go | 132 ++---------------- zetaclient/zetacore/tx_test.go | 2 +- 11 files changed, 59 insertions(+), 232 deletions(-) diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 1be64c3eaa..79e0a1e29f 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -24,6 +24,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/base" "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" + "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/orchestrator" "github.com/zeta-chain/zetacore/zetaclient/zetacore" @@ -163,9 +164,10 @@ func start(_ *cobra.Command, _ []string) error { startLogger.Info().Msgf("Authz is ready for granter %s grantee %s", granter, grantee) // Initialize zetaclient app context - appContext, err := orchestrator.CreateAppContext(cfg, zetacoreClient, startLogger) + appContext := context.NewAppContext(cfg) + err = zetacoreClient.UpdateAppContext(appContext, startLogger) if err != nil { - startLogger.Error().Err(err).Msg("error creating app context") + startLogger.Error().Err(err).Msg("error initializing app context") return err } diff --git a/zetaclient/chains/evm/observer/observer_test.go b/zetaclient/chains/evm/observer/observer_test.go index f84d1567f9..fbf7d558b3 100644 --- a/zetaclient/chains/evm/observer/observer_test.go +++ b/zetaclient/chains/evm/observer/observer_test.go @@ -58,7 +58,6 @@ func getAppContext( // feed chain params appCtx.Update( - cfg, observertypes.Keygen{}, []chains.Chain{evmChain}, newChainParams, diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index ffe5dfca97..691941bc0f 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -66,8 +66,7 @@ type ChainSigner interface { // ZetacoreClient is the client interface to interact with zetacore type ZetacoreClient interface { - GetLatestAppContext() (*clientcontext.AppContext, error) - UpdateAppContext(appContext *clientcontext.AppContext, init bool, sampledLogger zerolog.Logger) error + UpdateAppContext(appContext *clientcontext.AppContext, logger zerolog.Logger) error GetUpgradePlan() (*upgradetypes.Plan, error) GetChainParams() ([]*observertypes.ChainParams, error) GetSupportedChains() ([]*chains.Chain, error) @@ -119,8 +118,6 @@ type ZetacoreClient interface { GetBtcTssAddress(chainID int64) (string, error) GetZetaHotKeyBalance() (sdkmath.Int, error) GetInboundTrackersForChain(chainID int64) ([]crosschaintypes.InboundTracker, error) - Pause() - Unpause() } // BTCRPCClient is the interface for BTC RPC client diff --git a/zetaclient/context/app_context.go b/zetaclient/context/app_context.go index 1ac888c992..0605e59d15 100644 --- a/zetaclient/context/app_context.go +++ b/zetaclient/context/app_context.go @@ -45,26 +45,11 @@ func NewAppContext(cfg config.Config) *AppContext { } } -// CreateAppContext creates a new AppContext -func CreateAppContext( - keygen observertypes.Keygen, - chainsEnabled []chains.Chain, - chainParamMap map[int64]*observertypes.ChainParams, - btcNetParams *chaincfg.Params, - tssPubKey string, - crosschainFlags observertypes.CrosschainFlags, - blockHeaderEnabledChains []lightclienttypes.HeaderSupportedChain, -) *AppContext { - return &AppContext{ - mu: new(sync.RWMutex), - keygen: keygen, - chainsEnabled: chainsEnabled, - chainParamMap: chainParamMap, - btcNetParams: btcNetParams, - currentTssPubkey: tssPubKey, - crosschainFlags: crosschainFlags, - blockHeaderEnabledChains: blockHeaderEnabledChains, - } +// SetConfig sets a new config to the app context +func (c *AppContext) SetConfig(cfg config.Config) { + c.mu.Lock() + defer c.mu.Unlock() + c.config = cfg } // Config returns the app context config @@ -180,26 +165,9 @@ func (c *AppContext) GetBlockHeaderEnabledChains(chainID int64) (lightclienttype return lightclienttypes.HeaderSupportedChain{}, false } -// Update updates the inner config and app context -func (c *AppContext) UpdateContext(config config.Config, newContext *AppContext, logger zerolog.Logger) { - c.Update( - config, - newContext.GetKeygen(), - newContext.GetEnabledExternalChains(), - newContext.GetEnabledExternalChainParams(), - newContext.GetBTCNetParams(), - newContext.GetCurrentTssPubkey(), - newContext.GetCrossChainFlags(), - newContext.GetAllHeaderEnabledChains(), - false, - logger, - ) -} - // Update updates app context and params for all chains // this must be the ONLY function that writes to app context func (c *AppContext) Update( - config config.Config, keygen observertypes.Keygen, newChains []chains.Chain, newChainParams map[int64]*observertypes.ChainParams, @@ -249,7 +217,6 @@ func (c *AppContext) Update( c.btcNetParams = btcNetParams } - c.config = config c.keygen = keygen c.chainsEnabled = newChains c.chainParamMap = newChainParams diff --git a/zetaclient/context/app_context_test.go b/zetaclient/context/app_context_test.go index 9f1ff6ca97..1e07e84ae3 100644 --- a/zetaclient/context/app_context_test.go +++ b/zetaclient/context/app_context_test.go @@ -48,7 +48,6 @@ func getTestAppContext( // feed chain params appContext.Update( - cfg, observertypes.Keygen{}, []chains.Chain{evmChain}, newChainParams, @@ -185,7 +184,6 @@ func TestUpdateAppContext(t *testing.T) { require.NotNil(t, crosschainFlags) zetaContext.Update( - testCfg, keyGenToUpdate, enabledChainsToUpdate, newChainParamsToUpdate, @@ -279,7 +277,6 @@ func TestUpdateAppContext(t *testing.T) { verificationFlags := sample.HeaderSupportedChains() require.NotNil(t, crosschainFlags) zetaContext.Update( - testCfg, keyGenToUpdate, enabledChainsToUpdate, newChainParamsToUpdate, diff --git a/zetaclient/orchestrator/app_context_update.go b/zetaclient/orchestrator/app_context_update.go index 29e549312f..4582210d20 100644 --- a/zetaclient/orchestrator/app_context_update.go +++ b/zetaclient/orchestrator/app_context_update.go @@ -5,32 +5,11 @@ import ( "time" "github.com/pkg/errors" - "github.com/rs/zerolog" - "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/context" ) -// WatchUpgradePlan watches for upgrade plan and stops orchestrator if upgrade height is reached -func (oc *Orchestrator) WatchUpgradePlan() { - oc.logger.Std.Info().Msg("WatchUpgradePlan started") - - // detect upgrade plan every half Zeta block in order to hit every height - ticker := time.NewTicker(common.ZetaBlockTime / 2) - for range ticker.C { - reached, err := oc.UpgradeHeightReached() - if err != nil { - oc.logger.Sampled.Error().Err(err).Msg("error detecting upgrade plan") - } else if reached { - oc.Stop() - oc.logger.Std.Info().Msg("WatchUpgradePlan stopped") - return - } - } -} - // WatchAppContext watches for app context changes and updates app context func (oc *Orchestrator) WatchAppContext() { oc.logger.Std.Info().Msg("UpdateAppContext started") @@ -39,7 +18,7 @@ func (oc *Orchestrator) WatchAppContext() { for { select { case <-ticker.C: - err := UpdateAppContext(oc.appContext, oc.zetacoreClient, oc.logger.Std) + err := oc.UpdateAppContext() if err != nil { oc.logger.Std.Err(err).Msg("error updating zetaclient app context") } @@ -50,48 +29,44 @@ func (oc *Orchestrator) WatchAppContext() { } } -// CreateAppContext creates new app context from config and zetacore client -func CreateAppContext( - config config.Config, - zetacoreClient interfaces.ZetacoreClient, - logger zerolog.Logger, -) (*context.AppContext, error) { - // create app context from config - appContext := context.NewAppContext(config) - - // update app context from zetacore - err := UpdateAppContext(appContext, zetacoreClient, logger) - if err != nil { - return nil, errors.Wrap(err, "error updating app context") - } +// WatchUpgradePlan watches for upgrade plan and stops orchestrator if upgrade height is reached +func (oc *Orchestrator) WatchUpgradePlan() { + oc.logger.Std.Info().Msg("WatchUpgradePlan started") - return appContext, nil + // detect upgrade plan every half Zeta block in order to hit every height + ticker := time.NewTicker(common.ZetaBlockTime / 2) + for range ticker.C { + reached, err := oc.UpgradeHeightReached() + if err != nil { + oc.logger.Sampled.Error().Err(err).Msg("error detecting upgrade plan") + } else if reached { + oc.Stop() + oc.logger.Std.Info().Msg("WatchUpgradePlan stopped") + return + } + } } // UpdateAppContext updates zetaclient app context -func UpdateAppContext( - appContext *context.AppContext, - zetacoreClient interfaces.ZetacoreClient, - logger zerolog.Logger, -) error { - // reload config from file - newConfig, err := config.Load(appContext.Config().ZetaCoreHome) +func (oc *Orchestrator) UpdateAppContext() error { + // fetch latest app context from zetacore + err := oc.zetacoreClient.UpdateAppContext(oc.appContext, oc.logger.Std) if err != nil { - return errors.Wrapf( - err, - "UpdateAppContext: error loading config from path %s", - appContext.Config().ZetaCoreHome, - ) + return errors.Wrap(err, "UpdateAppContext: error updating app context from zetacore") } - // fetch latest app context from zetacore - newContext, err := zetacoreClient.GetLatestAppContext() + // reload config from file to allow for runtime config changes + // this allows operator to update zetaclient config without restarting zetaclient + zetazoreHome := oc.appContext.Config().ZetaCoreHome + newConfig, err := config.Load(zetazoreHome) if err != nil { - return errors.Wrap(err, "UpdateAppContext: error getting latest app context") + return errors.Wrapf(err, "UpdateAppContext: error loading config from path %s", zetazoreHome) } - // update inner config and app context - appContext.UpdateContext(newConfig, newContext, logger) + // set new config to app context + // we keep the old ZetaCoreHome because newConfig.ZetaCoreHome is empty after reload + newConfig.ZetaCoreHome = zetazoreHome + oc.appContext.SetConfig(newConfig) return nil } diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index 403401dad9..82b836904f 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -132,12 +132,12 @@ func NewOrchestrator( // Start all orchestrator routines func (oc *Orchestrator) Start() { - // watch for upgrade plan in zetacore - go oc.WatchUpgradePlan() - // watch for zetaclient app context changes go oc.WatchAppContext() + // watch for upgrade plan in zetacore + go oc.WatchUpgradePlan() + // watch for enabling/disabling chains go oc.WatchEnabledChains() diff --git a/zetaclient/orchestrator/orchestrator_test.go b/zetaclient/orchestrator/orchestrator_test.go index d7530f3f27..8b71367cb4 100644 --- a/zetaclient/orchestrator/orchestrator_test.go +++ b/zetaclient/orchestrator/orchestrator_test.go @@ -78,7 +78,6 @@ func CreateTestAppContext( // feed chain params appContext.Update( - cfg, observertypes.Keygen{}, []chains.Chain{evmChain, btcChain}, chainParamsMap, diff --git a/zetaclient/testutils/mocks/zetacore_client.go b/zetaclient/testutils/mocks/zetacore_client.go index e9b8b6f93d..c8866aaf85 100644 --- a/zetaclient/testutils/mocks/zetacore_client.go +++ b/zetaclient/testutils/mocks/zetacore_client.go @@ -53,14 +53,7 @@ func NewMockZetacoreClient() *MockZetacoreClient { } } -func (m *MockZetacoreClient) GetLatestAppContext() (*clientcontext.AppContext, error) { - if m.paused { - return nil, errors.New(ErrMsgPaused) - } - return nil, nil -} - -func (m *MockZetacoreClient) UpdateAppContext(_ *clientcontext.AppContext, _ bool, _ zerolog.Logger) error { +func (m *MockZetacoreClient) UpdateAppContext(_ *clientcontext.AppContext, _ zerolog.Logger) error { if m.paused { return errors.New(ErrMsgPaused) } diff --git a/zetaclient/zetacore/client.go b/zetaclient/zetacore/client.go index 836876c23a..226a7a12fb 100644 --- a/zetaclient/zetacore/client.go +++ b/zetaclient/zetacore/client.go @@ -41,8 +41,6 @@ type Client struct { broadcastLock *sync.RWMutex chainID string chain chains.Chain - stop chan struct{} - pause chan struct{} Telemetry *metrics.TelemetryServer // enableMockSDKClient is a flag that determines whether the mock cosmos sdk client should be used, primarily for @@ -132,10 +130,8 @@ func NewClient( encodingCfg: app.MakeEncodingConfig(), keys: keys, broadcastLock: &sync.RWMutex{}, - stop: make(chan struct{}), chainID: chainID, chain: zetaChain, - pause: make(chan struct{}), Telemetry: telemetry, enableMockSDKClient: false, mockSDKClient: nil, @@ -169,11 +165,6 @@ func (c *Client) GetKeys() keyinterfaces.ObserverKeys { return c.keys } -func (c *Client) Stop() { - c.logger.Info().Msgf("zetacore client is stopping") - close(c.stop) // this notifies all configupdater to stop -} - // GetAccountNumberAndSequenceNumber We do not use multiple KeyType for now , but this can be optionally used in the future to seprate TSS signer from Zetaclient GRantee func (c *Client) GetAccountNumberAndSequenceNumber(_ authz.KeyType) (uint64, uint64, error) { ctx, err := c.GetContext() @@ -226,102 +217,12 @@ func (c *Client) WaitForZetacoreToCreateBlocks() error { return nil } -// UpdateAppContext updates app context -// zetacore stores app context for all clients -func (c *Client) UpdateAppContext( - appContext *context.AppContext, - init bool, - sampledLogger zerolog.Logger, -) error { - bn, err := c.GetBlockHeight() - if err != nil { - return fmt.Errorf("failed to get zetablock height: %w", err) - } - plan, err := c.GetUpgradePlan() - if err != nil { - // if there is no active upgrade plan, plan will be nil, err will be nil as well. - return fmt.Errorf("failed to get upgrade plan: %w", err) - } - if plan != nil && bn == plan.Height-1 { // stop zetaclients; notify operator to upgrade and restart - c.logger.Warn(). - Msgf("Active upgrade plan detected and upgrade height reached: %s at height %d; ZetaClient is stopped;"+ - "please kill this process, replace zetaclientd binary with upgraded version, and restart zetaclientd", plan.Name, plan.Height) - c.pause <- struct{}{} // notify Orchestrator to stop Observers, Signers, and Orchestrator itself - } - - chainParams, err := c.GetChainParams() - if err != nil { - return fmt.Errorf("failed to get chain params: %w", err) - } - - newChainParams := make(map[int64]*observertypes.ChainParams) - - // check and update chain params for each chain - for _, chainParam := range chainParams { - err := observertypes.ValidateChainParams(chainParam) - if err != nil { - sampledLogger.Warn().Err(err).Msgf("Invalid chain params for chain %d", chainParam.ChainId) - continue - } - newChainParams[chainParam.ChainId] = chainParam - } - - supportedChains, err := c.GetSupportedChains() - if err != nil { - return fmt.Errorf("failed to get supported chains: %w", err) - } - newChains := make([]chains.Chain, len(supportedChains)) - for i, chain := range supportedChains { - newChains[i] = *chain - } - keyGen, err := c.GetKeyGen() - if err != nil { - c.logger.Info().Msg("Unable to fetch keygen from zetacore") - return fmt.Errorf("failed to get keygen: %w", err) - } - - // get latest TSS public key - tss, err := c.GetCurrentTss() - if err != nil { - c.logger.Info().Err(err).Msg("Unable to fetch TSS from zetacore") - return fmt.Errorf("failed to get current tss: %w", err) - } - tssPubKey := tss.GetTssPubkey() - - crosschainFlags, err := c.GetCrosschainFlags() - if err != nil { - c.logger.Info().Msg("Unable to fetch cross-chain flags from zetacore") - return fmt.Errorf("failed to get crosschain flags: %w", err) - } - - blockHeaderEnabledChains, err := c.GetBlockHeaderEnabledChains() - if err != nil { - c.logger.Info().Msg("Unable to fetch block header enabled chains from zetacore") - return err - } - - appContext.Update( - config.NewConfig(), - *keyGen, - newChains, - newChainParams, - &chaincfg.RegressionNetParams, - tssPubKey, - crosschainFlags, - blockHeaderEnabledChains, - init, - c.logger, - ) - - return nil -} - -// GetLatestAppContext queries zetacore to build the latest app context -func (c *Client) GetLatestAppContext() (*context.AppContext, error) { +// UpdateAppContext queries zetacore to update app context fields +func (c *Client) UpdateAppContext(appContext *context.AppContext, logger zerolog.Logger) error { // get latest supported chains supportedChains, err := c.GetSupportedChains() if err != nil { - return nil, errors.Wrap(err, "GetSupportedChains failed") + return errors.Wrap(err, "GetSupportedChains failed") } supportedChainsMap := make(map[int64]chains.Chain) for _, chain := range supportedChains { @@ -331,7 +232,7 @@ func (c *Client) GetLatestAppContext() (*context.AppContext, error) { // get latest chain parameters chainParams, err := c.GetChainParams() if err != nil { - return nil, errors.Wrap(err, "GetChainParams failed") + return errors.Wrap(err, "GetChainParams failed") } var btcNetParams *chaincfg.Params @@ -360,7 +261,7 @@ func (c *Client) GetLatestAppContext() (*context.AppContext, error) { if chains.IsBitcoinChain(chainParam.ChainId) { btcNetParams, err = chains.BitcoinNetParamsFromChainID(chainParam.ChainId) if err != nil { - return nil, errors.Wrapf(err, "failed to get Bitcoin network params for chain %d", chainParam.ChainId) + return errors.Wrapf(err, "BitcoinNetParamsFromChainID failed for chain %d", chainParam.ChainId) } } @@ -372,29 +273,30 @@ func (c *Client) GetLatestAppContext() (*context.AppContext, error) { // get latest keygen keyGen, err := c.GetKeyGen() if err != nil { - return nil, errors.Wrap(err, "GetKeyGen failed") + return errors.Wrap(err, "GetKeyGen failed") } // get latest TSS public key tss, err := c.GetCurrentTss() if err != nil { - return nil, errors.Wrap(err, "GetCurrentTss failed") + return errors.Wrap(err, "GetCurrentTss failed") } tssPubKey := tss.GetTssPubkey() // get latest crosschain flags crosschainFlags, err := c.GetCrosschainFlags() if err != nil { - return nil, errors.Wrap(err, "GetCrosschainFlags failed") + return errors.Wrap(err, "GetCrosschainFlags failed") } // get latest block header enabled chains blockHeaderEnabledChains, err := c.GetBlockHeaderEnabledChains() if err != nil { - return nil, errors.Wrap(err, "GetBlockHeaderEnabledChains failed") + return errors.Wrap(err, "GetBlockHeaderEnabledChains failed") } - return context.CreateAppContext( + // update app context fields + appContext.Update( *keyGen, chainsEnabled, chainParamMap, @@ -402,15 +304,11 @@ func (c *Client) GetLatestAppContext() (*context.AppContext, error) { tssPubKey, crosschainFlags, blockHeaderEnabledChains, - ), nil -} - -func (c *Client) Pause() { - <-c.pause -} + false, + logger, + ) -func (c *Client) Unpause() { - c.pause <- struct{}{} + return nil } func (c *Client) EnableMockSDKClient(client rpcclient.Client) { diff --git a/zetaclient/zetacore/tx_test.go b/zetaclient/zetacore/tx_test.go index 3521ee2382..fab38452df 100644 --- a/zetaclient/zetacore/tx_test.go +++ b/zetaclient/zetacore/tx_test.go @@ -335,7 +335,7 @@ func TestZetacore_UpdateAppContext(t *testing.T) { cfg := config.NewConfig() appCTX := context.NewAppContext(cfg) zetacoreBroadcast = MockBroadcast - err := client.UpdateAppContext(appCTX, false, zerolog.Logger{}) + err := client.UpdateAppContext(appCTX, zerolog.Logger{}) require.NoError(t, err) }) } From bf0468f33f5600925f3b9917cd773a9cb1058f79 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 1 Jul 2024 00:24:02 -0500 Subject: [PATCH 05/18] added some unit tests on context updater and chain activator --- cmd/zetaclientd-supervisor/lib.go | 2 +- cmd/zetaclientd/p2p_diagnostics.go | 2 +- cmd/zetaclientd/start.go | 18 +- cmd/zetaclientd/start_utils.go | 6 +- zetaclient/chains/base/logger.go | 4 +- zetaclient/chains/base/logger_test.go | 12 +- .../chains/evm/observer/inbound_test.go | 6 +- .../chains/evm/observer/observer_test.go | 3 +- .../chains/evm/observer/outbound_test.go | 2 +- zetaclient/compliance/compliance.go | 2 +- zetaclient/compliance/compliance_test.go | 2 +- zetaclient/config/config.go | 10 +- zetaclient/config/types.go | 4 +- zetaclient/context/app_context.go | 74 ++-- zetaclient/context/app_context_test.go | 364 ++++++------------ zetaclient/keys/keys.go | 2 +- zetaclient/keys/keys_test.go | 4 +- zetaclient/orchestrator/app_context_update.go | 2 - .../orchestrator/app_context_update_test.go | 157 ++++++++ zetaclient/orchestrator/chain_activate.go | 18 +- .../orchestrator/chain_activate_test.go | 239 ++++++++++++ zetaclient/orchestrator/orchestrator_test.go | 3 +- .../testdata/config/zetaclient_config.json | 114 ++++++ zetaclient/testutils/mocks/zetacore_client.go | 20 +- zetaclient/testutils/testdata.go | 9 + zetaclient/testutils/testdata_naming.go | 5 + zetaclient/tss/tss_signer.go | 2 +- zetaclient/zetacore/client.go | 7 +- 28 files changed, 735 insertions(+), 358 deletions(-) create mode 100644 zetaclient/testdata/config/zetaclient_config.json diff --git a/cmd/zetaclientd-supervisor/lib.go b/cmd/zetaclientd-supervisor/lib.go index e8f1b54470..07eb337f1b 100644 --- a/cmd/zetaclientd-supervisor/lib.go +++ b/cmd/zetaclientd-supervisor/lib.go @@ -41,7 +41,7 @@ func (w *serializedWriter) Write(p []byte) (n int, err error) { return w.upstream.Write(p) } -func getLogger(cfg config.Config, out io.Writer) zerolog.Logger { +func getLogger(cfg *config.Config, out io.Writer) zerolog.Logger { var logger zerolog.Logger switch cfg.LogFormat { case "json": diff --git a/cmd/zetaclientd/p2p_diagnostics.go b/cmd/zetaclientd/p2p_diagnostics.go index 330f520013..33fb4e2866 100644 --- a/cmd/zetaclientd/p2p_diagnostics.go +++ b/cmd/zetaclientd/p2p_diagnostics.go @@ -30,7 +30,7 @@ func RunDiagnostics( startLogger zerolog.Logger, peers p2p.AddrList, hotkeyPk cryptotypes.PrivKey, - cfg config.Config, + cfg *config.Config, ) error { startLogger.Warn().Msg("P2P Diagnostic mode enabled") startLogger.Warn().Msgf("seed peer: %s", peers) diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 79e0a1e29f..9e610a78d7 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -239,13 +239,16 @@ func start(_ *cobra.Command, _ []string) error { // Defensive check: Make sure the tss address is set to the current TSS address and not the newly generated one tss.CurrentPubkey = currentTss.TssPubkey - if tss.EVMAddress() == (ethcommon.Address{}) || tss.BTCAddress() == "" { - startLogger.Error().Msg("TSS address is not set in zetacore") + startLogger.Info().Msgf("Current TSS PubKey: %s", tss.CurrentPubkey) + if tss.EVMAddress() == (ethcommon.Address{}) { + return errors.New("Current TSS ETH address is empty") } - startLogger.Info(). - Msgf("Current TSS address \n ETH : %s \n BTC : %s \n PubKey : %s ", tss.EVMAddress(), tss.BTCAddress(), tss.CurrentPubkey) - if len(appContext.GetEnabledExternalChains()) == 0 { - startLogger.Error().Msgf("No external chains enabled in the zetacore %s ", cfg.String()) + startLogger.Info().Msgf("Current TSS ETH address: %s", tss.EVMAddress()) + if tss.BitcoinNetParams != nil { + if tss.BTCAddress() == "" { + return errors.New("Current TSS BTC address is empty") + } + startLogger.Info().Msgf("Current TSS BTC address: %s", tss.BTCAddress()) } // Stop zetaclient if this node is not an active observer @@ -289,7 +292,8 @@ func start(_ *cobra.Command, _ []string) error { // defer zetaSupplyChecker.Stop() //} - // Orchestrator wraps the zetacore client and adds the observers and signer maps to it . This is the high level object used for CCTX interactions + // Orchestrator wraps the app context, zetacore client, TSS and metrics server. + // This is the high level actor that monitors zetacore changes and coordinates CCTXs interactions. orch := orchestrator.NewOrchestrator( appContext, zetacoreClient, diff --git a/cmd/zetaclientd/start_utils.go b/cmd/zetaclientd/start_utils.go index 10f0b0beea..a363c6a62d 100644 --- a/cmd/zetaclientd/start_utils.go +++ b/cmd/zetaclientd/start_utils.go @@ -14,7 +14,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/config" ) -func waitForZetaCore(config config.Config, logger zerolog.Logger) { +func waitForZetaCore(config *config.Config, logger zerolog.Logger) { // wait until zetacore is up logger.Debug().Msg("Waiting for zetacore to open 9090 port...") for { @@ -55,8 +55,8 @@ func validatePeer(seedPeer string) error { // maskCfg sensitive fields are masked, currently only the EVM endpoints and bitcoin credentials, // // other fields can be added. -func maskCfg(cfg config.Config) string { - maskedCfg := cfg +func maskCfg(cfg *config.Config) string { + maskedCfg := *cfg maskedCfg.BitcoinConfig = config.BTCConfig{ RPCUsername: cfg.BitcoinConfig.RPCUsername, diff --git a/zetaclient/chains/base/logger.go b/zetaclient/chains/base/logger.go index eeffcfab3b..795b78e321 100644 --- a/zetaclient/chains/base/logger.go +++ b/zetaclient/chains/base/logger.go @@ -51,7 +51,7 @@ type ObserverLogger struct { } // InitLogger initializes the base loggers -func InitLogger(cfg config.Config) (Logger, error) { +func InitLogger(cfg *config.Config) (Logger, error) { // open compliance log file file, err := openComplianceLogFile(cfg) if err != nil { @@ -89,7 +89,7 @@ func InitLogger(cfg config.Config) (Logger, error) { } // openComplianceLogFile opens the compliance log file -func openComplianceLogFile(cfg config.Config) (*os.File, error) { +func openComplianceLogFile(cfg *config.Config) (*os.File, error) { // use zetacore home as default logPath := cfg.ZetaCoreHome if cfg.ComplianceConfig.LogPath != "" { diff --git a/zetaclient/chains/base/logger_test.go b/zetaclient/chains/base/logger_test.go index 07c0941f5a..3f8b7454c0 100644 --- a/zetaclient/chains/base/logger_test.go +++ b/zetaclient/chains/base/logger_test.go @@ -15,12 +15,12 @@ func TestInitLogger(t *testing.T) { tests := []struct { name string t *testing.T - cfg config.Config + cfg *config.Config fail bool }{ { name: "should be able to initialize json formatted logger", - cfg: config.Config{ + cfg: &config.Config{ LogFormat: "json", LogLevel: 1, // zerolog.InfoLevel, ComplianceConfig: config.ComplianceConfig{ @@ -31,7 +31,7 @@ func TestInitLogger(t *testing.T) { }, { name: "should be able to initialize plain text logger", - cfg: config.Config{ + cfg: &config.Config{ LogFormat: "text", LogLevel: 2, // zerolog.WarnLevel, ComplianceConfig: config.ComplianceConfig{ @@ -42,7 +42,7 @@ func TestInitLogger(t *testing.T) { }, { name: "should be able to initialize default formatted logger", - cfg: config.Config{ + cfg: &config.Config{ LogFormat: "unknown", LogLevel: 3, // zerolog.ErrorLevel, ComplianceConfig: config.ComplianceConfig{ @@ -53,7 +53,7 @@ func TestInitLogger(t *testing.T) { }, { name: "should be able to initialize sampled logger", - cfg: config.Config{ + cfg: &config.Config{ LogFormat: "json", LogLevel: 4, // zerolog.DebugLevel, LogSampler: true, @@ -64,7 +64,7 @@ func TestInitLogger(t *testing.T) { }, { name: "should fail on invalid compliance log path", - cfg: config.Config{ + cfg: &config.Config{ LogFormat: "json", LogLevel: 1, // zerolog.InfoLevel, ComplianceConfig: config.ComplianceConfig{ diff --git a/zetaclient/chains/evm/observer/inbound_test.go b/zetaclient/chains/evm/observer/inbound_test.go index 51f74252a8..5b323b1139 100644 --- a/zetaclient/chains/evm/observer/inbound_test.go +++ b/zetaclient/chains/evm/observer/inbound_test.go @@ -275,7 +275,7 @@ func Test_BuildInboundVoteMsgForZetaSentEvent(t *testing.T) { event := testutils.ParseReceiptZetaSent(receipt, connector) // create test compliance config - cfg := config.Config{ + cfg := &config.Config{ ComplianceConfig: config.ComplianceConfig{}, } @@ -323,7 +323,7 @@ func Test_BuildInboundVoteMsgForDepositedEvent(t *testing.T) { sender := ethcommon.HexToAddress(tx.From) // create test compliance config - cfg := config.Config{ + cfg := &config.Config{ ComplianceConfig: config.ComplianceConfig{}, } @@ -376,7 +376,7 @@ func Test_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { // create test compliance config ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, mocks.MockChainParams(1, 1)) - cfg := config.Config{ + cfg := &config.Config{ ComplianceConfig: config.ComplianceConfig{}, } diff --git a/zetaclient/chains/evm/observer/observer_test.go b/zetaclient/chains/evm/observer/observer_test.go index fbf7d558b3..03e798b87d 100644 --- a/zetaclient/chains/evm/observer/observer_test.go +++ b/zetaclient/chains/evm/observer/observer_test.go @@ -59,13 +59,12 @@ func getAppContext( // feed chain params appCtx.Update( observertypes.Keygen{}, + "", []chains.Chain{evmChain}, newChainParams, &chaincfg.RegressionNetParams, - "", *sample.CrosschainFlags(), sample.HeaderSupportedChains(), - true, zerolog.Logger{}, ) // create app context diff --git a/zetaclient/chains/evm/observer/outbound_test.go b/zetaclient/chains/evm/observer/outbound_test.go index 516d469bd8..3ebbbb31b2 100644 --- a/zetaclient/chains/evm/observer/outbound_test.go +++ b/zetaclient/chains/evm/observer/outbound_test.go @@ -82,7 +82,7 @@ func Test_IsOutboundProcessed(t *testing.T) { ob.SetTxNReceipt(nonce, receipt, outbound) // modify compliance config to restrict sender address - cfg := config.Config{ + cfg := &config.Config{ ComplianceConfig: config.ComplianceConfig{}, } cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.InboundParams.Sender} diff --git a/zetaclient/compliance/compliance.go b/zetaclient/compliance/compliance.go index bb8bdbd005..a60c129d12 100644 --- a/zetaclient/compliance/compliance.go +++ b/zetaclient/compliance/compliance.go @@ -13,7 +13,7 @@ import ( var restrictedAddressBook = map[string]bool{} // LoadComplianceConfig loads compliance config from zetaclient config -func LoadComplianceConfig(cfg config.Config) { +func LoadComplianceConfig(cfg *config.Config) { restrictedAddressBook = cfg.GetRestrictedAddressBook() } diff --git a/zetaclient/compliance/compliance_test.go b/zetaclient/compliance/compliance_test.go index 7f31243b9a..1b524f1f05 100644 --- a/zetaclient/compliance/compliance_test.go +++ b/zetaclient/compliance/compliance_test.go @@ -17,7 +17,7 @@ func TestCctxRestricted(t *testing.T) { cctx := testutils.LoadCctxByNonce(t, chain.ChainId, 6270) // create config - cfg := config.Config{ + cfg := &config.Config{ ComplianceConfig: config.ComplianceConfig{}, } diff --git a/zetaclient/config/config.go b/zetaclient/config/config.go index 990f45b4fa..c5b8cc10de 100644 --- a/zetaclient/config/config.go +++ b/zetaclient/config/config.go @@ -36,12 +36,12 @@ func Save(config *Config, path string) error { } // Load loads ZetaClient config from a filepath -func Load(path string) (Config, error) { +func Load(path string) (*Config, error) { // retrieve file file := filepath.Join(path, folder, filename) file, err := filepath.Abs(file) if err != nil { - return Config{}, err + return nil, err } file = filepath.Clean(file) @@ -49,11 +49,11 @@ func Load(path string) (Config, error) { cfg := NewConfig() input, err := os.ReadFile(file) if err != nil { - return Config{}, err + return nil, err } err = json.Unmarshal(input, &cfg) if err != nil { - return Config{}, err + return nil, err } // read keyring backend and use test by default @@ -61,7 +61,7 @@ func Load(path string) (Config, error) { cfg.KeyringBackend = KeyringBackendTest } if cfg.KeyringBackend != KeyringBackendFile && cfg.KeyringBackend != KeyringBackendTest { - return Config{}, fmt.Errorf("invalid keyring backend %s", cfg.KeyringBackend) + return nil, fmt.Errorf("invalid keyring backend %s", cfg.KeyringBackend) } // fields sanitization diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index b14cc8191b..4bedf71be0 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -78,8 +78,8 @@ type Config struct { ComplianceConfig ComplianceConfig `json:"ComplianceConfig"` } -func NewConfig() Config { - return Config{ +func NewConfig() *Config { + return &Config{ cfgLock: &sync.RWMutex{}, EVMChainConfigs: make(map[int64]EVMConfig), } diff --git a/zetaclient/context/app_context.go b/zetaclient/context/app_context.go index 0605e59d15..af8b07904a 100644 --- a/zetaclient/context/app_context.go +++ b/zetaclient/context/app_context.go @@ -16,12 +16,12 @@ import ( // AppContext contains zetaclient application context // these are initialized and updated at runtime periodically type AppContext struct { - config config.Config + config *config.Config keygen observertypes.Keygen + currentTssPubkey string chainsEnabled []chains.Chain chainParamMap map[int64]*observertypes.ChainParams btcNetParams *chaincfg.Params - currentTssPubkey string crosschainFlags observertypes.CrosschainFlags // blockHeaderEnabledChains is used to store the list of chains that have block header verification enabled @@ -29,36 +29,36 @@ type AppContext struct { blockHeaderEnabledChains []lightclienttypes.HeaderSupportedChain // mu is to protect the app context from concurrent access - mu *sync.RWMutex + mu sync.RWMutex } -// NewAppContext creates and returns new AppContext -// it is initializing chain params from provided config -func NewAppContext(cfg config.Config) *AppContext { +// NewAppContext creates empty app context with given config +func NewAppContext(cfg *config.Config) *AppContext { return &AppContext{ config: cfg, chainsEnabled: []chains.Chain{}, chainParamMap: make(map[int64]*observertypes.ChainParams), crosschainFlags: observertypes.CrosschainFlags{}, blockHeaderEnabledChains: []lightclienttypes.HeaderSupportedChain{}, - mu: new(sync.RWMutex), + mu: sync.RWMutex{}, } } // SetConfig sets a new config to the app context -func (c *AppContext) SetConfig(cfg config.Config) { +func (c *AppContext) SetConfig(cfg *config.Config) { c.mu.Lock() defer c.mu.Unlock() c.config = cfg } // Config returns the app context config -func (c *AppContext) Config() config.Config { +func (c *AppContext) Config() *config.Config { c.mu.RLock() defer c.mu.RUnlock() return c.config } +// GetKeygen returns the current keygen information func (c *AppContext) GetKeygen() observertypes.Keygen { c.mu.RLock() defer c.mu.RUnlock() @@ -76,6 +76,7 @@ func (c *AppContext) GetKeygen() observertypes.Keygen { } } +// GetCurrentTssPubkey returns the current TSS public key func (c *AppContext) GetCurrentTssPubkey() string { c.mu.RLock() defer c.mu.RUnlock() @@ -87,6 +88,7 @@ func (c *AppContext) GetEnabledExternalChains() []chains.Chain { c.mu.RLock() defer c.mu.RUnlock() + // deep copy chains externalChains := make([]chains.Chain, 0) for _, chain := range c.chainsEnabled { if chain.IsExternal { @@ -101,6 +103,7 @@ func (c *AppContext) GetEnabledBTCChains() []chains.Chain { c.mu.RLock() defer c.mu.RUnlock() + // deep copy btc chains btcChains := make([]chains.Chain, 0) for _, chain := range c.chainsEnabled { if chain.Consensus == chains.Consensus_bitcoin { @@ -124,6 +127,7 @@ func (c *AppContext) GetEnabledExternalChainParams() map[int64]*observertypes.Ch return copied } +// GetExternalChainParams returns chain params for a specific chain ID func (c *AppContext) GetExternalChainParams(chainID int64) (*observertypes.ChainParams, bool) { c.mu.RLock() defer c.mu.RUnlock() @@ -146,13 +150,6 @@ func (c *AppContext) GetCrossChainFlags() observertypes.CrosschainFlags { return c.crosschainFlags } -// GetAllHeaderEnabledChains returns all verification flags -func (c *AppContext) GetAllHeaderEnabledChains() []lightclienttypes.HeaderSupportedChain { - c.mu.RLock() - defer c.mu.RUnlock() - return c.blockHeaderEnabledChains -} - // GetBlockHeaderEnabledChains checks if block header verification is enabled for a specific chain func (c *AppContext) GetBlockHeaderEnabledChains(chainID int64) (lightclienttypes.HeaderSupportedChain, bool) { c.mu.RLock() @@ -169,46 +166,33 @@ func (c *AppContext) GetBlockHeaderEnabledChains(chainID int64) (lightclienttype // this must be the ONLY function that writes to app context func (c *AppContext) Update( keygen observertypes.Keygen, - newChains []chains.Chain, - newChainParams map[int64]*observertypes.ChainParams, - btcNetParams *chaincfg.Params, tssPubKey string, + chainsEnabled []chains.Chain, + chainParamMap map[int64]*observertypes.ChainParams, + btcNetParams *chaincfg.Params, crosschainFlags observertypes.CrosschainFlags, blockHeaderEnabledChains []lightclienttypes.HeaderSupportedChain, - init bool, logger zerolog.Logger, ) { c.mu.Lock() defer c.mu.Unlock() // Ignore whatever order zetacore organizes chain list in state - sort.SliceStable(newChains, func(i, j int) bool { - return newChains[i].ChainId < newChains[j].ChainId + sort.SliceStable(chainsEnabled, func(i, j int) bool { + return chainsEnabled[i].ChainId < chainsEnabled[j].ChainId }) - if len(newChains) == 0 { - logger.Warn().Msg("UpdateChainParams: No chains enabled in ZeroCore") + if len(chainsEnabled) == 0 { + logger.Warn().Msg("UpdateChainParams: no external chain enabled in the zetacore") } - // Add some warnings if chain list changes at runtime - if !init { - if len(c.chainsEnabled) != len(newChains) { - logger.Warn().Msgf( - "UpdateChainParams: ChainsEnabled changed at runtime!! current: %v, new: %v", - c.chainsEnabled, - newChains, - ) - } else { - for i, chain := range newChains { - if chain != c.chainsEnabled[i] { - logger.Warn().Msgf( - "UpdateChainParams: ChainsEnabled changed at runtime!! current: %v, new: %v", - c.chainsEnabled, - newChains, - ) - } - } - } + // Add log print if the number of enabled chains changes at runtime + if len(c.chainsEnabled) != len(chainsEnabled) { + logger.Info().Msgf( + "UpdateChainParams: number of enabled chains changed at runtime!! before: %d, after: %d", + len(c.chainsEnabled), + len(chainsEnabled), + ) } // btcNetParams points one of [mainnet, testnet, regnet] @@ -218,8 +202,8 @@ func (c *AppContext) Update( } c.keygen = keygen - c.chainsEnabled = newChains - c.chainParamMap = newChainParams + c.chainsEnabled = chainsEnabled + c.chainParamMap = chainParamMap c.currentTssPubkey = tssPubKey c.crosschainFlags = crosschainFlags c.blockHeaderEnabledChains = blockHeaderEnabledChains diff --git a/zetaclient/context/app_context_test.go b/zetaclient/context/app_context_test.go index 1e07e84ae3..92aa39be20 100644 --- a/zetaclient/context/app_context_test.go +++ b/zetaclient/context/app_context_test.go @@ -20,7 +20,6 @@ import ( func getTestAppContext( evmChain chains.Chain, evmChainParams *observertypes.ChainParams, - btcChainParams *observertypes.ChainParams, ccFlags *observertypes.CrosschainFlags, headerSupportedChains []lightclienttypes.HeaderSupportedChain, ) *context.AppContext { @@ -29,323 +28,178 @@ func getTestAppContext( cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ Chain: evmChain, } - if btcChainParams != nil { - cfg.BitcoinConfig = config.BTCConfig{ - RPCUsername: "test", - } - } // create app context appContext := context.NewAppContext(cfg) newChainParams := make(map[int64]*observertypes.ChainParams) newChainParams[evmChain.ChainId] = evmChainParams - newChainParams[btcChainParams.ChainId] = btcChainParams // create crosschain flags if not provided if ccFlags == nil { ccFlags = sample.CrosschainFlags() } - // feed chain params + // feed app context fields appContext.Update( observertypes.Keygen{}, + "", []chains.Chain{evmChain}, newChainParams, &chaincfg.RegressionNetParams, - "", *ccFlags, headerSupportedChains, - true, zerolog.Logger{}, ) return appContext } -func TestNewAppContext(t *testing.T) { +func Test_NewAppContext(t *testing.T) { t.Run("should create new app context with empty config", func(t *testing.T) { testCfg := config.NewConfig() - zetaContext := context.NewAppContext(testCfg) - require.NotNil(t, zetaContext) + appContext := context.NewAppContext(testCfg) + require.NotNil(t, appContext) + + // assert config + require.Equal(t, testCfg, appContext.Config()) // assert keygen - keyGen := zetaContext.GetKeygen() + keyGen := appContext.GetKeygen() require.Equal(t, observertypes.Keygen{}, keyGen) - // assert external chains - require.Empty(t, len(zetaContext.GetEnabledExternalChains())) - - // assert current tss pubkey - require.Equal(t, "", zetaContext.GetCurrentTssPubkey()) - - // assert external chain params - externalChainParams := zetaContext.GetEnabledExternalChainParams() - require.Empty(t, externalChainParams) - }) - - t.Run("should return nil chain params if chain id is not found", func(t *testing.T) { - // create config with btc config - testCfg := config.NewConfig() - testCfg.BitcoinConfig = config.BTCConfig{ - RPCUsername: "test_user", - RPCPassword: "test_password", - } - - // create app context with 0 chain id - zetaContext := context.NewAppContext(testCfg) - require.NotNil(t, zetaContext) - }) - - t.Run("should create new app context with config containing evm chain params", func(t *testing.T) { - testCfg := config.NewConfig() - testCfg.EVMChainConfigs = map[int64]config.EVMConfig{ - 1: { - Chain: chains.Chain{ - ChainName: 1, - ChainId: 1, - }, - }, - 2: { - Chain: chains.Chain{ - ChainName: 2, - ChainId: 2, - }, - }, - } - zetaContext := context.NewAppContext(testCfg) - require.NotNil(t, zetaContext) + // assert enabled external chains + require.Empty(t, appContext.GetEnabledExternalChains()) // assert external chain params - externalChainParams := zetaContext.GetEnabledExternalChainParams() - require.Equal(t, 2, len(externalChainParams)) - require.Equal(t, &observertypes.ChainParams{}, externalChainParams[1]) - require.Equal(t, &observertypes.ChainParams{}, externalChainParams[2]) + require.Empty(t, appContext.GetEnabledExternalChainParams()) - chainParams1, found := zetaContext.GetExternalChainParams(1) - require.True(t, found) - require.Equal(t, &observertypes.ChainParams{}, chainParams1) + // assert current tss pubkey + require.Equal(t, "", appContext.GetCurrentTssPubkey()) - chainParams2, found := zetaContext.GetExternalChainParams(2) - require.True(t, found) - require.Equal(t, &observertypes.ChainParams{}, chainParams2) + // assert crosschain flags + require.Equal(t, observertypes.CrosschainFlags{}, appContext.GetCrossChainFlags()) }) +} - t.Run("should create new app context with config containing btc config", func(t *testing.T) { - testCfg := config.NewConfig() - testCfg.BitcoinConfig = config.BTCConfig{ - RPCUsername: "test username", - RPCPassword: "test password", - RPCHost: "test host", - RPCParams: "test params", +func Test_SetGetConfig(t *testing.T) { + t.Run("should create new app context with empty config", func(t *testing.T) { + oldCfg := config.NewConfig() + appContext := context.NewAppContext(oldCfg) + require.NotNil(t, appContext) + require.Equal(t, oldCfg, appContext.Config()) + + // set new config + evmChain := chains.Ethereum + newCfg := config.NewConfig() + newCfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ + Chain: evmChain, } - zetaContext := context.NewAppContext(testCfg) - require.NotNil(t, zetaContext) + appContext.SetConfig(newCfg) + require.Equal(t, newCfg, appContext.Config()) }) } -func TestUpdateAppContext(t *testing.T) { - t.Run("should update app context after being created from empty config", func(t *testing.T) { - testCfg := config.NewConfig() - - zetaContext := context.NewAppContext(testCfg) - require.NotNil(t, zetaContext) - - keyGenToUpdate := observertypes.Keygen{ - Status: observertypes.KeygenStatus_KeyGenSuccess, - GranteePubkeys: []string{"testpubkey1"}, - } - enabledChainsToUpdate := []chains.Chain{ - { - ChainName: 1, - ChainId: 1, - IsExternal: true, - }, - { - ChainName: 2, - ChainId: 2, - IsExternal: true, - }, - chains.ZetaChainTestnet, - } - newChainParamsToUpdate := map[int64]*observertypes.ChainParams{ - 1: { - ChainId: 1, - }, - 2: { - ChainId: 2, - }, - 3: { - ChainId: 3, - }, - } - tssPubKeyToUpdate := "tsspubkeytest" - crosschainFlags := sample.CrosschainFlags() - verificationFlags := sample.HeaderSupportedChains() - - require.NotNil(t, crosschainFlags) - zetaContext.Update( - keyGenToUpdate, - enabledChainsToUpdate, - newChainParamsToUpdate, - &chaincfg.RegressionNetParams, - tssPubKeyToUpdate, - *crosschainFlags, - verificationFlags, - false, - log.Logger, - ) - - // assert keygen updated - keyGen := zetaContext.GetKeygen() - require.Equal(t, keyGenToUpdate, keyGen) - - // assert enabled external chains - require.Equal(t, enabledChainsToUpdate[0:2], zetaContext.GetEnabledExternalChains()) - - // assert current tss pubkey updated - require.Equal(t, tssPubKeyToUpdate, zetaContext.GetCurrentTssPubkey()) - - // assert evm chain params still empty because they were not specified in config - externalChainParams := zetaContext.GetEnabledExternalChainParams() - require.Empty(t, externalChainParams) +func Test_UpdateAndGetters(t *testing.T) { + // use evm and btc chains for testing + evmChain := chains.Ethereum + btcChain := chains.BitcoinMainnet + + // create sample parameters + keyGen := sample.Keygen(t) + chainsEnabled := []chains.Chain{evmChain, btcChain} + chainParamMap := map[int64]*observertypes.ChainParams{ + evmChain.ChainId: sample.ChainParams(evmChain.ChainId), + btcChain.ChainId: sample.ChainParams(btcChain.ChainId), + } + btcNetParams := &chaincfg.MainNetParams + tssPubKey := "tsspubkeytest" + ccFlags := *sample.CrosschainFlags() + headerSupportedChains := sample.HeaderSupportedChains() - ccFlags := zetaContext.GetCrossChainFlags() - require.Equal(t, *crosschainFlags, ccFlags) + // feed app context fields + appContext := context.NewAppContext(config.NewConfig()) + appContext.Update( + *keyGen, + tssPubKey, + chainsEnabled, + chainParamMap, + btcNetParams, + ccFlags, + headerSupportedChains, + log.Logger, + ) - verFlags := zetaContext.GetAllHeaderEnabledChains() - require.Equal(t, verificationFlags, verFlags) + t.Run("should get keygen", func(t *testing.T) { + result := appContext.GetKeygen() + require.Equal(t, *keyGen, result) }) - - t.Run( - "should update app context after being created from config with evm and btc chain params", - func(t *testing.T) { - testCfg := config.NewConfig() - testCfg.EVMChainConfigs = map[int64]config.EVMConfig{ - 1: { - Chain: chains.Chain{ - ChainName: 1, - ChainId: 1, - }, - }, - 2: { - Chain: chains.Chain{ - ChainName: 2, - ChainId: 2, - }, - }, - } - testCfg.BitcoinConfig = config.BTCConfig{ - RPCUsername: "test username", - RPCPassword: "test password", - RPCHost: "test host", - RPCParams: "test params", - } - - zetaContext := context.NewAppContext(testCfg) - require.NotNil(t, zetaContext) - - keyGenToUpdate := observertypes.Keygen{ - Status: observertypes.KeygenStatus_KeyGenSuccess, - GranteePubkeys: []string{"testpubkey1"}, - } - enabledChainsToUpdate := []chains.Chain{ - { - ChainName: 1, - ChainId: 1, - IsExternal: true, - }, - { - ChainName: 2, - ChainId: 2, - IsExternal: true, - }, - } - newChainParamsToUpdate := map[int64]*observertypes.ChainParams{ - 1: { - ChainId: 1, - }, - 2: { - ChainId: 2, - }, - chains.BitcoinTestnet.ChainId: { - ChainId: chains.BitcoinTestnet.ChainId, - }, - } - - tssPubKeyToUpdate := "tsspubkeytest" - crosschainFlags := sample.CrosschainFlags() - verificationFlags := sample.HeaderSupportedChains() - require.NotNil(t, crosschainFlags) - zetaContext.Update( - keyGenToUpdate, - enabledChainsToUpdate, - newChainParamsToUpdate, - &chaincfg.RegressionNetParams, - tssPubKeyToUpdate, - *crosschainFlags, - verificationFlags, - false, - log.Logger, - ) - - // assert keygen updated - keyGen := zetaContext.GetKeygen() - require.Equal(t, keyGenToUpdate, keyGen) - - // assert enabled chains updated - require.Equal(t, enabledChainsToUpdate, zetaContext.GetEnabledExternalChains()) - - // assert current tss pubkey updated - require.Equal(t, tssPubKeyToUpdate, zetaContext.GetCurrentTssPubkey()) - - // assert external chain params - externalChainParams := zetaContext.GetEnabledExternalChainParams() - require.Equal(t, newChainParamsToUpdate, externalChainParams) - - chainParams1, found := zetaContext.GetExternalChainParams(1) + t.Run("should get current tss pubkey", func(t *testing.T) { + result := appContext.GetCurrentTssPubkey() + require.Equal(t, tssPubKey, result) + }) + t.Run("should get external enabled chains", func(t *testing.T) { + result := appContext.GetEnabledExternalChains() + require.Equal(t, chainsEnabled, result) + }) + t.Run("should get enabled BTC chains", func(t *testing.T) { + result := appContext.GetEnabledBTCChains() + require.Equal(t, []chains.Chain{btcChain}, result) + }) + t.Run("should get enabled external chain params", func(t *testing.T) { + result := appContext.GetEnabledExternalChainParams() + require.Equal(t, chainParamMap, result) + }) + t.Run("should get external chain params by chain id", func(t *testing.T) { + for _, chain := range chainsEnabled { + result, found := appContext.GetExternalChainParams(chain.ChainId) require.True(t, found) - require.Equal(t, newChainParamsToUpdate[1], chainParams1) - - chainParams2, found := zetaContext.GetExternalChainParams(2) + require.Equal(t, chainParamMap[chain.ChainId], result) + } + }) + t.Run("should get btc network params", func(t *testing.T) { + result := appContext.GetBTCNetParams() + require.Equal(t, btcNetParams, result) + }) + t.Run("should get crosschain flags", func(t *testing.T) { + result := appContext.GetCrossChainFlags() + require.Equal(t, ccFlags, result) + }) + t.Run("should get block header enabled chains", func(t *testing.T) { + for _, chain := range headerSupportedChains { + result, found := appContext.GetBlockHeaderEnabledChains(chain.ChainId) require.True(t, found) - require.Equal(t, newChainParamsToUpdate[2], chainParams2) - - ccFlags := zetaContext.GetCrossChainFlags() - require.Equal(t, ccFlags, *crosschainFlags) - - verFlags := zetaContext.GetAllHeaderEnabledChains() - require.Equal(t, verFlags, verificationFlags) - }, - ) + require.Equal(t, chain, result) + } + }) } -func TestIsOutboundObservationEnabled(t *testing.T) { +func Test_IsOutboundObservationEnabled(t *testing.T) { // create test chain params and flags evmChain := chains.Ethereum ccFlags := *sample.CrosschainFlags() - verificationFlags := sample.HeaderSupportedChains() - chainParams := &observertypes.ChainParams{ + headerSupportedFlags := sample.HeaderSupportedChains() + evmChainParams := &observertypes.ChainParams{ ChainId: evmChain.ChainId, IsSupported: true, } t.Run("should return true if chain is supported and outbound flag is enabled", func(t *testing.T) { - appCTX := getTestAppContext(evmChain, chainParams, nil, &ccFlags, verificationFlags) - require.True(t, context.IsOutboundObservationEnabled(appCTX, *chainParams)) + appCTX := getTestAppContext(evmChain, evmChainParams, &ccFlags, headerSupportedFlags) + require.True(t, context.IsOutboundObservationEnabled(appCTX, *evmChainParams)) }) t.Run("should return false if chain is not supported yet", func(t *testing.T) { paramsUnsupported := &observertypes.ChainParams{ ChainId: evmChain.ChainId, IsSupported: false, } - appCTXUnsupported := getTestAppContext(evmChain, paramsUnsupported, nil, &ccFlags, verificationFlags) + appCTXUnsupported := getTestAppContext(evmChain, paramsUnsupported, &ccFlags, headerSupportedFlags) require.False(t, context.IsOutboundObservationEnabled(appCTXUnsupported, *paramsUnsupported)) }) t.Run("should return false if outbound flag is disabled", func(t *testing.T) { flagsDisabled := ccFlags flagsDisabled.IsOutboundEnabled = false - appCTXDisabled := getTestAppContext(evmChain, chainParams, nil, &flagsDisabled, verificationFlags) - require.False(t, context.IsOutboundObservationEnabled(appCTXDisabled, *chainParams)) + appCTXDisabled := getTestAppContext(evmChain, evmChainParams, &flagsDisabled, headerSupportedFlags) + require.False(t, context.IsOutboundObservationEnabled(appCTXDisabled, *evmChainParams)) }) } @@ -360,7 +214,7 @@ func TestIsInboundObservationEnabled(t *testing.T) { } t.Run("should return true if chain is supported and inbound flag is enabled", func(t *testing.T) { - appCTX := getTestAppContext(evmChain, chainParams, nil, &ccFlags, verificationFlags) + appCTX := getTestAppContext(evmChain, chainParams, &ccFlags, verificationFlags) require.True(t, context.IsInboundObservationEnabled(appCTX, *chainParams)) }) t.Run("should return false if chain is not supported yet", func(t *testing.T) { @@ -368,13 +222,13 @@ func TestIsInboundObservationEnabled(t *testing.T) { ChainId: evmChain.ChainId, IsSupported: false, } - appCTXUnsupported := getTestAppContext(evmChain, paramsUnsupported, nil, &ccFlags, verificationFlags) + appCTXUnsupported := getTestAppContext(evmChain, paramsUnsupported, &ccFlags, verificationFlags) require.False(t, context.IsInboundObservationEnabled(appCTXUnsupported, *paramsUnsupported)) }) t.Run("should return false if inbound flag is disabled", func(t *testing.T) { flagsDisabled := ccFlags flagsDisabled.IsInboundEnabled = false - appCTXDisabled := getTestAppContext(evmChain, chainParams, nil, &flagsDisabled, verificationFlags) + appCTXDisabled := getTestAppContext(evmChain, chainParams, &flagsDisabled, verificationFlags) require.False(t, context.IsInboundObservationEnabled(appCTXDisabled, *chainParams)) }) } diff --git a/zetaclient/keys/keys.go b/zetaclient/keys/keys.go index 3532c12233..a61a5a17ff 100644 --- a/zetaclient/keys/keys.go +++ b/zetaclient/keys/keys.go @@ -57,7 +57,7 @@ func GetGranteeKeyName(signerName string) string { } // GetKeyringKeybase return keyring and key info -func GetKeyringKeybase(cfg config.Config, hotkeyPassword string) (ckeys.Keyring, string, error) { +func GetKeyringKeybase(cfg *config.Config, hotkeyPassword string) (ckeys.Keyring, string, error) { granteeName := cfg.AuthzHotkey chainHomeFolder := cfg.ZetaCoreHome logger := log.Logger.With().Str("module", "GetKeyringKeybase").Logger() diff --git a/zetaclient/keys/keys_test.go b/zetaclient/keys/keys_test.go index 6f47c673d0..bdf98625f5 100644 --- a/zetaclient/keys/keys_test.go +++ b/zetaclient/keys/keys_test.go @@ -82,7 +82,7 @@ func (*KeysSuite) setupKeysForTest(c *C) string { func (ks *KeysSuite) TestGetKeyringKeybase(c *C) { keyring.Debug = true - cfg := config.Config{ + cfg := &config.Config{ AuthzHotkey: "bob", ZetaCoreHome: "/Users/test/.zetacored/", } @@ -102,7 +102,7 @@ func (ks *KeysSuite) TestNewKeys(c *C) { c.Assert(err, IsNil) }() - cfg := config.Config{ + cfg := &config.Config{ AuthzHotkey: signerNameForTest, ZetaCoreHome: folder, } diff --git a/zetaclient/orchestrator/app_context_update.go b/zetaclient/orchestrator/app_context_update.go index 4582210d20..a8e9a57338 100644 --- a/zetaclient/orchestrator/app_context_update.go +++ b/zetaclient/orchestrator/app_context_update.go @@ -64,8 +64,6 @@ func (oc *Orchestrator) UpdateAppContext() error { } // set new config to app context - // we keep the old ZetaCoreHome because newConfig.ZetaCoreHome is empty after reload - newConfig.ZetaCoreHome = zetazoreHome oc.appContext.SetConfig(newConfig) return nil diff --git a/zetaclient/orchestrator/app_context_update_test.go b/zetaclient/orchestrator/app_context_update_test.go index 503f45c907..88677c3bda 100644 --- a/zetaclient/orchestrator/app_context_update_test.go +++ b/zetaclient/orchestrator/app_context_update_test.go @@ -1 +1,158 @@ package orchestrator_test + +import ( + "encoding/json" + "path" + "testing" + + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/zetaclient/chains/base" + "github.com/zeta-chain/zetacore/zetaclient/config" + "github.com/zeta-chain/zetacore/zetaclient/orchestrator" + "github.com/zeta-chain/zetacore/zetaclient/testutils" + "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" +) + +// the relative path to the testdata directory +var TestDataDir = "../" + +func Test_UpdateAppContext(t *testing.T) { + // define test chains and chain params + evmChain := chains.Ethereum + btcChain := chains.BitcoinMainnet + evmChainParams := sample.ChainParams(evmChain.ChainId) + + // define test config + evmCfg := config.EVMConfig{ + Chain: evmChain, + Endpoint: "http://localhost:8545", + } + btcCfg := config.BTCConfig{ + RPCUsername: "user", + } + + // create app context + appCtx := createTestAppContext(evmCfg, btcCfg, evmChain, btcChain, evmChainParams, nil) + + t.Run("should update app context", func(t *testing.T) { + // create orchestrator + ztacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(appCtx, ztacoreClient, nil, base.Logger{}, testutils.SQLiteMemory, nil) + + // zetacoreHome directory + zetacoreHome := path.Join(TestDataDir, "testdata") + + // read archived config file and setup test zetacore home + cfg := testutils.LoadZetaclientConfig(t, TestDataDir) + cfg.ZetaCoreHome = zetacoreHome + + // set absolute path is needed to make the test pass + // config reloading will overwrite the relative path in config file + cfg.TssPath = config.GetPath(cfg.TssPath) + + // set zetacore home directory to the archived testdata directory + appCtx.Config().ZetaCoreHome = zetacoreHome + + // update app context + err := oc.UpdateAppContext() + require.NoError(t, err) + + // get new config + newCfg := appCtx.Config() + + // serialize old and new config + oldCfgData, err := json.Marshal(cfg) + require.NoError(t, err) + + newCfgData, err := json.Marshal(newCfg) + require.NoError(t, err) + + // compare old and new config + require.JSONEq(t, string(oldCfgData), string(newCfgData)) + }) + t.Run("should return error if zetacore client fails to update app context", func(t *testing.T) { + // create orchestrator + ztacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(appCtx, ztacoreClient, nil, base.Logger{}, testutils.SQLiteMemory, nil) + + // pause zetacore client to simulate error + ztacoreClient.Pause() + + // update app context + err := oc.UpdateAppContext() + require.ErrorContains(t, err, "error updating app context") + }) + t.Run("should return error if reading config file fails", func(t *testing.T) { + // create orchestrator + ztacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(appCtx, ztacoreClient, nil, base.Logger{}, testutils.SQLiteMemory, nil) + + // set invalid zetacore home directory + appCtx.Config().ZetaCoreHome = "/invalid/path" + + // update app context + err := oc.UpdateAppContext() + require.ErrorContains(t, err, "error loading config from path") + }) +} + +func Test_UpgradeHeightReached(t *testing.T) { + t.Run("should return true if upgrade height is reached", func(t *testing.T) { + // create orchestrator + zetacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(nil, zetacoreClient, nil, base.Logger{}, testutils.SQLiteMemory, nil) + + // set upgrade plan and current height + zetacoreClient.WithBlockHeight(99) + zetacoreClient.WithUpgradedPlan(&upgradetypes.Plan{ + Height: 100, + }) + + // check if upgrade height is reached + reached, err := oc.UpgradeHeightReached() + require.NoError(t, err) + require.True(t, reached) + }) + t.Run("should return error if failed to get upgrade plan", func(t *testing.T) { + // create orchestrator + zetacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(nil, zetacoreClient, nil, base.Logger{}, testutils.SQLiteMemory, nil) + + // pause zetacore client to simulate error + zetacoreClient.Pause() + + // check if upgrade height is reached + reached, err := oc.UpgradeHeightReached() + require.ErrorContains(t, err, "failed to get upgrade plan") + require.False(t, reached) + }) + t.Run("should return false if there is no active upgrade plan", func(t *testing.T) { + // create orchestrator + zetacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(nil, zetacoreClient, nil, base.Logger{}, testutils.SQLiteMemory, nil) + + // check if upgrade height is reached + reached, err := oc.UpgradeHeightReached() + require.NoError(t, err) + require.False(t, reached) + }) + t.Run("should return false if upgrade height is not reached", func(t *testing.T) { + // create orchestrator + zetacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(nil, zetacoreClient, nil, base.Logger{}, testutils.SQLiteMemory, nil) + + // set upgrade plan and current height + zetacoreClient.WithBlockHeight(98) + zetacoreClient.WithUpgradedPlan(&upgradetypes.Plan{ + Height: 100, + }) + + // check if upgrade height is reached + reached, err := oc.UpgradeHeightReached() + require.NoError(t, err) + require.False(t, reached) + }) +} diff --git a/zetaclient/orchestrator/chain_activate.go b/zetaclient/orchestrator/chain_activate.go index f1f8bd5aba..4ef4edc17a 100644 --- a/zetaclient/orchestrator/chain_activate.go +++ b/zetaclient/orchestrator/chain_activate.go @@ -84,6 +84,15 @@ func (oc *Orchestrator) CreateObserversEVM( connectorAddress := ethcommon.HexToAddress(chainParams.ConnectorContractAddress) erc20CustodyAddress := ethcommon.HexToAddress(chainParams.Erc20CustodyContractAddress) + // create RPC client + evmClient, err := ethclient.Dial(evmConfig.Endpoint) + if err != nil { + oc.logger.Std.Error(). + Err(err). + Msgf("CreateObserversEVM: error dailing endpoint %s for chain %d", evmConfig.Endpoint, evmConfig.Chain.ChainId) + continue + } + // create signer signer, err := evmsigner.NewSigner( evmConfig.Chain, @@ -103,15 +112,6 @@ func (oc *Orchestrator) CreateObserversEVM( continue } - // create RPC client - evmClient, err := ethclient.Dial(evmConfig.Endpoint) - if err != nil { - oc.logger.Std.Error(). - Err(err). - Msgf("CreateObserversEVM: error dailing endpoint %s for chain %d", evmConfig.Endpoint, evmConfig.Chain.ChainId) - continue - } - // create observer observer, err := evmobserver.NewObserver( evmConfig, diff --git a/zetaclient/orchestrator/chain_activate_test.go b/zetaclient/orchestrator/chain_activate_test.go index 503f45c907..824622f1a9 100644 --- a/zetaclient/orchestrator/chain_activate_test.go +++ b/zetaclient/orchestrator/chain_activate_test.go @@ -1 +1,240 @@ package orchestrator_test + +import ( + "testing" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/testutil/sample" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "github.com/zeta-chain/zetacore/zetaclient/chains/base" + "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/config" + context "github.com/zeta-chain/zetacore/zetaclient/context" + "github.com/zeta-chain/zetacore/zetaclient/orchestrator" + "github.com/zeta-chain/zetacore/zetaclient/testutils" + "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" +) + +// createTestAppContext creates a test app context with provided chain params and flags +func createTestAppContext( + evmCfg config.EVMConfig, + btcCfg config.BTCConfig, + evmChain chains.Chain, + btcChain chains.Chain, + evmChainParams *observertypes.ChainParams, + btcChainParams *observertypes.ChainParams, +) *context.AppContext { + // create config + cfg := config.NewConfig() + cfg.EVMChainConfigs[evmChain.ChainId] = evmCfg + cfg.BitcoinConfig = btcCfg + + // chains enabled + chainsEnabled := []chains.Chain{evmChain} + + // create chain param map + chainParamMap := make(map[int64]*observertypes.ChainParams) + if evmChainParams != nil { + chainParamMap[evmChain.ChainId] = evmChainParams + } + if btcChainParams != nil { + chainParamMap[btcChain.ChainId] = btcChainParams + chainsEnabled = append(chainsEnabled, btcChain) + } + + // create app context + appContext := context.NewAppContext(cfg) + + // create sample crosschain flags and header supported chains + ccFlags := sample.CrosschainFlags() + headerSupportedChains := sample.HeaderSupportedChains() + + // feed app context fields + appContext.Update( + observertypes.Keygen{}, + "testpubkey", + chainsEnabled, + chainParamMap, + &chaincfg.MainNetParams, + *ccFlags, + headerSupportedChains, + zerolog.Logger{}, + ) + return appContext +} + +func Test_CreateObserversEVM(t *testing.T) { + // define test chains and chain params + evmChain := chains.Ethereum + btcChain := chains.BitcoinMainnet + evmChainParams := sample.ChainParams(evmChain.ChainId) + + // test cases + tests := []struct { + name string + evmCfg config.EVMConfig + btcCfg config.BTCConfig + evmChain chains.Chain + btcChain chains.Chain + evmChainParams *observertypes.ChainParams + dbPath string + numObserverCreated int + }{ + { + name: "should create observers for EVM chain and BTC chain", + evmCfg: config.EVMConfig{ + Chain: evmChain, + Endpoint: "http://localhost:8545", + }, + btcCfg: config.BTCConfig{}, + evmChain: evmChain, + btcChain: btcChain, + evmChainParams: evmChainParams, + dbPath: testutils.SQLiteMemory, + numObserverCreated: 1, + }, + { + name: "should not create observer for EVM chain if chain params not found", + evmCfg: config.EVMConfig{ + Chain: evmChain, + Endpoint: "http://localhost:8545", + }, + btcCfg: config.BTCConfig{}, + evmChain: evmChain, + btcChain: btcChain, + evmChainParams: nil, + dbPath: testutils.SQLiteMemory, + numObserverCreated: 0, + }, + { + name: "should not create observer for EVM chain if endpoint is invalid", + evmCfg: config.EVMConfig{ + Chain: evmChain, + Endpoint: "invalid_endpoint", + }, + btcCfg: config.BTCConfig{}, + evmChain: evmChain, + btcChain: btcChain, + evmChainParams: evmChainParams, + dbPath: testutils.SQLiteMemory, + numObserverCreated: 0, + }, + { + name: "should not create observer for EVM chain if db path is invalid", + evmCfg: config.EVMConfig{ + Chain: evmChain, + Endpoint: "http://localhost:8545", + }, + btcCfg: config.BTCConfig{}, + evmChain: evmChain, + btcChain: btcChain, + evmChainParams: evmChainParams, + dbPath: "", + numObserverCreated: 0, + }, + } + + // run tests + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // create app context + appCtx := createTestAppContext(tt.evmCfg, tt.btcCfg, tt.evmChain, tt.btcChain, tt.evmChainParams, nil) + + // create orchestrator + ztacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(appCtx, ztacoreClient, nil, base.Logger{}, tt.dbPath, nil) + + // create observers + signerMap := make(map[int64]interfaces.ChainSigner) + observerMap := make(map[int64]interfaces.ChainObserver) + oc.CreateObserversEVM(signerMap, observerMap) + + // assert signer/observer map + require.Len(t, signerMap, tt.numObserverCreated) + require.Len(t, observerMap, tt.numObserverCreated) + + // assert signer/observer chain ID + if tt.numObserverCreated > 0 { + require.NotNil(t, signerMap[evmChain.ChainId]) + } + }) + } +} + +func Test_CreateObserversBTC(t *testing.T) { + // define test chains and chain params + evmChain := chains.Ethereum + btcChain := chains.BitcoinMainnet + btcChainParams := sample.ChainParams(btcChain.ChainId) + + // test cases + tests := []struct { + name string + evmCfg config.EVMConfig + btcCfg config.BTCConfig + evmChain chains.Chain + btcChain chains.Chain + btcChainParams *observertypes.ChainParams + dbPath string + numObserverCreated int + }{ + { + name: "should not create observer for BTC chain if btc config is missing", + evmCfg: config.EVMConfig{}, + btcCfg: config.BTCConfig{}, // empty config in file + evmChain: evmChain, + btcChain: btcChain, + btcChainParams: btcChainParams, + dbPath: testutils.SQLiteMemory, + numObserverCreated: 0, + }, + { + name: "should not create observer for BTC chain if chain is not enabled", + evmCfg: config.EVMConfig{}, + btcCfg: config.BTCConfig{ + RPCUsername: "user", + }, + evmChain: evmChain, + btcChain: btcChain, + btcChainParams: nil, // disabled btc chain + dbPath: testutils.SQLiteMemory, + numObserverCreated: 0, + }, + { + name: "should not create observer for BTC chain if failed to Ping endpoint", + evmCfg: config.EVMConfig{}, + btcCfg: config.BTCConfig{ + RPCUsername: "user", + }, + evmChain: evmChain, + btcChain: btcChain, + btcChainParams: btcChainParams, + dbPath: testutils.SQLiteMemory, + numObserverCreated: 0, + }, + } + + // run tests + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // create app context + appCtx := createTestAppContext(tt.evmCfg, tt.btcCfg, tt.evmChain, tt.btcChain, nil, tt.btcChainParams) + + // create orchestrator + ztacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(appCtx, ztacoreClient, nil, base.Logger{}, tt.dbPath, nil) + + // create observers + signerMap := make(map[int64]interfaces.ChainSigner) + observerMap := make(map[int64]interfaces.ChainObserver) + oc.CreateObserversBTC(signerMap, observerMap) + + // assert signer/observer map + require.Len(t, signerMap, tt.numObserverCreated) + require.Len(t, observerMap, tt.numObserverCreated) + }) + } +} diff --git a/zetaclient/orchestrator/orchestrator_test.go b/zetaclient/orchestrator/orchestrator_test.go index 8b71367cb4..8326dd1107 100644 --- a/zetaclient/orchestrator/orchestrator_test.go +++ b/zetaclient/orchestrator/orchestrator_test.go @@ -79,13 +79,12 @@ func CreateTestAppContext( // feed chain params appContext.Update( observertypes.Keygen{}, + "", []chains.Chain{evmChain, btcChain}, chainParamsMap, &chaincfg.RegressionNetParams, - "", *ccFlags, verificationFlags, - true, zerolog.Logger{}, ) return appContext diff --git a/zetaclient/testdata/config/zetaclient_config.json b/zetaclient/testdata/config/zetaclient_config.json new file mode 100644 index 0000000000..4f99002d96 --- /dev/null +++ b/zetaclient/testdata/config/zetaclient_config.json @@ -0,0 +1,114 @@ +{ + "Peer": "", + "PublicIP": "172.20.0.21", + "LogFormat": "text", + "LogLevel": 1, + "LogSampler": false, + "PreParamsPath": "/root/preparams/zetaclient0.json", + "ZetaCoreHome": "", + "ChainID": "athens_101-1", + "ZetaCoreURL": "zetacore0", + "AuthzGranter": "zeta1se2xay3sqeml2d8s3lumlgkkjmu4nsev0qyzaw", + "AuthzHotkey": "hotkey", + "P2PDiagnostic": false, + "ConfigUpdateTicker": 5, + "P2PDiagnosticTicker": 30, + "TssPath": "~/.tss", + "TestTssKeysign": false, + "KeyringBackend": "file", + "HsmMode": false, + "HsmHotKey": "hsm-hotkey", + "EVMChainConfigs": { + "1": { + "Chain": { + "chain_id": 1, + "chain_name": 1, + "vm": 1, + "is_external": true, + "cctx_gateway": 1 + }, + "Endpoint": "" + }, + "11155111": { + "Chain": { + "chain_id": 11155111, + "chain_name": 13, + "network_type": 1, + "vm": 1, + "is_external": true, + "cctx_gateway": 1 + }, + "Endpoint": "" + }, + "1337": { + "Chain": { + "chain_id": 1337, + "chain_name": 14, + "network_type": 2, + "vm": 1, + "is_external": true, + "cctx_gateway": 1 + }, + "Endpoint": "http://eth:8545" + }, + "5": { + "Chain": { + "chain_id": 5, + "chain_name": 6, + "network_type": 1, + "vm": 1, + "is_external": true, + "cctx_gateway": 1 + }, + "Endpoint": "" + }, + "56": { + "Chain": { + "chain_id": 56, + "chain_name": 5, + "network": 4, + "vm": 1, + "is_external": true, + "cctx_gateway": 1 + }, + "Endpoint": "" + }, + "80001": { + "Chain": { + "chain_id": 80001, + "chain_name": 7, + "network": 3, + "network_type": 1, + "vm": 1, + "is_external": true, + "cctx_gateway": 1 + }, + "Endpoint": "" + }, + "97": { + "Chain": { + "chain_id": 97, + "chain_name": 10, + "network": 4, + "network_type": 1, + "vm": 1, + "is_external": true, + "cctx_gateway": 1 + }, + "Endpoint": "" + } + }, + "BitcoinConfig": { + "RPCUsername": "smoketest", + "RPCPassword": "123", + "RPCHost": "bitcoin:18443", + "RPCParams": "regtest" + }, + "ComplianceConfig": { + "LogPath": "", + "RestrictedAddresses": [ + "0x8a81Ba8eCF2c418CAe624be726F505332DF119C6", + "bcrt1qzp4gt6fc7zkds09kfzaf9ln9c5rvrzxmy6qmpp" + ] + } +} diff --git a/zetaclient/testutils/mocks/zetacore_client.go b/zetaclient/testutils/mocks/zetacore_client.go index c8866aaf85..77c8bf346a 100644 --- a/zetaclient/testutils/mocks/zetacore_client.go +++ b/zetaclient/testutils/mocks/zetacore_client.go @@ -31,9 +31,15 @@ type MockZetacoreClient struct { paused bool zetaChain chains.Chain + // the mock block height + blockHeight int64 + // the mock observer keys keys keyinterfaces.ObserverKeys + // the mock upgrade plan + upgradePlan *upgradetypes.Plan + // the mock data for testing // pending cctxs pendingCctxs map[int64][]*crosschaintypes.CrossChainTx @@ -64,7 +70,7 @@ func (m *MockZetacoreClient) GetUpgradePlan() (*upgradetypes.Plan, error) { if m.paused { return nil, errors.New(ErrMsgPaused) } - return nil, nil + return m.upgradePlan, nil } func (m *MockZetacoreClient) GetChainParams() ([]*observerTypes.ChainParams, error) { @@ -186,7 +192,7 @@ func (m *MockZetacoreClient) GetBlockHeight() (int64, error) { if m.paused { return 0, errors.New(ErrMsgPaused) } - return 0, nil + return m.blockHeight, nil } func (m *MockZetacoreClient) GetLastBlockHeightByChain(_ chains.Chain) (*crosschaintypes.LastBlockHeight, error) { @@ -313,6 +319,16 @@ func (m *MockZetacoreClient) WithKeys(keys keyinterfaces.ObserverKeys) *MockZeta return m } +func (m *MockZetacoreClient) WithBlockHeight(height int64) *MockZetacoreClient { + m.blockHeight = height + return m +} + +func (m *MockZetacoreClient) WithUpgradedPlan(plan *upgradetypes.Plan) *MockZetacoreClient { + m.upgradePlan = plan + return m +} + func (m *MockZetacoreClient) WithPendingCctx(chainID int64, cctxs []*crosschaintypes.CrossChainTx) *MockZetacoreClient { m.pendingCctxs[chainID] = cctxs return m diff --git a/zetaclient/testutils/testdata.go b/zetaclient/testutils/testdata.go index fc028bf0ec..4437bbc017 100644 --- a/zetaclient/testutils/testdata.go +++ b/zetaclient/testutils/testdata.go @@ -22,6 +22,7 @@ const ( TestDataPathEVM = "testdata/evm" TestDataPathBTC = "testdata/btc" TestDataPathCctx = "testdata/cctx" + TestDataPathConfig = "testdata/config" RestrictedEVMAddressTest = "0x8a81Ba8eCF2c418CAe624be726F505332DF119C6" RestrictedBtcAddressTest = "bcrt1qzp4gt6fc7zkds09kfzaf9ln9c5rvrzxmy6qmpp" ) @@ -48,6 +49,14 @@ func LoadObjectFromJSONFile(t *testing.T, obj interface{}, filename string) { require.NoError(t, err) } +// LoadZetaclientConfig loads archived zetaclient config JSON file +func LoadZetaclientConfig(t *testing.T, dir string) *config.Config { + config := &config.Config{} + LoadObjectFromJSONFile(t, config, path.Join(dir, TestDataPathConfig, ConfigFileName())) + return config +} + +// ComplianceConfigTest returns a test compliance config func ComplianceConfigTest() config.ComplianceConfig { return config.ComplianceConfig{ RestrictedAddresses: []string{RestrictedEVMAddressTest, RestrictedBtcAddressTest}, diff --git a/zetaclient/testutils/testdata_naming.go b/zetaclient/testutils/testdata_naming.go index f0345e347c..704c91935a 100644 --- a/zetaclient/testutils/testdata_naming.go +++ b/zetaclient/testutils/testdata_naming.go @@ -6,6 +6,11 @@ import ( "github.com/zeta-chain/zetacore/pkg/coin" ) +// ConfigFileName returns the archived zetaclient config file name +func ConfigFileName() string { + return "zetaclient_config.json" +} + // FileNameEVMBlock returns unified archive file name for block func FileNameEVMBlock(chainID int64, blockNumber uint64, trimmed bool) string { if !trimmed { diff --git a/zetaclient/tss/tss_signer.go b/zetaclient/tss/tss_signer.go index 2774132b5e..4dcc0040e5 100644 --- a/zetaclient/tss/tss_signer.go +++ b/zetaclient/tss/tss_signer.go @@ -143,7 +143,7 @@ func SetupTSSServer( peer p2p.AddrList, privkey tmcrypto.PrivKey, preParams *keygen.LocalPreParams, - cfg config.Config, + cfg *config.Config, tssPassword string, ) (*tss.TssServer, error) { bootstrapPeers := peer diff --git a/zetaclient/zetacore/client.go b/zetaclient/zetacore/client.go index 226a7a12fb..0335d22cb5 100644 --- a/zetaclient/zetacore/client.go +++ b/zetaclient/zetacore/client.go @@ -51,7 +51,7 @@ type Client struct { // CreateClient is a helper function to create a new instance of Client func CreateClient( - cfg config.Config, + cfg *config.Config, telemetry *metrics.TelemetryServer, hotkeyPassword string, ) (*Client, error) { @@ -281,7 +281,7 @@ func (c *Client) UpdateAppContext(appContext *context.AppContext, logger zerolog if err != nil { return errors.Wrap(err, "GetCurrentTss failed") } - tssPubKey := tss.GetTssPubkey() + currentTssPubkey := tss.GetTssPubkey() // get latest crosschain flags crosschainFlags, err := c.GetCrosschainFlags() @@ -298,13 +298,12 @@ func (c *Client) UpdateAppContext(appContext *context.AppContext, logger zerolog // update app context fields appContext.Update( *keyGen, + currentTssPubkey, chainsEnabled, chainParamMap, btcNetParams, - tssPubKey, crosschainFlags, blockHeaderEnabledChains, - false, logger, ) From 0514cad9525685f57e64e4173ecadff6c89d7a05 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 1 Jul 2024 11:12:29 -0500 Subject: [PATCH 06/18] fix zetacore client query test --- zetaclient/zetacore/tx_test.go | 83 ++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/zetaclient/zetacore/tx_test.go b/zetaclient/zetacore/tx_test.go index fab38452df..d3d7b401ba 100644 --- a/zetaclient/zetacore/tx_test.go +++ b/zetaclient/zetacore/tx_test.go @@ -212,31 +212,40 @@ func TestZetacore_UpdateAppContext(t *testing.T) { grpcmock.WithPlanner(planner.FirstMatch()), grpcmock.WithListener(listener), func(s *grpcmock.Server) { - method := "/zetachain.zetacore.crosschain.Query/LastZetaHeight" - s.ExpectUnary(method). - UnlimitedTimes(). - WithPayload(crosschaintypes.QueryLastZetaHeightRequest{}). - Return(crosschaintypes.QueryLastZetaHeightResponse{Height: 12345}) - - method = "/cosmos.upgrade.v1beta1.Query/CurrentPlan" - s.ExpectUnary(method). - UnlimitedTimes(). - WithPayload(upgradetypes.QueryCurrentPlanRequest{}). - Return(upgradetypes.QueryCurrentPlanResponse{ - Plan: &upgradetypes.Plan{ - Name: "big upgrade", - Height: 100, - }, - }) - - method = "/zetachain.zetacore.observer.Query/GetChainParams" + // method := "/zetachain.zetacore.crosschain.Query/LastZetaHeight" + // s.ExpectUnary(method). + // UnlimitedTimes(). + // WithPayload(crosschaintypes.QueryLastZetaHeightRequest{}). + // Return(crosschaintypes.QueryLastZetaHeightResponse{Height: 12345}) + + // method = "/cosmos.upgrade.v1beta1.Query/CurrentPlan" + // s.ExpectUnary(method). + // UnlimitedTimes(). + // WithPayload(upgradetypes.QueryCurrentPlanRequest{}). + // Return(upgradetypes.QueryCurrentPlanResponse{ + // Plan: &upgradetypes.Plan{ + // Name: "big upgrade", + // Height: 100, + // }, + // }) + + method := "/zetachain.zetacore.observer.Query/GetChainParams" s.ExpectUnary(method). UnlimitedTimes(). WithPayload(observertypes.QueryGetChainParamsRequest{}). Return(observertypes.QueryGetChainParamsResponse{ChainParams: &observertypes.ChainParamsList{ ChainParams: []*observertypes.ChainParams{ { - ChainId: 7000, + ChainId: chains.ZetaChainMainnet.ChainId, + IsSupported: true, + }, + { + ChainId: chains.Ethereum.ChainId, + IsSupported: true, + }, + { + ChainId: chains.BitcoinMainnet.ChainId, + IsSupported: true, }, }, }}) @@ -248,24 +257,18 @@ func TestZetacore_UpdateAppContext(t *testing.T) { Return(observertypes.QuerySupportedChainsResponse{ Chains: []*chains.Chain{ { - chains.BitcoinMainnet.ChainId, - chains.BitcoinMainnet.ChainName, - chains.BscMainnet.Network, - chains.BscMainnet.NetworkType, - chains.BscMainnet.Vm, - chains.BscMainnet.Consensus, - chains.BscMainnet.IsExternal, - chains.BscMainnet.CctxGateway, + ChainId: chains.ZetaChainMainnet.ChainId, + IsExternal: chains.ZetaChainMainnet.IsExternal, }, { - chains.Ethereum.ChainId, - chains.Ethereum.ChainName, - chains.Ethereum.Network, - chains.Ethereum.NetworkType, - chains.Ethereum.Vm, - chains.Ethereum.Consensus, - chains.Ethereum.IsExternal, - chains.Ethereum.CctxGateway, + ChainId: chains.BitcoinMainnet.ChainId, + Consensus: chains.BitcoinMainnet.Consensus, + IsExternal: chains.BscMainnet.IsExternal, + }, + { + ChainId: chains.Ethereum.ChainId, + Consensus: chains.Ethereum.Consensus, + IsExternal: chains.Ethereum.IsExternal, }, }, }) @@ -335,8 +338,18 @@ func TestZetacore_UpdateAppContext(t *testing.T) { cfg := config.NewConfig() appCTX := context.NewAppContext(cfg) zetacoreBroadcast = MockBroadcast + + // Update app context err := client.UpdateAppContext(appCTX, zerolog.Logger{}) require.NoError(t, err) + + // Verify app context + require.Len(t, appCTX.GetEnabledExternalChainParams(), 2) + require.Len(t, appCTX.GetEnabledBTCChains(), 1) + chainParamMap := appCTX.GetEnabledExternalChainParams() + require.Len(t, chainParamMap, 2) + require.NotNil(t, chainParamMap[chains.Ethereum.ChainId]) + require.NotNil(t, chainParamMap[chains.BitcoinMainnet.ChainId]) }) } From 632ce52faee4c0f9d27330427bbb8c4085561b05 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 1 Jul 2024 12:51:42 -0500 Subject: [PATCH 07/18] take LoadDB() logic out from observer constructors; call LoadDB() only when activating new observers --- .../chains/bitcoin/observer/observer.go | 7 --- .../chains/bitcoin/observer/observer_test.go | 25 ++------- .../chains/bitcoin/observer/outbound_test.go | 2 +- .../chains/bitcoin/rpc/rpc_live_test.go | 3 +- .../chains/evm/observer/inbound_test.go | 38 +++++++------ zetaclient/chains/evm/observer/observer.go | 7 --- .../chains/evm/observer/observer_test.go | 54 +++++-------------- .../chains/evm/observer/outbound_test.go | 14 +++-- zetaclient/chains/evm/signer/signer_test.go | 2 - zetaclient/chains/interfaces/interfaces.go | 1 + zetaclient/orchestrator/chain_activate.go | 15 ++++-- .../orchestrator/chain_activate_test.go | 13 ----- zetaclient/testutils/mocks/chain_clients.go | 8 +++ 13 files changed, 65 insertions(+), 124 deletions(-) diff --git a/zetaclient/chains/bitcoin/observer/observer.go b/zetaclient/chains/bitcoin/observer/observer.go index c7e11ae6be..e0834708f3 100644 --- a/zetaclient/chains/bitcoin/observer/observer.go +++ b/zetaclient/chains/bitcoin/observer/observer.go @@ -113,7 +113,6 @@ func NewObserver( appContext *context.AppContext, zetacoreClient interfaces.ZetacoreClient, tss interfaces.TSSSigner, - dbpath string, logger base.Logger, ts *metrics.TelemetryServer, ) (*Observer, error) { @@ -155,12 +154,6 @@ func NewObserver( }, } - // load btc chain observer DB - err = ob.LoadDB(dbpath) - if err != nil { - return nil, err - } - return ob, nil } diff --git a/zetaclient/chains/bitcoin/observer/observer_test.go b/zetaclient/chains/bitcoin/observer/observer_test.go index 8fb8838ae3..e5433f796a 100644 --- a/zetaclient/chains/bitcoin/observer/observer_test.go +++ b/zetaclient/chains/bitcoin/observer/observer_test.go @@ -93,12 +93,15 @@ func MockBTCObserver( nil, nil, nil, - dbpath, base.Logger{}, nil, ) require.NoError(t, err) + // load db + err = ob.LoadDB(dbpath) + require.NoError(t, err) + return ob } @@ -116,7 +119,6 @@ func Test_NewObserver(t *testing.T) { appContext *context.AppContext coreClient interfaces.ZetacoreClient tss interfaces.TSSSigner - dbpath string logger base.Logger ts *metrics.TelemetryServer fail bool @@ -130,7 +132,6 @@ func Test_NewObserver(t *testing.T) { appContext: nil, coreClient: nil, tss: mocks.NewTSSMainnet(), - dbpath: sample.CreateTempDir(t), logger: base.Logger{}, ts: nil, fail: false, @@ -143,26 +144,11 @@ func Test_NewObserver(t *testing.T) { appContext: nil, coreClient: nil, tss: mocks.NewTSSMainnet(), - dbpath: sample.CreateTempDir(t), logger: base.Logger{}, ts: nil, fail: true, message: "error getting net params", }, - { - name: "should fail on invalid dbpath", - chain: chain, - chainParams: params, - appContext: nil, - coreClient: nil, - btcClient: mocks.NewMockBTCRPCClient().WithBlockCount(100), - tss: mocks.NewTSSMainnet(), - dbpath: "/invalid/dbpath", // invalid dbpath - logger: base.Logger{}, - ts: nil, - fail: true, - message: "error creating db path", - }, } // run tests @@ -176,7 +162,6 @@ func Test_NewObserver(t *testing.T) { tt.appContext, tt.coreClient, tt.tss, - tt.dbpath, tt.logger, tt.ts, ) @@ -254,7 +239,7 @@ func Test_LoadDB(t *testing.T) { // create observer dbpath := sample.CreateTempDir(t) - ob, err := observer.NewObserver(chain, btcClient, params, nil, nil, tss, dbpath, base.Logger{}, nil) + ob, err := observer.NewObserver(chain, btcClient, params, nil, nil, tss, base.Logger{}, nil) require.NoError(t, err) t.Run("should load db successfully", func(t *testing.T) { diff --git a/zetaclient/chains/bitcoin/observer/outbound_test.go b/zetaclient/chains/bitcoin/observer/outbound_test.go index 0aaeb7b600..588f6081fd 100644 --- a/zetaclient/chains/bitcoin/observer/outbound_test.go +++ b/zetaclient/chains/bitcoin/observer/outbound_test.go @@ -27,7 +27,7 @@ func MockBTCObserverMainnet(t *testing.T) *Observer { tss := mocks.NewTSSMainnet() // create Bitcoin observer - ob, err := NewObserver(chain, btcClient, params, nil, nil, tss, testutils.SQLiteMemory, base.Logger{}, nil) + ob, err := NewObserver(chain, btcClient, params, nil, nil, tss, base.Logger{}, nil) require.NoError(t, err) return ob diff --git a/zetaclient/chains/bitcoin/rpc/rpc_live_test.go b/zetaclient/chains/bitcoin/rpc/rpc_live_test.go index 97a373f94d..618d5f89f2 100644 --- a/zetaclient/chains/bitcoin/rpc/rpc_live_test.go +++ b/zetaclient/chains/bitcoin/rpc/rpc_live_test.go @@ -55,8 +55,7 @@ func (suite *BitcoinObserverTestSuite) SetupTest() { btcClient := mocks.NewMockBTCRPCClient() // create observer - ob, err := observer.NewObserver(chain, btcClient, params, nil, nil, tss, testutils.SQLiteMemory, - base.DefaultLogger(), nil) + ob, err := observer.NewObserver(chain, btcClient, params, nil, nil, tss, base.DefaultLogger(), nil) suite.Require().NoError(err) suite.Require().NotNil(ob) suite.rpcClient, err = getRPCClient(18332) diff --git a/zetaclient/chains/evm/observer/inbound_test.go b/zetaclient/chains/evm/observer/inbound_test.go index 5b323b1139..876bb84892 100644 --- a/zetaclient/chains/evm/observer/inbound_test.go +++ b/zetaclient/chains/evm/observer/inbound_test.go @@ -41,7 +41,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundParams.BallotIndex, ballot) @@ -57,7 +57,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - 1 - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) _, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) @@ -73,7 +73,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenZeta(tx, receipt, true) require.NoError(t, err) require.Equal(t, "", ballot) @@ -97,7 +97,6 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { nil, nil, nil, - memDBPath, lastBlock, mocks.MockChainParams(chainID, confirmation), ) @@ -126,7 +125,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundParams.BallotIndex, ballot) @@ -142,7 +141,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - 1 - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) _, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) @@ -158,7 +157,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenERC20(tx, receipt, true) require.NoError(t, err) require.Equal(t, "", ballot) @@ -182,7 +181,6 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { nil, nil, nil, - memDBPath, lastBlock, mocks.MockChainParams(chainID, confirmation), ) @@ -211,7 +209,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundParams.BallotIndex, ballot) @@ -221,7 +219,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - 1 - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) _, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) @@ -231,7 +229,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) require.ErrorContains(t, err, "not TSS address") require.Equal(t, "", ballot) @@ -242,7 +240,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) require.ErrorContains(t, err, "not a successful tx") require.Equal(t, "", ballot) @@ -253,7 +251,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(tx)) lastBlock := receipt.BlockNumber.Uint64() + confirmation - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, lastBlock, chainParam) ballot, err := ob.CheckAndVoteInboundTokenGas(tx, receipt, false) require.NoError(t, err) require.Equal(t, "", ballot) @@ -270,7 +268,7 @@ func Test_BuildInboundVoteMsgForZetaSentEvent(t *testing.T) { cctx := testutils.LoadCctxByInbound(t, chainID, coin.CoinType_Zeta, inboundHash) // parse ZetaSent event - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, mocks.MockChainParams(1, 1)) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, mocks.MockChainParams(1, 1)) connector := mocks.MockConnectorNonEth(t, chainID) event := testutils.ParseReceiptZetaSent(receipt, connector) @@ -317,7 +315,7 @@ func Test_BuildInboundVoteMsgForDepositedEvent(t *testing.T) { cctx := testutils.LoadCctxByInbound(t, chainID, coin.CoinType_ERC20, inboundHash) // parse Deposited event - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, mocks.MockChainParams(1, 1)) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, mocks.MockChainParams(1, 1)) custody := mocks.MockERC20Custody(t, chainID) event := testutils.ParseReceiptERC20Deposited(receipt, custody) sender := ethcommon.HexToAddress(tx.From) @@ -375,7 +373,7 @@ func Test_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { require.NoError(t, evm.ValidateEvmTransaction(txDonation)) // create test compliance config - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, mocks.MockChainParams(1, 1)) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, mocks.MockChainParams(1, 1)) cfg := &config.Config{ ComplianceConfig: config.ComplianceConfig{}, } @@ -445,7 +443,7 @@ func Test_ObserveTSSReceiveInBlock(t *testing.T) { lastBlock := receipt.BlockNumber.Uint64() + confirmation t.Run("should observe TSS receive in block", func(t *testing.T) { - ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, lastBlock, chainParam) // feed archived block and receipt evmJSONRPC.WithBlock(block) @@ -454,20 +452,20 @@ func Test_ObserveTSSReceiveInBlock(t *testing.T) { require.NoError(t, err) }) t.Run("should not observe on error getting block", func(t *testing.T) { - ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, lastBlock, chainParam) err := ob.ObserveTSSReceiveInBlock(blockNumber) // error getting block is expected because the mock JSONRPC contains no block require.ErrorContains(t, err, "error getting block") }) t.Run("should not observe on error getting receipt", func(t *testing.T) { - ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, lastBlock, chainParam) evmJSONRPC.WithBlock(block) err := ob.ObserveTSSReceiveInBlock(blockNumber) // error getting block is expected because the mock evmClient contains no receipt require.ErrorContains(t, err, "error getting receipt") }) t.Run("should not observe on error posting vote", func(t *testing.T) { - ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, memDBPath, lastBlock, chainParam) + ob := MockEVMObserver(t, chain, evmClient, evmJSONRPC, zetacoreClient, tss, lastBlock, chainParam) // feed archived block and pause zetacore client evmJSONRPC.WithBlock(block) diff --git a/zetaclient/chains/evm/observer/observer.go b/zetaclient/chains/evm/observer/observer.go index 61633d2b33..73d12b92d2 100644 --- a/zetaclient/chains/evm/observer/observer.go +++ b/zetaclient/chains/evm/observer/observer.go @@ -60,7 +60,6 @@ func NewObserver( appContext *clientcontext.AppContext, zetacoreClient interfaces.ZetacoreClient, tss interfaces.TSSSigner, - dbpath string, logger base.Logger, ts *metrics.TelemetryServer, ) (*Observer, error) { @@ -90,12 +89,6 @@ func NewObserver( outboundConfirmedTransactions: make(map[string]*ethtypes.Transaction), } - // open database and load data - err = ob.LoadDB(dbpath) - if err != nil { - return nil, err - } - return ob, nil } diff --git a/zetaclient/chains/evm/observer/observer_test.go b/zetaclient/chains/evm/observer/observer_test.go index 03e798b87d..ecb8d7a8e4 100644 --- a/zetaclient/chains/evm/observer/observer_test.go +++ b/zetaclient/chains/evm/observer/observer_test.go @@ -79,7 +79,6 @@ func MockEVMObserver( evmJSONRPC interfaces.EVMJSONRPCClient, zetacoreClient interfaces.ZetacoreClient, tss interfaces.TSSSigner, - dbpath string, lastBlock uint64, params observertypes.ChainParams, ) *observer.Observer { @@ -100,7 +99,7 @@ func MockEVMObserver( appCtx, evmCfg := getAppContext(chain, "", ¶ms) // create observer - ob, err := observer.NewObserver(evmCfg, evmClient, params, appCtx, zetacoreClient, tss, dbpath, base.Logger{}, nil) + ob, err := observer.NewObserver(evmCfg, evmClient, params, appCtx, zetacoreClient, tss, base.Logger{}, nil) require.NoError(t, err) ob.WithEvmJSONRPC(evmJSONRPC) ob.WithLastBlock(lastBlock) @@ -120,7 +119,6 @@ func Test_NewObserver(t *testing.T) { chainParams observertypes.ChainParams evmClient interfaces.EVMRPCClient tss interfaces.TSSSigner - dbpath string logger base.Logger ts *metrics.TelemetryServer fail bool @@ -135,41 +133,10 @@ func Test_NewObserver(t *testing.T) { chainParams: params, evmClient: mocks.NewMockEvmClient().WithBlockNumber(1000), tss: mocks.NewTSSMainnet(), - dbpath: sample.CreateTempDir(t), logger: base.Logger{}, ts: nil, fail: false, }, - { - name: "should fail on invalid dbpath", - evmCfg: config.EVMConfig{ - Chain: chain, - Endpoint: "http://localhost:8545", - }, - chainParams: params, - evmClient: mocks.NewMockEvmClient().WithBlockNumber(1000), - tss: mocks.NewTSSMainnet(), - dbpath: "/invalid/dbpath", // invalid dbpath - logger: base.Logger{}, - ts: nil, - fail: true, - message: "error creating db path", - }, - { - name: "should fail if RPC call fails", - evmCfg: config.EVMConfig{ - Chain: chain, - Endpoint: "http://localhost:8545", - }, - chainParams: params, - evmClient: mocks.NewMockEvmClient().WithError(fmt.Errorf("error RPC")), - tss: mocks.NewTSSMainnet(), - dbpath: sample.CreateTempDir(t), - logger: base.Logger{}, - ts: nil, - fail: true, - message: "error RPC", - }, } // run tests @@ -187,7 +154,6 @@ func Test_NewObserver(t *testing.T) { appCtx, zetacoreClient, tt.tss, - tt.dbpath, tt.logger, tt.ts, ) @@ -209,7 +175,7 @@ func Test_LoadDB(t *testing.T) { chain := chains.Ethereum params := mocks.MockChainParams(chain.ChainId, 10) dbpath := sample.CreateTempDir(t) - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, dbpath, 1, params) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, params) t.Run("should load db successfully", func(t *testing.T) { err := ob.LoadDB(dbpath) @@ -238,7 +204,7 @@ func Test_LoadDB(t *testing.T) { t.Run("should fail on RPC error", func(t *testing.T) { // create observer tempClient := mocks.NewMockEvmClient() - ob := MockEVMObserver(t, chain, tempClient, nil, nil, nil, dbpath, 1, params) + ob := MockEVMObserver(t, chain, tempClient, nil, nil, nil, 1, params) // set RPC error tempClient.WithError(fmt.Errorf("error RPC")) @@ -257,7 +223,11 @@ func Test_LoadLastBlockScanned(t *testing.T) { // create observer using mock evm client evmClient := mocks.NewMockEvmClient().WithBlockNumber(100) dbpath := sample.CreateTempDir(t) - ob := MockEVMObserver(t, chain, evmClient, nil, nil, nil, dbpath, 1, params) + ob := MockEVMObserver(t, chain, evmClient, nil, nil, nil, 1, params) + + // load db + err := ob.LoadDB(dbpath) + require.NoError(t, err) t.Run("should load last block scanned", func(t *testing.T) { // create db and write 123 as last block scanned @@ -281,7 +251,11 @@ func Test_LoadLastBlockScanned(t *testing.T) { t.Run("should fail on RPC error", func(t *testing.T) { // create observer on separate path, as we need to reset last block scanned otherPath := sample.CreateTempDir(t) - obOther := MockEVMObserver(t, chain, evmClient, nil, nil, nil, otherPath, 1, params) + obOther := MockEVMObserver(t, chain, evmClient, nil, nil, nil, 1, params) + + // load db + err := obOther.LoadDB(otherPath) + require.NoError(t, err) // reset last block scanned to 0 so that it will be loaded from RPC obOther.WithLastBlockScanned(0) @@ -290,7 +264,7 @@ func Test_LoadLastBlockScanned(t *testing.T) { evmClient.WithError(fmt.Errorf("error RPC")) // load last block scanned - err := obOther.LoadLastBlockScanned() + err = obOther.LoadLastBlockScanned() require.ErrorContains(t, err, "error RPC") }) } diff --git a/zetaclient/chains/evm/observer/outbound_test.go b/zetaclient/chains/evm/observer/outbound_test.go index 3ebbbb31b2..a2644c235e 100644 --- a/zetaclient/chains/evm/observer/outbound_test.go +++ b/zetaclient/chains/evm/observer/outbound_test.go @@ -20,8 +20,6 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" ) -const memDBPath = testutils.SQLiteMemory - // getContractsByChainID is a helper func to get contracts and addresses by chainID func getContractsByChainID( t *testing.T, @@ -61,7 +59,7 @@ func Test_IsOutboundProcessed(t *testing.T) { t.Run("should post vote and return true if outbound is processed", func(t *testing.T) { // create evm observer and set outbound and receipt - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, chainParam) ob.SetTxNReceipt(nonce, receipt, outbound) // post outbound vote @@ -78,7 +76,7 @@ func Test_IsOutboundProcessed(t *testing.T) { cctx.InboundParams.Sender = sample.EthAddress().Hex() // create evm observer and set outbound and receipt - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, chainParam) ob.SetTxNReceipt(nonce, receipt, outbound) // modify compliance config to restrict sender address @@ -96,7 +94,7 @@ func Test_IsOutboundProcessed(t *testing.T) { }) t.Run("should return false if outbound is not confirmed", func(t *testing.T) { // create evm observer and DO NOT set outbound as confirmed - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, chainParam) isIncluded, isConfirmed, err := ob.IsOutboundProcessed(cctx, zerolog.Logger{}) require.NoError(t, err) require.False(t, isIncluded) @@ -104,7 +102,7 @@ func Test_IsOutboundProcessed(t *testing.T) { }) t.Run("should fail if unable to parse ZetaReceived event", func(t *testing.T) { // create evm observer and set outbound and receipt - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, chainParam) ob.SetTxNReceipt(nonce, receipt, outbound) // set connector contract address to an arbitrary address to make event parsing fail @@ -152,7 +150,7 @@ func Test_IsOutboundProcessed_ContractError(t *testing.T) { t.Run("should fail if unable to get connector/custody contract", func(t *testing.T) { // create evm observer and set outbound and receipt - ob := MockEVMObserver(t, chain, nil, nil, nil, nil, memDBPath, 1, chainParam) + ob := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, chainParam) ob.SetTxNReceipt(nonce, receipt, outbound) abiConnector := zetaconnector.ZetaConnectorNonEthMetaData.ABI abiCustody := erc20custody.ERC20CustodyMetaData.ABI @@ -197,7 +195,7 @@ func Test_PostVoteOutbound(t *testing.T) { // create evm client using mock zetacore client and post outbound vote zetacoreClient := mocks.NewMockZetacoreClient() - ob := MockEVMObserver(t, chain, nil, nil, zetacoreClient, nil, memDBPath, 1, observertypes.ChainParams{}) + ob := MockEVMObserver(t, chain, nil, nil, zetacoreClient, nil, 1, observertypes.ChainParams{}) ob.PostVoteOutbound( cctx.Index, receipt, diff --git a/zetaclient/chains/evm/signer/signer_test.go b/zetaclient/chains/evm/signer/signer_test.go index de69dc6068..9aa7c32546 100644 --- a/zetaclient/chains/evm/signer/signer_test.go +++ b/zetaclient/chains/evm/signer/signer_test.go @@ -72,7 +72,6 @@ func getNewEvmChainObserver(t *testing.T, tss interfaces.TSSSigner) (*observer.O params := mocks.MockChainParams(evmcfg.Chain.ChainId, 10) cfg.EVMChainConfigs[chains.BscMainnet.ChainId] = evmcfg appCTX := context.NewAppContext(cfg) - dbpath := sample.CreateTempDir(t) logger := base.Logger{} ts := &metrics.TelemetryServer{} @@ -83,7 +82,6 @@ func getNewEvmChainObserver(t *testing.T, tss interfaces.TSSSigner) (*observer.O appCTX, mocks.NewMockZetacoreClient(), tss, - dbpath, logger, ts, ) diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index 691941bc0f..f684df2b4f 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -41,6 +41,7 @@ const ( type ChainObserver interface { Start() Stop() + LoadDB(dbPath string) error IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) SetChainParams(observertypes.ChainParams) GetChainParams() observertypes.ChainParams diff --git a/zetaclient/orchestrator/chain_activate.go b/zetaclient/orchestrator/chain_activate.go index 4ef4edc17a..617ec77c99 100644 --- a/zetaclient/orchestrator/chain_activate.go +++ b/zetaclient/orchestrator/chain_activate.go @@ -47,7 +47,7 @@ func (oc *Orchestrator) ActivateDeactivateChains() { for chainID, observer := range oc.observerMap { _, found := newObserverMap[chainID] if !found { - oc.logger.Std.Info().Msgf("orchestrator deactivating chain %d", chainID) + oc.logger.Std.Info().Msgf("ActivateDeactivateChains: deactivating chain %d", chainID) observer.Stop() delete(oc.signerMap, chainID) @@ -59,7 +59,16 @@ func (oc *Orchestrator) ActivateDeactivateChains() { for chainID, observer := range newObserverMap { _, found := oc.observerMap[chainID] if !found { - oc.logger.Std.Info().Msgf("orchestrator activating chain %d", chainID) + oc.logger.Std.Info().Msgf("ActivateDeactivateChains: activating chain %d", chainID) + + // open database and load data + err := observer.LoadDB(oc.dbPath) + if err != nil { + oc.logger.Std.Error(). + Err(err). + Msgf("ActivateDeactivateChains: error LoadDB for chain %d", chainID) + continue + } observer.Start() oc.signerMap[chainID] = newSignerMap[chainID] @@ -120,7 +129,6 @@ func (oc *Orchestrator) CreateObserversEVM( oc.appContext, oc.zetacoreClient, oc.tss, - oc.dbPath, oc.logger.Base, oc.ts, ) @@ -190,7 +198,6 @@ func (oc *Orchestrator) CreateObserversBTC( oc.appContext, oc.zetacoreClient, oc.tss, - oc.dbPath, oc.logger.Base, oc.ts, ) diff --git a/zetaclient/orchestrator/chain_activate_test.go b/zetaclient/orchestrator/chain_activate_test.go index 824622f1a9..1782eb2282 100644 --- a/zetaclient/orchestrator/chain_activate_test.go +++ b/zetaclient/orchestrator/chain_activate_test.go @@ -122,19 +122,6 @@ func Test_CreateObserversEVM(t *testing.T) { dbPath: testutils.SQLiteMemory, numObserverCreated: 0, }, - { - name: "should not create observer for EVM chain if db path is invalid", - evmCfg: config.EVMConfig{ - Chain: evmChain, - Endpoint: "http://localhost:8545", - }, - btcCfg: config.BTCConfig{}, - evmChain: evmChain, - btcChain: btcChain, - evmChainParams: evmChainParams, - dbPath: "", - numObserverCreated: 0, - }, } // run tests diff --git a/zetaclient/testutils/mocks/chain_clients.go b/zetaclient/testutils/mocks/chain_clients.go index 44f1a9ea71..b9f75cf169 100644 --- a/zetaclient/testutils/mocks/chain_clients.go +++ b/zetaclient/testutils/mocks/chain_clients.go @@ -30,6 +30,10 @@ func (ob *EVMObserver) Start() { func (ob *EVMObserver) Stop() { } +func (ob *EVMObserver) LoadDB(_ string) error { + return nil +} + func (ob *EVMObserver) IsOutboundProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { return false, false, nil } @@ -71,6 +75,10 @@ func (ob *BTCObserver) Start() { func (ob *BTCObserver) Stop() { } +func (ob *BTCObserver) LoadDB(_ string) error { + return nil +} + func (ob *BTCObserver) IsOutboundProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { return false, false, nil } From 3c1f2451d20f0e810d1ee0b8d76db35df0c27b0f Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 1 Jul 2024 16:35:42 -0500 Subject: [PATCH 08/18] unified log prints when activating/deactivating chains --- zetaclient/chains/base/observer.go | 4 ++-- zetaclient/chains/bitcoin/observer/observer.go | 3 ++- zetaclient/chains/evm/observer/outbound.go | 2 +- zetaclient/orchestrator/orchestrator.go | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/zetaclient/chains/base/observer.go b/zetaclient/chains/base/observer.go index fe6238d731..d0b71f4a3c 100644 --- a/zetaclient/chains/base/observer.go +++ b/zetaclient/chains/base/observer.go @@ -129,7 +129,7 @@ func NewObserver( // Stop notifies all goroutines to stop and closes the database. func (ob *Observer) Stop() { - ob.logger.Chain.Info().Msgf("observer is stopping for chain %d", ob.Chain().ChainId) + ob.logger.Chain.Info().Msgf("Observer is stopping for chain %d", ob.Chain().ChainId) close(ob.stop) // close database @@ -140,7 +140,7 @@ func (ob *Observer) Stop() { } ob.db = nil } - ob.Logger().Chain.Info().Msgf("observer stopped for chain %d", ob.Chain().ChainId) + ob.Logger().Chain.Info().Msgf("Observer stopped for chain %d", ob.Chain().ChainId) } // Chain returns the chain for the observer. diff --git a/zetaclient/chains/bitcoin/observer/observer.go b/zetaclient/chains/bitcoin/observer/observer.go index b245ab3d75..d40a4701ef 100644 --- a/zetaclient/chains/bitcoin/observer/observer.go +++ b/zetaclient/chains/bitcoin/observer/observer.go @@ -212,7 +212,7 @@ func (ob *Observer) Start() { // TODO(revamp): move ticker related functions to a specific file // TODO(revamp): move inner logic in a separate function func (ob *Observer) WatchRPCStatus() { - ob.logger.Chain.Info().Msgf("RPCStatus is starting") + ob.logger.Chain.Info().Msgf("WatchRPCStatus started for chain %d", ob.Chain().ChainId) ticker := time.NewTicker(60 * time.Second) for { @@ -267,6 +267,7 @@ func (ob *Observer) WatchRPCStatus() { Msgf("[OK] RPC status check: latest block number %d, timestamp %s (%.fs ago), tss addr %s, #utxos: %d", bn, blockTime, elapsedSeconds, tssAddr, len(res)) case <-ob.StopChannel(): + ob.Logger().Chain.Info().Msgf("WatchRPCStatus stopped for chain %d", ob.Chain().ChainId) return } } diff --git a/zetaclient/chains/evm/observer/outbound.go b/zetaclient/chains/evm/observer/outbound.go index 2b04bbf43f..c7e3ddf450 100644 --- a/zetaclient/chains/evm/observer/outbound.go +++ b/zetaclient/chains/evm/observer/outbound.go @@ -89,7 +89,7 @@ func (ob *Observer) WatchOutbound() { } ticker.UpdateInterval(ob.GetChainParams().OutboundTicker, ob.Logger().Outbound) case <-ob.StopChannel(): - ob.Logger().Outbound.Info().Msgf("WatchOutbound: stopped for chain %d", ob.Chain().ChainId) + ob.Logger().Outbound.Info().Msgf("WatchOutbound stopped for chain %d", ob.Chain().ChainId) return } } diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index 9f583902b5..afade0a160 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -153,7 +153,7 @@ func (oc *Orchestrator) Start() { // AwaitStopSignals waits for stop signals func (oc *Orchestrator) AwaitStopSignals() { - oc.logger.Std.Info().Msgf("orchestrator awaiting the os.Interrupt, syscall.SIGTERM signals...") + oc.logger.Std.Info().Msgf("Orchestrator awaiting the os.Interrupt, syscall.SIGTERM signals...") // subscribe to stop signals ch := make(chan os.Signal, 1) @@ -162,7 +162,7 @@ func (oc *Orchestrator) AwaitStopSignals() { // stop orchestrator oc.Stop() - oc.logger.Std.Info().Msgf("orchestrator stopped on signal: %s", sig) + oc.logger.Std.Info().Msgf("Orchestrator stopped on signal: %s", sig) } // Stop notifies all zetaclient goroutines to stop From ca9142df3482543925d3ae397b7754eb7f3d7894 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 1 Jul 2024 17:00:46 -0500 Subject: [PATCH 09/18] fix unit test conflicts and thread safety --- zetaclient/orchestrator/chain_activate.go | 8 +++ zetaclient/orchestrator/orchestrator.go | 4 ++ zetaclient/orchestrator/orchestrator_test.go | 53 +------------------- 3 files changed, 13 insertions(+), 52 deletions(-) diff --git a/zetaclient/orchestrator/chain_activate.go b/zetaclient/orchestrator/chain_activate.go index 617ec77c99..684179b711 100644 --- a/zetaclient/orchestrator/chain_activate.go +++ b/zetaclient/orchestrator/chain_activate.go @@ -50,6 +50,10 @@ func (oc *Orchestrator) ActivateDeactivateChains() { oc.logger.Std.Info().Msgf("ActivateDeactivateChains: deactivating chain %d", chainID) observer.Stop() + + // remove signer and observer from maps + oc.mu.Lock() + defer oc.mu.Unlock() delete(oc.signerMap, chainID) delete(oc.observerMap, chainID) } @@ -71,6 +75,10 @@ func (oc *Orchestrator) ActivateDeactivateChains() { } observer.Start() + + // add signer and observer to maps + oc.mu.Lock() + defer oc.mu.Unlock() oc.signerMap[chainID] = newSignerMap[chainID] oc.observerMap[chainID] = observer } diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index afade0a160..4995c36935 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -187,7 +187,9 @@ func (oc *Orchestrator) Stop() { // GetUpdatedSigner returns signer with updated chain parameters func (oc *Orchestrator) GetUpdatedSigner(chainID int64) (interfaces.ChainSigner, error) { + oc.mu.Lock() signer, found := oc.signerMap[chainID] + oc.mu.Unlock() if !found { return nil, fmt.Errorf("signer not found for chainID %d", chainID) } @@ -215,7 +217,9 @@ func (oc *Orchestrator) GetUpdatedSigner(chainID int64) (interfaces.ChainSigner, // GetUpdatedChainObserver returns chain observer with updated chain parameters func (oc *Orchestrator) GetUpdatedChainObserver(chainID int64) (interfaces.ChainObserver, error) { + oc.mu.Lock() observer, found := oc.observerMap[chainID] + oc.mu.Unlock() if !found { return nil, fmt.Errorf("chain observer not found for chainID %d", chainID) } diff --git a/zetaclient/orchestrator/orchestrator_test.go b/zetaclient/orchestrator/orchestrator_test.go index e9ffffa584..70ffae0806 100644 --- a/zetaclient/orchestrator/orchestrator_test.go +++ b/zetaclient/orchestrator/orchestrator_test.go @@ -56,11 +56,8 @@ func MockOrchestrator( return orchestrator } -<<<<<<< HEAD +// CreateTestAppContext creates a test app context for orchestrator testing func CreateTestAppContext( -======= -func CreateAppContext( ->>>>>>> 2d5519f4d64bb05b64e6f2c7ec9ef6a87e97610f evmChain, btcChain chains.Chain, evmChainParams, btcChainParams *observertypes.ChainParams, ) *context.AppContext { @@ -72,42 +69,24 @@ func CreateAppContext( cfg.BitcoinConfig = config.BTCConfig{ RPCHost: "localhost", } -<<<<<<< HEAD // new app context appContext := context.NewAppContext(cfg) chainParamsMap := make(map[int64]*observertypes.ChainParams) chainParamsMap[evmChain.ChainId] = evmChainParams chainParamsMap[btcChain.ChainId] = btcChainParams -======= - // new zetacore context - appContext := context.New(cfg, zerolog.Nop()) - evmChainParamsMap := make(map[int64]*observertypes.ChainParams) - evmChainParamsMap[evmChain.ChainId] = evmChainParams ->>>>>>> 2d5519f4d64bb05b64e6f2c7ec9ef6a87e97610f ccFlags := sample.CrosschainFlags() verificationFlags := sample.HeaderSupportedChains() // feed chain params appContext.Update( -<<<<<<< HEAD observertypes.Keygen{}, -======= - &observertypes.Keygen{}, - []chains.Chain{evmChain, btcChain}, - evmChainParamsMap, - btcChainParams, ->>>>>>> 2d5519f4d64bb05b64e6f2c7ec9ef6a87e97610f "", []chains.Chain{evmChain, btcChain}, chainParamsMap, &chaincfg.RegressionNetParams, *ccFlags, verificationFlags, -<<<<<<< HEAD zerolog.Logger{}, -======= - true, ->>>>>>> 2d5519f4d64bb05b64e6f2c7ec9ef6a87e97610f ) return appContext } @@ -131,26 +110,16 @@ func Test_GetUpdatedSigner(t *testing.T) { } t.Run("signer should not be found", func(t *testing.T) { -<<<<<<< HEAD appCtx := CreateTestAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) -======= - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - context := CreateAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) ->>>>>>> 2d5519f4d64bb05b64e6f2c7ec9ef6a87e97610f // BSC signer should not be found _, err := orchestrator.GetUpdatedSigner(chains.BscMainnet.ChainId) require.ErrorContains(t, err, "signer not found") }) t.Run("should be able to update connector and erc20 custody address", func(t *testing.T) { -<<<<<<< HEAD appCtx := CreateTestAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) -======= - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - context := CreateAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) ->>>>>>> 2d5519f4d64bb05b64e6f2c7ec9ef6a87e97610f // update signer with new connector and erc20 custody address signer, err := orchestrator.GetUpdatedSigner(evmChain.ChainId) require.NoError(t, err) @@ -207,27 +176,17 @@ func Test_GetUpdatedChainObserver(t *testing.T) { } t.Run("evm chain observer should not be found", func(t *testing.T) { -<<<<<<< HEAD appCtx := CreateTestAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) -======= - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - coreContext := CreateAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) ->>>>>>> 2d5519f4d64bb05b64e6f2c7ec9ef6a87e97610f // BSC chain observer should not be found _, err := orchestrator.GetUpdatedChainObserver(chains.BscMainnet.ChainId) require.ErrorContains(t, err, "chain observer not found") }) t.Run("chain params in evm chain observer should be updated successfully", func(t *testing.T) { -<<<<<<< HEAD appCtx := CreateTestAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) -======= - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - coreContext := CreateAppContext(evmChain, btcChain, evmChainParamsNew, btcChainParams) ->>>>>>> 2d5519f4d64bb05b64e6f2c7ec9ef6a87e97610f // update evm chain observer with new chain params chainOb, err := orchestrator.GetUpdatedChainObserver(evmChain.ChainId) require.NoError(t, err) @@ -235,27 +194,17 @@ func Test_GetUpdatedChainObserver(t *testing.T) { require.True(t, observertypes.ChainParamsEqual(*evmChainParamsNew, chainOb.GetChainParams())) }) t.Run("btc chain observer should not be found", func(t *testing.T) { -<<<<<<< HEAD appCtx := CreateTestAppContext(btcChain, btcChain, evmChainParams, btcChainParamsNew) orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) -======= - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - coreContext := CreateAppContext(btcChain, btcChain, evmChainParams, btcChainParamsNew) ->>>>>>> 2d5519f4d64bb05b64e6f2c7ec9ef6a87e97610f // BTC testnet chain observer should not be found _, err := orchestrator.GetUpdatedChainObserver(chains.BitcoinTestnet.ChainId) require.ErrorContains(t, err, "chain observer not found") }) t.Run("chain params in btc chain observer should be updated successfully", func(t *testing.T) { -<<<<<<< HEAD appCtx := CreateTestAppContext(btcChain, btcChain, evmChainParams, btcChainParamsNew) orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) -======= - orchestrator := MockOrchestrator(t, nil, evmChain, btcChain, evmChainParams, btcChainParams) - coreContext := CreateAppContext(btcChain, btcChain, evmChainParams, btcChainParamsNew) ->>>>>>> 2d5519f4d64bb05b64e6f2c7ec9ef6a87e97610f // update btc chain observer with new chain params chainOb, err := orchestrator.GetUpdatedChainObserver(btcChain.ChainId) require.NoError(t, err) From d068a70e1a0ec5c1de9590d9901fbb18bd226391 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 1 Jul 2024 23:18:47 -0500 Subject: [PATCH 10/18] fix e2e test --- zetaclient/orchestrator/chain_activate.go | 4 ++-- zetaclient/orchestrator/chain_activate_test.go | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/zetaclient/orchestrator/chain_activate.go b/zetaclient/orchestrator/chain_activate.go index 684179b711..4ab2be3006 100644 --- a/zetaclient/orchestrator/chain_activate.go +++ b/zetaclient/orchestrator/chain_activate.go @@ -53,9 +53,9 @@ func (oc *Orchestrator) ActivateDeactivateChains() { // remove signer and observer from maps oc.mu.Lock() - defer oc.mu.Unlock() delete(oc.signerMap, chainID) delete(oc.observerMap, chainID) + oc.mu.Unlock() } } @@ -78,9 +78,9 @@ func (oc *Orchestrator) ActivateDeactivateChains() { // add signer and observer to maps oc.mu.Lock() - defer oc.mu.Unlock() oc.signerMap[chainID] = newSignerMap[chainID] oc.observerMap[chainID] = observer + oc.mu.Unlock() } } } diff --git a/zetaclient/orchestrator/chain_activate_test.go b/zetaclient/orchestrator/chain_activate_test.go index 1782eb2282..c16f19d153 100644 --- a/zetaclient/orchestrator/chain_activate_test.go +++ b/zetaclient/orchestrator/chain_activate_test.go @@ -84,7 +84,7 @@ func Test_CreateObserversEVM(t *testing.T) { numObserverCreated int }{ { - name: "should create observers for EVM chain and BTC chain", + name: "should create observers for EVM chain", evmCfg: config.EVMConfig{ Chain: evmChain, Endpoint: "http://localhost:8545", @@ -146,6 +146,7 @@ func Test_CreateObserversEVM(t *testing.T) { // assert signer/observer chain ID if tt.numObserverCreated > 0 { require.NotNil(t, signerMap[evmChain.ChainId]) + require.NotNil(t, observerMap[evmChain.ChainId]) } }) } From 6c56d98766600f050d2cf235d62ed125a0a51ff5 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Mon, 1 Jul 2024 23:46:18 -0500 Subject: [PATCH 11/18] fix unit test --- zetaclient/chains/evm/signer/signer.go | 10 +++------- zetaclient/chains/evm/signer/signer_test.go | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/zetaclient/chains/evm/signer/signer.go b/zetaclient/chains/evm/signer/signer.go index 126e3ca654..873d865fb9 100644 --- a/zetaclient/chains/evm/signer/signer.go +++ b/zetaclient/chains/evm/signer/signer.go @@ -97,7 +97,7 @@ func NewSigner( baseSigner := base.NewSigner(chain, appContext, tss, ts, logger) // create EVM client - client, ethSigner, err := getEVMRPC(endpoint) + client, ethSigner, err := getEVMRPC(endpoint, chain.ChainId) if err != nil { return nil, err } @@ -814,7 +814,7 @@ func (signer *Signer) reportToOutboundTracker( } // getEVMRPC is a helper function to set up the client and signer, also initializes a mock client for unit tests -func getEVMRPC(endpoint string) (interfaces.EVMRPCClient, ethtypes.Signer, error) { +func getEVMRPC(endpoint string, chainID int64) (interfaces.EVMRPCClient, ethtypes.Signer, error) { if endpoint == mocks.EVMRPCEnabled { chainID := big.NewInt(chains.BscMainnet.ChainId) ethSigner := ethtypes.NewLondonSigner(chainID) @@ -827,11 +827,7 @@ func getEVMRPC(endpoint string) (interfaces.EVMRPCClient, ethtypes.Signer, error return nil, nil, err } - chainID, err := client.ChainID(context.TODO()) - if err != nil { - return nil, nil, err - } - ethSigner := ethtypes.LatestSignerForChainID(chainID) + ethSigner := ethtypes.LatestSignerForChainID(big.NewInt(chainID)) return client, ethSigner, nil } diff --git a/zetaclient/chains/evm/signer/signer_test.go b/zetaclient/chains/evm/signer/signer_test.go index 40a5792501..0445a77c20 100644 --- a/zetaclient/chains/evm/signer/signer_test.go +++ b/zetaclient/chains/evm/signer/signer_test.go @@ -433,7 +433,7 @@ func TestSigner_BroadcastOutbound(t *testing.T) { func TestSigner_getEVMRPC(t *testing.T) { t.Run("getEVMRPC error dialing", func(t *testing.T) { - client, signer, err := getEVMRPC("invalidEndpoint") + client, signer, err := getEVMRPC("invalidEndpoint", chains.Ethereum.ChainId) require.Nil(t, client) require.Nil(t, signer) require.Error(t, err) From 4f61c380bd8629ea044d7284eabf99c09b447131 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 3 Jul 2024 15:28:29 -0500 Subject: [PATCH 12/18] add unit test to chain activation deactivation; replace pointers of config with non-pointer config and improve thread safety by deep clone --- cmd/zetaclientd-supervisor/lib.go | 2 +- cmd/zetaclientd/debug.go | 2 +- cmd/zetaclientd/p2p_diagnostics.go | 2 +- cmd/zetaclientd/start.go | 2 +- cmd/zetaclientd/start_utils.go | 6 +- zetaclient/chains/base/logger.go | 4 +- zetaclient/chains/base/logger_test.go | 12 +- zetaclient/chains/base/observer_test.go | 4 +- zetaclient/chains/base/signer_test.go | 2 +- .../chains/evm/observer/inbound_test.go | 6 +- .../chains/evm/observer/observer_test.go | 2 +- .../chains/evm/observer/outbound_test.go | 2 +- zetaclient/chains/evm/signer/signer_test.go | 4 +- zetaclient/compliance/compliance.go | 2 +- zetaclient/compliance/compliance_test.go | 2 +- zetaclient/config/config.go | 10 +- zetaclient/config/config_chain.go | 3 - zetaclient/config/types.go | 50 +++--- zetaclient/config/types_test.go | 21 +++ zetaclient/context/app_context.go | 12 +- zetaclient/context/app_context_test.go | 8 +- zetaclient/keys/keys.go | 2 +- zetaclient/keys/keys_test.go | 4 +- .../orchestrator/app_context_update_test.go | 8 +- zetaclient/orchestrator/chain_activate.go | 38 +++-- .../orchestrator/chain_activate_test.go | 156 +++++++++++++++++- zetaclient/orchestrator/orchestrator_test.go | 2 +- zetaclient/testutils/testdata.go | 6 +- zetaclient/tss/tss_signer.go | 2 +- zetaclient/zetacore/client.go | 2 +- zetaclient/zetacore/tx_test.go | 2 +- 31 files changed, 286 insertions(+), 94 deletions(-) diff --git a/cmd/zetaclientd-supervisor/lib.go b/cmd/zetaclientd-supervisor/lib.go index 07eb337f1b..e8f1b54470 100644 --- a/cmd/zetaclientd-supervisor/lib.go +++ b/cmd/zetaclientd-supervisor/lib.go @@ -41,7 +41,7 @@ func (w *serializedWriter) Write(p []byte) (n int, err error) { return w.upstream.Write(p) } -func getLogger(cfg *config.Config, out io.Writer) zerolog.Logger { +func getLogger(cfg config.Config, out io.Writer) zerolog.Logger { var logger zerolog.Logger switch cfg.LogFormat { case "json": diff --git a/cmd/zetaclientd/debug.go b/cmd/zetaclientd/debug.go index d64cee7df5..afd56d7904 100644 --- a/cmd/zetaclientd/debug.go +++ b/cmd/zetaclientd/debug.go @@ -56,7 +56,7 @@ func debugCmd(_ *cobra.Command, args []string) error { return err } - appContext := clientcontext.NewAppContext(cfg) + appContext := clientcontext.New(cfg) chainID, err := strconv.ParseInt(args[1], 10, 64) if err != nil { diff --git a/cmd/zetaclientd/p2p_diagnostics.go b/cmd/zetaclientd/p2p_diagnostics.go index 33fb4e2866..330f520013 100644 --- a/cmd/zetaclientd/p2p_diagnostics.go +++ b/cmd/zetaclientd/p2p_diagnostics.go @@ -30,7 +30,7 @@ func RunDiagnostics( startLogger zerolog.Logger, peers p2p.AddrList, hotkeyPk cryptotypes.PrivKey, - cfg *config.Config, + cfg config.Config, ) error { startLogger.Warn().Msg("P2P Diagnostic mode enabled") startLogger.Warn().Msgf("seed peer: %s", peers) diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 9e610a78d7..354d866888 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -164,7 +164,7 @@ func start(_ *cobra.Command, _ []string) error { startLogger.Info().Msgf("Authz is ready for granter %s grantee %s", granter, grantee) // Initialize zetaclient app context - appContext := context.NewAppContext(cfg) + appContext := context.New(cfg) err = zetacoreClient.UpdateAppContext(appContext, startLogger) if err != nil { startLogger.Error().Err(err).Msg("error initializing app context") diff --git a/cmd/zetaclientd/start_utils.go b/cmd/zetaclientd/start_utils.go index a363c6a62d..10f0b0beea 100644 --- a/cmd/zetaclientd/start_utils.go +++ b/cmd/zetaclientd/start_utils.go @@ -14,7 +14,7 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/config" ) -func waitForZetaCore(config *config.Config, logger zerolog.Logger) { +func waitForZetaCore(config config.Config, logger zerolog.Logger) { // wait until zetacore is up logger.Debug().Msg("Waiting for zetacore to open 9090 port...") for { @@ -55,8 +55,8 @@ func validatePeer(seedPeer string) error { // maskCfg sensitive fields are masked, currently only the EVM endpoints and bitcoin credentials, // // other fields can be added. -func maskCfg(cfg *config.Config) string { - maskedCfg := *cfg +func maskCfg(cfg config.Config) string { + maskedCfg := cfg maskedCfg.BitcoinConfig = config.BTCConfig{ RPCUsername: cfg.BitcoinConfig.RPCUsername, diff --git a/zetaclient/chains/base/logger.go b/zetaclient/chains/base/logger.go index 795b78e321..eeffcfab3b 100644 --- a/zetaclient/chains/base/logger.go +++ b/zetaclient/chains/base/logger.go @@ -51,7 +51,7 @@ type ObserverLogger struct { } // InitLogger initializes the base loggers -func InitLogger(cfg *config.Config) (Logger, error) { +func InitLogger(cfg config.Config) (Logger, error) { // open compliance log file file, err := openComplianceLogFile(cfg) if err != nil { @@ -89,7 +89,7 @@ func InitLogger(cfg *config.Config) (Logger, error) { } // openComplianceLogFile opens the compliance log file -func openComplianceLogFile(cfg *config.Config) (*os.File, error) { +func openComplianceLogFile(cfg config.Config) (*os.File, error) { // use zetacore home as default logPath := cfg.ZetaCoreHome if cfg.ComplianceConfig.LogPath != "" { diff --git a/zetaclient/chains/base/logger_test.go b/zetaclient/chains/base/logger_test.go index 3f8b7454c0..07c0941f5a 100644 --- a/zetaclient/chains/base/logger_test.go +++ b/zetaclient/chains/base/logger_test.go @@ -15,12 +15,12 @@ func TestInitLogger(t *testing.T) { tests := []struct { name string t *testing.T - cfg *config.Config + cfg config.Config fail bool }{ { name: "should be able to initialize json formatted logger", - cfg: &config.Config{ + cfg: config.Config{ LogFormat: "json", LogLevel: 1, // zerolog.InfoLevel, ComplianceConfig: config.ComplianceConfig{ @@ -31,7 +31,7 @@ func TestInitLogger(t *testing.T) { }, { name: "should be able to initialize plain text logger", - cfg: &config.Config{ + cfg: config.Config{ LogFormat: "text", LogLevel: 2, // zerolog.WarnLevel, ComplianceConfig: config.ComplianceConfig{ @@ -42,7 +42,7 @@ func TestInitLogger(t *testing.T) { }, { name: "should be able to initialize default formatted logger", - cfg: &config.Config{ + cfg: config.Config{ LogFormat: "unknown", LogLevel: 3, // zerolog.ErrorLevel, ComplianceConfig: config.ComplianceConfig{ @@ -53,7 +53,7 @@ func TestInitLogger(t *testing.T) { }, { name: "should be able to initialize sampled logger", - cfg: &config.Config{ + cfg: config.Config{ LogFormat: "json", LogLevel: 4, // zerolog.DebugLevel, LogSampler: true, @@ -64,7 +64,7 @@ func TestInitLogger(t *testing.T) { }, { name: "should fail on invalid compliance log path", - cfg: &config.Config{ + cfg: config.Config{ LogFormat: "json", LogLevel: 1, // zerolog.InfoLevel, ComplianceConfig: config.ComplianceConfig{ diff --git a/zetaclient/chains/base/observer_test.go b/zetaclient/chains/base/observer_test.go index 2ec1bcd14a..54513acce4 100644 --- a/zetaclient/chains/base/observer_test.go +++ b/zetaclient/chains/base/observer_test.go @@ -25,7 +25,7 @@ func createObserver(t *testing.T) *base.Observer { // constructor parameters chain := chains.Ethereum chainParams := *sample.ChainParams(chain.ChainId) - appContext := context.NewAppContext(config.NewConfig()) + appContext := context.New(config.NewConfig()) zetacoreClient := mocks.NewMockZetacoreClient() tss := mocks.NewTSSMainnet() @@ -51,7 +51,7 @@ func TestNewObserver(t *testing.T) { // constructor parameters chain := chains.Ethereum chainParams := *sample.ChainParams(chain.ChainId) - appContext := context.NewAppContext(config.NewConfig()) + appContext := context.New(config.NewConfig()) zetacoreClient := mocks.NewMockZetacoreClient() tss := mocks.NewTSSMainnet() blockCacheSize := base.DefaultBlockCacheSize diff --git a/zetaclient/chains/base/signer_test.go b/zetaclient/chains/base/signer_test.go index ea38f79122..5235b21683 100644 --- a/zetaclient/chains/base/signer_test.go +++ b/zetaclient/chains/base/signer_test.go @@ -17,7 +17,7 @@ import ( func createSigner(_ *testing.T) *base.Signer { // constructor parameters chain := chains.Ethereum - appContext := context.NewAppContext(config.NewConfig()) + appContext := context.New(config.NewConfig()) tss := mocks.NewTSSMainnet() logger := base.DefaultLogger() diff --git a/zetaclient/chains/evm/observer/inbound_test.go b/zetaclient/chains/evm/observer/inbound_test.go index 876bb84892..469eeef524 100644 --- a/zetaclient/chains/evm/observer/inbound_test.go +++ b/zetaclient/chains/evm/observer/inbound_test.go @@ -273,7 +273,7 @@ func Test_BuildInboundVoteMsgForZetaSentEvent(t *testing.T) { event := testutils.ParseReceiptZetaSent(receipt, connector) // create test compliance config - cfg := &config.Config{ + cfg := config.Config{ ComplianceConfig: config.ComplianceConfig{}, } @@ -321,7 +321,7 @@ func Test_BuildInboundVoteMsgForDepositedEvent(t *testing.T) { sender := ethcommon.HexToAddress(tx.From) // create test compliance config - cfg := &config.Config{ + cfg := config.Config{ ComplianceConfig: config.ComplianceConfig{}, } @@ -374,7 +374,7 @@ func Test_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { // create test compliance config ob := MockEVMObserver(t, chain, nil, nil, nil, nil, 1, mocks.MockChainParams(1, 1)) - cfg := &config.Config{ + cfg := config.Config{ ComplianceConfig: config.ComplianceConfig{}, } diff --git a/zetaclient/chains/evm/observer/observer_test.go b/zetaclient/chains/evm/observer/observer_test.go index ecb8d7a8e4..2e7cdd5434 100644 --- a/zetaclient/chains/evm/observer/observer_test.go +++ b/zetaclient/chains/evm/observer/observer_test.go @@ -52,7 +52,7 @@ func getAppContext( } // create app context - appCtx := context.NewAppContext(cfg) + appCtx := context.New(cfg) newChainParams := make(map[int64]*observertypes.ChainParams) newChainParams[evmChain.ChainId] = evmChainParams diff --git a/zetaclient/chains/evm/observer/outbound_test.go b/zetaclient/chains/evm/observer/outbound_test.go index a2644c235e..411c4c9764 100644 --- a/zetaclient/chains/evm/observer/outbound_test.go +++ b/zetaclient/chains/evm/observer/outbound_test.go @@ -80,7 +80,7 @@ func Test_IsOutboundProcessed(t *testing.T) { ob.SetTxNReceipt(nonce, receipt, outbound) // modify compliance config to restrict sender address - cfg := &config.Config{ + cfg := config.Config{ ComplianceConfig: config.ComplianceConfig{}, } cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.InboundParams.Sender} diff --git a/zetaclient/chains/evm/signer/signer_test.go b/zetaclient/chains/evm/signer/signer_test.go index 0445a77c20..3165c20348 100644 --- a/zetaclient/chains/evm/signer/signer_test.go +++ b/zetaclient/chains/evm/signer/signer_test.go @@ -47,7 +47,7 @@ func getNewEvmSigner(tss interfaces.TSSSigner) (*Signer, error) { return NewSigner( chains.BscMainnet, - context.NewAppContext(cfg), + context.New(cfg), tss, nil, logger, @@ -71,7 +71,7 @@ func getNewEvmChainObserver(t *testing.T, tss interfaces.TSSSigner) (*observer.O evmClient := mocks.NewMockEvmClient().WithBlockNumber(1000) params := mocks.MockChainParams(evmcfg.Chain.ChainId, 10) cfg.EVMChainConfigs[chains.BscMainnet.ChainId] = evmcfg - appContext := context.NewAppContext(cfg) + appContext := context.New(cfg) logger := base.Logger{} ts := &metrics.TelemetryServer{} diff --git a/zetaclient/compliance/compliance.go b/zetaclient/compliance/compliance.go index 521eba2758..9b4dd16077 100644 --- a/zetaclient/compliance/compliance.go +++ b/zetaclient/compliance/compliance.go @@ -14,7 +14,7 @@ import ( var restrictedAddressBook = map[string]bool{} // LoadComplianceConfig loads compliance config from zetaclient config -func LoadComplianceConfig(cfg *config.Config) { +func LoadComplianceConfig(cfg config.Config) { restrictedAddressBook = cfg.GetRestrictedAddressBook() } diff --git a/zetaclient/compliance/compliance_test.go b/zetaclient/compliance/compliance_test.go index 1b524f1f05..7f31243b9a 100644 --- a/zetaclient/compliance/compliance_test.go +++ b/zetaclient/compliance/compliance_test.go @@ -17,7 +17,7 @@ func TestCctxRestricted(t *testing.T) { cctx := testutils.LoadCctxByNonce(t, chain.ChainId, 6270) // create config - cfg := &config.Config{ + cfg := config.Config{ ComplianceConfig: config.ComplianceConfig{}, } diff --git a/zetaclient/config/config.go b/zetaclient/config/config.go index 2a85235885..e91cf24a78 100644 --- a/zetaclient/config/config.go +++ b/zetaclient/config/config.go @@ -40,12 +40,12 @@ func Save(config *Config, path string) error { } // Load loads ZetaClient config from a filepath -func Load(path string) (*Config, error) { +func Load(path string) (Config, error) { // retrieve file file := filepath.Join(path, folder, filename) file, err := filepath.Abs(file) if err != nil { - return nil, err + return Config{}, err } file = filepath.Clean(file) @@ -53,11 +53,11 @@ func Load(path string) (*Config, error) { cfg := NewConfig() input, err := os.ReadFile(file) if err != nil { - return nil, err + return Config{}, err } err = json.Unmarshal(input, &cfg) if err != nil { - return nil, err + return Config{}, err } // read keyring backend and use test by default @@ -65,7 +65,7 @@ func Load(path string) (*Config, error) { cfg.KeyringBackend = KeyringBackendTest } if cfg.KeyringBackend != KeyringBackendFile && cfg.KeyringBackend != KeyringBackendTest { - return nil, fmt.Errorf("invalid keyring backend %s", cfg.KeyringBackend) + return Config{}, fmt.Errorf("invalid keyring backend %s", cfg.KeyringBackend) } // fields sanitization diff --git a/zetaclient/config/config_chain.go b/zetaclient/config/config_chain.go index 5946c4ca62..6e15211156 100644 --- a/zetaclient/config/config_chain.go +++ b/zetaclient/config/config_chain.go @@ -1,8 +1,6 @@ package config import ( - "sync" - "github.com/zeta-chain/zetacore/pkg/chains" ) @@ -36,7 +34,6 @@ func GetERC20CustodyABI() string { // It is initialize with default chain configs func New() Config { return Config{ - cfgLock: &sync.RWMutex{}, EVMChainConfigs: evmChainsConfigs, BitcoinConfig: bitcoinConfigRegnet, } diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index 05e2249fba..6e1b8e935e 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -3,7 +3,6 @@ package config import ( "encoding/json" "strings" - "sync" "github.com/zeta-chain/zetacore/pkg/chains" ) @@ -57,8 +56,6 @@ type ComplianceConfig struct { // TODO: use snake case for json fields // https://github.com/zeta-chain/node/issues/1020 type Config struct { - cfgLock *sync.RWMutex `json:"-"` - Peer string `json:"Peer"` PublicIP string `json:"PublicIP"` LogFormat string `json:"LogFormat"` @@ -88,26 +85,14 @@ type Config struct { // NewConfig returns a new Config with initialize EVM chain mapping and a new mutex // TODO(revamp): consolidate with New function -func NewConfig() *Config { - return &Config{ - cfgLock: &sync.RWMutex{}, +func NewConfig() Config { + return Config{ EVMChainConfigs: make(map[int64]EVMConfig), } } -// GetEVMConfig returns the EVM config for the given chain ID -func (c Config) GetEVMConfig(chainID int64) (EVMConfig, bool) { - c.cfgLock.RLock() - defer c.cfgLock.RUnlock() - evmCfg, found := c.EVMChainConfigs[chainID] - return evmCfg, found -} - // GetAllEVMConfigs returns a map of all EVM configs func (c Config) GetAllEVMConfigs() map[int64]EVMConfig { - c.cfgLock.RLock() - defer c.cfgLock.RUnlock() - // deep copy evm configs copied := make(map[int64]EVMConfig, len(c.EVMChainConfigs)) for chainID, evmConfig := range c.EVMChainConfigs { @@ -118,9 +103,6 @@ func (c Config) GetAllEVMConfigs() map[int64]EVMConfig { // GetBTCConfig returns the BTC config func (c Config) GetBTCConfig() (BTCConfig, bool) { - c.cfgLock.RLock() - defer c.cfgLock.RUnlock() - return c.BitcoinConfig, c.BitcoinConfig != (BTCConfig{}) } @@ -145,9 +127,27 @@ func (c Config) GetRestrictedAddressBook() map[string]bool { return restrictedAddresses } -// GetKeyringBackend returns the keyring backend -func (c *Config) GetKeyringBackend() KeyringBackend { - c.cfgLock.RLock() - defer c.cfgLock.RUnlock() - return c.KeyringBackend +// Clone returns a deep copy of the config +// config is accessed concurrently, so a deep copy is needed +func (c Config) Clone() Config { + // deep copy evm config map + copiedEVMConfigs := make(map[int64]EVMConfig, len(c.EVMChainConfigs)) + for chainID, evmConfig := range c.EVMChainConfigs { + copiedEVMConfigs[chainID] = evmConfig + } + + // deep copy compliance config address array + copiedComplianceConfig := c.ComplianceConfig + copiedRestrictedAddresses := make([]string, len(copiedComplianceConfig.RestrictedAddresses)) + copy(copiedRestrictedAddresses, copiedComplianceConfig.RestrictedAddresses) + copiedComplianceConfig.RestrictedAddresses = copiedRestrictedAddresses + + // duplicate a config + copied := c + + // set deep copied evm configs, and compliance config + copied.EVMChainConfigs = copiedEVMConfigs + copied.ComplianceConfig = copiedComplianceConfig + + return copied } diff --git a/zetaclient/config/types_test.go b/zetaclient/config/types_test.go index d7b82b3200..bb5dd35a0e 100644 --- a/zetaclient/config/types_test.go +++ b/zetaclient/config/types_test.go @@ -1 +1,22 @@ package config_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/zetaclient/testutils" +) + +// the relative path to the testdata directory +var TestDataDir = "../" + +func Test_Clone(t *testing.T) { + // read archived zetaclient config file + cfg := testutils.LoadZetaclientConfig(t, TestDataDir) + + // clone the config + clone := cfg.Clone() + + // assert that the cloned config is equal to the original config + require.Equal(t, cfg, clone) +} diff --git a/zetaclient/context/app_context.go b/zetaclient/context/app_context.go index 818eccd8b3..2bd43a6f14 100644 --- a/zetaclient/context/app_context.go +++ b/zetaclient/context/app_context.go @@ -17,7 +17,7 @@ import ( // AppContext contains zetaclient application context // these are initialized and updated at runtime periodically type AppContext struct { - config *config.Config + config config.Config keygen observertypes.Keygen currentTssPubkey string chainsEnabled []chains.Chain @@ -33,8 +33,8 @@ type AppContext struct { mu sync.RWMutex } -// NewAppContext creates empty app context with given config -func NewAppContext(cfg *config.Config) *AppContext { +// New creates empty app context with given config +func New(cfg config.Config) *AppContext { return &AppContext{ config: cfg, chainsEnabled: []chains.Chain{}, @@ -46,17 +46,17 @@ func NewAppContext(cfg *config.Config) *AppContext { } // SetConfig sets a new config to the app context -func (a *AppContext) SetConfig(cfg *config.Config) { +func (a *AppContext) SetConfig(cfg config.Config) { a.mu.Lock() defer a.mu.Unlock() a.config = cfg } // Config returns the app context config -func (a *AppContext) Config() *config.Config { +func (a *AppContext) Config() config.Config { a.mu.RLock() defer a.mu.RUnlock() - return a.config + return a.config.Clone() } // GetKeygen returns the current keygen information diff --git a/zetaclient/context/app_context_test.go b/zetaclient/context/app_context_test.go index 13d01350ec..7ec1e8f939 100644 --- a/zetaclient/context/app_context_test.go +++ b/zetaclient/context/app_context_test.go @@ -20,7 +20,7 @@ func Test_NewAppContext(t *testing.T) { t.Run("should create new app context with empty config", func(t *testing.T) { testCfg := config.NewConfig() - appContext := context.NewAppContext(testCfg) + appContext := context.New(testCfg) require.NotNil(t, appContext) // assert config @@ -47,7 +47,7 @@ func Test_NewAppContext(t *testing.T) { func Test_SetGetConfig(t *testing.T) { t.Run("should create new app context with empty config", func(t *testing.T) { oldCfg := config.NewConfig() - appContext := context.NewAppContext(oldCfg) + appContext := context.New(oldCfg) require.NotNil(t, appContext) require.Equal(t, oldCfg, appContext.Config()) @@ -80,7 +80,7 @@ func Test_UpdateAndGetters(t *testing.T) { headerSupportedChains := sample.HeaderSupportedChains() // feed app context fields - appContext := context.NewAppContext(config.NewConfig()) + appContext := context.New(config.NewConfig()) appContext.Update( *keyGen, tssPubKey, @@ -212,7 +212,7 @@ func makeAppContext( } // create app context - appContext := context.NewAppContext(cfg) + appContext := context.New(cfg) newChainParams := make(map[int64]*observertypes.ChainParams) newChainParams[evmChain.ChainId] = evmChainParams diff --git a/zetaclient/keys/keys.go b/zetaclient/keys/keys.go index b998685a24..0fdef6c204 100644 --- a/zetaclient/keys/keys.go +++ b/zetaclient/keys/keys.go @@ -61,7 +61,7 @@ func GetGranteeKeyName(signerName string) string { } // GetKeyringKeybase return keyring and key info -func GetKeyringKeybase(cfg *config.Config, hotkeyPassword string) (ckeys.Keyring, string, error) { +func GetKeyringKeybase(cfg config.Config, hotkeyPassword string) (ckeys.Keyring, string, error) { granteeName := cfg.AuthzHotkey chainHomeFolder := cfg.ZetaCoreHome logger := log.Logger.With().Str("module", "GetKeyringKeybase").Logger() diff --git a/zetaclient/keys/keys_test.go b/zetaclient/keys/keys_test.go index bdf98625f5..6f47c673d0 100644 --- a/zetaclient/keys/keys_test.go +++ b/zetaclient/keys/keys_test.go @@ -82,7 +82,7 @@ func (*KeysSuite) setupKeysForTest(c *C) string { func (ks *KeysSuite) TestGetKeyringKeybase(c *C) { keyring.Debug = true - cfg := &config.Config{ + cfg := config.Config{ AuthzHotkey: "bob", ZetaCoreHome: "/Users/test/.zetacored/", } @@ -102,7 +102,7 @@ func (ks *KeysSuite) TestNewKeys(c *C) { c.Assert(err, IsNil) }() - cfg := &config.Config{ + cfg := config.Config{ AuthzHotkey: signerNameForTest, ZetaCoreHome: folder, } diff --git a/zetaclient/orchestrator/app_context_update_test.go b/zetaclient/orchestrator/app_context_update_test.go index 88677c3bda..ea0b6d926f 100644 --- a/zetaclient/orchestrator/app_context_update_test.go +++ b/zetaclient/orchestrator/app_context_update_test.go @@ -54,7 +54,9 @@ func Test_UpdateAppContext(t *testing.T) { cfg.TssPath = config.GetPath(cfg.TssPath) // set zetacore home directory to the archived testdata directory - appCtx.Config().ZetaCoreHome = zetacoreHome + copyConfig := appCtx.Config() + copyConfig.ZetaCoreHome = zetacoreHome + appCtx.SetConfig(copyConfig) // update app context err := oc.UpdateAppContext() @@ -91,7 +93,9 @@ func Test_UpdateAppContext(t *testing.T) { oc := orchestrator.NewOrchestrator(appCtx, ztacoreClient, nil, base.Logger{}, testutils.SQLiteMemory, nil) // set invalid zetacore home directory - appCtx.Config().ZetaCoreHome = "/invalid/path" + copyConfig := appCtx.Config() + copyConfig.ZetaCoreHome = "/invalid/path" + appCtx.SetConfig(copyConfig) // update app context err := oc.UpdateAppContext() diff --git a/zetaclient/orchestrator/chain_activate.go b/zetaclient/orchestrator/chain_activate.go index 4ab2be3006..9432b67011 100644 --- a/zetaclient/orchestrator/chain_activate.go +++ b/zetaclient/orchestrator/chain_activate.go @@ -20,11 +20,11 @@ import ( func (oc *Orchestrator) WatchEnabledChains() { oc.logger.Std.Info().Msg("WatchChainActivation started") - ticker := time.NewTicker(common.ZetaBlockTime) + ticker := time.NewTicker(common.ZetaBlockTime * 2) for { select { case <-ticker.C: - oc.ActivateDeactivateChains() + oc.ActivateAndDeactivateChains() case <-oc.stop: oc.logger.Std.Info().Msg("WatchChainActivation stopped") return @@ -32,8 +32,8 @@ func (oc *Orchestrator) WatchEnabledChains() { } } -// ActivateDeactivateChains activates or deactivates chain observers and signers -func (oc *Orchestrator) ActivateDeactivateChains() { +// ActivateAndDeactivateChains activates and deactivates chains according to chain params and config file +func (oc *Orchestrator) ActivateAndDeactivateChains() { // create new signer and observer maps // Note: the keys of the two maps are chain IDs and they are always exactly matched newSignerMap := make(map[int64]interfaces.ChainSigner) @@ -43,37 +43,51 @@ func (oc *Orchestrator) ActivateDeactivateChains() { oc.CreateObserversEVM(newSignerMap, newObserverMap) oc.CreateObserversBTC(newSignerMap, newObserverMap) + // activate newly supported chains and deactivate chains that are no longer supported + oc.DeactivateChains(newObserverMap) + oc.ActivateChains(newSignerMap, newObserverMap) +} + +// DeactivateChains deactivates chains that are no longer supported +func (oc *Orchestrator) DeactivateChains( + newObserverMap map[int64]interfaces.ChainObserver, +) { // loop through existing observer map to deactivate chains that are not in new observer map + oc.mu.Lock() + defer oc.mu.Unlock() for chainID, observer := range oc.observerMap { _, found := newObserverMap[chainID] if !found { - oc.logger.Std.Info().Msgf("ActivateDeactivateChains: deactivating chain %d", chainID) - + oc.logger.Std.Info().Msgf("DeactivateChains: deactivating chain %d", chainID) observer.Stop() // remove signer and observer from maps - oc.mu.Lock() delete(oc.signerMap, chainID) delete(oc.observerMap, chainID) - oc.mu.Unlock() + oc.logger.Std.Info().Msgf("DeactivateChains: deactivated chain %d", chainID) } } +} +// ActivateChains activates newly supported chains +func (oc *Orchestrator) ActivateChains( + newSignerMap map[int64]interfaces.ChainSigner, + newObserverMap map[int64]interfaces.ChainObserver, +) { // loop through new observer map to activate chains that are not in existing observer map for chainID, observer := range newObserverMap { _, found := oc.observerMap[chainID] if !found { - oc.logger.Std.Info().Msgf("ActivateDeactivateChains: activating chain %d", chainID) + oc.logger.Std.Info().Msgf("ActivateChains: activating chain %d", chainID) // open database and load data err := observer.LoadDB(oc.dbPath) if err != nil { oc.logger.Std.Error(). Err(err). - Msgf("ActivateDeactivateChains: error LoadDB for chain %d", chainID) + Msgf("ActivateChains: error LoadDB for chain %d", chainID) continue } - observer.Start() // add signer and observer to maps @@ -81,6 +95,8 @@ func (oc *Orchestrator) ActivateDeactivateChains() { oc.signerMap[chainID] = newSignerMap[chainID] oc.observerMap[chainID] = observer oc.mu.Unlock() + + oc.logger.Std.Info().Msgf("ActivateChains: activated chain %d", chainID) } } } diff --git a/zetaclient/orchestrator/chain_activate_test.go b/zetaclient/orchestrator/chain_activate_test.go index c16f19d153..be470967fb 100644 --- a/zetaclient/orchestrator/chain_activate_test.go +++ b/zetaclient/orchestrator/chain_activate_test.go @@ -46,7 +46,7 @@ func createTestAppContext( } // create app context - appContext := context.NewAppContext(cfg) + appContext := context.New(cfg) // create sample crosschain flags and header supported chains ccFlags := sample.CrosschainFlags() @@ -66,6 +66,160 @@ func createTestAppContext( return appContext } +func Test_ActivateChains(t *testing.T) { + // define test chain and chain params + evmChain := chains.Ethereum + evmChainParams := sample.ChainParams(evmChain.ChainId) + + // test cases + tests := []struct { + name string + evmCfg config.EVMConfig + btcCfg config.BTCConfig + evmChain chains.Chain + btcChain chains.Chain + evmChainParams *observertypes.ChainParams + dbPath string + fail bool + }{ + { + name: "should activate newly supported chains that are not in existing observer map", + evmCfg: config.EVMConfig{ + Chain: evmChain, + Endpoint: "http://localhost:8545", + }, + btcCfg: config.BTCConfig{}, // btc chain is not needed for this test + evmChain: evmChain, + evmChainParams: evmChainParams, + dbPath: testutils.SQLiteMemory, + fail: false, + }, + { + name: "should not activate chain if dbPath is invalid", + evmCfg: config.EVMConfig{ + Chain: evmChain, + Endpoint: "http://localhost:8545", + }, + btcCfg: config.BTCConfig{}, // btc chain is not needed for this test + evmChain: evmChain, + evmChainParams: evmChainParams, + dbPath: "", // invalid db path + fail: true, + }, + } + + // run tests + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // create app context + appCtx := createTestAppContext(tt.evmCfg, tt.btcCfg, tt.evmChain, tt.btcChain, tt.evmChainParams, nil) + + // create orchestrator + ztacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(appCtx, ztacoreClient, nil, base.Logger{}, tt.dbPath, nil) + + // create new signer and observer maps + newSignerMap := make(map[int64]interfaces.ChainSigner) + newObserverMap := make(map[int64]interfaces.ChainObserver) + oc.CreateObserversEVM(newSignerMap, newObserverMap) + + // activate chains + oc.ActivateChains(newSignerMap, newObserverMap) + + // assert signer/observer map + ob, err1 := oc.GetUpdatedChainObserver(tt.evmChain.ChainId) + signer, err2 := oc.GetUpdatedSigner(tt.evmChain.ChainId) + + if tt.fail { + require.Error(t, err1) + require.Error(t, err2) + require.Nil(t, ob) + require.Nil(t, signer) + } else { + require.NoError(t, err1) + require.NoError(t, err2) + require.NotNil(t, ob) + require.NotNil(t, signer) + } + }) + } +} + +func Test_DeactivateChains(t *testing.T) { + // define test chain and chain params + evmChain := chains.Ethereum + evmChainParams := sample.ChainParams(evmChain.ChainId) + + // test cases + tests := []struct { + name string + evmCfg config.EVMConfig + btcCfg config.BTCConfig + evmChain chains.Chain + btcChain chains.Chain + evmChainParams *observertypes.ChainParams + dbPath string + }{ + { + name: "should deactivate chains that are not in new observer map", + evmCfg: config.EVMConfig{ + Chain: evmChain, + Endpoint: "http://localhost:8545", + }, + btcCfg: config.BTCConfig{}, // btc chain is not needed for this test + evmChain: evmChain, + evmChainParams: evmChainParams, + dbPath: testutils.SQLiteMemory, + }, + } + + // run tests + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // create app context + appCtx := createTestAppContext(tt.evmCfg, tt.btcCfg, tt.evmChain, tt.btcChain, tt.evmChainParams, nil) + + // create orchestrator + ztacoreClient := mocks.NewMockZetacoreClient() + oc := orchestrator.NewOrchestrator(appCtx, ztacoreClient, nil, base.Logger{}, tt.dbPath, nil) + + // create new signer and observer maps + newSignerMap := make(map[int64]interfaces.ChainSigner) + newObserverMap := make(map[int64]interfaces.ChainObserver) + oc.CreateObserversEVM(newSignerMap, newObserverMap) + + // activate chains + oc.ActivateChains(newSignerMap, newObserverMap) + + // assert signer/observer map + ob, err := oc.GetUpdatedChainObserver(tt.evmChain.ChainId) + require.NoError(t, err) + require.NotNil(t, ob) + + // create new config and set EVM chain params as empty + newCfg := appCtx.Config() + newCfg.EVMChainConfigs = make(map[int64]config.EVMConfig) + appCtx.SetConfig(newCfg) + + // create maps again based on newly updated config + newSignerMap = make(map[int64]interfaces.ChainSigner) + newObserverMap = make(map[int64]interfaces.ChainObserver) + oc.CreateObserversEVM(newSignerMap, newObserverMap) + + // deactivate chains + oc.DeactivateChains(newObserverMap) + + // assert signer/observer map + ob, err1 := oc.GetUpdatedChainObserver(tt.evmChain.ChainId) + signer, err2 := oc.GetUpdatedSigner(tt.evmChain.ChainId) + require.Error(t, err1) + require.Error(t, err2) + require.Nil(t, ob) + require.Nil(t, signer) + }) + } +} + func Test_CreateObserversEVM(t *testing.T) { // define test chains and chain params evmChain := chains.Ethereum diff --git a/zetaclient/orchestrator/orchestrator_test.go b/zetaclient/orchestrator/orchestrator_test.go index 70ffae0806..492d6e6d01 100644 --- a/zetaclient/orchestrator/orchestrator_test.go +++ b/zetaclient/orchestrator/orchestrator_test.go @@ -70,7 +70,7 @@ func CreateTestAppContext( RPCHost: "localhost", } // new app context - appContext := context.NewAppContext(cfg) + appContext := context.New(cfg) chainParamsMap := make(map[int64]*observertypes.ChainParams) chainParamsMap[evmChain.ChainId] = evmChainParams chainParamsMap[btcChain.ChainId] = btcChainParams diff --git a/zetaclient/testutils/testdata.go b/zetaclient/testutils/testdata.go index e00896a3e7..8ce792bbf1 100644 --- a/zetaclient/testutils/testdata.go +++ b/zetaclient/testutils/testdata.go @@ -50,9 +50,9 @@ func LoadObjectFromJSONFile(t *testing.T, obj interface{}, filename string) { } // LoadZetaclientConfig loads archived zetaclient config JSON file -func LoadZetaclientConfig(t *testing.T, dir string) *config.Config { - config := &config.Config{} - LoadObjectFromJSONFile(t, config, path.Join(dir, TestDataPathConfig, ConfigFileName())) +func LoadZetaclientConfig(t *testing.T, dir string) config.Config { + config := config.Config{} + LoadObjectFromJSONFile(t, &config, path.Join(dir, TestDataPathConfig, ConfigFileName())) return config } diff --git a/zetaclient/tss/tss_signer.go b/zetaclient/tss/tss_signer.go index c11542d425..22885c66da 100644 --- a/zetaclient/tss/tss_signer.go +++ b/zetaclient/tss/tss_signer.go @@ -149,7 +149,7 @@ func SetupTSSServer( peer p2p.AddrList, privkey tmcrypto.PrivKey, preParams *keygen.LocalPreParams, - cfg *config.Config, + cfg config.Config, tssPassword string, ) (*tss.TssServer, error) { bootstrapPeers := peer diff --git a/zetaclient/zetacore/client.go b/zetaclient/zetacore/client.go index 1c332dbc47..7d1cb52599 100644 --- a/zetaclient/zetacore/client.go +++ b/zetaclient/zetacore/client.go @@ -52,7 +52,7 @@ type Client struct { // CreateClient is a helper function to create a new instance of Client func CreateClient( - cfg *config.Config, + cfg config.Config, telemetry *metrics.TelemetryServer, hotkeyPassword string, ) (*Client, error) { diff --git a/zetaclient/zetacore/tx_test.go b/zetaclient/zetacore/tx_test.go index 71938f8af0..a647725301 100644 --- a/zetaclient/zetacore/tx_test.go +++ b/zetaclient/zetacore/tx_test.go @@ -319,7 +319,7 @@ func TestZetacore_UpdateAppContext(t *testing.T) { t.Run("zetacore update success", func(t *testing.T) { cfg := config.NewConfig() - appCTX := context.NewAppContext(cfg) + appCTX := context.New(cfg) zetacoreBroadcast = MockBroadcast // Update app context From b52654f8856fd9ce599599a003d3c3cf3290d2de Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 3 Jul 2024 16:37:01 -0500 Subject: [PATCH 13/18] give more accurate names to methods --- zetaclient/orchestrator/chain_activate.go | 12 ++++++------ zetaclient/orchestrator/chain_activate_test.go | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/zetaclient/orchestrator/chain_activate.go b/zetaclient/orchestrator/chain_activate.go index 613fd3ade4..50426b0565 100644 --- a/zetaclient/orchestrator/chain_activate.go +++ b/zetaclient/orchestrator/chain_activate.go @@ -52,8 +52,8 @@ func (oc *Orchestrator) ActivateAndDeactivateChains() { newObserverMap := make(map[int64]interfaces.ChainObserver) // create new signers and observers - oc.CreateObserversEVM(newSignerMap, newObserverMap) - oc.CreateObserversBTC(newSignerMap, newObserverMap) + oc.CreateSignerObserverEVM(newSignerMap, newObserverMap) + oc.CreateSignerObserverBTC(newSignerMap, newObserverMap) // activate newly supported chains and deactivate chains that are no longer supported oc.DeactivateChains(newObserverMap) @@ -113,8 +113,8 @@ func (oc *Orchestrator) ActivateChains( } } -// CreateObserversEVM creates signer and observer maps for all enabled EVM chains -func (oc *Orchestrator) CreateObserversEVM( +// CreateSignerObserverEVM creates signer and observer maps for all enabled EVM chains +func (oc *Orchestrator) CreateSignerObserverEVM( resultSignerMap map[int64]interfaces.ChainSigner, resultObserverMap map[int64]interfaces.ChainObserver, ) { @@ -181,8 +181,8 @@ func (oc *Orchestrator) CreateObserversEVM( } } -// CreateObserversBTC creates signer and observer maps for all enabled BTC chains -func (oc *Orchestrator) CreateObserversBTC( +// CreateSignerObserverBTC creates signer and observer maps for all enabled BTC chains +func (oc *Orchestrator) CreateSignerObserverBTC( resultSignerMap map[int64]interfaces.ChainSigner, resultObserverMap map[int64]interfaces.ChainObserver, ) { diff --git a/zetaclient/orchestrator/chain_activate_test.go b/zetaclient/orchestrator/chain_activate_test.go index aa826bc9f7..7df06647f5 100644 --- a/zetaclient/orchestrator/chain_activate_test.go +++ b/zetaclient/orchestrator/chain_activate_test.go @@ -122,7 +122,7 @@ func Test_ActivateChains(t *testing.T) { // create new signer and observer maps newSignerMap := make(map[int64]interfaces.ChainSigner) newObserverMap := make(map[int64]interfaces.ChainObserver) - oc.CreateObserversEVM(newSignerMap, newObserverMap) + oc.CreateSignerObserverEVM(newSignerMap, newObserverMap) // activate chains oc.ActivateChains(newSignerMap, newObserverMap) @@ -187,7 +187,7 @@ func Test_DeactivateChains(t *testing.T) { // create new signer and observer maps newSignerMap := make(map[int64]interfaces.ChainSigner) newObserverMap := make(map[int64]interfaces.ChainObserver) - oc.CreateObserversEVM(newSignerMap, newObserverMap) + oc.CreateSignerObserverEVM(newSignerMap, newObserverMap) // activate chains oc.ActivateChains(newSignerMap, newObserverMap) @@ -205,7 +205,7 @@ func Test_DeactivateChains(t *testing.T) { // create maps again based on newly updated config newSignerMap = make(map[int64]interfaces.ChainSigner) newObserverMap = make(map[int64]interfaces.ChainObserver) - oc.CreateObserversEVM(newSignerMap, newObserverMap) + oc.CreateSignerObserverEVM(newSignerMap, newObserverMap) // deactivate chains oc.DeactivateChains(newObserverMap) @@ -221,7 +221,7 @@ func Test_DeactivateChains(t *testing.T) { } } -func Test_CreateObserversEVM(t *testing.T) { +func Test_CreateSignerObserverEVM(t *testing.T) { // define test chains and chain params evmChain := chains.Ethereum btcChain := chains.BitcoinMainnet @@ -292,7 +292,7 @@ func Test_CreateObserversEVM(t *testing.T) { // create observers signerMap := make(map[int64]interfaces.ChainSigner) observerMap := make(map[int64]interfaces.ChainObserver) - oc.CreateObserversEVM(signerMap, observerMap) + oc.CreateSignerObserverEVM(signerMap, observerMap) // assert signer/observer map require.Len(t, signerMap, tt.numObserverCreated) @@ -307,7 +307,7 @@ func Test_CreateObserversEVM(t *testing.T) { } } -func Test_CreateObserversBTC(t *testing.T) { +func Test_CreateSignerObserverBTC(t *testing.T) { // define test chains and chain params evmChain := chains.Ethereum btcChain := chains.BitcoinMainnet @@ -373,7 +373,7 @@ func Test_CreateObserversBTC(t *testing.T) { // create observers signerMap := make(map[int64]interfaces.ChainSigner) observerMap := make(map[int64]interfaces.ChainObserver) - oc.CreateObserversBTC(signerMap, observerMap) + oc.CreateSignerObserverBTC(signerMap, observerMap) // assert signer/observer map require.Len(t, signerMap, tt.numObserverCreated) From d6eee46766df3b43dc822698fb2dd9f35234a460 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Wed, 3 Jul 2024 18:16:13 -0500 Subject: [PATCH 14/18] fix unit tests --- zetaclient/chains/interfaces/interfaces.go | 2 ++ .../orchestrator/chain_activate_test.go | 20 ++++++++++++- zetaclient/testutils/mocks/chain_clients.go | 16 ++++++++++ zetaclient/zetacore/client.go | 3 +- zetaclient/zetacore/tx_test.go | 30 +++++++++++++++---- 5 files changed, 63 insertions(+), 8 deletions(-) diff --git a/zetaclient/chains/interfaces/interfaces.go b/zetaclient/chains/interfaces/interfaces.go index 77c2cbd966..8b3cf97921 100644 --- a/zetaclient/chains/interfaces/interfaces.go +++ b/zetaclient/chains/interfaces/interfaces.go @@ -42,7 +42,9 @@ const ( type ChainObserver interface { Start() Stop() + OpenDB(dbPath string, dbName string) error LoadDB(dbPath string) error + SaveLastBlockScanned(blockNumber uint64) error IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) SetChainParams(observertypes.ChainParams) GetChainParams() observertypes.ChainParams diff --git a/zetaclient/orchestrator/chain_activate_test.go b/zetaclient/orchestrator/chain_activate_test.go index 7df06647f5..4304398a17 100644 --- a/zetaclient/orchestrator/chain_activate_test.go +++ b/zetaclient/orchestrator/chain_activate_test.go @@ -124,6 +124,15 @@ func Test_ActivateChains(t *testing.T) { newObserverMap := make(map[int64]interfaces.ChainObserver) oc.CreateSignerObserverEVM(newSignerMap, newObserverMap) + // open db and save last block number to db to avoid RPC call + ob := newObserverMap[tt.evmChain.ChainId] + require.NotNil(t, ob) + err := ob.OpenDB(testutils.SQLiteMemory, tt.evmCfg.Chain.ChainName.String()) + require.NoError(t, err) + + err = ob.SaveLastBlockScanned(100) + require.NoError(t, err) + // activate chains oc.ActivateChains(newSignerMap, newObserverMap) @@ -189,11 +198,20 @@ func Test_DeactivateChains(t *testing.T) { newObserverMap := make(map[int64]interfaces.ChainObserver) oc.CreateSignerObserverEVM(newSignerMap, newObserverMap) + // open db and save last block number to db to avoid RPC call + ob := newObserverMap[tt.evmChain.ChainId] + require.NotNil(t, ob) + err := ob.OpenDB(testutils.SQLiteMemory, tt.evmCfg.Chain.ChainName.String()) + require.NoError(t, err) + + err = ob.SaveLastBlockScanned(100) + require.NoError(t, err) + // activate chains oc.ActivateChains(newSignerMap, newObserverMap) // assert signer/observer map - ob, err := oc.GetUpdatedChainObserver(tt.evmChain.ChainId) + ob, err = oc.GetUpdatedChainObserver(tt.evmChain.ChainId) require.NoError(t, err) require.NotNil(t, ob) diff --git a/zetaclient/testutils/mocks/chain_clients.go b/zetaclient/testutils/mocks/chain_clients.go index b9f75cf169..38690eda92 100644 --- a/zetaclient/testutils/mocks/chain_clients.go +++ b/zetaclient/testutils/mocks/chain_clients.go @@ -30,10 +30,18 @@ func (ob *EVMObserver) Start() { func (ob *EVMObserver) Stop() { } +func (ob *EVMObserver) OpenDB(_, _ string) error { + return nil +} + func (ob *EVMObserver) LoadDB(_ string) error { return nil } +func (ob *EVMObserver) SaveLastBlockScanned(_ uint64) error { + return nil +} + func (ob *EVMObserver) IsOutboundProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { return false, false, nil } @@ -75,10 +83,18 @@ func (ob *BTCObserver) Start() { func (ob *BTCObserver) Stop() { } +func (ob *BTCObserver) OpenDB(_, _ string) error { + return nil +} + func (ob *BTCObserver) LoadDB(_ string) error { return nil } +func (ob *BTCObserver) SaveLastBlockScanned(_ uint64) error { + return nil +} + func (ob *BTCObserver) IsOutboundProcessed(_ *crosschaintypes.CrossChainTx, _ zerolog.Logger) (bool, bool, error) { return false, false, nil } diff --git a/zetaclient/zetacore/client.go b/zetaclient/zetacore/client.go index 722e53d3b3..a8fffd9ea1 100644 --- a/zetaclient/zetacore/client.go +++ b/zetaclient/zetacore/client.go @@ -266,7 +266,8 @@ func (c *Client) UpdateAppContext(appContext *context.AppContext, logger zerolog // just in case (zetacore already validated) err := observertypes.ValidateChainParams(chainParam) if err != nil { - return errors.Wrapf(err, "Invalid chain params for chain %d", chainParam.ChainId) + logger.Error().Err(err).Msgf("Invalid chain params for chain %d", chainParam.ChainId) + continue } // zetaclient detects Bitcoin network (regnet, testnet, mainnet) from chain params in zetacore diff --git a/zetaclient/zetacore/tx_test.go b/zetaclient/zetacore/tx_test.go index 5bf8babb3d..92672f442c 100644 --- a/zetaclient/zetacore/tx_test.go +++ b/zetaclient/zetacore/tx_test.go @@ -4,14 +4,16 @@ import ( "bytes" "encoding/hex" "errors" - "github.com/zeta-chain/zetacore/testutil/sample" - authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "math/big" "net" "os" "testing" + "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" sdktypes "github.com/cosmos/cosmos-sdk/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -228,12 +230,28 @@ func TestZetacore_UpdateAppContext(t *testing.T) { IsSupported: true, }, { - ChainId: chains.Ethereum.ChainId, - IsSupported: true, + ChainId: chains.Ethereum.ChainId, + ConfirmationCount: 32, + InboundTicker: 15, + OutboundTicker: 15, + GasPriceTicker: 300, + OutboundScheduleInterval: 64, + OutboundScheduleLookahead: 10, + BallotThreshold: sdk.MustNewDecFromStr("0.6"), + MinObserverDelegation: sdk.MustNewDecFromStr("1000000000000000000"), + IsSupported: true, }, { - ChainId: chains.BitcoinMainnet.ChainId, - IsSupported: true, + ChainId: chains.BitcoinMainnet.ChainId, + ConfirmationCount: 32, + InboundTicker: 15, + OutboundTicker: 15, + GasPriceTicker: 300, + OutboundScheduleInterval: 64, + OutboundScheduleLookahead: 10, + BallotThreshold: sdk.MustNewDecFromStr("0.6"), + MinObserverDelegation: sdk.MustNewDecFromStr("1000000000000000000"), + IsSupported: true, }, }, }}) From e46e0e43cbea110a018ff480fa41639da7956be4 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 4 Jul 2024 11:21:17 -0500 Subject: [PATCH 15/18] update method names according PR review feedback --- zetaclient/orchestrator/chain_activate.go | 10 +++++----- zetaclient/orchestrator/orchestrator.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zetaclient/orchestrator/chain_activate.go b/zetaclient/orchestrator/chain_activate.go index 50426b0565..68cc1d91af 100644 --- a/zetaclient/orchestrator/chain_activate.go +++ b/zetaclient/orchestrator/chain_activate.go @@ -16,15 +16,15 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/config" ) -// WatchEnabledChains watches for run-time chain activation and deactivation -func (oc *Orchestrator) WatchEnabledChains() { +// WatchActivatedChains watches for run-time chain activation and deactivation +func (oc *Orchestrator) WatchActivatedChains() { oc.logger.Std.Info().Msg("WatchChainActivation started") ticker := time.NewTicker(common.ZetaBlockTime * 2) for { select { case <-ticker.C: - oc.ActivateAndDeactivateChains() + oc.UpdateActivatedChains() case <-oc.stop: oc.logger.Std.Info().Msg("WatchChainActivation stopped") return @@ -32,7 +32,7 @@ func (oc *Orchestrator) WatchEnabledChains() { } } -// ActivateAndDeactivateChains activates and deactivates chains according to chain params and config file +// UpdateActivatedChains updates activated chains accordingly according to chain params and config file // // The chains to be activated: // - chain params flag 'IsSupported' is true AND @@ -45,7 +45,7 @@ func (oc *Orchestrator) WatchEnabledChains() { // Note: // - zetaclient will reload config file periodically and update in-memory config accordingly. // - As an tss signer, please make sure the config file is always well configured and not missing any chain -func (oc *Orchestrator) ActivateAndDeactivateChains() { +func (oc *Orchestrator) UpdateActivatedChains() { // create new signer and observer maps // Note: the keys of the two maps are chain IDs and they are always exactly matched newSignerMap := make(map[int64]interfaces.ChainSigner) diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index 55829071a4..89ce7f9400 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -144,8 +144,8 @@ func (oc *Orchestrator) Start() { // watch for upgrade plan in zetacore go oc.WatchUpgradePlan() - // watch for enabling/disabling chains - go oc.WatchEnabledChains() + // watch for chain activation/deactivation + go oc.WatchActivatedChains() // schedule pending cctxs across all enabled chains go oc.SchedulePendingCctxs() From 4b850b373281d356becd7a142438d3eb514d6504 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 4 Jul 2024 18:55:29 -0500 Subject: [PATCH 16/18] added unit test for orchestrator method Stop() --- zetaclient/orchestrator/orchestrator_test.go | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/zetaclient/orchestrator/orchestrator_test.go b/zetaclient/orchestrator/orchestrator_test.go index b3d9f099e7..9b7ab147ca 100644 --- a/zetaclient/orchestrator/orchestrator_test.go +++ b/zetaclient/orchestrator/orchestrator_test.go @@ -52,6 +52,8 @@ func MockOrchestrator( evmChain.ChainId: evmObserver, btcChain.ChainId: btcObserver, }, + stop: make(chan struct{}), + stopped: false, } return orchestrator } @@ -92,6 +94,28 @@ func CreateTestAppContext( return appContext } +func Test_Stop(t *testing.T) { + // initial parameters for orchestrator creation + evmChain := chains.Ethereum + btcChain := chains.BitcoinMainnet + evmChainParams := &observertypes.ChainParams{ + ChainId: evmChain.ChainId, + } + btcChainParams := &observertypes.ChainParams{ + ChainId: btcChain.ChainId, + } + + // create orchestrator + appCtx := CreateTestAppContext(evmChain, btcChain, evmChainParams, btcChainParams) + orchestrator := MockOrchestrator(t, appCtx, nil, evmChain, btcChain, evmChainParams, btcChainParams) + + // stop orchestrator 1st time + orchestrator.Stop() + + // stop orchestrator 2nd time without panic + orchestrator.Stop() +} + func Test_GetUpdatedSigner(t *testing.T) { // initial parameters for orchestrator creation evmChain := chains.Ethereum From 58feb66680f07776aaaeb1b4eb831829205da459 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Thu, 4 Jul 2024 19:54:18 -0500 Subject: [PATCH 17/18] add error handling to avoid unexpected chain deactivation due to endpoint connection failure --- zetaclient/orchestrator/chain_activate.go | 68 +++++++++++-------- .../orchestrator/chain_activate_test.go | 32 +++++++-- 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/zetaclient/orchestrator/chain_activate.go b/zetaclient/orchestrator/chain_activate.go index 68cc1d91af..983b920f1e 100644 --- a/zetaclient/orchestrator/chain_activate.go +++ b/zetaclient/orchestrator/chain_activate.go @@ -1,10 +1,12 @@ package orchestrator import ( + "fmt" "time" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" + "github.com/pkg/errors" btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer" btcrpc "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/rpc" @@ -24,7 +26,10 @@ func (oc *Orchestrator) WatchActivatedChains() { for { select { case <-ticker.C: - oc.UpdateActivatedChains() + err := oc.UpdateActivatedChains() + if err != nil { + oc.logger.Sampled.Error().Err(err).Msg("UpdateActivatedChains failed") + } case <-oc.stop: oc.logger.Std.Info().Msg("WatchChainActivation stopped") return @@ -45,19 +50,27 @@ func (oc *Orchestrator) WatchActivatedChains() { // Note: // - zetaclient will reload config file periodically and update in-memory config accordingly. // - As an tss signer, please make sure the config file is always well configured and not missing any chain -func (oc *Orchestrator) UpdateActivatedChains() { +func (oc *Orchestrator) UpdateActivatedChains() error { // create new signer and observer maps // Note: the keys of the two maps are chain IDs and they are always exactly matched newSignerMap := make(map[int64]interfaces.ChainSigner) newObserverMap := make(map[int64]interfaces.ChainObserver) // create new signers and observers - oc.CreateSignerObserverEVM(newSignerMap, newObserverMap) - oc.CreateSignerObserverBTC(newSignerMap, newObserverMap) + err := oc.CreateSignerObserverEVM(newSignerMap, newObserverMap) + if err != nil { + return err + } + err = oc.CreateSignerObserverBTC(newSignerMap, newObserverMap) + if err != nil { + return err + } // activate newly supported chains and deactivate chains that are no longer supported oc.DeactivateChains(newObserverMap) oc.ActivateChains(newSignerMap, newObserverMap) + + return nil } // DeactivateChains deactivates chains that are no longer supported @@ -117,12 +130,12 @@ func (oc *Orchestrator) ActivateChains( func (oc *Orchestrator) CreateSignerObserverEVM( resultSignerMap map[int64]interfaces.ChainSigner, resultObserverMap map[int64]interfaces.ChainObserver, -) { +) error { // create EVM-chain signers for _, evmConfig := range oc.appContext.Config().GetAllEVMConfigs() { chainParams, found := oc.appContext.GetExternalChainParams(evmConfig.Chain.ChainId) if !found { - oc.logger.Sampled.Warn(). + oc.logger.Sampled.Error(). Msgf("CreateObserversEVM: chain parameter not found for chain %d", evmConfig.Chain.ChainId) continue } @@ -132,10 +145,12 @@ func (oc *Orchestrator) CreateSignerObserverEVM( // create RPC client evmClient, err := ethclient.Dial(evmConfig.Endpoint) if err != nil { - oc.logger.Std.Error(). - Err(err). - Msgf("CreateObserversEVM: error dailing endpoint %s for chain %d", evmConfig.Endpoint, evmConfig.Chain.ChainId) - continue + return errors.Wrapf( + err, + "error dailing endpoint %s for chain %d", + evmConfig.Endpoint, + evmConfig.Chain.ChainId, + ) } // create signer @@ -151,10 +166,7 @@ func (oc *Orchestrator) CreateSignerObserverEVM( connectorAddress, erc20CustodyAddress) if err != nil { - oc.logger.Std.Error(). - Err(err). - Msgf("CreateObserversEVM: error NewSigner for chain %d", evmConfig.Chain.ChainId) - continue + return errors.Wrapf(err, "error NewSigner for chain %d", evmConfig.Chain.ChainId) } // create observer @@ -169,23 +181,22 @@ func (oc *Orchestrator) CreateSignerObserverEVM( oc.ts, ) if err != nil { - oc.logger.Std.Error(). - Err(err). - Msgf("CreateObserversEVM: error NewObserver for chain %d", evmConfig.Chain.ChainId) - continue + return errors.Wrapf(err, "error NewObserver for chain %d", evmConfig.Chain.ChainId) } // add signer and observer to result maps resultSignerMap[evmConfig.Chain.ChainId] = signer resultObserverMap[evmConfig.Chain.ChainId] = observer } + + return nil } // CreateSignerObserverBTC creates signer and observer maps for all enabled BTC chains func (oc *Orchestrator) CreateSignerObserverBTC( resultSignerMap map[int64]interfaces.ChainSigner, resultObserverMap map[int64]interfaces.ChainObserver, -) { +) error { // get enabled BTC chains and config btcChains := oc.appContext.GetEnabledBTCChains() btcConfig, found := oc.appContext.Config().GetBTCConfig() @@ -193,11 +204,10 @@ func (oc *Orchestrator) CreateSignerObserverBTC( // currently only one single BTC chain is supported if !found { oc.logger.Sampled.Warn().Msg("CreateObserversBTC: BTC config not found") - return + return nil } if len(btcChains) != 1 { - oc.logger.Std.Error().Msgf("CreateObserversBTC: want single BTC chain, got %d", len(btcChains)) - return + return fmt.Errorf("want single BTC chain, got %d", len(btcChains)) } // create BTC-chain signers and observers @@ -205,7 +215,7 @@ func (oc *Orchestrator) CreateSignerObserverBTC( for _, btcChain := range btcChains { chainParams, found := oc.appContext.GetExternalChainParams(btcChain.ChainId) if !found { - oc.logger.Sampled.Warn(). + oc.logger.Sampled.Error(). Msgf("CreateObserversBTC: chain parameter not found for chain %d", btcChain.ChainId) continue } @@ -213,17 +223,13 @@ func (oc *Orchestrator) CreateSignerObserverBTC( // create RPC client btcClient, err := btcrpc.NewRPCClient(btcConfig) if err != nil { - oc.logger.Std.Error(). - Err(err). - Msgf("CreateObserversBTC: error NewRPCClient for chain %s", btcChain.String()) - continue + return errors.Wrapf(err, "error NewRPCClient for chain %d", btcChain.ChainId) } // create signer signer, err := btcsigner.NewSigner(btcChain, oc.appContext, oc.tss, oc.ts, oc.logger.Base, btcConfig) if err != nil { - oc.logger.Std.Error().Err(err).Msgf("CreateObserversBTC: error NewSigner for chain %d", btcChain.ChainId) - continue + return errors.Wrapf(err, "error NewSigner for chain %d", btcChain.ChainId) } // create observer @@ -238,11 +244,13 @@ func (oc *Orchestrator) CreateSignerObserverBTC( oc.ts, ) if err != nil { - oc.logger.Std.Error().Err(err).Msgf("NewObserver error for bitcoin chain %s", btcChain.String()) + return errors.Wrapf(err, "error NewObserver for chain %d", btcChain.ChainId) } // add signer and observer to result maps resultSignerMap[btcChain.ChainId] = signer resultObserverMap[btcChain.ChainId] = observer } + + return nil } diff --git a/zetaclient/orchestrator/chain_activate_test.go b/zetaclient/orchestrator/chain_activate_test.go index 4304398a17..95728fb6cd 100644 --- a/zetaclient/orchestrator/chain_activate_test.go +++ b/zetaclient/orchestrator/chain_activate_test.go @@ -255,6 +255,8 @@ func Test_CreateSignerObserverEVM(t *testing.T) { evmChainParams *observertypes.ChainParams dbPath string numObserverCreated int + fail bool + message string }{ { name: "should create observers for EVM chain", @@ -268,6 +270,7 @@ func Test_CreateSignerObserverEVM(t *testing.T) { evmChainParams: evmChainParams, dbPath: testutils.SQLiteMemory, numObserverCreated: 1, + fail: false, }, { name: "should not create observer for EVM chain if chain params not found", @@ -281,9 +284,10 @@ func Test_CreateSignerObserverEVM(t *testing.T) { evmChainParams: nil, dbPath: testutils.SQLiteMemory, numObserverCreated: 0, + fail: false, }, { - name: "should not create observer for EVM chain if endpoint is invalid", + name: "should fail if endpoint is invalid", evmCfg: config.EVMConfig{ Chain: evmChain, Endpoint: "invalid_endpoint", @@ -294,6 +298,8 @@ func Test_CreateSignerObserverEVM(t *testing.T) { evmChainParams: evmChainParams, dbPath: testutils.SQLiteMemory, numObserverCreated: 0, + fail: true, + message: "error dailing endpoint", }, } @@ -310,7 +316,12 @@ func Test_CreateSignerObserverEVM(t *testing.T) { // create observers signerMap := make(map[int64]interfaces.ChainSigner) observerMap := make(map[int64]interfaces.ChainObserver) - oc.CreateSignerObserverEVM(signerMap, observerMap) + err := oc.CreateSignerObserverEVM(signerMap, observerMap) + + // assert error if it should fail + if tt.fail { + require.ErrorContains(t, err, tt.message) + } // assert signer/observer map require.Len(t, signerMap, tt.numObserverCreated) @@ -341,6 +352,8 @@ func Test_CreateSignerObserverBTC(t *testing.T) { btcChainParams *observertypes.ChainParams dbPath string numObserverCreated int + fail bool + message string }{ { name: "should not create observer for BTC chain if btc config is missing", @@ -353,7 +366,7 @@ func Test_CreateSignerObserverBTC(t *testing.T) { numObserverCreated: 0, }, { - name: "should not create observer for BTC chain if chain is not enabled", + name: "should fail if number of BTC chain is not 1", evmCfg: config.EVMConfig{}, btcCfg: config.BTCConfig{ RPCUsername: "user", @@ -363,9 +376,11 @@ func Test_CreateSignerObserverBTC(t *testing.T) { btcChainParams: nil, // disabled btc chain dbPath: testutils.SQLiteMemory, numObserverCreated: 0, + fail: true, + message: "want single BTC chain, got 0", }, { - name: "should not create observer for BTC chain if failed to Ping endpoint", + name: "should fail if unable to create rpc client", evmCfg: config.EVMConfig{}, btcCfg: config.BTCConfig{ RPCUsername: "user", @@ -375,6 +390,8 @@ func Test_CreateSignerObserverBTC(t *testing.T) { btcChainParams: btcChainParams, dbPath: testutils.SQLiteMemory, numObserverCreated: 0, + fail: true, + message: "error NewRPCClient", }, } @@ -391,7 +408,12 @@ func Test_CreateSignerObserverBTC(t *testing.T) { // create observers signerMap := make(map[int64]interfaces.ChainSigner) observerMap := make(map[int64]interfaces.ChainObserver) - oc.CreateSignerObserverBTC(signerMap, observerMap) + err := oc.CreateSignerObserverBTC(signerMap, observerMap) + + // assert error if it should fail + if tt.fail { + require.ErrorContains(t, err, tt.message) + } // assert signer/observer map require.Len(t, signerMap, tt.numObserverCreated) From e063c2a62ed05c843f8a1c99aad3674af45ddb87 Mon Sep 17 00:00:00 2001 From: Charlie Chen Date: Fri, 5 Jul 2024 09:20:43 -0500 Subject: [PATCH 18/18] fix code generate mocker version --- testutil/keeper/mocks/crosschain/account.go | 2 +- testutil/keeper/mocks/crosschain/authority.go | 2 +- testutil/keeper/mocks/crosschain/bank.go | 2 +- testutil/keeper/mocks/crosschain/fungible.go | 2 +- testutil/keeper/mocks/crosschain/ibccrosschain.go | 2 +- testutil/keeper/mocks/crosschain/lightclient.go | 2 +- testutil/keeper/mocks/crosschain/observer.go | 2 +- testutil/keeper/mocks/crosschain/staking.go | 2 +- testutil/keeper/mocks/emissions/account.go | 2 +- testutil/keeper/mocks/emissions/bank.go | 2 +- testutil/keeper/mocks/emissions/observer.go | 2 +- testutil/keeper/mocks/emissions/staking.go | 2 +- testutil/keeper/mocks/fungible/account.go | 2 +- testutil/keeper/mocks/fungible/authority.go | 2 +- testutil/keeper/mocks/fungible/bank.go | 2 +- testutil/keeper/mocks/fungible/evm.go | 2 +- testutil/keeper/mocks/fungible/observer.go | 2 +- testutil/keeper/mocks/ibccrosschain/crosschain.go | 2 +- testutil/keeper/mocks/ibccrosschain/transfer.go | 2 +- testutil/keeper/mocks/lightclient/authority.go | 2 +- testutil/keeper/mocks/observer/authority.go | 2 +- testutil/keeper/mocks/observer/lightclient.go | 2 +- testutil/keeper/mocks/observer/slashing.go | 2 +- testutil/keeper/mocks/observer/staking.go | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/testutil/keeper/mocks/crosschain/account.go b/testutil/keeper/mocks/crosschain/account.go index e2d05b0425..fbd7c0377b 100644 --- a/testutil/keeper/mocks/crosschain/account.go +++ b/testutil/keeper/mocks/crosschain/account.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/crosschain/authority.go b/testutil/keeper/mocks/crosschain/authority.go index 1f01f4c694..d38c117dd6 100644 --- a/testutil/keeper/mocks/crosschain/authority.go +++ b/testutil/keeper/mocks/crosschain/authority.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/crosschain/bank.go b/testutil/keeper/mocks/crosschain/bank.go index 5bba791ed5..90f4e17e29 100644 --- a/testutil/keeper/mocks/crosschain/bank.go +++ b/testutil/keeper/mocks/crosschain/bank.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/crosschain/fungible.go b/testutil/keeper/mocks/crosschain/fungible.go index a889b153df..9eab868481 100644 --- a/testutil/keeper/mocks/crosschain/fungible.go +++ b/testutil/keeper/mocks/crosschain/fungible.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/crosschain/ibccrosschain.go b/testutil/keeper/mocks/crosschain/ibccrosschain.go index 17e407f0c0..177c8ec395 100644 --- a/testutil/keeper/mocks/crosschain/ibccrosschain.go +++ b/testutil/keeper/mocks/crosschain/ibccrosschain.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/crosschain/lightclient.go b/testutil/keeper/mocks/crosschain/lightclient.go index 14296a330c..d5ee740bc3 100644 --- a/testutil/keeper/mocks/crosschain/lightclient.go +++ b/testutil/keeper/mocks/crosschain/lightclient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/crosschain/observer.go b/testutil/keeper/mocks/crosschain/observer.go index 236e8775fa..6e6bd7d951 100644 --- a/testutil/keeper/mocks/crosschain/observer.go +++ b/testutil/keeper/mocks/crosschain/observer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/crosschain/staking.go b/testutil/keeper/mocks/crosschain/staking.go index f8fcba918b..5b7d3c501f 100644 --- a/testutil/keeper/mocks/crosschain/staking.go +++ b/testutil/keeper/mocks/crosschain/staking.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/emissions/account.go b/testutil/keeper/mocks/emissions/account.go index 3468f329f1..a660d40e72 100644 --- a/testutil/keeper/mocks/emissions/account.go +++ b/testutil/keeper/mocks/emissions/account.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/emissions/bank.go b/testutil/keeper/mocks/emissions/bank.go index 065aec8fa3..8149b5e6af 100644 --- a/testutil/keeper/mocks/emissions/bank.go +++ b/testutil/keeper/mocks/emissions/bank.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/emissions/observer.go b/testutil/keeper/mocks/emissions/observer.go index 67b185a71a..594f090907 100644 --- a/testutil/keeper/mocks/emissions/observer.go +++ b/testutil/keeper/mocks/emissions/observer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/emissions/staking.go b/testutil/keeper/mocks/emissions/staking.go index ba70aeffce..69f1b60081 100644 --- a/testutil/keeper/mocks/emissions/staking.go +++ b/testutil/keeper/mocks/emissions/staking.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/fungible/account.go b/testutil/keeper/mocks/fungible/account.go index 8c8bccbfea..0522e833b4 100644 --- a/testutil/keeper/mocks/fungible/account.go +++ b/testutil/keeper/mocks/fungible/account.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/fungible/authority.go b/testutil/keeper/mocks/fungible/authority.go index 567c4af2e0..ed9dbd0e28 100644 --- a/testutil/keeper/mocks/fungible/authority.go +++ b/testutil/keeper/mocks/fungible/authority.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/fungible/bank.go b/testutil/keeper/mocks/fungible/bank.go index 2f7b0f200c..db14226310 100644 --- a/testutil/keeper/mocks/fungible/bank.go +++ b/testutil/keeper/mocks/fungible/bank.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/fungible/evm.go b/testutil/keeper/mocks/fungible/evm.go index 7e72935cb4..28fd46e25c 100644 --- a/testutil/keeper/mocks/fungible/evm.go +++ b/testutil/keeper/mocks/fungible/evm.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/fungible/observer.go b/testutil/keeper/mocks/fungible/observer.go index a96ff25709..7a003686e4 100644 --- a/testutil/keeper/mocks/fungible/observer.go +++ b/testutil/keeper/mocks/fungible/observer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/ibccrosschain/crosschain.go b/testutil/keeper/mocks/ibccrosschain/crosschain.go index 5443ed5dbd..e7e665d659 100644 --- a/testutil/keeper/mocks/ibccrosschain/crosschain.go +++ b/testutil/keeper/mocks/ibccrosschain/crosschain.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/ibccrosschain/transfer.go b/testutil/keeper/mocks/ibccrosschain/transfer.go index ef94f78cf9..7cbab3b6a0 100644 --- a/testutil/keeper/mocks/ibccrosschain/transfer.go +++ b/testutil/keeper/mocks/ibccrosschain/transfer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/lightclient/authority.go b/testutil/keeper/mocks/lightclient/authority.go index 7aa33659d6..8d058dbfc0 100644 --- a/testutil/keeper/mocks/lightclient/authority.go +++ b/testutil/keeper/mocks/lightclient/authority.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/observer/authority.go b/testutil/keeper/mocks/observer/authority.go index 531db8662c..9150ffcfc5 100644 --- a/testutil/keeper/mocks/observer/authority.go +++ b/testutil/keeper/mocks/observer/authority.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/observer/lightclient.go b/testutil/keeper/mocks/observer/lightclient.go index 4c4c4e5f9e..442d006a5f 100644 --- a/testutil/keeper/mocks/observer/lightclient.go +++ b/testutil/keeper/mocks/observer/lightclient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/observer/slashing.go b/testutil/keeper/mocks/observer/slashing.go index be79a8f8a8..a7793ef8dc 100644 --- a/testutil/keeper/mocks/observer/slashing.go +++ b/testutil/keeper/mocks/observer/slashing.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks diff --git a/testutil/keeper/mocks/observer/staking.go b/testutil/keeper/mocks/observer/staking.go index c59156a7cb..90007b6c35 100644 --- a/testutil/keeper/mocks/observer/staking.go +++ b/testutil/keeper/mocks/observer/staking.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.42.2. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks