diff --git a/bridge/cmd/root.go b/bridge/cmd/root.go index 500ec873..fbbef620 100644 --- a/bridge/cmd/root.go +++ b/bridge/cmd/root.go @@ -1,12 +1,14 @@ package cmd import ( + "fmt" "path/filepath" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/maticnetwork/heimdall/helper" + tendermintLogger "github.com/tendermint/tendermint/libs/log" ) const ( @@ -16,6 +18,8 @@ const ( startListenBlockFlag = "start-listen-block" ) +var logger = helper.Logger.With("module", "bridge/cmd/") + // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "bridge", @@ -26,19 +30,69 @@ var rootCmd = &cobra.Command{ }, } -func BridgeCommands() *cobra.Command { +func BridgeCommands(viperInstance *viper.Viper, loggerInstance tendermintLogger.Logger, caller string) *cobra.Command { + DecorateWithBridgeRootFlags(rootCmd, viperInstance, loggerInstance, caller) return rootCmd } +func DecorateWithBridgeRootFlags( + cmd *cobra.Command, viperInstance *viper.Viper, loggerInstance tendermintLogger.Logger, caller string, +) { + cmd.PersistentFlags().StringP(helper.TendermintNodeFlag, "n", helper.DefaultTendermintNode, "Node to connect to") + + if err := viperInstance.BindPFlag(helper.TendermintNodeFlag, + cmd.PersistentFlags().Lookup(helper.TendermintNodeFlag)); err != nil { + loggerInstance.Error(fmt.Sprintf("%v | BindPFlag | %v", caller, helper.TendermintNodeFlag), "Error", err) + } + + cmd.PersistentFlags().String(helper.HomeFlag, helper.DefaultNodeHome, "directory for config and data") + + if err := viperInstance.BindPFlag(helper.HomeFlag, cmd.PersistentFlags().Lookup(helper.HomeFlag)); err != nil { + loggerInstance.Error(fmt.Sprintf("%v | BindPFlag | %v", caller, helper.HomeFlag), "Error", err) + } + + // bridge storage db + cmd.PersistentFlags().String( + bridgeDBFlag, + "", + "Bridge db path (default /bridge/storage)", + ) + + if err := viperInstance.BindPFlag(bridgeDBFlag, cmd.PersistentFlags().Lookup(bridgeDBFlag)); err != nil { + loggerInstance.Error(fmt.Sprintf("%v | BindPFlag | %v", caller, bridgeDBFlag), "Error", err) + } + + // bridge chain id + cmd.PersistentFlags().String( + bttcChainIDFlag, + helper.DefaultBttcChainID, + "Bttc chain id", + ) + + if err := viperInstance.BindPFlag(bttcChainIDFlag, cmd.PersistentFlags().Lookup(bttcChainIDFlag)); err != nil { + loggerInstance.Error(fmt.Sprintf("%v | BindPFlag | %v", caller, bttcChainIDFlag), "Error", err) + } +} + // InitTendermintViperConfig sets global viper configuration needed to heimdall func InitTendermintViperConfig(cmd *cobra.Command) { + // set appropriate bridge db. + AdjustBridgeDBValue(cmd, viper.GetViper()) + + // start heimdall config. + helper.InitDeliveryConfig("") +} + +// function is called to set appropriate bridge db path. +func AdjustBridgeDBValue(cmd *cobra.Command, v *viper.Viper) { tendermintNode, _ := cmd.Flags().GetString(helper.TendermintNodeFlag) homeValue, _ := cmd.Flags().GetString(helper.HomeFlag) - withDeliveryConfigValue, _ := cmd.Flags().GetString(helper.WithDeliveryConfigFlag) + withDeliveryConfigFlag, _ := cmd.Flags().GetString(helper.WithDeliveryConfigFlag) bridgeDBValue, _ := cmd.Flags().GetString(bridgeDBFlag) - borChainIDValue, _ := cmd.Flags().GetString(bttcChainIDFlag) + bttcChainIDValue, _ := cmd.Flags().GetString(bttcChainIDFlag) rootChainTypeValue, _ := cmd.Flags().GetString(rootChainTypeFlag) startListenBlockValue, _ := cmd.Flags().GetInt64(startListenBlockFlag) + // bridge-db directory (default storage) if bridgeDBValue == "" { bridgeDBValue = filepath.Join(homeValue, "bridge", "storage") @@ -47,40 +101,9 @@ func InitTendermintViperConfig(cmd *cobra.Command) { // set to viper viper.Set(helper.TendermintNodeFlag, tendermintNode) viper.Set(helper.HomeFlag, homeValue) - viper.Set(helper.WithDeliveryConfigFlag, withDeliveryConfigValue) + viper.Set(helper.WithDeliveryConfigFlag, withDeliveryConfigFlag) viper.Set(bridgeDBFlag, bridgeDBValue) - viper.Set(bttcChainIDFlag, borChainIDValue) + viper.Set(bttcChainIDFlag, bttcChainIDValue) viper.Set(rootChainTypeFlag, rootChainTypeValue) viper.Set(startListenBlockFlag, startListenBlockValue) - // start heimdall config - helper.InitDeliveryConfig("") -} - -func init() { - var logger = helper.Logger.With("module", "bridge/cmd/") - - rootCmd.PersistentFlags().StringP(helper.TendermintNodeFlag, "n", helper.DefaultTendermintNode, "Node to connect to") - rootCmd.PersistentFlags().String(helper.HomeFlag, helper.DefaultNodeHome, "directory for config and data") - rootCmd.PersistentFlags().String( - helper.WithDeliveryConfigFlag, - "", - "Delivery config file path (default /config/delivery-config.toml)", - ) - // bridge storage db - rootCmd.PersistentFlags().String( - bridgeDBFlag, - "", - "Bridge db path (default /bridge/storage)", - ) - // bridge chain id - rootCmd.PersistentFlags().String( - bttcChainIDFlag, - helper.DefaultBttcChainID, - "Bor chain id", - ) - - // bind all flags with viper - if err := viper.BindPFlags(rootCmd.Flags()); err != nil { - logger.Error("init | BindPFlag | rootCmd.Flags", "Error", err) - } } diff --git a/bridge/cmd/start.go b/bridge/cmd/start.go index 55a267b5..85308b05 100644 --- a/bridge/cmd/start.go +++ b/bridge/cmd/start.go @@ -1,15 +1,15 @@ package cmd import ( - "fmt" + "context" "os" "os/signal" - "sync" "syscall" "time" "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" + "golang.org/x/sync/errgroup" "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/common" @@ -27,116 +27,180 @@ import ( const ( waitDuration = 1 * time.Minute - logLevel = "log_level" ) -// GetStartCmd returns the start command to start bridge -func GetStartCmd() *cobra.Command { - var logger = helper.Logger.With("module", "bridge/cmd/") - startCmd := &cobra.Command{ - Use: "start", - Short: "Start bridge server", - Run: func(cmd *cobra.Command, args []string) { - - // create codec - cdc := app.MakeCodec() - // queue connector & http client - _queueConnector := queue.NewQueueConnector(helper.GetConfig().AmqpURL) - _queueConnector.StartWorker() - - _txBroadcaster := broadcaster.NewTxBroadcaster(cdc) - _httpClient := httpClient.NewHTTP(helper.GetConfig().TendermintRPCUrl, "/websocket") - - // selected services to start - services := []common.Service{} - services = append(services, - listener.NewListenerService(cdc, _queueConnector, _httpClient), - processor.NewProcessorService(cdc, _queueConnector, _httpClient, _txBroadcaster), - ) - - // sync group - var wg sync.WaitGroup - - // go routine to catch signal - catchSignal := make(chan os.Signal, 1) - signal.Notify(catchSignal, os.Interrupt, syscall.SIGTERM) - go func() { - // sig is a ^C, handle it - for range catchSignal { - // stop processes - logger.Info("Received stop signal - Stopping all services") - for _, service := range services { - if err := service.Stop(); err != nil { - logger.Error("GetStartCmd | service.Stop", "Error", err) - } +func StartBridge(shutdownCtx context.Context, isStandAlone bool) error { + // create codec + cdc := app.MakeCodec() + // queue connector & http client + _queueConnector := queue.NewQueueConnector(helper.GetConfig().AmqpURL) + _queueConnector.StartWorker() + + _txBroadcaster := broadcaster.NewTxBroadcaster(cdc) + _httpClient := httpClient.NewHTTP(helper.GetConfig().TendermintRPCUrl, "/websocket") + + // selected services to start + services := []common.Service{} + services = append(services, + listener.NewListenerService(cdc, _queueConnector, _httpClient), + processor.NewProcessorService(cdc, _queueConnector, _httpClient, _txBroadcaster), + ) + + var waitGroup errgroup.Group + + if isStandAlone { + // go routine to catch signal + catchSignal := make(chan os.Signal, 1) + signal.Notify(catchSignal, os.Interrupt, syscall.SIGTERM) + + cancelCtx, cancel := context.WithCancel(shutdownCtx) + shutdownCtx = cancelCtx + + waitGroup.Go(func() error { + // sig is a ^C, handle it + for range catchSignal { + // stop processes + logger.Info("Received stop signal - Stopping all services") + for _, service := range services { + if err := service.Stop(); err != nil { + logger.Error("GetStartCmd | service.Stop", "Error", err) } + } - // stop http client - if err := _httpClient.Stop(); err != nil { - logger.Error("GetStartCmd | _httpClient.Stop", "Error", err) - } + // stop http client + if err := _httpClient.Stop(); err != nil { + logger.Error("GetStartCmd | _httpClient.Stop", "Error", err) + } + + // stop db instance + util.CloseBridgeDBInstance() + + cancel() + } + + return nil + }) + } else { + // if not standalone, wait for shutdown context to stop services + waitGroup.Go(func() error { + <-shutdownCtx.Done() - // stop db instance - util.CloseBridgeDBInstance() + logger.Info("Received stop signal - Stopping all delivery bridge services") - // exit - os.Exit(1) + for _, service := range services { + srv := service + if srv.IsRunning() { + if err := srv.Stop(); err != nil { + logger.Error("GetStartCmd | service.Stop", "Error", err) + + return err + } } - }() + } + + // stop http client + if err := _httpClient.Stop(); err != nil { + logger.Error("GetStartCmd | _httpClient.Stop", "Error", err) - // Start http client - err := _httpClient.Start() - if err != nil { - panic(fmt.Sprintf("Error connecting to server %v", err)) + return err } - // cli context - cliCtx := cliContext.NewCLIContext().WithCodec(cdc) - cliCtx.BroadcastMode = client.BroadcastAsync - cliCtx.TrustNode = true - - // start bridge services only when node fully synced - for { - if !util.IsCatchingUp(cliCtx) { - logger.Info("Node upto date, starting bridge services") - break - } else { - logger.Info("Waiting for heimdall to be synced") - } - time.Sleep(waitDuration) + // stop db instance + util.CloseBridgeDBInstance() + + return nil + }) + } + + // Start http client + err := _httpClient.Start() + if err != nil { + logger.Error("Error connecting to server: %v", err) + + return err + } + + // cli context + cliCtx := cliContext.NewCLIContext().WithCodec(cdc) + cliCtx.BroadcastMode = client.BroadcastAsync + cliCtx.TrustNode = true + + // start bridge service only when node fully synced + loop := true + for loop { + select { + case <-shutdownCtx.Done(): + return nil + + case <-time.After(waitDuration): + if !util.IsCatchingUp(cliCtx) { + logger.Info("Node up to date, starting bridge services") + + loop = false + } else { + logger.Info("Waiting for heimdall to be synced") } + } + } - // strt all processes - for _, service := range services { - go func(serv common.Service) { - defer wg.Done() - // TODO handle error while starting service - if err := serv.Start(); err != nil { - logger.Error("GetStartCmd | serv.Start", "Error", err) - } - <-serv.Quit() - }(service) + // start all services + for _, service := range services { + srv := service + + waitGroup.Go(func() error { + if err := srv.Start(); err != nil { + logger.Error("GetStartCmd | serv.Start", "Error", err) + + return err } - // wait for all processes - wg.Add(len(services)) - wg.Wait() + + <-srv.Quit() + + return nil + }) + } + + if err := waitGroup.Wait(); err != nil { + logger.Error("Bridge stopped", "err", err) + + return err + } + + if isStandAlone { + os.Exit(1) + } + + return nil +} + +// GetStartCmd returns the start command to start bridge. +func GetStartCmd() *cobra.Command { + startCmd := &cobra.Command{ + Use: "start", + Short: "Start bridge server", + Run: func(cmd *cobra.Command, args []string) { + _ = StartBridge(context.Background(), true) }} // log level - startCmd.Flags().String(logLevel, "info", "Log level for bridge") - if err := viper.BindPFlag(logLevel, startCmd.Flags().Lookup(logLevel)); err != nil { + startCmd.Flags().String(helper.LogLevel, "info", "Log level for bridge") + + if err := viper.BindPFlag(helper.LogLevel, startCmd.Flags().Lookup(helper.LogLevel)); err != nil { logger.Error("GetStartCmd | BindPFlag | logLevel", "Error", err) } startCmd.Flags().Bool("all", false, "start all bridge services") + if err := viper.BindPFlag("all", startCmd.Flags().Lookup("all")); err != nil { logger.Error("GetStartCmd | BindPFlag | all", "Error", err) } startCmd.Flags().StringSlice("only", []string{}, "comma separated bridge services to start") + if err := viper.BindPFlag("only", startCmd.Flags().Lookup("only")); err != nil { logger.Error("GetStartCmd | BindPFlag | only", "Error", err) } + return startCmd } diff --git a/bridge/setu/listener/base.go b/bridge/setu/listener/base.go index 15cd9934..67694a78 100644 --- a/bridge/setu/listener/base.go +++ b/bridge/setu/listener/base.go @@ -29,11 +29,11 @@ type Listener interface { StartHeaderProcess(context.Context) - StartPolling(context.Context, time.Duration, bool) + StartPolling(context.Context, time.Duration, bool, *big.Int) StartSubscription(context.Context, ethereum.Subscription) - ProcessHeader(*types.Header) + ProcessHeader(*blockHeader) Stop() @@ -54,7 +54,7 @@ type BaseListener struct { chainClient *ethclient.Client // header channel - HeaderChannel chan *types.Header + HeaderChannel chan *blockHeader // cancel function for poll/subscription cancelSubscription context.CancelFunc @@ -75,6 +75,11 @@ type BaseListener struct { storageClient *leveldb.DB } +type blockHeader struct { + header *types.Header // block header + isFinalized bool // if the block is a finalized block or not +} + // NewBaseListener creates a new BaseListener. func NewBaseListener(cdc *codec.Codec, queueConnector *queue.QueueConnector, httpClient *httpClient.HTTP, chainClient *ethclient.Client, name string, impl Listener) *BaseListener { @@ -103,7 +108,7 @@ func NewBaseListener(cdc *codec.Codec, queueConnector *queue.QueueConnector, htt contractConnector: contractCaller, chainClient: chainClient, - HeaderChannel: make(chan *types.Header), + HeaderChannel: make(chan *blockHeader), } } @@ -159,7 +164,7 @@ func (bl *BaseListener) StartHeaderProcess(ctx context.Context) { // startPolling starts polling // needAlign is used to decide whether the ticker is align to 1970 UTC. // if true, the ticker will always tick as it begins at 1970 UTC. -func (bl *BaseListener) StartPolling(ctx context.Context, pollInterval time.Duration, needAlign bool) { +func (bl *BaseListener) StartPolling(ctx context.Context, pollInterval time.Duration, needAlign bool, number *big.Int) { // How often to fire the passed in function in second interval := pollInterval firstInterval := interval @@ -182,10 +187,36 @@ func (bl *BaseListener) StartPolling(ctx context.Context, pollInterval time.Dura ticker.Reset(interval) }) - header, err := bl.chainClient.HeaderByNumber(ctx, nil) + var bHeader *blockHeader + + header, err := bl.chainClient.HeaderByNumber(ctx, number) if err == nil && header != nil { - // send data to channel - bl.HeaderChannel <- header + if number != nil { + // finalized was requested + bHeader = &blockHeader{header: header, isFinalized: true} + } else { + // latest was requested + bHeader = &blockHeader{header: header, isFinalized: false} + } + } + + // if error occurred and finalized was requested, fall back to latest block + if err != nil && number != nil { + bl.Logger.Error("Error in fetching finalized block header while polling", "err", err) + + header, err = bl.chainClient.HeaderByNumber(ctx, nil) + if err == nil && header != nil { + bHeader = &blockHeader{header: header, isFinalized: false} + } + } + + if err != nil { + bl.Logger.Error("Error in fetching block header while polling", "err", err) + } + + // push data to the channel + if bHeader != nil { + bl.HeaderChannel <- bHeader } case <-ctx.Done(): diff --git a/bridge/setu/listener/heimdall.go b/bridge/setu/listener/heimdall.go index dca993a6..d9ceaf48 100644 --- a/bridge/setu/listener/heimdall.go +++ b/bridge/setu/listener/heimdall.go @@ -3,13 +3,13 @@ package listener import ( "context" "encoding/json" + "math/big" "strconv" "sync" "sync/atomic" "time" "github.com/RichardKnop/machinery/v1/tasks" - "github.com/ethereum/go-ethereum/core/types" "github.com/maticnetwork/heimdall/bridge/setu/util" "github.com/maticnetwork/heimdall/helper" @@ -54,7 +54,7 @@ func (hl *HeimdallListener) Start() error { hl.Logger.Info("Start polling for events", "pollInterval", pollInterval) - go hl.StartPolling(headerCtx, pollInterval, false) + go hl.StartPolling(headerCtx, pollInterval, false, nil) go hl.StartPollingEventRecord(headerCtx, pollInterval, false) @@ -62,13 +62,14 @@ func (hl *HeimdallListener) Start() error { } // ProcessHeader - -func (hl *HeimdallListener) ProcessHeader(*types.Header) { +func (hl *HeimdallListener) ProcessHeader(header *blockHeader) { } // StartPolling - starts polling for heimdall events // needAlign is used to decide whether the ticker is align to 1970 UTC. // if true, the ticker will always tick as it begins at 1970 UTC. -func (hl *HeimdallListener) StartPolling(ctx context.Context, pollInterval time.Duration, needAlign bool) { +func (hl *HeimdallListener) StartPolling(ctx context.Context, pollInterval time.Duration, needAlign bool, + number *big.Int) { // How often to fire the passed in function in second interval := pollInterval firstInterval := interval diff --git a/bridge/setu/listener/maticchain.go b/bridge/setu/listener/maticchain.go index 7aafb3e5..48c1267c 100644 --- a/bridge/setu/listener/maticchain.go +++ b/bridge/setu/listener/maticchain.go @@ -5,7 +5,6 @@ import ( "time" "github.com/RichardKnop/machinery/v1/tasks" - "github.com/ethereum/go-ethereum/core/types" "github.com/maticnetwork/heimdall/helper" ) @@ -34,16 +33,9 @@ func (ml *MaticChainListener) Start() error { // start header process go ml.StartHeaderProcess(headerCtx) - // subscribe to new head - subscription, err := ml.contractConnector.MaticChainClient.SubscribeNewHead(ctx, ml.HeaderChannel) - if err != nil { - // start go routine to poll for new header using client object - ml.Logger.Info("Start polling for header blocks", "pollInterval", helper.GetConfig().CheckpointerPollInterval) - go ml.StartPolling(ctx, helper.GetConfig().CheckpointerPollInterval, true) - } else { - // start go routine to listen new header using subscription - go ml.StartSubscription(ctx, subscription) - } + ml.Logger.Info("Start polling for header blocks", "pollInterval", helper.GetConfig().CheckpointerPollInterval) + + go ml.StartPolling(ctx, helper.GetConfig().CheckpointerPollInterval, true, nil) // subscribed to new head ml.Logger.Info("Subscribed to new head") @@ -52,7 +44,8 @@ func (ml *MaticChainListener) Start() error { } // ProcessHeader - process headerblock from maticchain -func (ml *MaticChainListener) ProcessHeader(newHeader *types.Header) { +func (ml *MaticChainListener) ProcessHeader(newBlockHeader *blockHeader) { + newHeader := newBlockHeader.header ml.Logger.Debug("New block detected", "blockNumber", newHeader.Number) // Marshall header block and publish to queue headerBytes, err := newHeader.MarshalJSON() diff --git a/bridge/setu/listener/rootchain.go b/bridge/setu/listener/rootchain.go index 523b9c52..b098e6bd 100644 --- a/bridge/setu/listener/rootchain.go +++ b/bridge/setu/listener/rootchain.go @@ -7,13 +7,14 @@ import ( "strconv" "time" + "github.com/ethereum/go-ethereum/rpc" + hmtypes "github.com/maticnetwork/heimdall/types" "github.com/RichardKnop/machinery/v1/tasks" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" ethCommon "github.com/ethereum/go-ethereum/common" - ethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/maticnetwork/heimdall/bridge/setu/util" chainmanagerTypes "github.com/maticnetwork/heimdall/chainmanager/types" "github.com/maticnetwork/heimdall/helper" @@ -102,16 +103,15 @@ func (rl *RootChainListener) Start() error { // start header process go rl.StartHeaderProcess(headerCtx) - // subscribe to new head - subscription, err := rl.chainClient.SubscribeNewHead(ctx, rl.HeaderChannel) - if err != nil { - // start go routine to poll for new header using client object - rl.Logger.Info("Start polling for root chain header blocks", - "root", rl.rootChainType, "pollInterval", rl.pollInterval) - go rl.StartPolling(ctx, rl.pollInterval, false) + if rl.rootChainType == hmtypes.RootChainTypeEth { + var number *big.Int + if util.GetFinalizedEthOpen(rl.cliCtx) { + number = big.NewInt(int64(rpc.FinalizedBlockNumber)) + } + + go rl.StartPolling(ctx, rl.pollInterval, false, number) } else { - // start go routine to listen new header using subscription - go rl.StartSubscription(ctx, subscription) + go rl.StartPolling(ctx, rl.pollInterval, false, nil) } // subscribed to new head @@ -121,7 +121,8 @@ func (rl *RootChainListener) Start() error { } // ProcessHeader - process headerblock from rootchain -func (rl *RootChainListener) ProcessHeader(newHeader *ethTypes.Header) { +func (rl *RootChainListener) ProcessHeader(newBlockHeader *blockHeader) { + newHeader := newBlockHeader.header rl.Logger.Debug("New block detected", "root", rl.rootChainType, "blockNumber", newHeader.Number) // check if heimdall is busy @@ -155,19 +156,24 @@ func (rl *RootChainListener) ProcessHeader(newHeader *ethTypes.Header) { } requiredConfirmations := rootchainContext.ChainmanagerParams.MainchainTxConfirmations latestNumber := newHeader.Number + fromBlock := latestNumber - // confirmation - confirmationBlocks := big.NewInt(0).SetUint64(requiredConfirmations) + if !newBlockHeader.isFinalized { + // confirmation + confirmationBlocks := big.NewInt(0).SetUint64(requiredConfirmations) - if latestNumber.Cmp(confirmationBlocks) <= 0 { - rl.Logger.Error("Block number less than Confirmations required", - "root", rl.rootChainType, "blockNumber", latestNumber.Uint64, "confirmationsRequired", confirmationBlocks.Uint64) - return - } - latestNumber = latestNumber.Sub(latestNumber, confirmationBlocks) + if latestNumber.Cmp(confirmationBlocks) <= 0 { + rl.Logger.Error("Block number less than Confirmations required", + "root", rl.rootChainType, "blockNumber", latestNumber.Uint64, "confirmationsRequired", confirmationBlocks.Uint64) - // default fromBlock - fromBlock := latestNumber + return + } + + latestNumber = latestNumber.Sub(latestNumber, confirmationBlocks) + + // default fromBlock + fromBlock = latestNumber + } // get last block from storage hasLastBlock, _ := rl.storageClient.Has([]byte(rl.blockKey), nil) @@ -186,7 +192,6 @@ func (rl *RootChainListener) ProcessHeader(newHeader *ethTypes.Header) { fromBlock = big.NewInt(0).SetUint64(result + 1) } } - } // to block diff --git a/bridge/setu/listener/tron.go b/bridge/setu/listener/tron.go index 5c324470..7e9fa088 100644 --- a/bridge/setu/listener/tron.go +++ b/bridge/setu/listener/tron.go @@ -72,14 +72,14 @@ func (tl *TronListener) Start() error { tl.Logger.Info("Start polling for events", "pollInterval", pollInterval) // poll for new header using client object - go tl.StartPolling(headerCtx, pollInterval, false) + go tl.StartPolling(headerCtx, pollInterval, false, nil) return nil } // startPolling starts polling // needAlign is used to decide whether the ticker is align to 1970 UTC. // if true, the ticker will always tick as it begins at 1970 UTC. -func (tl *TronListener) StartPolling(ctx context.Context, pollInterval time.Duration, needAlign bool) { +func (tl *TronListener) StartPolling(ctx context.Context, pollInterval time.Duration, needAlign bool, number *big.Int) { // How often to fire the passed in function in second interval := pollInterval firstInterval := interval @@ -105,8 +105,10 @@ func (tl *TronListener) StartPolling(ctx context.Context, pollInterval time.Dura headerNum, err := tl.contractConnector.GetTronLatestBlockNumber() if err == nil { // send data to channel - tl.HeaderChannel <- &(ethTypes.Header{ - Number: big.NewInt(headerNum), + tl.HeaderChannel <- &(blockHeader{ + header: ðTypes.Header{ + Number: big.NewInt(headerNum), + }, }) } @@ -119,7 +121,8 @@ func (tl *TronListener) StartPolling(ctx context.Context, pollInterval time.Dura } // ProcessHeader - process headerblock from rootchain -func (tl *TronListener) ProcessHeader(newHeader *ethTypes.Header) { +func (tl *TronListener) ProcessHeader(newBlockHeader *blockHeader) { + newHeader := newBlockHeader.header tl.Logger.Debug("New block detected", "blockNumber", newHeader.Number) busyLimit := helper.GetConfig().TronUnconfirmedTxsBusyLimit @@ -143,13 +146,16 @@ func (tl *TronListener) ProcessHeader(newHeader *ethTypes.Header) { latestNumber := newHeader.Number // confirmation confirmationBlocks := big.NewInt(int64(chainManagerParams.TronchainTxConfirmations)) + if !newBlockHeader.isFinalized { + if latestNumber.Cmp(confirmationBlocks) <= 0 { + tl.Logger.Error("Block number less than Confirmations required", "blockNumber", + latestNumber.Uint64, "confirmationsRequired", confirmationBlocks.Uint64) - if latestNumber.Cmp(confirmationBlocks) <= 0 { - tl.Logger.Error("Block number less than Confirmations required", "blockNumber", latestNumber.Uint64, "confirmationsRequired", confirmationBlocks.Uint64) - return - } - latestNumber = latestNumber.Sub(latestNumber, confirmationBlocks) + return + } + latestNumber = latestNumber.Sub(latestNumber, confirmationBlocks) + } // default fromBlock fromBlock := latestNumber diff --git a/bridge/setu/util/common.go b/bridge/setu/util/common.go index 7f2d4457..b3fd47e7 100644 --- a/bridge/setu/util/common.go +++ b/bridge/setu/util/common.go @@ -570,6 +570,17 @@ func GetDynamicCheckpointFeature(cliCtx cliContext.CLIContext) (*featureManagerT return GetTargetFeatureConfig(cliCtx, featureManagerTypes.DynamicCheckpoint) } +func GetFinalizedEthOpen(cliCtx cliContext.CLIContext) bool { + feature, err := GetTargetFeatureConfig(cliCtx, featureManagerTypes.FinalizedEth) + if err != nil { + logger.Error("Error fetching finalized root chain feature", "err", err) + + return false + } + + return feature.IsOpen +} + // GetTargetFeatureConfig return target feature config. func GetTargetFeatureConfig( cliCtx cliContext.CLIContext, feature string, diff --git a/checkpoint/client/cli/query.go b/checkpoint/client/cli/query.go index 53d8caae..c8ebcaa7 100644 --- a/checkpoint/client/cli/query.go +++ b/checkpoint/client/cli/query.go @@ -35,7 +35,7 @@ func GetQueryCmd(cdc *codec.Codec) *cobra.Command { GetQueryParams(cdc), GetCheckpointBuffer(cdc), GetLastNoACK(cdc), - GetHeaderFromIndex(cdc), + GetCheckpointByNumber(cdc), GetCheckpointCount(cdc), GetQueryActivateHeight(cdc), )..., @@ -140,11 +140,11 @@ func GetLastNoACK(cdc *codec.Codec) *cobra.Command { return cmd } -// GetHeaderFromIndex get checkpoint given header index -func GetHeaderFromIndex(cdc *codec.Codec) *cobra.Command { +// GetCheckpointByNumber get checkpoint given number. +func GetCheckpointByNumber(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "header", - Short: "get checkpoint (header) from index", + Short: "get checkpoint (header) by number", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) headerNumber := viper.GetUint64(FlagHeaderNumber) @@ -168,7 +168,7 @@ func GetHeaderFromIndex(cdc *codec.Codec) *cobra.Command { cmd.Flags().Uint64(FlagHeaderNumber, 0, "--header=") if err := cmd.MarkFlagRequired(FlagHeaderNumber); err != nil { - logger.Error("GetHeaderFromIndex | MarkFlagRequired | FlagHeaderNumber", "Error", err) + logger.Error("GetCheckpointByNumber | MarkFlagRequired | FlagHeaderNumber", "Error", err) } return cmd diff --git a/checkpoint/client/cli/tx.go b/checkpoint/client/cli/tx.go index 2d81cde5..ae074ead 100644 --- a/checkpoint/client/cli/tx.go +++ b/checkpoint/client/cli/tx.go @@ -229,7 +229,12 @@ func SendCheckpointACKTx(cdc *codec.Codec) *cobra.Command { var rootChainAddress common.Address switch rootChain { case hmTypes.RootChainTypeEth, hmTypes.RootChainTypeBsc: - receipt, err = contractCallerObj.GetConfirmedTxReceipt(txHash.EthHash(), chainmanagerParams.MainchainTxConfirmations, rootChain) + finalizedEthOpen := false + if rootChain == hmTypes.RootChainTypeEth { + finalizedEthOpen = util.GetFinalizedEthOpen(cliCtx) + } + receipt, err = contractCallerObj.GetConfirmedTxReceipt(txHash.EthHash(), + chainmanagerParams.MainchainTxConfirmations, rootChain, finalizedEthOpen) if err != nil || receipt == nil { return errors.New("transaction is not confirmed yet. Please wait for sometime and try again") } diff --git a/checkpoint/client/rest/query.go b/checkpoint/client/rest/query.go index cf96d491..7bd4acc1 100644 --- a/checkpoint/client/rest/query.go +++ b/checkpoint/client/rest/query.go @@ -46,6 +46,17 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { r.HandleFunc("/checkpoints/{root}/{number}", checkpointByNumberHandlerFunc(cliCtx)).Methods("GET") } +// nolint: tagliatelle +type CheckpointWithID struct { + ID uint64 `json:"id"` + Proposer hmTypes.HeimdallAddress `json:"proposer"` + StartBlock uint64 `json:"start_block"` + EndBlock uint64 `json:"end_block"` + RootHash hmTypes.HeimdallHash `json:"root_hash"` + BorChainID string `json:"bor_chain_id"` + TimeStamp uint64 `json:"timestamp"` +} + // HTTP request handler to query the auth params values func paramsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { @@ -160,7 +171,9 @@ func checkpointCountHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { // RestLogger.Debug("Fetching number of checkpoints from state") - ackCountBytes, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAckCount), queryParams) + + ackCountBytes, height, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAckCount), queryParams) if err != nil { hmRest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return @@ -391,7 +404,9 @@ func overviewHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { // last no ack var lastNoACKTime uint64 - lastNoACKBytes, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryLastNoAck), nil) + + lastNoACKBytes, height, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryLastNoAck), nil) if err == nil { // check content if ok := hmRest.ReturnNotFoundIfNoContent(w, lastNoACKBytes, "No last-no-ack count found"); ok { @@ -420,6 +435,8 @@ func overviewHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { hmRest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } + + cliCtx = cliCtx.WithHeight(height) rest.PostProcessResponse(w, cliCtx, result) } } @@ -450,7 +467,8 @@ func latestCheckpointHandlerFunc(cliCtx context.CLIContext) http.HandlerFunc { // Get ack count // - ackcountBytes, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAckCount), queryParams) + ackcountBytes, height, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAckCount), queryParams) if err != nil { hmRest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -485,7 +503,8 @@ func latestCheckpointHandlerFunc(cliCtx context.CLIContext) http.HandlerFunc { // Get checkpoint // - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCheckpoint), queryParams) + res, _, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCheckpoint), queryParams) if err != nil { hmRest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -496,7 +515,37 @@ func latestCheckpointHandlerFunc(cliCtx context.CLIContext) http.HandlerFunc { return } - rest.PostProcessResponse(w, cliCtx, res) + var checkpointUnmarshal hmTypes.Checkpoint + if err = json.Unmarshal(res, &checkpointUnmarshal); err != nil { + hmRest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + + return + } + + checkpointWithID := &CheckpointWithID{ + ID: ackCount, + Proposer: checkpointUnmarshal.Proposer, + StartBlock: checkpointUnmarshal.StartBlock, + EndBlock: checkpointUnmarshal.EndBlock, + RootHash: checkpointUnmarshal.RootHash, + BorChainID: checkpointUnmarshal.BorChainID, + TimeStamp: checkpointUnmarshal.TimeStamp, + } + + resWithID, err := json.Marshal(checkpointWithID) + if err != nil { + hmRest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + + return + } + + // error if no checkpoint found + if ok := hmRest.ReturnNotFoundIfNoContent(w, resWithID, "No checkpoint found"); !ok { + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, resWithID) } } @@ -529,7 +578,8 @@ func checkpointByNumberHandlerFunc(cliCtx context.CLIContext) http.HandlerFunc { } // query checkpoint - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCheckpoint), queryParams) + res, height, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCheckpoint), queryParams) if err != nil { hmRest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -540,7 +590,32 @@ func checkpointByNumberHandlerFunc(cliCtx context.CLIContext) http.HandlerFunc { return } - rest.PostProcessResponse(w, cliCtx, res) + var checkpointUnmarshal hmTypes.Checkpoint + if err = json.Unmarshal(res, &checkpointUnmarshal); err != nil { + hmRest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + + return + } + + checkpointWithID := &CheckpointWithID{ + ID: number, + Proposer: checkpointUnmarshal.Proposer, + StartBlock: checkpointUnmarshal.StartBlock, + EndBlock: checkpointUnmarshal.EndBlock, + RootHash: checkpointUnmarshal.RootHash, + BorChainID: checkpointUnmarshal.BorChainID, + TimeStamp: checkpointUnmarshal.TimeStamp, + } + + resWithID, err := json.Marshal(checkpointWithID) + if err != nil { + hmRest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, resWithID) } } @@ -582,7 +657,8 @@ func checkpointListhandlerFn( } // query checkpoint - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCheckpointList), queryParams) + res, height, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCheckpointList), queryParams) if err != nil { hmRest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -593,6 +669,7 @@ func checkpointListhandlerFn( return } + cliCtx = cliCtx.WithHeight(height) rest.PostProcessResponse(w, cliCtx, res) } } @@ -654,7 +731,8 @@ func checkpointActivationHeightHandlerFunc(cliCtx context.CLIContext) http.Handl } // query checkpoint - activationHeightBytes, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCheckpointActivation), queryParams) + activationHeightBytes, height, err := cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryCheckpointActivation), queryParams) if err != nil { hmRest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -676,6 +754,7 @@ func checkpointActivationHeightHandlerFunc(cliCtx context.CLIContext) http.Handl return } + cliCtx = cliCtx.WithHeight(height) rest.PostProcessResponse(w, cliCtx, result) } } diff --git a/clerk/querier.go b/clerk/querier.go index a75f82bb..4531571e 100644 --- a/clerk/querier.go +++ b/clerk/querier.go @@ -4,6 +4,9 @@ import ( "encoding/json" "fmt" + featuremanagerTypes "github.com/maticnetwork/heimdall/featuremanager/types" + "github.com/maticnetwork/heimdall/featuremanager/util" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" ethTypes "github.com/ethereum/go-ethereum/core/types" @@ -100,18 +103,21 @@ func handleQueryRecordSequence(ctx sdk.Context, req abci.RequestQuery, keeper Ke chainParams := keeper.chainKeeper.GetParams(ctx) var receipt *ethTypes.Receipt var err error + // get main tx receipt switch params.RootChainType { case hmTypes.RootChainTypeEth: + targetFeature := util.GetFeatureConfig().GetFeature(ctx, featuremanagerTypes.FinalizedEth) + finalizedEth := targetFeature.IsOpen receipt, err = contractCallerObj.GetConfirmedTxReceipt(hmTypes.HexToHeimdallHash(params.TxHash).EthHash(), - chainParams.MainchainTxConfirmations, hmTypes.RootChainTypeEth) + chainParams.MainchainTxConfirmations, hmTypes.RootChainTypeEth, finalizedEth) case hmTypes.RootChainTypeBsc: bscChain, err := keeper.chainKeeper.GetChainParams(ctx, hmTypes.RootChainTypeBsc) if err != nil { return nil, sdk.ErrInternal(fmt.Sprintf("wrong chain type = " + params.RootChainType + "plealse pass correct chainType like bsc")) } receipt, err = contractCallerObj.GetConfirmedTxReceipt(hmTypes.HexToHeimdallHash(params.TxHash).EthHash(), - bscChain.TxConfirmations, hmTypes.RootChainTypeBsc) + bscChain.TxConfirmations, hmTypes.RootChainTypeBsc, false) case hmTypes.RootChainTypeTron: receipt, err = contractCallerObj.GetTronTransactionReceipt(hmTypes.HexToHeimdallHash(params.TxHash).TronHash().Hex()) default: diff --git a/clerk/side_handler.go b/clerk/side_handler.go index f8c688d8..4111a96c 100644 --- a/clerk/side_handler.go +++ b/clerk/side_handler.go @@ -5,6 +5,9 @@ import ( "math/big" "strconv" + featuremanagerTypes "github.com/maticnetwork/heimdall/featuremanager/types" + "github.com/maticnetwork/heimdall/featuremanager/util" + ethCommon "github.com/ethereum/go-ethereum/common" ethTypes "github.com/ethereum/go-ethereum/core/types" @@ -66,10 +69,13 @@ func SideHandleMsgEventRecord(ctx sdk.Context, k Keeper, msg types.MsgEventRecor var err error // get main tx receipt var contractAddress ethCommon.Address + switch msg.RootChainType { case hmTypes.RootChainTypeEth: + targetFeature := util.GetFeatureConfig().GetFeature(ctx, featuremanagerTypes.FinalizedEth) + finalizedEth := targetFeature.IsOpen receipt, err = contractCaller.GetConfirmedTxReceipt(msg.TxHash.EthHash(), params.MainchainTxConfirmations, - hmTypes.RootChainTypeEth) + hmTypes.RootChainTypeEth, finalizedEth) if err != nil || receipt == nil { return hmCommon.ErrorSideTx(k.Codespace(), common.CodeWaitFrConfirmation) } @@ -81,7 +87,7 @@ func SideHandleMsgEventRecord(ctx sdk.Context, k Keeper, msg types.MsgEventRecor return hmCommon.ErrorSideTx(k.Codespace(), common.CodeWrongRootChainType) } receipt, err = contractCaller.GetConfirmedTxReceipt(msg.TxHash.EthHash(), bscChain.TxConfirmations, - hmTypes.RootChainTypeBsc) + hmTypes.RootChainTypeBsc, false) if err != nil || receipt == nil { return hmCommon.ErrorSideTx(k.Codespace(), common.CodeWaitFrConfirmation) } diff --git a/cmd/deliveryd/main.go b/cmd/deliveryd/main.go index 39575c1c..d2d8c46f 100644 --- a/cmd/deliveryd/main.go +++ b/cmd/deliveryd/main.go @@ -16,13 +16,13 @@ import ( "time" "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" ethCommon "github.com/ethereum/go-ethereum/common" + bridgeCmd "github.com/maticnetwork/heimdall/bridge/cmd" "github.com/spf13/cobra" "github.com/spf13/viper" abci "github.com/tendermint/tendermint/abci/types" @@ -44,7 +44,6 @@ import ( "github.com/maticnetwork/heimdall/app" authTypes "github.com/maticnetwork/heimdall/auth/types" - hmbridge "github.com/maticnetwork/heimdall/bridge/cmd" "github.com/maticnetwork/heimdall/cmd/deliveryd/rollback" "github.com/maticnetwork/heimdall/helper" @@ -159,7 +158,7 @@ func main() { rootCmd.AddCommand(showAccountCmd()) rootCmd.AddCommand(showPrivateKeyCmd()) rootCmd.AddCommand(restServer.ServeCommands(cdc, restServer.RegisterRoutes)) - rootCmd.AddCommand(hmbridge.BridgeCommands()) + rootCmd.AddCommand(bridgeCmd.BridgeCommands(viper.GetViper(), logger, "main")) rootCmd.AddCommand(VerifyGenesis(ctx, cdc)) rootCmd.AddCommand(initCmd(ctx, cdc)) rootCmd.AddCommand(testnetCmd(ctx, cdc)) @@ -213,18 +212,42 @@ which accepts a path for the resulting pprof file. `, RunE: func(cmd *cobra.Command, args []string) error { ctx.Logger.Info("starting ABCI with Tendermint") - err := startInProcess(shutdownCtx, ctx, appCreator, cdc) + + startRestServer, _ := cmd.Flags().GetBool(helper.RestServerFlag) + startBridge, _ := cmd.Flags().GetBool(helper.BridgeFlag) + + err := startInProcess(shutdownCtx, ctx, cmd, appCreator, cdc, startRestServer, startBridge) return err }, } + cmd.Flags().Bool( + helper.RestServerFlag, + false, + "Start rest service", + ) + + cmd.Flags().Bool( + helper.BridgeFlag, + false, + "Start bridge service", + ) + cmd.PersistentFlags().String(helper.LogLevel, ctx.Config.LogLevel, "Log level") if err := viper.BindPFlag(helper.LogLevel, cmd.PersistentFlags().Lookup(helper.LogLevel)); err != nil { logger.Error("main | BindPFlag | helper.LogLevel", "Error", err) } + // bridge flags = start flags (all, only) + root bridge cmd flags. + cmd.Flags().Bool("all", false, "start all bridge services") + cmd.Flags().StringSlice("only", []string{}, "comma separated bridge services to start") + bridgeCmd.DecorateWithBridgeRootFlags(cmd, viper.GetViper(), logger, "main") + + // rest server flags + restServer.DecorateWithRestFlags(cmd) + // core flags for the ABCI application cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address") cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file") @@ -241,9 +264,6 @@ which accepts a path for the resulting pprof file. cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file") - // delivery flags - cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to") - cmd.Flags().String(client.FlagNode, helper.DefaultTendermintNode, "Address of the node to connect to") cmd.Flags().String(helper.FlagClientHome, helper.DefaultCLIHome, "client's home directory") // add support for all Tendermint-specific command line options @@ -253,8 +273,8 @@ which accepts a path for the resulting pprof file. } // nolint: cyclop -func startInProcess(shutdownCtx context.Context, ctx *server.Context, - appCreator server.AppCreator, cdc *codec.Codec, +func startInProcess(shutdownCtx context.Context, ctx *server.Context, cmd *cobra.Command, + appCreator server.AppCreator, cdc *codec.Codec, startRestServer bool, startBridge bool, ) error { cfg := ctx.Config home := cfg.RootDir @@ -334,9 +354,30 @@ func startInProcess(shutdownCtx context.Context, ctx *server.Context, } // using group context makes sense in case that if one of - // the processes produces error the rest will go and shutdown + // the processes produces error the rest will go and shutdown. errGroup, gCtx := errgroup.WithContext(shutdownCtx) + // start rest server. + if startRestServer { + waitForRest := make(chan struct{}) + + errGroup.Go(func() error { + return restServer.StartRestServer(gCtx, cdc, restServer.RegisterRoutes, waitForRest) + }) + + // Start rest server first, then start bridge. + <-waitForRest + } + + // start bridge. + if startBridge { + bridgeCmd.AdjustBridgeDBValue(cmd, viper.GetViper()) + + errGroup.Go(func() error { + return bridgeCmd.StartBridge(gCtx, false) + }) + } + // stop phase for Tendermint node errGroup.Go(func() error { // wait here for interrupt signal or diff --git a/featuremanager/keeper.go b/featuremanager/keeper.go index c2f8279e..534ddad7 100644 --- a/featuremanager/keeper.go +++ b/featuremanager/keeper.go @@ -63,6 +63,8 @@ func (k Keeper) RegisterFeature() { // all new type of features should be registered here. k.addFeature("feature-x") k.addFeature(types.DynamicCheckpoint) + k.addFeature(types.SupportMapMarshaling) + k.addFeature(types.FinalizedEth) } func (k Keeper) HasFeature(feature string) bool { diff --git a/featuremanager/types/keys.go b/featuremanager/types/keys.go index 54650a2a..06600f78 100644 --- a/featuremanager/types/keys.go +++ b/featuremanager/types/keys.go @@ -18,5 +18,7 @@ const ( ) const ( - DynamicCheckpoint = "DynamicCheckpoint" + SupportMapMarshaling = "SupportMapMarshaling" + DynamicCheckpoint = "DynamicCheckpoint" + FinalizedEth = "FinalizedEth" ) diff --git a/go.mod b/go.mod index c4ae0faf..760399c0 100644 --- a/go.mod +++ b/go.mod @@ -149,4 +149,4 @@ replace github.com/tendermint/tendermint => github.com/maticnetwork/tendermint v replace github.com/cosmos/cosmos-sdk => github.com/maticnetwork/cosmos-sdk v0.37.5-0.20220311095845-81690c6a53e7 -replace github.com/ethereum/go-ethereum => github.com/maticnetwork/bor v0.2.16 +replace github.com/ethereum/go-ethereum => github.com/maticnetwork/bor v0.2.18-0.20220922050621-c91d4ca1fa4f diff --git a/go.sum b/go.sum index fefc1965..ec52181b 100644 --- a/go.sum +++ b/go.sum @@ -503,6 +503,8 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsI github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/maticnetwork/bor v0.2.16 h1:pz3MNdTotTWlcFXKnTSDolJ3VEmrYL+sqz6FsaWtJAQ= github.com/maticnetwork/bor v0.2.16/go.mod h1:tskr68Tlk2R6NLBQW2gaiQKx3BCdRvsGkcnhFUPKyh4= +github.com/maticnetwork/bor v0.2.18-0.20220922050621-c91d4ca1fa4f h1:GJwlDTapZGPFhVNjflv2lOPSp0qB0p2PJ137qbaJRcY= +github.com/maticnetwork/bor v0.2.18-0.20220922050621-c91d4ca1fa4f/go.mod h1:tskr68Tlk2R6NLBQW2gaiQKx3BCdRvsGkcnhFUPKyh4= github.com/maticnetwork/cosmos-sdk v0.37.5-0.20220311095845-81690c6a53e7 h1:8NoEtDFvY0r9KTow/jgEwOfTCPnXOs6MlEdUhRUQY78= github.com/maticnetwork/cosmos-sdk v0.37.5-0.20220311095845-81690c6a53e7/go.mod h1:uW55Ru86N5o3L8SVkVL1TPE+mV/WRM2la8sC3TR/Ajc= github.com/maticnetwork/tendermint v0.26.0-dev0.0.20220803122020-9c20f6000900 h1:mtI3GQCzvC5tRIGSUKo+PfS1FIUAGmMGXHUMxhPsV68= diff --git a/helper/call.go b/helper/call.go index 782fe4af..8b6d5b11 100644 --- a/helper/call.go +++ b/helper/call.go @@ -34,6 +34,9 @@ import ( hmTypes "github.com/maticnetwork/heimdall/types" ) +// ContractsABIsMap is a cached map holding the ABIs of the contracts. +var ContractsABIsMap = make(map[string]*abi.ABI) + // IContractCaller represents contract caller type IContractCaller interface { GetHeaderInfo(headerID uint64, rootChainInstance *rootchain.Rootchain, childBlockInterval uint64) (root common.Hash, start, end, createdAt uint64, proposer types.HeimdallAddress, err error) @@ -48,7 +51,7 @@ type IContractCaller interface { GetCheckpointSign(txHash common.Hash) ([]byte, []byte, []byte, error) GetMainChainBlock(*big.Int, string) (*ethTypes.Header, error) GetMaticChainBlock(*big.Int) (*ethTypes.Header, error) - GetConfirmedTxReceipt(common.Hash, uint64, string) (*ethTypes.Receipt, error) + GetConfirmedTxReceipt(common.Hash, uint64, string, bool) (*ethTypes.Receipt, error) GetBlockNumberFromTxHash(common.Hash) (*big.Int, error) // decode header event @@ -141,6 +144,10 @@ type rpcTransaction struct { txExtraInfo } +const ( + LRUCapacity = 5000 +) + // NewContractCaller contract caller func NewContractCaller() (contractCallerObj ContractCaller, err error) { contractCallerObj.MainChainClient = GetMainClient() @@ -150,47 +157,20 @@ func NewContractCaller() (contractCallerObj ContractCaller, err error) { contractCallerObj.MainChainRPC = GetMainChainRPCClient() contractCallerObj.BscChainRPC = GetBscChainRPCClient() contractCallerObj.MaticChainRPC = GetMaticRPCClient() - contractCallerObj.ReceiptCache, _ = NewLru(5000) + contractCallerObj.ReceiptCache, err = NewLru(LRUCapacity) - // - // ABIs - // - - if contractCallerObj.RootChainABI, err = getABI(string(rootchain.RootchainABI)); err != nil { - return - } - - if contractCallerObj.StakingInfoABI, err = getABI(string(stakinginfo.StakinginfoABI)); err != nil { - return - } - - if contractCallerObj.ValidatorSetABI, err = getABI(string(validatorset.ValidatorsetABI)); err != nil { - return - } - - if contractCallerObj.StateReceiverABI, err = getABI(string(statereceiver.StatereceiverABI)); err != nil { - return - } - - if contractCallerObj.StateSenderABI, err = getABI(string(statesender.StatesenderABI)); err != nil { - return - } - - if contractCallerObj.StakeManagerABI, err = getABI(string(stakemanager.StakemanagerABI)); err != nil { - return + if err != nil { + return contractCallerObj, err } - if contractCallerObj.SlashManagerABI, err = getABI(string(slashmanager.SlashmanagerABI)); err != nil { - return - } + contractCallerObj.ContractInstanceCache = make(map[string]interface{}) - if contractCallerObj.MaticTokenABI, err = getABI(string(erc20.Erc20ABI)); err != nil { - return + // package global cache (string->ABI) + if err = populateABIs(&contractCallerObj); err != nil { + return contractCallerObj, err } - contractCallerObj.ContractInstanceCache = make(map[string]interface{}) - - return + return contractCallerObj, nil } // GetRootChainInstance returns RootChain contract instance for selected base chain @@ -435,6 +415,18 @@ func (c *ContractCaller) GetMainChainBlock(blockNum *big.Int, rootChain string) return latestBlock, nil } +func (c *ContractCaller) GetEthFinalizedBlock() (header *ethTypes.Header, err error) { + finalizedHeader, err := c.MainChainClient.HeaderByNumber(context.Background(), + big.NewInt(int64(rpc.FinalizedBlockNumber))) + if err != nil { + Logger.Error("Unable to connect to main chain", "Error", err) + + return + } + + return finalizedHeader, nil +} + // GetMaticChainBlock returns child chain block header func (c *ContractCaller) GetMaticChainBlock(blockNum *big.Int) (header *ethTypes.Header, err error) { latestBlock, err := c.MaticChainClient.HeaderByNumber(context.Background(), blockNum) @@ -481,8 +473,9 @@ func (c *ContractCaller) GetLogs(fromBlock *big.Int, toBlock *big.Int, addrs []c } // GetConfirmedTxReceipt returns confirmed tx receipt -func (c *ContractCaller) GetConfirmedTxReceipt(tx common.Hash, requiredConfirmations uint64, rootChain string) (*ethTypes.Receipt, error) { - +func (c *ContractCaller) GetConfirmedTxReceipt(tx common.Hash, requiredConfirmations uint64, + rootChain string, ethFinalizedOpen bool, +) (*ethTypes.Receipt, error) { var receipt *ethTypes.Receipt = nil receiptCache, ok := c.ReceiptCache.Get(tx.String()) @@ -501,27 +494,48 @@ func (c *ContractCaller) GetConfirmedTxReceipt(tx common.Hash, requiredConfirmat receipt, _ = receiptCache.(*ethTypes.Receipt) } + receiptBlockNumber := receipt.BlockNumber.Uint64() Logger.Debug("Tx included in block", "root", rootChain, "block", receipt.BlockNumber.Uint64(), "tx", tx) - latestBlkNumber := c.LatestBlockCache[rootChain] - if latestBlkNumber-receipt.BlockNumber.Uint64() >= requiredConfirmations { - Logger.Debug("receipt block is confirmed by cache", - "root", rootChain, "latestBlockCached", latestBlkNumber, "receiptBlock", receipt.BlockNumber.Uint64()) - return receipt, nil - } - // get main chain block - latestBlk, err := c.GetMainChainBlock(nil, rootChain) - if err != nil { - Logger.Error("error getting latest block from main chain", "Error", err) - return nil, err - } - Logger.Debug("Latest block on main chain obtained", "root", rootChain, "Block", latestBlk.Number.Uint64()) - c.LatestBlockCache[rootChain] = latestBlk.Number.Uint64() - diff := latestBlk.Number.Uint64() - receipt.BlockNumber.Uint64() - if diff < requiredConfirmations { - return nil, errors.New("not enough confirmations") + var latestFinalizedBlock *ethTypes.Header + + if rootChain == hmTypes.RootChainTypeEth && ethFinalizedOpen { + var err error + + latestFinalizedBlock, err = c.GetEthFinalizedBlock() + if err != nil { + Logger.Error("error getting latest finalized block from main chain", "error", err) + } } + if latestFinalizedBlock != nil { + if receiptBlockNumber > latestFinalizedBlock.Number.Uint64() { + Logger.Error("receipt block is not finalized", "receiptBlockNumber", receiptBlockNumber) + + return nil, errors.New("not enough confirmations") + } + } else { + latestBlkNumber := c.LatestBlockCache[rootChain] + if latestBlkNumber-receipt.BlockNumber.Uint64() >= requiredConfirmations { + Logger.Debug("receipt block is confirmed by cache", + "root", rootChain, "latestBlockCached", latestBlkNumber, "receiptBlock", receipt.BlockNumber.Uint64()) + + return receipt, nil + } + // get main chain block + latestBlk, err := c.GetMainChainBlock(nil, rootChain) + if err != nil { + Logger.Error("error getting latest block from main chain", "Error", err) + + return nil, err + } + Logger.Debug("Latest block on main chain obtained", "root", rootChain, "Block", latestBlk.Number.Uint64()) + c.LatestBlockCache[rootChain] = latestBlk.Number.Uint64() + diff := latestBlk.Number.Uint64() - receipt.BlockNumber.Uint64() + if diff < requiredConfirmations { + return nil, errors.New("not enough confirmations") + } + } return receipt, nil } @@ -736,7 +750,6 @@ func (c *ContractCaller) DecodeUnJailedEvent(contractAddress common.Address, rec // CurrentAccountStateRoot get current account root from on chain func (c *ContractCaller) CurrentAccountStateRoot(stakingInfoInstance *stakinginfo.Stakinginfo) ([32]byte, error) { accountStateRoot, err := stakingInfoInstance.GetAccountStateRoot(nil) - if err != nil { Logger.Error("Unable to get current account state roor", "Error", err) var emptyArr [32]byte @@ -819,9 +832,10 @@ func (c *ContractCaller) GetMaticTxReceipt(txHash common.Hash) (*ethTypes.Receip func (c *ContractCaller) getTxReceipt(client *ethclient.Client, txHash common.Hash) (*ethTypes.Receipt, error) { return client.TransactionReceipt(context.Background(), txHash) } + func (c *ContractCaller) GetTronTransactionReceipt(txID string) (*ethTypes.Receipt, error) { // create filter - var txIDs = []string{txID} + txIDs := []string{txID} queryFilter := tron.FilterOtherParams{ BaseQueryParam: tron.GetDefaultBaseParm(), Method: tron.GetTransactionByHash, @@ -843,6 +857,76 @@ func (c *ContractCaller) GetTronTransactionReceipt(txID string) (*ethTypes.Recei return &transactionReceipt.Result, nil } +// utility and helper methods + +// populateABIs fills the package level cache for contracts' ABIs +// When called the first time, ContractsABIsMap will be filled and getABI method won't be invoked the next times +// This reduces the number of calls to json decode methods made by the contract caller +// It uses ABIs' definitions instead of contracts addresses, as the latter might not be available at init time. +func populateABIs(contractCallerObj *ContractCaller) error { + var ccAbi *abi.ABI + + var err error + + contractsABIs := [8]string{ + rootchain.RootchainABI, stakinginfo.StakinginfoABI, validatorset.ValidatorsetABI, + statereceiver.StatereceiverABI, statesender.StatesenderABI, stakemanager.StakemanagerABI, + slashmanager.SlashmanagerABI, erc20.Erc20ABI, + } + + // iterate over supported ABIs + for _, contractABI := range contractsABIs { + ccAbi, err = chooseContractCallerABI(contractCallerObj, contractABI) + if err != nil { + Logger.Error("Error while fetching contract caller ABI", "error", err) + + return err + } + + if ContractsABIsMap[contractABI] == nil { + // fills cached abi map + if *ccAbi, err = getABI(contractABI); err != nil { + Logger.Error("Error while getting ABI for contract caller", "name", contractABI, "error", err) + + return err + } + + ContractsABIsMap[contractABI] = ccAbi + + Logger.Debug("ABI initialized", "name", contractABI) + } else { + // use cached abi + *ccAbi = *ContractsABIsMap[contractABI] + } + } + + return nil +} + +// chooseContractCallerABI extracts and returns the abo.ABI object from the contractCallerObj based on its abi string. +func chooseContractCallerABI(contractCallerObj *ContractCaller, abi string) (*abi.ABI, error) { + switch abi { + case rootchain.RootchainABI: + return &contractCallerObj.RootChainABI, nil + case stakinginfo.StakinginfoABI: + return &contractCallerObj.StakingInfoABI, nil + case validatorset.ValidatorsetABI: + return &contractCallerObj.ValidatorSetABI, nil + case statereceiver.StatereceiverABI: + return &contractCallerObj.StateReceiverABI, nil + case statesender.StatesenderABI: + return &contractCallerObj.StateSenderABI, nil + case stakemanager.StakemanagerABI: + return &contractCallerObj.StakeManagerABI, nil + case slashmanager.SlashmanagerABI: + return &contractCallerObj.SlashManagerABI, nil + case erc20.Erc20ABI: + return &contractCallerObj.MaticTokenABI, nil + } + + return nil, errors.New("no ABI associated with such data") +} + // // private abi methods // @@ -898,9 +982,8 @@ func (c *ContractCaller) GetTronStakingSyncNonce(validatorID uint64, stakingMana return 0 } // Unpack the results - var ( - ret0 = new(*big.Int) - ) + + ret0 := new(*big.Int) if err := c.StakeManagerABI.UnpackIntoInterface(ret0, "validatorNonce", result); err != nil { Logger.Error("Error unpack validator nonce", "error", err, "validatorId", validatorID) @@ -914,7 +997,7 @@ func (c *ContractCaller) GetTronEventsByContractAddress(address []string, from, for _, adr := range address { decodedAddress = append(decodedAddress, adr[2:]) } - //create filter + // create filter filter := tron.NewFilter{ Address: decodedAddress, FromBlock: "0x" + strconv.FormatInt(from, 16), @@ -985,7 +1068,8 @@ func (c *ContractCaller) GetStartListenBlock(rootChainType string) uint64 { } func (c *ContractCaller) GetTronHeaderInfo(headerID uint64, contractAddress string, childBlockInterval uint64) ( - root common.Hash, start, end, createdAt uint64, proposer types.HeimdallAddress, err error) { + root common.Hash, start, end, createdAt uint64, proposer types.HeimdallAddress, err error, +) { // Pack the input btsPack, err := c.RootChainABI.Pack("headerBlocks", big.NewInt(0).Mul(big.NewInt(0).SetUint64(headerID), big.NewInt(0).SetUint64(childBlockInterval))) diff --git a/helper/config.go b/helper/config.go index d70e7b73..0cf0ac7d 100644 --- a/helper/config.go +++ b/helper/config.go @@ -32,6 +32,8 @@ const ( HomeFlag = "home" FlagClientHome = "home-client" ChainFlag = "chain" + RestServerFlag = "rest-server" + BridgeFlag = "bridge" LogLevel = "log_level" SeedsFlag = "seeds" @@ -97,7 +99,8 @@ const ( DefaultBttcChainID string = "15001" - DefaultChain = "mainnet" + DefaultLogsType = "json" + DefaultChain = "mainnet" secretFilePerm = 0600 ) diff --git a/helper/mocks/IContractCaller.go b/helper/mocks/IContractCaller.go index 967764eb..2a6d84c3 100644 --- a/helper/mocks/IContractCaller.go +++ b/helper/mocks/IContractCaller.go @@ -456,7 +456,7 @@ func (_m *IContractCaller) GetCheckpointSign(txHash common.Hash) ([]byte, []byte } // GetConfirmedTxReceipt provides a mock function with given fields: _a0, _a1, _a2 -func (_m *IContractCaller) GetConfirmedTxReceipt(_a0 common.Hash, _a1 uint64, _a2 string) (*types.Receipt, error) { +func (_m *IContractCaller) GetConfirmedTxReceipt(_a0 common.Hash, _a1 uint64, _a2 string, _a3 bool) (*types.Receipt, error) { ret := _m.Called(_a0, _a1, _a2) var r0 *types.Receipt diff --git a/helper/tx.go b/helper/tx.go index 5dc15996..b28954f9 100644 --- a/helper/tx.go +++ b/helper/tx.go @@ -120,6 +120,7 @@ func (c *ContractCaller) SendCheckpoint(signedData []byte, sigs [][3]*big.Int, Logger.Debug("Sending new checkpoint", "sigs", strings.Join(s, ","), "data", hex.EncodeToString(signedData), + "rootChain", rootChain, ) tx, err := rootChainInstance.SubmitCheckpoint(auth, signedData, sigs) @@ -127,7 +128,8 @@ func (c *ContractCaller) SendCheckpoint(signedData []byte, sigs [][3]*big.Int, Logger.Error("Error while submitting checkpoint", "error", err) return err } - Logger.Info("Submitted new checkpoint to rootchain successfully", "txHash", tx.Hash().String()) + + Logger.Info("Submitted new checkpoint to rootchain successfully", "rootChain", rootChain, "txHash", tx.Hash().String()) return } diff --git a/params/subspace/subspace.go b/params/subspace/subspace.go index 2c83e4c9..bec82b4e 100644 --- a/params/subspace/subspace.go +++ b/params/subspace/subspace.go @@ -21,16 +21,38 @@ const ( ParamsWithMultiChains string = "ParamsWithMultiChains" FeatureParams string = "FeatureParams" SupportFeature string = "SupportFeature" + + MainChain string = "delivery-199" + DonaoChain string = "delivery-1029" + InnerChain string = ":delivery-22125" ) // hasMap is used for marshalling and unmarshaling for map. -func hasMap(key string) bool { +func hasMap(ctx sdk.Context, s Subspace, key string) bool { switch key { case ParamsWithMultiChains: + // This is a patch fix for ParamsWithMultiChains. + // Previously, ParamsWithMultiChains use codec for marshaling but + // codec cannot marshal map in fix order which may trigger consensus issues. + + // hasMap aims to judge whether we should marshal struct with json. + // After featuremanager is activatated (SupportFeature is not empty, e.g. + // propose SupportMapMarshaling feature to activate featuremanager), + // ParamsWithMultiChains will use json format to marshal and unmarshal to + // fix above issues. + + switch ctx.ChainID() { + case MainChain, DonaoChain, InnerChain: + store := s.kvStore(ctx) + + if bz := store.Get([]byte(SupportFeature)); bz == nil { + return false + } + } + fallthrough - case FeatureParams: - fallthrough - case SupportFeature: + + case FeatureParams, SupportFeature: return true } @@ -108,7 +130,7 @@ func (s Subspace) Get(ctx sdk.Context, key []byte, ptr interface{}) { var err error - if hasMap(string(key)) { + if hasMap(ctx, s, string(key)) { err = json.Unmarshal(bz, ptr) if err != nil { err = s.cdc.UnmarshalJSON(bz, ptr) @@ -132,7 +154,7 @@ func (s Subspace) GetIfExists(ctx sdk.Context, key []byte, ptr interface{}) { var err error - if hasMap(string(key)) { + if hasMap(ctx, s, string(key)) { err = json.Unmarshal(bz, ptr) if err != nil { err = s.cdc.UnmarshalJSON(bz, ptr) @@ -192,7 +214,7 @@ func (s Subspace) Set(ctx sdk.Context, key []byte, param interface{}) { var err error - if hasMap(string(key)) { + if hasMap(ctx, s, string(key)) { data, err = json.Marshal(param) } else { data, err = s.cdc.MarshalJSON(param) diff --git a/server/root.go b/server/root.go index a9f81363..de4e4cbf 100644 --- a/server/root.go +++ b/server/root.go @@ -1,8 +1,11 @@ package server import ( + "context" + "net" "net/http" "os" + "time" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/lcd" @@ -22,6 +25,33 @@ import ( _ "github.com/maticnetwork/heimdall/server/statik" ) +const ( + healthCheckInterval = 10 * time.Millisecond +) + +func StartRestServer(mainCtx context.Context, cdc *codec.Codec, + registerRoutesFn func(*lcd.RestServer), restCh chan struct{}, +) error { + restServer := lcd.NewRestServer(cdc) + registerRoutesFn(restServer) + + go restServerHealthCheck(restCh) + + logger := tmLog.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") + + err := restServer.Start( + viper.GetString(client.FlagListenAddr), + viper.GetInt(client.FlagMaxOpenConnections), + 0, + 0, + ) + if err != nil { + logger.Error("Cannot start REST server", "Error", err) + } + + return err +} + // ServeCommands will generate a long-running rest server // (aka Light Client Daemon) that exposes functionality similar // to the cli, but over rest @@ -31,28 +61,27 @@ func ServeCommands(cdc *codec.Codec, registerRoutesFn func(*lcd.RestServer)) *co Short: "Start LCD (light-client daemon), a local REST server", RunE: func(cmd *cobra.Command, args []string) error { helper.InitDeliveryConfig("") + restCh := make(chan struct{}, 1) + err := StartRestServer(context.Background(), cdc, registerRoutesFn, restCh) - rs := lcd.NewRestServer(cdc) - registerRoutesFn(rs) - logger := tmLog.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") - err := rs.Start( - viper.GetString(client.FlagListenAddr), - viper.GetInt(client.FlagMaxOpenConnections), - 0, - 0, - ) - - logger.Info("REST server started") return err }, } + + DecorateWithRestFlags(cmd) + + return cmd +} + +// function is called whenever is the reste server flags has to be added to command. +func DecorateWithRestFlags(cmd *cobra.Command) { cmd.Flags().String(client.FlagListenAddr, "tcp://0.0.0.0:1317", "The address for the server to listen on") cmd.Flags().Bool(client.FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)") - cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to") - cmd.Flags().String(client.FlagNode, helper.DefaultTendermintNode, "Address of the node to connect to") cmd.Flags().Int(client.FlagMaxOpenConnections, 1000, "The number of maximum open connections") - return cmd + // heimdall specific flags for rest server start + cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to") + cmd.Flags().String(client.FlagNode, helper.DefaultTendermintNode, "Address of the node to connect to") } // RegisterRoutes register routes of all modules @@ -72,20 +101,6 @@ func RegisterRoutes(rs *lcd.RestServer) { // register rest routes app.ModuleBasics.RegisterRESTRoutes(rs.CliCtx, rs.Mux) - - // list all paths - // rs.Mux.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { - // t, err := route.GetPathTemplate() - // if err != nil { - // return err - // } - // r, err := route.GetMethods() - // if err != nil { - // return err - // } - // fmt.Println(strings.Join(r, ","), t) - // return nil - // }) } func registerSwaggerUI(rs *lcd.RestServer) { @@ -96,3 +111,25 @@ func registerSwaggerUI(rs *lcd.RestServer) { staticServer := http.FileServer(statikFS) rs.Mux.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) } + +// Check locally if rest server port has been opened. +func restServerHealthCheck(restCh chan struct{}) { + address := viper.GetString(client.FlagListenAddr) + + for { + conn, err := net.Dial("tcp", address[6:]) + if err != nil { + time.Sleep(healthCheckInterval) + + continue + } + + if conn != nil { + defer conn.Close() + } + + close(restCh) + + break + } +} diff --git a/staking/client/cli/tx.go b/staking/client/cli/tx.go index 490a197c..880afa6b 100644 --- a/staking/client/cli/tx.go +++ b/staking/client/cli/tx.go @@ -90,10 +90,10 @@ func SendValidatorJoinTx(cdc *codec.Codec) *cobra.Command { if err != nil { return err } - + finalizedEthOpen := util.GetFinalizedEthOpen(cliCtx) // get main tx receipt receipt, err := contractCallerObj.GetConfirmedTxReceipt(hmTypes.HexToHeimdallHash(txhash).EthHash(), - chainmanagerParams.MainchainTxConfirmations, hmTypes.RootChainTypeEth) + chainmanagerParams.MainchainTxConfirmations, hmTypes.RootChainTypeEth, finalizedEthOpen) if err != nil || receipt == nil { return errors.New("Transaction is not confirmed yet. Please wait for sometime and try again") }