From 2324802fbb268095bcd75d3287e465b9931ad42b Mon Sep 17 00:00:00 2001 From: Roman Behma <13855864+begmaroman@users.noreply.github.com> Date: Mon, 20 Jun 2022 15:44:28 +0100 Subject: [PATCH] Added a logic to self-heal StateSynced and StakeUpdate events (#837) --- bor/beginblocker.go | 3 +- bor/handler.go | 2 +- bor/keeper.go | 18 +- bridge/setu/listener/base.go | 2 +- bridge/setu/listener/rootchain.go | 255 ++++++++++---------- bridge/setu/listener/rootchain_events.go | 209 ++++++++++++++++ bridge/setu/listener/rootchain_selfheal.go | 181 ++++++++++++++ bridge/setu/processor/checkpoint.go | 2 +- bridge/setu/util/common.go | 70 ++++-- contracts/stakinginfo/stakinginfo_helper.go | 13 + contracts/statereceiver/statereceiver.abi | 230 +++++------------- contracts/statereceiver/statereceiver.go | 239 ++++-------------- contracts/statesender/statesender_helper.go | 13 + helper/backoff.go | 18 ++ helper/backoff_test.go | 46 ++++ helper/call.go | 35 ++- helper/config.go | 9 + helper/util.go | 4 +- staking/client/cli/tx.go | 2 +- 19 files changed, 816 insertions(+), 535 deletions(-) create mode 100644 bridge/setu/listener/rootchain_events.go create mode 100644 bridge/setu/listener/rootchain_selfheal.go create mode 100644 contracts/stakinginfo/stakinginfo_helper.go create mode 100644 contracts/statesender/statesender_helper.go create mode 100644 helper/backoff.go create mode 100644 helper/backoff_test.go diff --git a/bor/beginblocker.go b/bor/beginblocker.go index f6300cebb..73e6fcaf1 100644 --- a/bor/beginblocker.go +++ b/bor/beginblocker.go @@ -12,8 +12,7 @@ import ( hmTypes "github.com/maticnetwork/heimdall/types" ) -func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) { - +func BeginBlocker(ctx sdk.Context, _ abci.RequestBeginBlock, k Keeper) { if ctx.BlockHeight() == int64(helper.SpanOverrideBlockHeight) { k.Logger(ctx).Info("overriding span BeginBlocker", "height", ctx.BlockHeight()) j, ok := rest.SPAN_OVERRIDES[helper.GenesisDoc.ChainID] diff --git a/bor/handler.go b/bor/handler.go index 6eaaf3cb6..2e704d5c4 100644 --- a/bor/handler.go +++ b/bor/handler.go @@ -66,7 +66,7 @@ func HandleMsgProposeSpan(ctx sdk.Context, msg types.MsgProposeSpan, k Keeper) s spanDuration := k.GetParams(ctx).SpanDuration if spanDuration != (msg.EndBlock - msg.StartBlock + 1) { k.Logger(ctx).Error("Span duration of proposed span is wrong", - "proposedSpanDuration", (msg.EndBlock - msg.StartBlock + 1), + "proposedSpanDuration", msg.EndBlock-msg.StartBlock+1, "paramsSpanDuration", spanDuration, ) return common.ErrInvalidSpanDuration(k.Codespace()).Result() diff --git a/bor/keeper.go b/bor/keeper.go index a7fb8b9fa..488f9c8ba 100644 --- a/bor/keeper.go +++ b/bor/keeper.go @@ -11,7 +11,7 @@ import ( "github.com/maticnetwork/bor/common" "github.com/maticnetwork/heimdall/bor/types" - chainmanager "github.com/maticnetwork/heimdall/chainmanager" + "github.com/maticnetwork/heimdall/chainmanager" "github.com/maticnetwork/heimdall/helper" "github.com/maticnetwork/heimdall/params/subspace" "github.com/maticnetwork/heimdall/staking" @@ -45,7 +45,7 @@ type Keeper struct { chainKeeper chainmanager.Keeper } -// NewKeeper create new keeper +// NewKeeper is the constructor of Keeper func NewKeeper( cdc *codec.Codec, storeKey sdk.StoreKey, @@ -55,8 +55,7 @@ func NewKeeper( stakingKeeper staking.Keeper, caller helper.ContractCaller, ) Keeper { - // create keeper - keeper := Keeper{ + return Keeper{ cdc: cdc, storeKey: storeKey, paramSpace: paramSpace.WithKeyTable(types.ParamKeyTable()), @@ -65,7 +64,6 @@ func NewKeeper( sk: stakingKeeper, contractCaller: caller, } - return keeper } // Codespace returns the codespace @@ -152,9 +150,6 @@ func (k *Keeper) GetAllSpans(ctx sdk.Context) (spans []*hmTypes.Span) { func (k *Keeper) GetSpanList(ctx sdk.Context, page uint64, limit uint64) ([]hmTypes.Span, error) { store := ctx.KVStore(k.storeKey) - // create spans - var spans []hmTypes.Span - // have max limit if limit > 20 { limit = 20 @@ -164,6 +159,7 @@ func (k *Keeper) GetSpanList(ctx sdk.Context, page uint64, limit uint64) ([]hmTy iterator := hmTypes.KVStorePrefixIteratorPaginated(store, SpanPrefixKey, uint(page), uint(limit)) // loop through validators to get valid validators + var spans []hmTypes.Span for ; iterator.Valid(); iterator.Next() { var span hmTypes.Span if err := k.cdc.UnmarshalBinaryBare(iterator.Value(), &span); err == nil { @@ -251,7 +247,9 @@ func (k *Keeper) SelectNextProducers(ctx sdk.Context, seed common.Hash) (vals [] val.VotingPower = int64(value) vals = append(vals, val) } - } // sort by address + } + + // sort by address vals = hmTypes.SortValidatorByAddress(vals) return vals, nil @@ -324,7 +322,7 @@ func (k *Keeper) GetParams(ctx sdk.Context) (params types.Params) { // Utils // -// IterateSpansAndApplyFn interate spans and apply the given function. +// IterateSpansAndApplyFn iterates spans and apply the given function. func (k *Keeper) IterateSpansAndApplyFn(ctx sdk.Context, f func(span hmTypes.Span) error) { store := ctx.KVStore(k.storeKey) diff --git a/bridge/setu/listener/base.go b/bridge/setu/listener/base.go index e2165f77c..201f1bde4 100644 --- a/bridge/setu/listener/base.go +++ b/bridge/setu/listener/base.go @@ -154,7 +154,7 @@ func (bl *BaseListener) StartHeaderProcess(ctx context.Context) { } } -// startPolling starts polling +// StartPolling starts polling func (bl *BaseListener) StartPolling(ctx context.Context, pollInterval time.Duration) { // How often to fire the passed in function in second interval := pollInterval diff --git a/bridge/setu/listener/rootchain.go b/bridge/setu/listener/rootchain.go index 4667d2453..107787078 100644 --- a/bridge/setu/listener/rootchain.go +++ b/bridge/setu/listener/rootchain.go @@ -45,17 +45,18 @@ func NewRootChainListener() *RootChainListener { if err != nil { panic(err) } + abis := []*abi.ABI{ &contractCaller.RootChainABI, &contractCaller.StateSenderABI, &contractCaller.StakingInfoABI, } - rootchainListener := &RootChainListener{ + + return &RootChainListener{ abis: abis, stakingInfoAbi: &contractCaller.StakingInfoABI, stateSenderAbi: &contractCaller.StateSenderABI, } - return rootchainListener } // Start starts new block subscription @@ -83,10 +84,11 @@ func (rl *RootChainListener) Start() error { // start go routine to listen new header using subscription go rl.StartSubscription(ctx, subscription) } - - // subscribed to new head rl.Logger.Info("Subscribed to new head") + // Start self-healing process + go rl.startSelfHealing(ctx) + return nil } @@ -99,12 +101,12 @@ func (rl *RootChainListener) ProcessHeader(newHeader *types.Header) { if err != nil { return } + requiredConfirmations := rootchainContext.ChainmanagerParams.MainchainTxConfirmations latestNumber := newHeader.Number // confirmation confirmationBlocks := big.NewInt(0).SetUint64(requiredConfirmations) - if latestNumber.Cmp(confirmationBlocks) <= 0 { rl.Logger.Error("Block number less than Confirmations required", "blockNumber", latestNumber.Uint64, "confirmationsRequired", confirmationBlocks.Uint64) return @@ -131,44 +133,41 @@ func (rl *RootChainListener) ProcessHeader(newHeader *types.Header) { } } - // to block + // Prepare blocks range toBlock := latestNumber - if toBlock.Cmp(fromBlock) == -1 { fromBlock = toBlock } - // set last block to storage - if err := rl.storageClient.Put([]byte(lastRootBlockKey), []byte(toBlock.String()), nil); err != nil { + // Set last block to storage + if err = rl.storageClient.Put([]byte(lastRootBlockKey), []byte(toBlock.String()), nil); err != nil { rl.Logger.Error("rl.storageClient.Put", "Error", err) } - // query events + // Handle events rl.queryAndBroadcastEvents(rootchainContext, fromBlock, toBlock) } +// queryAndBroadcastEvents fetches supported events from the rootchain and handles all of them func (rl *RootChainListener) queryAndBroadcastEvents(rootchainContext *RootChainListenerContext, fromBlock *big.Int, toBlock *big.Int) { rl.Logger.Info("Query rootchain event logs", "fromBlock", fromBlock, "toBlock", toBlock) - // current public key - pubkey := helper.GetPubKey() - pubkeyBytes := pubkey[1:] + ctx, cancel := context.WithTimeout(context.Background(), rl.contractConnector.MainChainTimeout) + defer cancel() // get chain params chainParams := rootchainContext.ChainmanagerParams.ChainParams - // draft a query - query := ethereum.FilterQuery{FromBlock: fromBlock, ToBlock: toBlock, Addresses: []ethCommon.Address{ - chainParams.RootChainAddress.EthAddress(), - chainParams.StakingInfoAddress.EthAddress(), - chainParams.StateSenderAddress.EthAddress(), - }} - // get logs from rootchain by filter - - ethClientTimeout := rl.contractConnector.MainChainTimeout - ctx, cancel := context.WithTimeout(context.Background(), ethClientTimeout) - defer cancel() - logs, err := rl.contractConnector.MainChainClient.FilterLogs(ctx, query) + // Fetch events from the rootchain + logs, err := rl.contractConnector.MainChainClient.FilterLogs(ctx, ethereum.FilterQuery{ + FromBlock: fromBlock, + ToBlock: toBlock, + Addresses: []ethCommon.Address{ + chainParams.RootChainAddress.EthAddress(), + chainParams.StakingInfoAddress.EthAddress(), + chainParams.StateSenderAddress.EthAddress(), + }, + }) if err != nil { rl.Logger.Error("Error while filtering logs", "error", err) return @@ -176,109 +175,121 @@ func (rl *RootChainListener) queryAndBroadcastEvents(rootchainContext *RootChain rl.Logger.Debug("New logs found", "numberOfLogs", len(logs)) } - // process filtered log + pubkey := helper.GetPubKey() + pubkeyBytes := pubkey[1:] + + // Process filtered log for _, vLog := range logs { topic := vLog.Topics[0].Bytes() for _, abiObject := range rl.abis { selectedEvent := helper.EventByID(abiObject, topic) - logBytes, _ := json.Marshal(vLog) - if selectedEvent != nil { - rl.Logger.Debug("ReceivedEvent", "eventname", selectedEvent.Name) - switch selectedEvent.Name { - case "NewHeaderBlock": - if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, selectedEvent); isCurrentValidator { - rl.sendTaskWithDelay("sendCheckpointAckToHeimdall", selectedEvent.Name, logBytes, delay, selectedEvent) - } - case "Staked": - event := new(stakinginfo.StakinginfoStaked) - if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { - rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) - } - if bytes.Equal(event.SignerPubkey, pubkeyBytes) { - // topup has to be processed first before validator join. so adding delay. - delay := util.TaskDelayBetweenEachVal - rl.sendTaskWithDelay("sendValidatorJoinToHeimdall", selectedEvent.Name, logBytes, delay, event) - } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { - // topup has to be processed first before validator join. so adding delay. - delay = delay + util.TaskDelayBetweenEachVal - rl.sendTaskWithDelay("sendValidatorJoinToHeimdall", selectedEvent.Name, logBytes, delay, event) - } - - case "StakeUpdate": - event := new(stakinginfo.StakinginfoStakeUpdate) - if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { - rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) - } - if util.IsEventSender(rl.cliCtx, event.ValidatorId.Uint64()) { - rl.sendTaskWithDelay("sendStakeUpdateToHeimdall", selectedEvent.Name, logBytes, 0, event) - } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { - rl.sendTaskWithDelay("sendStakeUpdateToHeimdall", selectedEvent.Name, logBytes, delay, event) - } - - case "SignerChange": - event := new(stakinginfo.StakinginfoSignerChange) - if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { - rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) - } - if bytes.Equal(event.SignerPubkey, pubkeyBytes) { - rl.sendTaskWithDelay("sendSignerChangeToHeimdall", selectedEvent.Name, logBytes, 0, event) - } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { - rl.sendTaskWithDelay("sendSignerChangeToHeimdall", selectedEvent.Name, logBytes, delay, event) - } - - case "UnstakeInit": - event := new(stakinginfo.StakinginfoUnstakeInit) - if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { - rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) - } - if util.IsEventSender(rl.cliCtx, event.ValidatorId.Uint64()) { - rl.sendTaskWithDelay("sendUnstakeInitToHeimdall", selectedEvent.Name, logBytes, 0, event) - } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { - rl.sendTaskWithDelay("sendUnstakeInitToHeimdall", selectedEvent.Name, logBytes, delay, event) - } - - case "StateSynced": - event := new(statesender.StatesenderStateSynced) - if err := helper.UnpackLog(rl.stateSenderAbi, event, selectedEvent.Name, &vLog); err != nil { - rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) - } - rl.Logger.Info("StateSyncedEvent: detected", "stateSyncId", event.Id) - if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { - rl.sendTaskWithDelay("sendStateSyncedToHeimdall", selectedEvent.Name, logBytes, delay, event) - } - - case "TopUpFee": - event := new(stakinginfo.StakinginfoTopUpFee) - if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { - rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) - } - if bytes.Equal(event.User.Bytes(), helper.GetAddress()) { - rl.sendTaskWithDelay("sendTopUpFeeToHeimdall", selectedEvent.Name, logBytes, 0, event) - } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { - rl.sendTaskWithDelay("sendTopUpFeeToHeimdall", selectedEvent.Name, logBytes, delay, event) - } - - case "Slashed": - if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, selectedEvent); isCurrentValidator { - rl.sendTaskWithDelay("sendTickAckToHeimdall", selectedEvent.Name, logBytes, delay, selectedEvent) - } - - case "UnJailed": - event := new(stakinginfo.StakinginfoUnJailed) - if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { - rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) - } - if util.IsEventSender(rl.cliCtx, event.ValidatorId.Uint64()) { - rl.sendTaskWithDelay("sendUnjailToHeimdall", selectedEvent.Name, logBytes, 0, event) - } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { - rl.sendTaskWithDelay("sendUnjailToHeimdall", selectedEvent.Name, logBytes, delay, event) - } - } + if selectedEvent == nil { + continue } + + rl.handleLog(vLog, selectedEvent, pubkeyBytes) + } + } +} + +// handleLog handles the given log +func (rl *RootChainListener) handleLog(vLog types.Log, selectedEvent *abi.Event, pubkeyBytes []byte) { + logBytes, _ := json.Marshal(vLog) + + rl.Logger.Debug("ReceivedEvent", "eventname", selectedEvent.Name) + switch selectedEvent.Name { + case "NewHeaderBlock": + if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, selectedEvent); isCurrentValidator { + rl.sendTaskWithDelay("sendCheckpointAckToHeimdall", selectedEvent.Name, logBytes, delay, selectedEvent) + } + case "Staked": + event := new(stakinginfo.StakinginfoStaked) + if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { + rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) + } + if bytes.Equal(event.SignerPubkey, pubkeyBytes) { + // topup has to be processed first before validator join. so adding delay. + delay := util.TaskDelayBetweenEachVal + rl.sendTaskWithDelay("sendValidatorJoinToHeimdall", selectedEvent.Name, logBytes, delay, event) + } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { + // topup has to be processed first before validator join. so adding delay. + delay = delay + util.TaskDelayBetweenEachVal + rl.sendTaskWithDelay("sendValidatorJoinToHeimdall", selectedEvent.Name, logBytes, delay, event) + } + + case "StakeUpdate": + event := new(stakinginfo.StakinginfoStakeUpdate) + if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { + rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) + } + if util.IsEventSender(rl.cliCtx, event.ValidatorId.Uint64()) { + rl.sendTaskWithDelay("sendStakeUpdateToHeimdall", selectedEvent.Name, logBytes, 0, event) + } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { + rl.sendTaskWithDelay("sendStakeUpdateToHeimdall", selectedEvent.Name, logBytes, delay, event) + } + + case "SignerChange": + event := new(stakinginfo.StakinginfoSignerChange) + if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { + rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) + } + if bytes.Equal(event.SignerPubkey, pubkeyBytes) { + rl.sendTaskWithDelay("sendSignerChangeToHeimdall", selectedEvent.Name, logBytes, 0, event) + } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { + rl.sendTaskWithDelay("sendSignerChangeToHeimdall", selectedEvent.Name, logBytes, delay, event) + } + + case "UnstakeInit": + event := new(stakinginfo.StakinginfoUnstakeInit) + if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { + rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) + } + if util.IsEventSender(rl.cliCtx, event.ValidatorId.Uint64()) { + rl.sendTaskWithDelay("sendUnstakeInitToHeimdall", selectedEvent.Name, logBytes, 0, event) + } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { + rl.sendTaskWithDelay("sendUnstakeInitToHeimdall", selectedEvent.Name, logBytes, delay, event) + } + + case "StateSynced": + event := new(statesender.StatesenderStateSynced) + if err := helper.UnpackLog(rl.stateSenderAbi, event, selectedEvent.Name, &vLog); err != nil { + rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) + } + rl.Logger.Info("StateSyncedEvent: detected", "stateSyncId", event.Id) + if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { + rl.sendTaskWithDelay("sendStateSyncedToHeimdall", selectedEvent.Name, logBytes, delay, event) + } + + case "TopUpFee": + event := new(stakinginfo.StakinginfoTopUpFee) + if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { + rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) + } + if bytes.Equal(event.User.Bytes(), helper.GetAddress()) { + rl.sendTaskWithDelay("sendTopUpFeeToHeimdall", selectedEvent.Name, logBytes, 0, event) + } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { + rl.sendTaskWithDelay("sendTopUpFeeToHeimdall", selectedEvent.Name, logBytes, delay, event) + } + + case "Slashed": + if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, selectedEvent); isCurrentValidator { + rl.sendTaskWithDelay("sendTickAckToHeimdall", selectedEvent.Name, logBytes, delay, selectedEvent) + } + + case "UnJailed": + event := new(stakinginfo.StakinginfoUnJailed) + if err := helper.UnpackLog(rl.stakingInfoAbi, event, selectedEvent.Name, &vLog); err != nil { + rl.Logger.Error("Error while parsing event", "name", selectedEvent.Name, "error", err) + } + if util.IsEventSender(rl.cliCtx, event.ValidatorId.Uint64()) { + rl.sendTaskWithDelay("sendUnjailToHeimdall", selectedEvent.Name, logBytes, 0, event) + } else if isCurrentValidator, delay := util.CalculateTaskDelay(rl.cliCtx, event); isCurrentValidator { + rl.sendTaskWithDelay("sendUnjailToHeimdall", selectedEvent.Name, logBytes, delay, event) } } } +// sendTaskWithDelay sends the given task using the given delay time func (rl *RootChainListener) sendTaskWithDelay(taskName string, eventName string, logBytes []byte, delay time.Duration, event interface{}) { defer util.LogElapsedTimeForStateSyncedEvent(event, "sendTaskWithDelay", time.Now()) @@ -301,16 +312,14 @@ func (rl *RootChainListener) sendTaskWithDelay(taskName string, eventName string eta := time.Now().Add(delay) signature.ETA = &eta rl.Logger.Info("Sending task", "taskName", taskName, "currentTime", time.Now(), "delayTime", eta) + _, err := rl.queueConnector.Server.SendTask(signature) if err != nil { rl.Logger.Error("Error sending task", "taskName", taskName, "error", err) } } -// -// utils -// - +// getRootChainContext returns the root chain context func (rl *RootChainListener) getRootChainContext() (*RootChainListenerContext, error) { chainmanagerParams, err := util.GetChainmanagerParams(rl.cliCtx) if err != nil { diff --git a/bridge/setu/listener/rootchain_events.go b/bridge/setu/listener/rootchain_events.go new file mode 100644 index 000000000..b0e6db67c --- /dev/null +++ b/bridge/setu/listener/rootchain_events.go @@ -0,0 +1,209 @@ +package listener + +import ( + "context" + "errors" + "math/big" + + ethereum "github.com/maticnetwork/bor" + "github.com/maticnetwork/bor/accounts/abi/bind" + "github.com/maticnetwork/bor/common" + "github.com/maticnetwork/bor/core/types" + "github.com/maticnetwork/heimdall/helper" + + "github.com/maticnetwork/heimdall/contracts/stakinginfo" + "github.com/maticnetwork/heimdall/contracts/statesender" +) + +var ( + errNoEventsFound = errors.New("no events found") +) + +// getLatestStateID returns state ID from the latest StateSynced event +func (rl *RootChainListener) getLatestStateID(ctx context.Context) (*big.Int, error) { + rootchainContext, err := rl.getRootChainContext() + if err != nil { + return nil, err + } + + latestEvent, err := rl.getLatestEvent(ctx, ethereum.FilterQuery{ + Addresses: []common.Address{ + rootchainContext.ChainmanagerParams.ChainParams.StateSenderAddress.EthAddress(), + }, + Topics: [][]common.Hash{ + {statesender.GetStateSyncedEventID()}, + {}, + {}, + }, + }) + if err != nil { + return nil, err + } + + var event statesender.StatesenderStateSynced + if err = helper.UnpackLog(rl.stateSenderAbi, event, "StateSynced", latestEvent); err != nil { + return nil, err + } + + return event.Id, nil +} + +// getCurrentStateID returns the current state ID handled by the polygon chain +func (rl *RootChainListener) getCurrentStateID(ctx context.Context) (*big.Int, error) { + rootchainContext, err := rl.getRootChainContext() + if err != nil { + return nil, err + } + + stateReceiverInstance, err := rl.contractConnector.GetStateReceiverInstance( + rootchainContext.ChainmanagerParams.ChainParams.StateReceiverAddress.EthAddress(), + ) + if err != nil { + return nil, err + } + + stateId, err := stateReceiverInstance.LastStateId(&bind.CallOpts{Context: ctx}) + if err != nil { + return nil, err + } + + return stateId, nil +} + +// getStateSync returns the StateSynced event based on the given state ID +func (rl *RootChainListener) getStateSync(ctx context.Context, stateId int64) (*statesender.StatesenderStateSynced, error) { + rootchainContext, err := rl.getRootChainContext() + if err != nil { + return nil, err + } + + events, err := rl.contractConnector.MainChainClient.FilterLogs(ctx, ethereum.FilterQuery{ + Addresses: []common.Address{ + rootchainContext.ChainmanagerParams.ChainParams.StateSenderAddress.EthAddress(), + }, + Topics: [][]common.Hash{ + {statesender.GetStateSyncedEventID()}, + {common.BytesToHash(big.NewInt(stateId).Bytes())}, + {}, + }, + }) + if err != nil { + return nil, err + } + + if len(events) == 0 { + return nil, errNoEventsFound + } + + var event statesender.StatesenderStateSynced + if err = helper.UnpackLog(rl.stateSenderAbi, event, "StateSynced", &events[0]); err != nil { + return nil, err + } + + return &event, nil +} + +// getLatestNonce returns the nonce from the latest StakeUpdate event +func (rl *RootChainListener) getLatestNonce(ctx context.Context, validatorId uint64) (uint64, error) { + rootchainContext, err := rl.getRootChainContext() + if err != nil { + return 0, err + } + + latestEvent, err := rl.getLatestEvent(ctx, ethereum.FilterQuery{ + Addresses: []common.Address{ + rootchainContext.ChainmanagerParams.ChainParams.StakingInfoAddress.EthAddress(), + }, + Topics: [][]common.Hash{ + {stakinginfo.GetStakeUpdateEventID()}, + {common.BytesToHash(big.NewInt(0).SetUint64(validatorId).Bytes())}, + {}, + {}, + }, + }) + if err != nil { + return 0, err + } + + var event stakinginfo.StakinginfoStakeUpdate + if err = helper.UnpackLog(rl.stakingInfoAbi, &event, "StakeUpdate", latestEvent); err != nil { + return 0, err + } + + return event.Nonce.Uint64(), nil +} + +// getStakeUpdate returns StakeUpdate event based on the given validator ID and nonce +func (rl *RootChainListener) getStakeUpdate(ctx context.Context, validatorId, nonce uint64) (*stakinginfo.StakinginfoStakeUpdate, error) { + rootchainContext, err := rl.getRootChainContext() + if err != nil { + return nil, err + } + + events, err := rl.contractConnector.MainChainClient.FilterLogs(ctx, ethereum.FilterQuery{ + Addresses: []common.Address{ + rootchainContext.ChainmanagerParams.ChainParams.StakingInfoAddress.EthAddress(), + }, + Topics: [][]common.Hash{ + {stakinginfo.GetStakeUpdateEventID()}, + {common.BytesToHash(big.NewInt(0).SetUint64(validatorId).Bytes())}, + {common.BytesToHash(big.NewInt(0).SetUint64(nonce).Bytes())}, + {}, + }, + }) + if err != nil { + return nil, err + } + + if len(events) == 0 { + return nil, errNoEventsFound + } + + var event stakinginfo.StakinginfoStakeUpdate + if err = helper.UnpackLog(rl.stakingInfoAbi, &event, "StakeUpdate", &events[0]); err != nil { + return nil, err + } + + return &event, nil +} + +// getLatestEvent returns the latest event based on the given filters +func (rl *RootChainListener) getLatestEvent(ctx context.Context, filters ethereum.FilterQuery) (*types.Log, error) { + const blocksRange = 1000 + const maxIterations = 100 + + currentBlock, err := rl.contractConnector.MainChainClient.BlockByNumber(ctx, nil) + if err != nil { + return nil, err + } + + currentBlockNumber := currentBlock.Number().Uint64() + fromBlockNumber := currentBlockNumber - blocksRange + toBlockNumber := currentBlockNumber + + var latestEvent *types.Log + for i := 0; i < maxIterations; i++ { + filters.FromBlock = big.NewInt(0).SetUint64(fromBlockNumber) + filters.ToBlock = big.NewInt(0).SetUint64(toBlockNumber) + + var events []types.Log + if events, err = rl.contractConnector.MainChainClient.FilterLogs(ctx, filters); err != nil { + return nil, err + } + + if len(events) == 0 { + toBlockNumber = fromBlockNumber + fromBlockNumber -= blocksRange + continue + } + + latestEvent = &events[len(events)-1] + break + } + + if latestEvent == nil { + return nil, errNoEventsFound + } + + return latestEvent, nil +} diff --git a/bridge/setu/listener/rootchain_selfheal.go b/bridge/setu/listener/rootchain_selfheal.go new file mode 100644 index 000000000..e91352fee --- /dev/null +++ b/bridge/setu/listener/rootchain_selfheal.go @@ -0,0 +1,181 @@ +package listener + +import ( + "context" + "sync" + "time" + + "github.com/maticnetwork/bor/core/types" + + "github.com/maticnetwork/heimdall/bridge/setu/util" + "github.com/maticnetwork/heimdall/contracts/stakinginfo" + "github.com/maticnetwork/heimdall/contracts/statesender" + "github.com/maticnetwork/heimdall/helper" +) + +// startSelfHealing starts self-healing processes for all required events +func (rl *RootChainListener) startSelfHealing(ctx context.Context) { + stakeUpdateTicker := time.NewTicker(helper.GetConfig().SHStakeUpdateInterval) + stateSyncedTicker := time.NewTicker(helper.GetConfig().SHStateSyncedInterval) + + for { + select { + case <-stakeUpdateTicker.C: + rl.processStakeUpdate(ctx) + case <-stateSyncedTicker.C: + rl.processStateSync(ctx) + case <-ctx.Done(): + rl.Logger.Info("Stopping stake update worker") + stakeUpdateTicker.Stop() + stateSyncedTicker.Stop() + return + } + } +} + +// processStakeUpdate checks if validators are in sync, otherwise syncs them by broadcasting missing events +func (rl *RootChainListener) processStakeUpdate(ctx context.Context) { + // Fetch all heimdall validators + validatorSet, err := util.GetValidatorSet(rl.cliCtx) + if err != nil { + rl.Logger.Error("Error getting heimdall validators", "error", err) + return + } + rl.Logger.Info("Fetched validators list from heimdall", "len", len(validatorSet.Validators)) + + // Make sure each validator is in sync + var wg sync.WaitGroup + for _, validator := range validatorSet.Validators { + wg.Add(1) + go func(id, nonce uint64) { + defer wg.Done() + + var ethereumNonce uint64 + if err = helper.ExponentialBackoff(func() error { + ethereumNonce, err = rl.getLatestNonce(ctx, id) + return err + }, 3, time.Second); err != nil { + rl.Logger.Error("Error getting nonce for validator from L1", "error", err, "id", id) + return + } + + if ethereumNonce <= nonce { + return + } + + nonce++ + + rl.Logger.Info("Processing stake update for validator", "id", id, "nonce", nonce) + + var stakeUpdate *stakinginfo.StakinginfoStakeUpdate + if err = helper.ExponentialBackoff(func() error { + stakeUpdate, err = rl.getStakeUpdate(ctx, id, nonce) + return err + }, 3, time.Second); err != nil { + rl.Logger.Error("Error getting stake update for validator", "error", err, "id", id) + return + } + + if _, err = rl.processEvent(ctx, stakeUpdate.Raw); err != nil { + rl.Logger.Error("Error processing stake update for validator", "error", err, "id", id) + } + }(validator.ID.Uint64(), validator.Nonce) + } + wg.Wait() +} + +// processStateSync checks if chains are in sync, otherwise syncs them by broadcasting missing events +func (rl *RootChainListener) processStateSync(ctx context.Context) { + latestPolygonStateId, err := rl.getCurrentStateID(ctx) + if err != nil { + rl.Logger.Error("Unable to fetch latest state id from state receiver contract", "error", err) + return + } + + latestEthereumStateId, err := rl.getLatestStateID(ctx) + if err != nil { + rl.Logger.Error("Unable to fetch latest state id from state sender contract", "error", err) + return + } + + if latestEthereumStateId.Cmp(latestPolygonStateId) != 1 { + return + } + + for i := latestPolygonStateId.Int64() + 1; i <= latestEthereumStateId.Int64(); i++ { + if _, err = util.GetClerkEventRecord(rl.cliCtx, i); err == nil { + rl.Logger.Info("State found on heimdall", "id", i) + continue + } + + rl.Logger.Info("Processing state sync", "id", i) + + var stateSynced *statesender.StatesenderStateSynced + if err = helper.ExponentialBackoff(func() error { + stateSynced, err = rl.getStateSync(ctx, i) + return err + }, 3, time.Second); err != nil { + rl.Logger.Error("Error getting state sync", "error", err, "id", i) + continue + } + + var ignore bool + if ignore, err = rl.processEvent(ctx, stateSynced.Raw); err != nil { + rl.Logger.Error("Unable to update state id on heimdall", "error", err) + i-- + continue + } + + if !ignore { + time.Sleep(1 * time.Second) + + statusCheck := 0 + for { + if _, err = util.GetClerkEventRecord(rl.cliCtx, i); err == nil { + rl.Logger.Info("State found on heimdall", "id", i) + break + } + + rl.Logger.Info("State not found on heimdall", "id", i) + time.Sleep(1 * time.Second) + + if statusCheck++; statusCheck > 15 { + break + } + } + + if statusCheck > 15 { + i-- + continue + } + } + } +} + +func (rl *RootChainListener) processEvent(ctx context.Context, event types.Log) (bool, error) { + blockTime, err := rl.contractConnector.GetMainChainBlockTime(ctx, event.BlockNumber) + if err != nil { + rl.Logger.Error("Unable to get block time", "error", err) + return false, err + } + + if time.Since(blockTime) < helper.GetConfig().SHMaxDepthDuration { + rl.Logger.Info("Block time is less than an hour, skipping state sync") + return true, err + } + + pubkey := helper.GetPubKey() + pubkeyBytes := pubkey[1:] + + topic := event.Topics[0].Bytes() + for _, abiObject := range rl.abis { + selectedEvent := helper.EventByID(abiObject, topic) + if selectedEvent == nil { + continue + } + + rl.handleLog(event, selectedEvent, pubkeyBytes) + } + + return false, nil +} diff --git a/bridge/setu/processor/checkpoint.go b/bridge/setu/processor/checkpoint.go index 98b22e199..ccfc4b783 100644 --- a/bridge/setu/processor/checkpoint.go +++ b/bridge/setu/processor/checkpoint.go @@ -267,7 +267,7 @@ func (cp *CheckpointProcessor) sendCheckpointAckToHeimdall(eventName string, che ) // fetch latest checkpoint - latestCheckpoint, err := util.GetlastestCheckpoint(cp.cliCtx) + latestCheckpoint, err := util.GetLatestCheckpoint(cp.cliCtx) // event checkpoint is older than or equal to latest checkpoint if err == nil && latestCheckpoint != nil && latestCheckpoint.EndBlock >= event.End.Uint64() { cp.Logger.Debug("Checkpoint ack is already submitted", "start", event.Start, "end", event.End) diff --git a/bridge/setu/util/common.go b/bridge/setu/util/common.go index 52ad205d6..b0176e0a1 100644 --- a/bridge/setu/util/common.go +++ b/bridge/setu/util/common.go @@ -24,6 +24,7 @@ import ( authTypes "github.com/maticnetwork/heimdall/auth/types" chainManagerTypes "github.com/maticnetwork/heimdall/chainmanager/types" checkpointTypes "github.com/maticnetwork/heimdall/checkpoint/types" + clerktypes "github.com/maticnetwork/heimdall/clerk/types" "github.com/maticnetwork/heimdall/contracts/statesender" "github.com/maticnetwork/heimdall/helper" "github.com/maticnetwork/heimdall/types" @@ -51,6 +52,7 @@ const ( StakingTxStatusURL = "/staking/isoldtx" TopupTxStatusURL = "/topup/isoldtx" ClerkTxStatusURL = "/clerk/isoldtx" + ClerkEventRecordURL = "/clerk/event-record/%d" LatestSlashInfoBytesURL = "/slashing/latest_slash_info_bytes" TickSlashInfoListURL = "/slashing/tick_slash_infos" SlashingTxStatusURL = "/slashing/isoldtx" @@ -157,16 +159,10 @@ func CalculateTaskDelay(cliCtx cliContext.CLIContext, event interface{}) (bool, // calculate validator position valPosition := 0 isCurrentValidator := false - response, err := helper.FetchFromAPI(cliCtx, helper.GetHeimdallServerEndpoint(CurrentValidatorSetURL)) - if err != nil { - logger.Error("Unable to send request for current validatorset", "url", CurrentValidatorSetURL, "error", err) - return isCurrentValidator, 0 - } - // unmarshall data from buffer - var validatorSet hmtypes.ValidatorSet - err = json.Unmarshal(response.Result, &validatorSet) + + validatorSet, err := GetValidatorSet(cliCtx) if err != nil { - logger.Error("Error unmarshalling current validatorset data ", "error", err) + logger.Error("Error getting current validatorset data ", "error", err) return isCurrentValidator, 0 } @@ -325,14 +321,13 @@ func GetChainmanagerParams(cliCtx cliContext.CLIContext) (*chainManagerTypes.Par cliCtx, helper.GetHeimdallServerEndpoint(ChainManagerParamsURL), ) - if err != nil { logger.Error("Error fetching chainmanager params", "err", err) return nil, err } var params chainManagerTypes.Params - if err := json.Unmarshal(response.Result, ¶ms); err != nil { + if err = json.Unmarshal(response.Result, ¶ms); err != nil { logger.Error("Error unmarshalling chainmanager params", "url", ChainManagerParamsURL, "err", err) return nil, err } @@ -382,8 +377,8 @@ func GetBufferedCheckpoint(cliCtx cliContext.CLIContext) (*hmtypes.Checkpoint, e return &checkpoint, nil } -// GetlastestCheckpoint return last successful checkpoint -func GetlastestCheckpoint(cliCtx cliContext.CLIContext) (*hmtypes.Checkpoint, error) { +// GetLatestCheckpoint return last successful checkpoint +func GetLatestCheckpoint(cliCtx cliContext.CLIContext) (*hmtypes.Checkpoint, error) { response, err := helper.FetchFromAPI( cliCtx, helper.GetHeimdallServerEndpoint(LatestCheckpointURL), @@ -395,7 +390,7 @@ func GetlastestCheckpoint(cliCtx cliContext.CLIContext) (*hmtypes.Checkpoint, er } var checkpoint hmtypes.Checkpoint - if err := json.Unmarshal(response.Result, &checkpoint); err != nil { + if err = json.Unmarshal(response.Result, &checkpoint); err != nil { logger.Error("Error unmarshalling latest checkpoint", "url", LatestCheckpointURL, "err", err) return nil, err } @@ -413,7 +408,7 @@ func AppendPrefix(signerPubKey []byte) []byte { return signerPubKey } -// GetValidatorNonce fethes validator nonce and height +// GetValidatorNonce fetches validator nonce and height func GetValidatorNonce(cliCtx cliContext.CLIContext, validatorID uint64) (uint64, int64, error) { var validator hmtypes.Validator @@ -426,24 +421,39 @@ func GetValidatorNonce(cliCtx cliContext.CLIContext, validatorID uint64) (uint64 return 0, 0, err } - err = json.Unmarshal(result.Result, &validator) - if err != nil { + if err = json.Unmarshal(result.Result, &validator); err != nil { logger.Error("error unmarshalling validator data", "error", err) return 0, 0, err } - logger.Debug("Validator data recieved ", "validator", validator.String()) + logger.Debug("Validator data received ", "validator", validator.String()) return validator.Nonce, result.Height, nil } -// GetlastestCheckpoint return last successful checkpoint +// GetValidatorSet fetches the current validator set +func GetValidatorSet(cliCtx cliContext.CLIContext) (*hmtypes.ValidatorSet, error) { + response, err := helper.FetchFromAPI(cliCtx, helper.GetHeimdallServerEndpoint(CurrentValidatorSetURL)) + if err != nil { + logger.Error("Unable to send request for current validatorset", "url", CurrentValidatorSetURL, "error", err) + return nil, err + } + + var validatorSet hmtypes.ValidatorSet + if err = json.Unmarshal(response.Result, &validatorSet); err != nil { + logger.Error("Error unmarshalling current validatorset data ", "error", err) + return nil, err + } + + return &validatorSet, nil +} + +// GetBlockHeight return last successful checkpoint func GetBlockHeight(cliCtx cliContext.CLIContext) int64 { response, err := helper.FetchFromAPI( cliCtx, helper.GetHeimdallServerEndpoint(CountCheckpointURL), ) - if err != nil { logger.Debug("Error fetching latest block height", "err", err) return 0 @@ -452,6 +462,26 @@ func GetBlockHeight(cliCtx cliContext.CLIContext) int64 { return response.Height } +// GetClerkEventRecord return last successful checkpoint +func GetClerkEventRecord(cliCtx cliContext.CLIContext, stateId int64) (*clerktypes.EventRecord, error) { + response, err := helper.FetchFromAPI( + cliCtx, + helper.GetHeimdallServerEndpoint(fmt.Sprintf(ClerkEventRecordURL, stateId)), + ) + if err != nil { + logger.Error("Error fetching event record by state ID", "error", err) + return nil, err + } + + var eventRecord clerktypes.EventRecord + if err = json.Unmarshal(response.Result, &eventRecord); err != nil { + logger.Error("Error unmarshalling event record", "error", err) + return nil, err + } + + return &eventRecord, nil +} + func GetUnconfirmedTxnCount(event interface{}) int { defer LogElapsedTimeForStateSyncedEvent(event, "GetUnconfirmedTxnCount", time.Now()) diff --git a/contracts/stakinginfo/stakinginfo_helper.go b/contracts/stakinginfo/stakinginfo_helper.go new file mode 100644 index 000000000..20902d9e6 --- /dev/null +++ b/contracts/stakinginfo/stakinginfo_helper.go @@ -0,0 +1,13 @@ +package stakinginfo + +import "github.com/maticnetwork/bor/common" + +const ( + // StakeUpdateEventID is the topic ID of StakeUpdate event + StakeUpdateEventID = "0x35af9eea1f0e7b300b0a14fae90139a072470e44daa3f14b5069bebbc1265bda" +) + +// GetStakeUpdateEventID returns the hash of StakeUpdate event ID +func GetStakeUpdateEventID() common.Hash { + return common.HexToHash(StakeUpdateEventID) +} diff --git a/contracts/statereceiver/statereceiver.abi b/contracts/statereceiver/statereceiver.abi index 6493aee10..395f6dc97 100644 --- a/contracts/statereceiver/statereceiver.abi +++ b/contracts/statereceiver/statereceiver.abi @@ -1,169 +1,61 @@ -[ - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "states", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "recordBytes", - "type": "bytes" - } - ], - "name": "commitState", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getPendingStates", - "outputs": [ - { - "name": "", - "type": "uint256[]" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "SYSTEM_ADDRESS", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "validatorSet", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "vote", - "type": "bytes" - }, - { - "name": "sigs", - "type": "bytes" - }, - { - "name": "txBytes", - "type": "bytes" - }, - { - "name": "proof", - "type": "bytes" - } - ], - "name": "validateValidatorSet", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "isValidatorSetContract", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "stateId", - "type": "uint256" - } - ], - "name": "proposeState", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "signer", - "type": "address" - } - ], - "name": "isProducer", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "signer", - "type": "address" - } - ], - "name": "isValidator", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - } -] +[{ + "constant": true, + "inputs": [], + "name": "SYSTEM_ADDRESS", + "outputs": [{ + "internalType": "address", + "name": "", + "type": "address" + }], + "payable": false, + "stateMutability": "view", + "type": "function" +}, { + "constant": true, + "inputs": [], + "name": "lastStateId", + "outputs": [{ + "internalType": "uint256", + "name": "", + "type": "uint256" + }], + "payable": false, + "stateMutability": "view", + "type": "function" +}, { + "constant": false, + "inputs": [{ + "internalType": "uint256", + "name": "syncTime", + "type": "uint256" + }, { + "internalType": "bytes", + "name": "recordBytes", + "type": "bytes" + }], + "name": "commitState", + "outputs": [{ + "internalType": "bool", + "name": "success", + "type": "bool" + }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" +}, { + "constant": false, + "inputs": [{ + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }], + "name": "onStateReceive", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" +}] \ No newline at end of file diff --git a/contracts/statereceiver/statereceiver.go b/contracts/statereceiver/statereceiver.go index 7d9477598..d89e7b9fa 100644 --- a/contracts/statereceiver/statereceiver.go +++ b/contracts/statereceiver/statereceiver.go @@ -4,6 +4,7 @@ package statereceiver import ( + "errors" "math/big" "strings" @@ -17,10 +18,10 @@ import ( // Reference imports to suppress errors if they are not otherwise used. var ( + _ = errors.New _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound - _ = abi.U256 _ = bind.Bind _ = common.Big1 _ = types.BloomLookup @@ -28,7 +29,7 @@ var ( ) // StatereceiverABI is the input ABI used to generate the binding from. -const StatereceiverABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"states\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"recordBytes\",\"type\":\"bytes\"}],\"name\":\"commitState\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getPendingStates\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"SYSTEM_ADDRESS\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"validatorSet\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"vote\",\"type\":\"bytes\"},{\"name\":\"sigs\",\"type\":\"bytes\"},{\"name\":\"txBytes\",\"type\":\"bytes\"},{\"name\":\"proof\",\"type\":\"bytes\"}],\"name\":\"validateValidatorSet\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"isValidatorSetContract\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"stateId\",\"type\":\"uint256\"}],\"name\":\"proposeState\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"isProducer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"isValidator\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]" +const StatereceiverABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"SYSTEM_ADDRESS\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"lastStateId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"syncTime\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"recordBytes\",\"type\":\"bytes\"}],\"name\":\"commitState\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onStateReceive\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" // Statereceiver is an auto generated Go binding around an Ethereum contract. type Statereceiver struct { @@ -138,7 +139,7 @@ func bindStatereceiver(address common.Address, caller bind.ContractCaller, trans // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_Statereceiver *StatereceiverRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_Statereceiver *StatereceiverRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _Statereceiver.Contract.StatereceiverCaller.contract.Call(opts, result, method, params...) } @@ -157,7 +158,7 @@ func (_Statereceiver *StatereceiverRaw) Transact(opts *bind.TransactOpts, method // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_Statereceiver *StatereceiverCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_Statereceiver *StatereceiverCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _Statereceiver.Contract.contract.Call(opts, result, method, params...) } @@ -174,7 +175,7 @@ func (_Statereceiver *StatereceiverTransactorRaw) Transact(opts *bind.TransactOp // SYSTEMADDRESS is a free data retrieval call binding the contract method 0x3434735f. // -// Solidity: function SYSTEM_ADDRESS() constant returns(address) +// Solidity: function SYSTEM_ADDRESS() view returns(address) func (_Statereceiver *StatereceiverCaller) SYSTEMADDRESS(opts *bind.CallOpts) (common.Address, error) { var ( ret0 = new(common.Address) @@ -186,233 +187,83 @@ func (_Statereceiver *StatereceiverCaller) SYSTEMADDRESS(opts *bind.CallOpts) (c // SYSTEMADDRESS is a free data retrieval call binding the contract method 0x3434735f. // -// Solidity: function SYSTEM_ADDRESS() constant returns(address) +// Solidity: function SYSTEM_ADDRESS() view returns(address) func (_Statereceiver *StatereceiverSession) SYSTEMADDRESS() (common.Address, error) { return _Statereceiver.Contract.SYSTEMADDRESS(&_Statereceiver.CallOpts) } // SYSTEMADDRESS is a free data retrieval call binding the contract method 0x3434735f. // -// Solidity: function SYSTEM_ADDRESS() constant returns(address) +// Solidity: function SYSTEM_ADDRESS() view returns(address) func (_Statereceiver *StatereceiverCallerSession) SYSTEMADDRESS() (common.Address, error) { return _Statereceiver.Contract.SYSTEMADDRESS(&_Statereceiver.CallOpts) } -// GetPendingStates is a free data retrieval call binding the contract method 0x21ec23b6. +// LastStateId is a free data retrieval call binding the contract method 0x5407ca67. // -// Solidity: function getPendingStates() constant returns(uint256[]) -func (_Statereceiver *StatereceiverCaller) GetPendingStates(opts *bind.CallOpts) ([]*big.Int, error) { +// Solidity: function lastStateId() view returns(uint256) +func (_Statereceiver *StatereceiverCaller) LastStateId(opts *bind.CallOpts) (*big.Int, error) { var ( - ret0 = new([]*big.Int) + ret0 = new(*big.Int) ) out := ret0 - err := _Statereceiver.contract.Call(opts, out, "getPendingStates") + err := _Statereceiver.contract.Call(opts, out, "lastStateId") return *ret0, err -} - -// GetPendingStates is a free data retrieval call binding the contract method 0x21ec23b6. -// -// Solidity: function getPendingStates() constant returns(uint256[]) -func (_Statereceiver *StatereceiverSession) GetPendingStates() ([]*big.Int, error) { - return _Statereceiver.Contract.GetPendingStates(&_Statereceiver.CallOpts) -} - -// GetPendingStates is a free data retrieval call binding the contract method 0x21ec23b6. -// -// Solidity: function getPendingStates() constant returns(uint256[]) -func (_Statereceiver *StatereceiverCallerSession) GetPendingStates() ([]*big.Int, error) { - return _Statereceiver.Contract.GetPendingStates(&_Statereceiver.CallOpts) -} - -// IsProducer is a free data retrieval call binding the contract method 0xf5521022. -// -// Solidity: function isProducer(address signer) constant returns(bool) -func (_Statereceiver *StatereceiverCaller) IsProducer(opts *bind.CallOpts, signer common.Address) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _Statereceiver.contract.Call(opts, out, "isProducer", signer) - return *ret0, err -} - -// IsProducer is a free data retrieval call binding the contract method 0xf5521022. -// -// Solidity: function isProducer(address signer) constant returns(bool) -func (_Statereceiver *StatereceiverSession) IsProducer(signer common.Address) (bool, error) { - return _Statereceiver.Contract.IsProducer(&_Statereceiver.CallOpts, signer) -} - -// IsProducer is a free data retrieval call binding the contract method 0xf5521022. -// -// Solidity: function isProducer(address signer) constant returns(bool) -func (_Statereceiver *StatereceiverCallerSession) IsProducer(signer common.Address) (bool, error) { - return _Statereceiver.Contract.IsProducer(&_Statereceiver.CallOpts, signer) -} - -// IsValidator is a free data retrieval call binding the contract method 0xfacd743b. -// -// Solidity: function isValidator(address signer) constant returns(bool) -func (_Statereceiver *StatereceiverCaller) IsValidator(opts *bind.CallOpts, signer common.Address) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _Statereceiver.contract.Call(opts, out, "isValidator", signer) - return *ret0, err -} - -// IsValidator is a free data retrieval call binding the contract method 0xfacd743b. -// -// Solidity: function isValidator(address signer) constant returns(bool) -func (_Statereceiver *StatereceiverSession) IsValidator(signer common.Address) (bool, error) { - return _Statereceiver.Contract.IsValidator(&_Statereceiver.CallOpts, signer) -} - -// IsValidator is a free data retrieval call binding the contract method 0xfacd743b. -// -// Solidity: function isValidator(address signer) constant returns(bool) -func (_Statereceiver *StatereceiverCallerSession) IsValidator(signer common.Address) (bool, error) { - return _Statereceiver.Contract.IsValidator(&_Statereceiver.CallOpts, signer) -} - -// IsValidatorSetContract is a free data retrieval call binding the contract method 0xd79e60b7. -// -// Solidity: function isValidatorSetContract() constant returns(bool) -func (_Statereceiver *StatereceiverCaller) IsValidatorSetContract(opts *bind.CallOpts) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _Statereceiver.contract.Call(opts, out, "isValidatorSetContract") - return *ret0, err -} - -// IsValidatorSetContract is a free data retrieval call binding the contract method 0xd79e60b7. -// -// Solidity: function isValidatorSetContract() constant returns(bool) -func (_Statereceiver *StatereceiverSession) IsValidatorSetContract() (bool, error) { - return _Statereceiver.Contract.IsValidatorSetContract(&_Statereceiver.CallOpts) -} - -// IsValidatorSetContract is a free data retrieval call binding the contract method 0xd79e60b7. -// -// Solidity: function isValidatorSetContract() constant returns(bool) -func (_Statereceiver *StatereceiverCallerSession) IsValidatorSetContract() (bool, error) { - return _Statereceiver.Contract.IsValidatorSetContract(&_Statereceiver.CallOpts) -} - -// States is a free data retrieval call binding the contract method 0x017a9105. -// -// Solidity: function states(uint256 ) constant returns(bool) -func (_Statereceiver *StatereceiverCaller) States(opts *bind.CallOpts, arg0 *big.Int) (bool, error) { - var ( - ret0 = new(bool) - ) - out := ret0 - err := _Statereceiver.contract.Call(opts, out, "states", arg0) - return *ret0, err -} - -// States is a free data retrieval call binding the contract method 0x017a9105. -// -// Solidity: function states(uint256 ) constant returns(bool) -func (_Statereceiver *StatereceiverSession) States(arg0 *big.Int) (bool, error) { - return _Statereceiver.Contract.States(&_Statereceiver.CallOpts, arg0) -} -// States is a free data retrieval call binding the contract method 0x017a9105. -// -// Solidity: function states(uint256 ) constant returns(bool) -func (_Statereceiver *StatereceiverCallerSession) States(arg0 *big.Int) (bool, error) { - return _Statereceiver.Contract.States(&_Statereceiver.CallOpts, arg0) -} - -// ValidatorSet is a free data retrieval call binding the contract method 0x9426e226. -// -// Solidity: function validatorSet() constant returns(address) -func (_Statereceiver *StatereceiverCaller) ValidatorSet(opts *bind.CallOpts) (common.Address, error) { - var ( - ret0 = new(common.Address) - ) - out := ret0 - err := _Statereceiver.contract.Call(opts, out, "validatorSet") - return *ret0, err -} - -// ValidatorSet is a free data retrieval call binding the contract method 0x9426e226. -// -// Solidity: function validatorSet() constant returns(address) -func (_Statereceiver *StatereceiverSession) ValidatorSet() (common.Address, error) { - return _Statereceiver.Contract.ValidatorSet(&_Statereceiver.CallOpts) -} - -// ValidatorSet is a free data retrieval call binding the contract method 0x9426e226. -// -// Solidity: function validatorSet() constant returns(address) -func (_Statereceiver *StatereceiverCallerSession) ValidatorSet() (common.Address, error) { - return _Statereceiver.Contract.ValidatorSet(&_Statereceiver.CallOpts) -} - -// CommitState is a paid mutator transaction binding the contract method 0x080356b7. -// -// Solidity: function commitState(bytes recordBytes) returns() -func (_Statereceiver *StatereceiverTransactor) CommitState(opts *bind.TransactOpts, recordBytes []byte) (*types.Transaction, error) { - return _Statereceiver.contract.Transact(opts, "commitState", recordBytes) } -// CommitState is a paid mutator transaction binding the contract method 0x080356b7. +// LastStateId is a free data retrieval call binding the contract method 0x5407ca67. // -// Solidity: function commitState(bytes recordBytes) returns() -func (_Statereceiver *StatereceiverSession) CommitState(recordBytes []byte) (*types.Transaction, error) { - return _Statereceiver.Contract.CommitState(&_Statereceiver.TransactOpts, recordBytes) +// Solidity: function lastStateId() view returns(uint256) +func (_Statereceiver *StatereceiverSession) LastStateId() (*big.Int, error) { + return _Statereceiver.Contract.LastStateId(&_Statereceiver.CallOpts) } -// CommitState is a paid mutator transaction binding the contract method 0x080356b7. +// LastStateId is a free data retrieval call binding the contract method 0x5407ca67. // -// Solidity: function commitState(bytes recordBytes) returns() -func (_Statereceiver *StatereceiverTransactorSession) CommitState(recordBytes []byte) (*types.Transaction, error) { - return _Statereceiver.Contract.CommitState(&_Statereceiver.TransactOpts, recordBytes) +// Solidity: function lastStateId() view returns(uint256) +func (_Statereceiver *StatereceiverCallerSession) LastStateId() (*big.Int, error) { + return _Statereceiver.Contract.LastStateId(&_Statereceiver.CallOpts) } -// ProposeState is a paid mutator transaction binding the contract method 0xede01f17. +// CommitState is a paid mutator transaction binding the contract method 0x19494a17. // -// Solidity: function proposeState(uint256 stateId) returns() -func (_Statereceiver *StatereceiverTransactor) ProposeState(opts *bind.TransactOpts, stateId *big.Int) (*types.Transaction, error) { - return _Statereceiver.contract.Transact(opts, "proposeState", stateId) +// Solidity: function commitState(uint256 syncTime, bytes recordBytes) returns(bool success) +func (_Statereceiver *StatereceiverTransactor) CommitState(opts *bind.TransactOpts, syncTime *big.Int, recordBytes []byte) (*types.Transaction, error) { + return _Statereceiver.contract.Transact(opts, "commitState", syncTime, recordBytes) } -// ProposeState is a paid mutator transaction binding the contract method 0xede01f17. +// CommitState is a paid mutator transaction binding the contract method 0x19494a17. // -// Solidity: function proposeState(uint256 stateId) returns() -func (_Statereceiver *StatereceiverSession) ProposeState(stateId *big.Int) (*types.Transaction, error) { - return _Statereceiver.Contract.ProposeState(&_Statereceiver.TransactOpts, stateId) +// Solidity: function commitState(uint256 syncTime, bytes recordBytes) returns(bool success) +func (_Statereceiver *StatereceiverSession) CommitState(syncTime *big.Int, recordBytes []byte) (*types.Transaction, error) { + return _Statereceiver.Contract.CommitState(&_Statereceiver.TransactOpts, syncTime, recordBytes) } -// ProposeState is a paid mutator transaction binding the contract method 0xede01f17. +// CommitState is a paid mutator transaction binding the contract method 0x19494a17. // -// Solidity: function proposeState(uint256 stateId) returns() -func (_Statereceiver *StatereceiverTransactorSession) ProposeState(stateId *big.Int) (*types.Transaction, error) { - return _Statereceiver.Contract.ProposeState(&_Statereceiver.TransactOpts, stateId) +// Solidity: function commitState(uint256 syncTime, bytes recordBytes) returns(bool success) +func (_Statereceiver *StatereceiverTransactorSession) CommitState(syncTime *big.Int, recordBytes []byte) (*types.Transaction, error) { + return _Statereceiver.Contract.CommitState(&_Statereceiver.TransactOpts, syncTime, recordBytes) } -// ValidateValidatorSet is a paid mutator transaction binding the contract method 0xd0504f89. +// OnStateReceive is a paid mutator transaction binding the contract method 0x26c53bea. // -// Solidity: function validateValidatorSet(bytes vote, bytes sigs, bytes txBytes, bytes proof) returns() -func (_Statereceiver *StatereceiverTransactor) ValidateValidatorSet(opts *bind.TransactOpts, vote []byte, sigs []byte, txBytes []byte, proof []byte) (*types.Transaction, error) { - return _Statereceiver.contract.Transact(opts, "validateValidatorSet", vote, sigs, txBytes, proof) +// Solidity: function onStateReceive(uint256 id, bytes data) returns() +func (_Statereceiver *StatereceiverTransactor) OnStateReceive(opts *bind.TransactOpts, id *big.Int, data []byte) (*types.Transaction, error) { + return _Statereceiver.contract.Transact(opts, "onStateReceive", id, data) } -// ValidateValidatorSet is a paid mutator transaction binding the contract method 0xd0504f89. +// OnStateReceive is a paid mutator transaction binding the contract method 0x26c53bea. // -// Solidity: function validateValidatorSet(bytes vote, bytes sigs, bytes txBytes, bytes proof) returns() -func (_Statereceiver *StatereceiverSession) ValidateValidatorSet(vote []byte, sigs []byte, txBytes []byte, proof []byte) (*types.Transaction, error) { - return _Statereceiver.Contract.ValidateValidatorSet(&_Statereceiver.TransactOpts, vote, sigs, txBytes, proof) +// Solidity: function onStateReceive(uint256 id, bytes data) returns() +func (_Statereceiver *StatereceiverSession) OnStateReceive(id *big.Int, data []byte) (*types.Transaction, error) { + return _Statereceiver.Contract.OnStateReceive(&_Statereceiver.TransactOpts, id, data) } -// ValidateValidatorSet is a paid mutator transaction binding the contract method 0xd0504f89. +// OnStateReceive is a paid mutator transaction binding the contract method 0x26c53bea. // -// Solidity: function validateValidatorSet(bytes vote, bytes sigs, bytes txBytes, bytes proof) returns() -func (_Statereceiver *StatereceiverTransactorSession) ValidateValidatorSet(vote []byte, sigs []byte, txBytes []byte, proof []byte) (*types.Transaction, error) { - return _Statereceiver.Contract.ValidateValidatorSet(&_Statereceiver.TransactOpts, vote, sigs, txBytes, proof) +// Solidity: function onStateReceive(uint256 id, bytes data) returns() +func (_Statereceiver *StatereceiverTransactorSession) OnStateReceive(id *big.Int, data []byte) (*types.Transaction, error) { + return _Statereceiver.Contract.OnStateReceive(&_Statereceiver.TransactOpts, id, data) } diff --git a/contracts/statesender/statesender_helper.go b/contracts/statesender/statesender_helper.go new file mode 100644 index 000000000..70ace65bf --- /dev/null +++ b/contracts/statesender/statesender_helper.go @@ -0,0 +1,13 @@ +package statesender + +import "github.com/maticnetwork/bor/common" + +const ( + // StateSyncedEventID is the topic ID of StateSynced event + StateSyncedEventID = "0x103fed9db65eac19c4d870f49ab7520fe03b99f1838e5996caf47e9e43308392" +) + +// GetStateSyncedEventID returns the hash of StateSynced event ID +func GetStateSyncedEventID() common.Hash { + return common.HexToHash(StateSyncedEventID) +} diff --git a/helper/backoff.go b/helper/backoff.go new file mode 100644 index 000000000..e23be0ee6 --- /dev/null +++ b/helper/backoff.go @@ -0,0 +1,18 @@ +package helper + +import ( + "time" +) + +// ExponentialBackoff performs exponential backoff attempts on a given action +func ExponentialBackoff(action func() error, max uint, wait time.Duration) error { + var err error + for i := uint(0); i < max; i++ { + if err = action(); err == nil { + return nil + } + time.Sleep(wait) + wait *= 2 + } + return err +} diff --git a/helper/backoff_test.go b/helper/backoff_test.go new file mode 100644 index 000000000..13c572730 --- /dev/null +++ b/helper/backoff_test.go @@ -0,0 +1,46 @@ +package helper + +import ( + "errors" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestExponentialBackoff(t *testing.T) { + t.Run("success", func(t *testing.T) { + i := 0 + outcomes := []bool{false, false, true} + t0 := time.Now() + err := ExponentialBackoff(func() error { + outcome := outcomes[i] + i++ + if outcome { + return nil + } + return errors.New("bad") + }, 3, 150*time.Millisecond) + + elapsed := time.Since(t0) + + require.NoError(t, err) + require.Equal(t, i, 3) + require.True(t, elapsed >= 450*time.Millisecond) + }) + + t.Run("failed", func(t *testing.T) { + i := 0 + t0 := time.Now() + err := ExponentialBackoff(func() error { + i++ + return errors.New("bad") + }, 3, 100*time.Millisecond) + + elapsed := time.Since(t0) + + require.Error(t, err) + require.Equal(t, i, 3) + require.True(t, elapsed >= 600*time.Millisecond) + }) +} diff --git a/helper/call.go b/helper/call.go index f2419d7bf..064ae000c 100644 --- a/helper/call.go +++ b/helper/call.go @@ -15,6 +15,7 @@ import ( ethTypes "github.com/maticnetwork/bor/core/types" "github.com/maticnetwork/bor/ethclient" "github.com/maticnetwork/bor/rpc" + "github.com/maticnetwork/heimdall/contracts/erc20" "github.com/maticnetwork/heimdall/contracts/rootchain" "github.com/maticnetwork/heimdall/contracts/slashmanager" @@ -23,7 +24,6 @@ import ( "github.com/maticnetwork/heimdall/contracts/statereceiver" "github.com/maticnetwork/heimdall/contracts/statesender" "github.com/maticnetwork/heimdall/contracts/validatorset" - "github.com/maticnetwork/heimdall/types" ) @@ -129,35 +129,35 @@ func NewContractCaller() (contractCallerObj ContractCaller, err error) { // ABIs // - if contractCallerObj.RootChainABI, err = getABI(string(rootchain.RootchainABI)); err != nil { + if contractCallerObj.RootChainABI, err = getABI(rootchain.RootchainABI); err != nil { return } - if contractCallerObj.StakingInfoABI, err = getABI(string(stakinginfo.StakinginfoABI)); err != nil { + if contractCallerObj.StakingInfoABI, err = getABI(stakinginfo.StakinginfoABI); err != nil { return } - if contractCallerObj.ValidatorSetABI, err = getABI(string(validatorset.ValidatorsetABI)); err != nil { + if contractCallerObj.ValidatorSetABI, err = getABI(validatorset.ValidatorsetABI); err != nil { return } - if contractCallerObj.StateReceiverABI, err = getABI(string(statereceiver.StatereceiverABI)); err != nil { + if contractCallerObj.StateReceiverABI, err = getABI(statereceiver.StatereceiverABI); err != nil { return } - if contractCallerObj.StateSenderABI, err = getABI(string(statesender.StatesenderABI)); err != nil { + if contractCallerObj.StateSenderABI, err = getABI(statesender.StatesenderABI); err != nil { return } - if contractCallerObj.StakeManagerABI, err = getABI(string(stakemanager.StakemanagerABI)); err != nil { + if contractCallerObj.StakeManagerABI, err = getABI(stakemanager.StakemanagerABI); err != nil { return } - if contractCallerObj.SlashManagerABI, err = getABI(string(slashmanager.SlashmanagerABI)); err != nil { + if contractCallerObj.SlashManagerABI, err = getABI(slashmanager.SlashmanagerABI); err != nil { return } - if contractCallerObj.MaticTokenABI, err = getABI(string(erc20.Erc20ABI)); err != nil { + if contractCallerObj.MaticTokenABI, err = getABI(erc20.Erc20ABI); err != nil { return } @@ -384,6 +384,20 @@ func (c *ContractCaller) GetMainChainBlock(blockNum *big.Int) (header *ethTypes. return latestBlock, nil } +// GetMainChainBlockTime returns main chain block time +func (c *ContractCaller) GetMainChainBlockTime(ctx context.Context, blockNum uint64) (time.Time, error) { + ctx, cancel := context.WithTimeout(ctx, c.MainChainTimeout) + defer cancel() + + latestBlock, err := c.MainChainClient.BlockByNumber(ctx, big.NewInt(0).SetUint64(blockNum)) + if err != nil { + Logger.Error("Unable to connect to main chain", "error", err) + return time.Time{}, err + } + + return time.Unix(int64(latestBlock.Time()), 0), nil +} + // GetMaticChainBlock returns child chain block header func (c *ContractCaller) GetMaticChainBlock(blockNum *big.Int) (header *ethTypes.Header, err error) { ctx, cancel := context.WithTimeout(context.Background(), c.MaticChainTimeout) @@ -428,8 +442,7 @@ func (c *ContractCaller) IsTxConfirmed(tx common.Hash, requiredConfirmations uin // GetConfirmedTxReceipt returns confirmed tx receipt func (c *ContractCaller) GetConfirmedTxReceipt(tx common.Hash, requiredConfirmations uint64) (*ethTypes.Receipt, error) { - - var receipt *ethTypes.Receipt = nil + var receipt *ethTypes.Receipt receiptCache, ok := c.ReceiptCache.Get(tx.String()) if !ok { diff --git a/helper/config.go b/helper/config.go index 959c81aa5..5607e15fc 100644 --- a/helper/config.go +++ b/helper/config.go @@ -66,6 +66,9 @@ const ( DefaultNoACKPollInterval = 1010 * time.Second DefaultClerkPollInterval = 10 * time.Second DefaultSpanPollInterval = 1 * time.Minute + DefaultSHStateSyncedInterval = 1 * time.Minute + DefaultSHStakeUpdateInterval = 5 * time.Minute + DefaultSHMaxDepthDuration = time.Hour DefaultMainchainGasLimit = uint64(5000000) @@ -121,6 +124,9 @@ type Configuration struct { NoACKPollInterval time.Duration `mapstructure:"noack_poll_interval"` // Poll interval for ack service to send no-ack in case of no checkpoints ClerkPollInterval time.Duration `mapstructure:"clerk_poll_interval"` SpanPollInterval time.Duration `mapstructure:"span_poll_interval"` + SHStateSyncedInterval time.Duration `mapstructure:"sh_state_synced_interval"` // Interval to self-heal StateSynced events if missing + SHStakeUpdateInterval time.Duration `mapstructure:"sh_stake_update_interval"` // Interval to self-heal StakeUpdate events if missing + SHMaxDepthDuration time.Duration `mapstructure:"sh_max_depth_duration"` // Max duration that allows to suggest self-healing is not needed // wait time related options NoACKWaitTime time.Duration `mapstructure:"no_ack_wait_time"` // Time ack service waits to clear buffer and elect new proposer @@ -271,6 +277,9 @@ func GetDefaultHeimdallConfig() Configuration { NoACKPollInterval: DefaultNoACKPollInterval, ClerkPollInterval: DefaultClerkPollInterval, SpanPollInterval: DefaultSpanPollInterval, + SHStateSyncedInterval: DefaultSHStateSyncedInterval, + SHStakeUpdateInterval: DefaultSHStakeUpdateInterval, + SHMaxDepthDuration: DefaultSHMaxDepthDuration, NoACKWaitTime: NoACKWaitTime, diff --git a/helper/util.go b/helper/util.go index 0b5fe64fd..29818437d 100644 --- a/helper/util.go +++ b/helper/util.go @@ -789,12 +789,12 @@ func FetchFromAPI(cliCtx cliContext.CLIContext, URL string) (result rest.Respons } // unmarshall data from buffer var response rest.ResponseWithHeight - if err := cliCtx.Codec.UnmarshalJSON(body, &response); err != nil { + if err = cliCtx.Codec.UnmarshalJSON(body, &response); err != nil { return result, err } return response, nil } Logger.Debug("Error while fetching data from URL", "status", resp.StatusCode, "URL", URL) - return result, fmt.Errorf("Error while fetching data from url: %v, status: %v", URL, resp.StatusCode) + return result, fmt.Errorf("error while fetching data from url: %v, status: %v", URL, resp.StatusCode) } diff --git a/staking/client/cli/tx.go b/staking/client/cli/tx.go index ae6374c1b..01eaa9958 100644 --- a/staking/client/cli/tx.go +++ b/staking/client/cli/tx.go @@ -106,7 +106,7 @@ func SendValidatorJoinTx(cdc *codec.Codec) *cobra.Command { topic := vLog.Topics[0].Bytes() selectedEvent := helper.EventByID(abiObject, topic) if selectedEvent != nil && selectedEvent.Name == eventName { - if err := helper.UnpackLog(abiObject, event, eventName, vLog); err != nil { + if err = helper.UnpackLog(abiObject, event, eventName, vLog); err != nil { return err }