diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 826a45dae2..645fce60bb 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -2,6 +2,7 @@ package bor import ( "bytes" + "context" "encoding/hex" "encoding/json" "errors" @@ -273,14 +274,14 @@ func (c *Bor) Author(header *types.Header) (common.Address, error) { } // VerifyHeader checks whether a header conforms to the consensus rules. -func (c *Bor) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { +func (c *Bor) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, _ bool) error { return c.verifyHeader(chain, header, nil) } // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The // method returns a quit channel to abort the operations and a results channel to // retrieve the async verifications (the order is that of the input slice). -func (c *Bor) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { +func (c *Bor) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, _ []bool) (chan<- struct{}, <-chan error) { abort := make(chan struct{}) results := make(chan error, len(headers)) @@ -487,6 +488,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co // at a checkpoint block without a parent (light client CHT), or we have piled // up more headers than allowed to be reorged (chain reinit from a freezer), // consider the checkpoint trusted and snapshot it. + // TODO fix this // nolint:nestif if number == 0 { @@ -496,7 +498,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co hash := checkpoint.Hash() // get validators and current span - validators, err := c.spanner.GetCurrentValidators(hash, number+1) + validators, err := c.spanner.GetCurrentValidators(context.Background(), hash, number+1) if err != nil { return nil, err } @@ -539,7 +541,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co // check if snapshot is nil if snap == nil { - return nil, fmt.Errorf("Unknown error while retrieving snapshot at block number %v", number) + return nil, fmt.Errorf("unknown error while retrieving snapshot at block number %v", number) } // Previous snapshot found, apply any pending headers on top of it @@ -568,7 +570,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co // VerifyUncles implements consensus.Engine, always returning an error for any // uncles as this consensus mechanism doesn't permit uncles. -func (c *Bor) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { +func (c *Bor) VerifyUncles(_ consensus.ChainReader, block *types.Block) error { if len(block.Uncles()) > 0 { return errors.New("uncles not allowed") } @@ -662,7 +664,7 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e // get validator set if number if IsSprintStart(number+1, c.config.Sprint) { - newValidators, err := c.spanner.GetCurrentValidators(header.ParentHash, number+1) + newValidators, err := c.spanner.GetCurrentValidators(context.Background(), header.ParentHash, number+1) if err != nil { return errors.New("unknown validators") } @@ -706,24 +708,27 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given. -func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { - stateSyncData := []*types.StateSyncData{} - - var err error +func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, _ []*types.Transaction, _ []*types.Header) { + var ( + stateSyncData []*types.StateSyncData + err error + ) headerNumber := header.Number.Uint64() if headerNumber%c.config.Sprint == 0 { + ctx := context.Background() + cx := statefull.ChainContext{Chain: chain, Bor: c} // check and commit span - if err := c.checkAndCommitSpan(state, header, cx); err != nil { + if err := c.checkAndCommitSpan(ctx, state, header, cx); err != nil { log.Error("Error while committing span", "error", err) return } if c.HeimdallClient != nil { // commit statees - stateSyncData, err = c.CommitStates(state, header, cx) + stateSyncData, err = c.CommitStates(ctx, state, header, cx) if err != nil { log.Error("Error while committing states", "error", err) return @@ -780,16 +785,18 @@ func (c *Bor) changeContractCodeIfNeeded(headerNumber uint64, state *state.State // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // nor block rewards given, and returns the final block. -func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { - stateSyncData := []*types.StateSyncData{} +func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, _ []*types.Header, receipts []*types.Receipt) (*types.Block, error) { + var stateSyncData []*types.StateSyncData headerNumber := header.Number.Uint64() if headerNumber%c.config.Sprint == 0 { + ctx := context.Background() + cx := statefull.ChainContext{Chain: chain, Bor: c} // check and commit span - err := c.checkAndCommitSpan(state, header, cx) + err := c.checkAndCommitSpan(ctx, state, header, cx) if err != nil { log.Error("Error while committing span", "error", err) return nil, err @@ -797,7 +804,7 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ if c.HeimdallClient != nil { // commit states - stateSyncData, err = c.CommitStates(state, header, cx) + stateSyncData, err = c.CommitStates(ctx, state, header, cx) if err != nil { log.Error("Error while committing states", "error", err) return nil, err @@ -932,7 +939,7 @@ func Sign(signFn SignerFn, signer common.Address, header *types.Header, c *param // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty // that a new block should have based on the previous blocks in the chain and the // current signer. -func (c *Bor) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { +func (c *Bor) CalcDifficulty(chain consensus.ChainHeaderReader, _ uint64, parent *types.Header) *big.Int { snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil) if err != nil { return nil @@ -969,37 +976,38 @@ func (c *Bor) Close() error { } func (c *Bor) checkAndCommitSpan( + ctx context.Context, state *state.StateDB, header *types.Header, chain core.ChainContext, ) error { headerNumber := header.Number.Uint64() - span, err := c.spanner.GetCurrentSpan(header.ParentHash) + currentSpan, err := c.spanner.GetCurrentSpan(ctx, header.ParentHash) if err != nil { return err } - if c.needToCommitSpan(span, headerNumber) { - return c.FetchAndCommitSpan(span.ID+1, state, header, chain) + if c.needToCommitSpan(currentSpan, headerNumber) { + return c.FetchAndCommitSpan(ctx, currentSpan.ID+1, state, header, chain) } return nil } -func (c *Bor) needToCommitSpan(span *span.Span, headerNumber uint64) bool { +func (c *Bor) needToCommitSpan(currentSpan *span.Span, headerNumber uint64) bool { // if span is nil - if span == nil { + if currentSpan == nil { return false } // check span is not set initially - if span.EndBlock == 0 { + if currentSpan.EndBlock == 0 { return true } // if current block is first block of last sprint in current span - if span.EndBlock > c.config.Sprint && span.EndBlock-c.config.Sprint+1 == headerNumber { + if currentSpan.EndBlock > c.config.Sprint && currentSpan.EndBlock-c.config.Sprint+1 == headerNumber { return true } @@ -1007,6 +1015,7 @@ func (c *Bor) needToCommitSpan(span *span.Span, headerNumber uint64) bool { } func (c *Bor) FetchAndCommitSpan( + ctx context.Context, newSpanID uint64, state *state.StateDB, header *types.Header, @@ -1016,14 +1025,14 @@ func (c *Bor) FetchAndCommitSpan( if c.HeimdallClient == nil { // fixme: move to a new mock or fake and remove c.HeimdallClient completely - s, err := c.getNextHeimdallSpanForTest(newSpanID, header, chain) + s, err := c.getNextHeimdallSpanForTest(ctx, newSpanID, header, chain) if err != nil { return err } heimdallSpan = *s } else { - response, err := c.HeimdallClient.Span(newSpanID) + response, err := c.HeimdallClient.Span(ctx, newSpanID) if err != nil { return err } @@ -1040,11 +1049,12 @@ func (c *Bor) FetchAndCommitSpan( ) } - return c.spanner.CommitSpan(heimdallSpan, state, header, chain) + return c.spanner.CommitSpan(ctx, heimdallSpan, state, header, chain) } // CommitStates commit states func (c *Bor) CommitStates( + ctx context.Context, state *state.StateDB, header *types.Header, chain statefull.ChainContext, @@ -1065,7 +1075,7 @@ func (c *Bor) CommitStates( "fromID", lastStateID+1, "to", to.Format(time.RFC3339)) - eventRecords, err := c.HeimdallClient.StateSyncEvents(lastStateID+1, to.Unix()) + eventRecords, err := c.HeimdallClient.StateSyncEvents(ctx, lastStateID+1, to.Unix()) if err != nil { log.Error("Error occurred when fetching state sync events", "stateID", lastStateID+1, "error", err) } @@ -1127,8 +1137,8 @@ func (c *Bor) SetHeimdallClient(h IHeimdallClient) { c.HeimdallClient = h } -func (c *Bor) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) { - return c.spanner.GetCurrentValidators(headerHash, blockNumber) +func (c *Bor) GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) { + return c.spanner.GetCurrentValidators(ctx, headerHash, blockNumber) } // @@ -1136,13 +1146,14 @@ func (c *Bor) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ( // func (c *Bor) getNextHeimdallSpanForTest( + ctx context.Context, newSpanID uint64, header *types.Header, chain core.ChainContext, ) (*span.HeimdallSpan, error) { headerNumber := header.Number.Uint64() - spanBor, err := c.spanner.GetCurrentSpan(header.ParentHash) + spanBor, err := c.spanner.GetCurrentSpan(ctx, header.ParentHash) if err != nil { return nil, err } diff --git a/consensus/bor/contract/client.go b/consensus/bor/contract/client.go index d65fb5bb15..dbf3fa0883 100644 --- a/consensus/bor/contract/client.go +++ b/consensus/bor/contract/client.go @@ -88,7 +88,7 @@ func (gc *GenesisContractsClient) CommitState( } msg := statefull.GetSystemMessage(common.HexToAddress(gc.StateReceiverContract), data) - gasUsed, err := statefull.ApplyMessage(msg, state, header, gc.chainConfig, chCtx) + gasUsed, err := statefull.ApplyMessage(context.Background(), msg, state, header, gc.chainConfig, chCtx) // Logging event log with time and individual gasUsed log.Info("→ committing new state", "eventRecord", event.String(gasUsed)) diff --git a/consensus/bor/heimdall.go b/consensus/bor/heimdall.go index 217de13fe9..e640e47d8a 100644 --- a/consensus/bor/heimdall.go +++ b/consensus/bor/heimdall.go @@ -1,6 +1,8 @@ package bor import ( + "context" + "github.com/ethereum/go-ethereum/consensus/bor/clerk" "github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint" "github.com/ethereum/go-ethereum/consensus/bor/heimdall/span" @@ -8,8 +10,8 @@ import ( //go:generate mockgen -destination=../../tests/bor/mocks/IHeimdallClient.go -package=mocks . IHeimdallClient type IHeimdallClient interface { - StateSyncEvents(fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) - Span(spanID uint64) (*span.HeimdallSpan, error) - FetchLatestCheckpoint() (*checkpoint.Checkpoint, error) + StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) + Span(ctx context.Context, spanID uint64) (*span.HeimdallSpan, error) + FetchLatestCheckpoint(ctx context.Context) (*checkpoint.Checkpoint, error) Close() } diff --git a/consensus/bor/heimdall/client.go b/consensus/bor/heimdall/client.go index 2d42cfc31b..64db4a6293 100644 --- a/consensus/bor/heimdall/client.go +++ b/consensus/bor/heimdall/client.go @@ -17,12 +17,17 @@ import ( "github.com/ethereum/go-ethereum/log" ) -// errShutdownDetected is returned if a shutdown was detected -var errShutdownDetected = errors.New("shutdown detected") +var ( + // ErrShutdownDetected is returned if a shutdown was detected + ErrShutdownDetected = errors.New("shutdown detected") + ErrNoResponse = errors.New("got a nil response") + ErrNotSuccessfulResponse = errors.New("error while fetching data from Heimdall") +) const ( stateFetchLimit = 50 apiHeimdallTimeout = 5 * time.Second + retryCall = 5 * time.Second ) type StateSyncEventsResponse struct { @@ -59,7 +64,7 @@ const ( fetchSpanFormat = "bor/span/%d" ) -func (h *HeimdallClient) StateSyncEvents(fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) { +func (h *HeimdallClient) StateSyncEvents(ctx context.Context, fromID uint64, to int64) ([]*clerk.EventRecordWithTime, error) { eventRecords := make([]*clerk.EventRecordWithTime, 0) for { @@ -70,7 +75,7 @@ func (h *HeimdallClient) StateSyncEvents(fromID uint64, to int64) ([]*clerk.Even log.Info("Fetching state sync events", "queryParams", url.RawQuery) - response, err := FetchWithRetry[StateSyncEventsResponse](h.client, url, h.closeCh) + response, err := FetchWithRetry[StateSyncEventsResponse](ctx, h.client, url, h.closeCh) if err != nil { return nil, err } @@ -96,13 +101,13 @@ func (h *HeimdallClient) StateSyncEvents(fromID uint64, to int64) ([]*clerk.Even return eventRecords, nil } -func (h *HeimdallClient) Span(spanID uint64) (*span.HeimdallSpan, error) { +func (h *HeimdallClient) Span(ctx context.Context, spanID uint64) (*span.HeimdallSpan, error) { url, err := spanURL(h.urlString, spanID) if err != nil { return nil, err } - response, err := FetchWithRetry[SpanResponse](h.client, url, h.closeCh) + response, err := FetchWithRetry[SpanResponse](ctx, h.client, url, h.closeCh) if err != nil { return nil, err } @@ -111,13 +116,13 @@ func (h *HeimdallClient) Span(spanID uint64) (*span.HeimdallSpan, error) { } // FetchLatestCheckpoint fetches the latest bor submitted checkpoint from heimdall -func (h *HeimdallClient) FetchLatestCheckpoint() (*checkpoint.Checkpoint, error) { +func (h *HeimdallClient) FetchLatestCheckpoint(ctx context.Context) (*checkpoint.Checkpoint, error) { url, err := latestCheckpointURL(h.urlString) if err != nil { return nil, err } - response, err := FetchWithRetry[checkpoint.CheckpointResponse](h.client, url, h.closeCh) + response, err := FetchWithRetry[checkpoint.CheckpointResponse](ctx, h.client, url, h.closeCh) if err != nil { return nil, err } @@ -126,58 +131,75 @@ func (h *HeimdallClient) FetchLatestCheckpoint() (*checkpoint.Checkpoint, error) } // FetchWithRetry returns data from heimdall with retry -func FetchWithRetry[T any](client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) { - // attempt counter - attempt := 1 - result := new(T) - - ctx, cancel := context.WithTimeout(context.Background(), apiHeimdallTimeout) - +func FetchWithRetry[T any](ctx context.Context, client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) { // request data once - body, err := internalFetch(ctx, client, url) - - cancel() - - if err == nil && body != nil { - err = json.Unmarshal(body, result) - if err != nil { - return nil, err - } - + result, err := Fetch[T](ctx, client, url) + if err == nil { return result, nil } + // attempt counter + attempt := 1 + + log.Warn("an error while trying fetching from Heimdall", "attempt", attempt, "error", err) + // create a new ticker for retrying the request - ticker := time.NewTicker(5 * time.Second) + ticker := time.NewTicker(retryCall) defer ticker.Stop() + const logEach = 5 + +retryLoop: for { log.Info("Retrying again in 5 seconds to fetch data from Heimdall", "path", url.Path, "attempt", attempt) + attempt++ + select { + case <-ctx.Done(): + log.Debug("Shutdown detected, terminating request by context.Done") + + return nil, ctx.Err() case <-closeCh: - log.Debug("Shutdown detected, terminating request") + log.Debug("Shutdown detected, terminating request by closing") - return nil, errShutdownDetected + return nil, ErrShutdownDetected case <-ticker.C: - ctx, cancel = context.WithTimeout(context.Background(), apiHeimdallTimeout) - - body, err = internalFetch(ctx, client, url) - - cancel() - - if err == nil && body != nil { - err = json.Unmarshal(body, result) - if err != nil { - return nil, err + result, err = Fetch[T](ctx, client, url) + if err != nil { + if attempt%logEach == 0 { + log.Warn("an error while trying fetching from Heimdall", "attempt", attempt, "error", err) } - return result, nil + continue retryLoop } + + return result, nil } } } +// Fetch returns data from heimdall +func Fetch[T any](ctx context.Context, client http.Client, url *url.URL) (*T, error) { + result := new(T) + + body, err := internalFetchWithTimeout(ctx, client, url) + if err != nil { + return nil, err + } + + if body == nil { + return nil, ErrNoResponse + } + + err = json.Unmarshal(body, result) + if err != nil { + return nil, err + } + + return result, nil +} + func spanURL(urlString string, spanID uint64) (*url.URL, error) { return makeURL(urlString, fmt.Sprintf(fetchSpanFormat, spanID), "") } @@ -215,11 +237,12 @@ func internalFetch(ctx context.Context, client http.Client, u *url.URL) ([]byte, if err != nil { return nil, err } + defer res.Body.Close() // check status code if res.StatusCode != 200 && res.StatusCode != 204 { - return nil, fmt.Errorf("Error while fetching data from Heimdall") + return nil, fmt.Errorf("%w: response code %d", ErrNotSuccessfulResponse, res.StatusCode) } // unmarshall data from buffer @@ -236,6 +259,14 @@ func internalFetch(ctx context.Context, client http.Client, u *url.URL) ([]byte, return body, nil } +func internalFetchWithTimeout(ctx context.Context, client http.Client, url *url.URL) ([]byte, error) { + ctx, cancel := context.WithTimeout(ctx, apiHeimdallTimeout) + defer cancel() + + // request data once + return internalFetch(ctx, client, url) +} + // Close sends a signal to stop the running process func (h *HeimdallClient) Close() { close(h.closeCh) diff --git a/consensus/bor/heimdall/span/spanner.go b/consensus/bor/heimdall/span/spanner.go index 7bf6e350ee..1007b19f03 100644 --- a/consensus/bor/heimdall/span/spanner.go +++ b/consensus/bor/heimdall/span/spanner.go @@ -39,7 +39,7 @@ func NewChainSpanner(ethAPI api.Caller, validatorSet abi.ABI, chainConfig *param } // GetCurrentSpan get current span from contract -func (c *ChainSpanner) GetCurrentSpan(headerHash common.Hash) (*Span, error) { +func (c *ChainSpanner) GetCurrentSpan(ctx context.Context, headerHash common.Hash) (*Span, error) { // block blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false) @@ -58,7 +58,7 @@ func (c *ChainSpanner) GetCurrentSpan(headerHash common.Hash) (*Span, error) { gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) // todo: would we like to have a timeout here? - result, err := c.ethAPI.Call(context.Background(), ethapi.TransactionArgs{ + result, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{ Gas: &gas, To: &toAddress, Data: &msgData, @@ -89,8 +89,8 @@ func (c *ChainSpanner) GetCurrentSpan(headerHash common.Hash) (*Span, error) { } // GetCurrentValidators get current validators -func (c *ChainSpanner) GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) { - ctx, cancel := context.WithCancel(context.Background()) +func (c *ChainSpanner) GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) { + ctx, cancel := context.WithCancel(ctx) defer cancel() // method @@ -146,7 +146,7 @@ func (c *ChainSpanner) GetCurrentValidators(headerHash common.Hash, blockNumber const method = "commitSpan" -func (c *ChainSpanner) CommitSpan(heimdallSpan HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error { +func (c *ChainSpanner) CommitSpan(ctx context.Context, heimdallSpan HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error { // get validators bytes validators := make([]valset.MinimalVal, 0, len(heimdallSpan.ValidatorSet.Validators)) for _, val := range heimdallSpan.ValidatorSet.Validators { @@ -194,7 +194,7 @@ func (c *ChainSpanner) CommitSpan(heimdallSpan HeimdallSpan, state *state.StateD msg := statefull.GetSystemMessage(c.validatorContractAddress, data) // apply message - _, err = statefull.ApplyMessage(msg, state, header, c.chainConfig, chainContext) + _, err = statefull.ApplyMessage(ctx, msg, state, header, c.chainConfig, chainContext) return err } diff --git a/consensus/bor/span.go b/consensus/bor/span.go index 4867635b8e..86a58fa42e 100644 --- a/consensus/bor/span.go +++ b/consensus/bor/span.go @@ -1,6 +1,8 @@ package bor import ( + "context" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/bor/heimdall/span" "github.com/ethereum/go-ethereum/consensus/bor/valset" @@ -11,7 +13,7 @@ import ( //go:generate mockgen -destination=./span_mock.go -package=bor . Spanner type Spanner interface { - GetCurrentSpan(headerHash common.Hash) (*span.Span, error) - GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) - CommitSpan(heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error + GetCurrentSpan(ctx context.Context, headerHash common.Hash) (*span.Span, error) + GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) + CommitSpan(ctx context.Context, heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error } diff --git a/consensus/bor/span_mock.go b/consensus/bor/span_mock.go index 12ed945234..6d5f62e25d 100644 --- a/consensus/bor/span_mock.go +++ b/consensus/bor/span_mock.go @@ -5,6 +5,7 @@ package bor import ( + context "context" reflect "reflect" common "github.com/ethereum/go-ethereum/common" @@ -40,45 +41,45 @@ func (m *MockSpanner) EXPECT() *MockSpannerMockRecorder { } // CommitSpan mocks base method. -func (m *MockSpanner) CommitSpan(arg0 span.HeimdallSpan, arg1 *state.StateDB, arg2 *types.Header, arg3 core.ChainContext) error { +func (m *MockSpanner) CommitSpan(arg0 context.Context, arg1 span.HeimdallSpan, arg2 *state.StateDB, arg3 *types.Header, arg4 core.ChainContext) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CommitSpan", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "CommitSpan", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(error) return ret0 } // CommitSpan indicates an expected call of CommitSpan. -func (mr *MockSpannerMockRecorder) CommitSpan(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockSpannerMockRecorder) CommitSpan(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), arg0, arg1, arg2, arg3, arg4) } // GetCurrentSpan mocks base method. -func (m *MockSpanner) GetCurrentSpan(arg0 common.Hash) (*span.Span, error) { +func (m *MockSpanner) GetCurrentSpan(arg0 context.Context, arg1 common.Hash) (*span.Span, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentSpan", arg0) + ret := m.ctrl.Call(m, "GetCurrentSpan", arg0, arg1) ret0, _ := ret[0].(*span.Span) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentSpan indicates an expected call of GetCurrentSpan. -func (mr *MockSpannerMockRecorder) GetCurrentSpan(arg0 interface{}) *gomock.Call { +func (mr *MockSpannerMockRecorder) GetCurrentSpan(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSpan", reflect.TypeOf((*MockSpanner)(nil).GetCurrentSpan), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSpan", reflect.TypeOf((*MockSpanner)(nil).GetCurrentSpan), arg0, arg1) } // GetCurrentValidators mocks base method. -func (m *MockSpanner) GetCurrentValidators(arg0 common.Hash, arg1 uint64) ([]*valset.Validator, error) { +func (m *MockSpanner) GetCurrentValidators(arg0 context.Context, arg1 common.Hash, arg2 uint64) ([]*valset.Validator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1) + ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1, arg2) ret0, _ := ret[0].([]*valset.Validator) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCurrentValidators indicates an expected call of GetCurrentValidators. -func (mr *MockSpannerMockRecorder) GetCurrentValidators(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockSpannerMockRecorder) GetCurrentValidators(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidators", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidators), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidators", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidators), arg0, arg1, arg2) } diff --git a/consensus/bor/statefull/processor.go b/consensus/bor/statefull/processor.go index e30fb4fd21..c87d4f4ff4 100644 --- a/consensus/bor/statefull/processor.go +++ b/consensus/bor/statefull/processor.go @@ -1,6 +1,7 @@ package statefull import ( + "context" "math" "math/big" @@ -59,6 +60,7 @@ func GetSystemMessage(toAddress common.Address, data []byte) callmsg { // apply message func ApplyMessage( + _ context.Context, msg callmsg, state *state.StateDB, header *types.Header, diff --git a/core/tests/blockchain_repair_test.go b/core/tests/blockchain_repair_test.go index 8e86e4721d..15ab67dd1c 100644 --- a/core/tests/blockchain_repair_test.go +++ b/core/tests/blockchain_repair_test.go @@ -1796,7 +1796,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() spanner := bor.NewMockSpanner(ctrl) - spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any()).Return([]*valset.Validator{ + spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{ { ID: 0, Address: miner.TestBankAddress, diff --git a/eth/backend.go b/eth/backend.go index 0b8a956cfa..e3376056a2 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -18,6 +18,7 @@ package eth import ( + "context" "errors" "fmt" "math/big" @@ -632,7 +633,8 @@ func (s *Ethereum) startCheckpointWhitelistService() { } // first run the checkpoint whitelist - err := s.handleWhitelistCheckpoint() + // TODO: add context timeout if needed + err := s.handleWhitelistCheckpoint(context.Background()) if err != nil { if errors.Is(err, ErrBorConsensusWithoutHeimdall) || errors.Is(err, ErrNotBorConsensus) { return @@ -647,7 +649,8 @@ func (s *Ethereum) startCheckpointWhitelistService() { for { select { case <-ticker.C: - err := s.handleWhitelistCheckpoint() + // TODO: add context timeout if needed + err = s.handleWhitelistCheckpoint(context.Background()) if err != nil { log.Warn("unable to whitelist checkpoint", "err", err) } @@ -663,7 +666,7 @@ var ( ) // handleWhitelistCheckpoint handles the checkpoint whitelist mechanism. -func (s *Ethereum) handleWhitelistCheckpoint() error { +func (s *Ethereum) handleWhitelistCheckpoint(ctx context.Context) error { ethHandler := (*ethHandler)(s.handler) bor, ok := ethHandler.chain.Engine().(*bor.Bor) @@ -675,7 +678,7 @@ func (s *Ethereum) handleWhitelistCheckpoint() error { return ErrBorConsensusWithoutHeimdall } - endBlockNum, endBlockHash, err := ethHandler.fetchWhitelistCheckpoint(bor) + endBlockNum, endBlockHash, err := ethHandler.fetchWhitelistCheckpoint(ctx, bor) if err != nil { return err } diff --git a/eth/handler_bor.go b/eth/handler_bor.go index 11896f3c47..eca4b0c7eb 100644 --- a/eth/handler_bor.go +++ b/eth/handler_bor.go @@ -35,9 +35,9 @@ var ( // fetchWhitelistCheckpoint fetched the latest checkpoint from it's local heimdall // and verifies the data against bor data. -func (h *ethHandler) fetchWhitelistCheckpoint(bor *bor.Bor) (uint64, common.Hash, error) { +func (h *ethHandler) fetchWhitelistCheckpoint(ctx context.Context, bor *bor.Bor) (uint64, common.Hash, error) { // check for checkpoint whitelisting: bor - checkpoint, err := bor.HeimdallClient.FetchLatestCheckpoint() + checkpoint, err := bor.HeimdallClient.FetchLatestCheckpoint(ctx) if err != nil { log.Debug("Failed to fetch latest checkpoint for whitelisting") return 0, common.Hash{}, errCheckpoint @@ -51,7 +51,7 @@ func (h *ethHandler) fetchWhitelistCheckpoint(bor *bor.Bor) (uint64, common.Hash } // verify the root hash of checkpoint - roothash, err := h.ethAPI.GetRootHash(context.Background(), checkpoint.StartBlock.Uint64(), checkpoint.EndBlock.Uint64()) + roothash, err := h.ethAPI.GetRootHash(ctx, checkpoint.StartBlock.Uint64(), checkpoint.EndBlock.Uint64()) if err != nil { log.Debug("Failed to get root hash of checkpoint while whitelisting") return 0, common.Hash{}, errRootHash diff --git a/miner/fake_miner.go b/miner/fake_miner.go index 7fcdbc3299..43b397c428 100644 --- a/miner/fake_miner.go +++ b/miner/fake_miner.go @@ -47,7 +47,7 @@ func NewBorDefaultMiner(t *testing.T) *DefaultBorMiner { ethAPI.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() spanner := bor.NewMockSpanner(ctrl) - spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any()).Return([]*valset.Validator{ + spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{ { ID: 0, Address: common.Address{0x1}, diff --git a/miner/worker_test.go b/miner/worker_test.go index 72f9287af6..9bb2a538ce 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -75,7 +75,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool, isBor bool) { ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() spanner := bor.NewMockSpanner(ctrl) - spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any()).Return([]*valset.Validator{ + spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{ { ID: 0, Address: TestBankAddress, @@ -622,7 +622,7 @@ func BenchmarkBorMining(b *testing.B) { ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() spanner := bor.NewMockSpanner(ctrl) - spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any()).Return([]*valset.Validator{ + spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{ { ID: 0, Address: TestBankAddress, diff --git a/tests/bor/bor_test.go b/tests/bor/bor_test.go index 36d515c557..d179c6ea9b 100644 --- a/tests/bor/bor_test.go +++ b/tests/bor/bor_test.go @@ -1,9 +1,9 @@ //go:build integration -// +build integration package bor import ( + "context" "encoding/hex" "io" "math/big" @@ -41,11 +41,11 @@ func TestInsertingSpanSizeBlocks(t *testing.T) { h, heimdallSpan, ctrl := getMockedHeimdallClient(t) defer ctrl.Finish() - _, span := loadSpanFromFile(t) + _, currentSpan := loadSpanFromFile(t) h.EXPECT().Close().AnyTimes() - h.EXPECT().FetchLatestCheckpoint().Return(&checkpoint.Checkpoint{ - Proposer: span.SelectedProducers[0].Address, + h.EXPECT().FetchLatestCheckpoint(gomock.Any()).Return(&checkpoint.Checkpoint{ + Proposer: currentSpan.SelectedProducers[0].Address, StartBlock: big.NewInt(0), EndBlock: big.NewInt(int64(spanSize)), }, nil).AnyTimes() @@ -62,7 +62,7 @@ func TestInsertingSpanSizeBlocks(t *testing.T) { insertNewBlock(t, chain, block) } - validators, err := _bor.GetCurrentValidators(block.Hash(), spanSize) // check validator set at the first block of new span + validators, err := _bor.GetCurrentValidators(context.Background(), block.Hash(), spanSize) // check validator set at the first block of new span if err != nil { t.Fatalf("%s", err) } @@ -100,7 +100,7 @@ func TestFetchStateSyncEvents(t *testing.T) { h := mocks.NewMockIHeimdallClient(ctrl) h.EXPECT().Close().AnyTimes() - h.EXPECT().Span(uint64(1)).Return(&res.Result, nil).AnyTimes() + h.EXPECT().Span(gomock.Any(), uint64(1)).Return(&res.Result, nil).AnyTimes() // B.2 Mock State Sync events fromID := uint64(1) @@ -112,7 +112,7 @@ func TestFetchStateSyncEvents(t *testing.T) { sample.Time = time.Unix(to-int64(eventCount+1), 0) // last event.Time will be just < to eventRecords := generateFakeStateSyncEvents(sample, eventCount) - h.EXPECT().StateSyncEvents(fromID, to).Return(eventRecords, nil).AnyTimes() + h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).AnyTimes() _bor.SetHeimdallClient(h) block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor) @@ -135,7 +135,7 @@ func TestFetchStateSyncEvents_2(t *testing.T) { h := mocks.NewMockIHeimdallClient(ctrl) h.EXPECT().Close().AnyTimes() - h.EXPECT().Span(uint64(1)).Return(&res.Result, nil).AnyTimes() + h.EXPECT().Span(gomock.Any(), uint64(1)).Return(&res.Result, nil).AnyTimes() // Mock State Sync events // at # sprintSize, events are fetched for [fromID, (block-sprint).Time) @@ -154,7 +154,7 @@ func TestFetchStateSyncEvents_2(t *testing.T) { buildStateEvent(sample, 6, 4), // id = 6, time = 4 } - h.EXPECT().StateSyncEvents(fromID, to).Return(eventRecords, nil).AnyTimes() + h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).AnyTimes() _bor.SetHeimdallClient(h) // Insert blocks for 0th sprint @@ -178,7 +178,7 @@ func TestFetchStateSyncEvents_2(t *testing.T) { buildStateEvent(sample, 5, 7), buildStateEvent(sample, 6, 4), } - h.EXPECT().StateSyncEvents(fromID, to).Return(eventRecords, nil).AnyTimes() + h.EXPECT().StateSyncEvents(gomock.Any(), fromID, to).Return(eventRecords, nil).AnyTimes() for i := sprintSize + 1; i <= spanSize; i++ { block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor) @@ -227,8 +227,10 @@ func TestOutOfTurnSigning(t *testing.T) { expectedDifficulty := uint64(3 - expectedSuccessionNumber) // len(validators) - succession header := block.Header() - header.Time += (bor.CalcProducerDelay(header.Number.Uint64(), expectedSuccessionNumber, init.genesis.Config.Bor) - - bor.CalcProducerDelay(header.Number.Uint64(), 0, init.genesis.Config.Bor)) + + header.Time += bor.CalcProducerDelay(header.Number.Uint64(), expectedSuccessionNumber, init.genesis.Config.Bor) - + bor.CalcProducerDelay(header.Number.Uint64(), 0, init.genesis.Config.Bor) + sign(t, header, signerKey, init.genesis.Config.Bor) block = types.NewBlockWithHeader(header) @@ -282,9 +284,9 @@ func getMockedHeimdallClient(t *testing.T) (*mocks.MockIHeimdallClient, *span.He _, heimdallSpan := loadSpanFromFile(t) - h.EXPECT().Span(uint64(1)).Return(heimdallSpan, nil).AnyTimes() + h.EXPECT().Span(gomock.Any(), uint64(1)).Return(heimdallSpan, nil).AnyTimes() - h.EXPECT().StateSyncEvents(gomock.Any(), gomock.Any()). + h.EXPECT().StateSyncEvents(gomock.Any(), gomock.Any(), gomock.Any()). Return([]*clerk.EventRecordWithTime{getSampleEventRecord(t)}, nil).AnyTimes() return h, heimdallSpan, ctrl diff --git a/tests/bor/mocks/IHeimdallClient.go b/tests/bor/mocks/IHeimdallClient.go index f770ed9fa8..15e6a70990 100644 --- a/tests/bor/mocks/IHeimdallClient.go +++ b/tests/bor/mocks/IHeimdallClient.go @@ -5,6 +5,7 @@ package mocks import ( + context "context" reflect "reflect" clerk "github.com/ethereum/go-ethereum/consensus/bor/clerk" @@ -49,46 +50,46 @@ func (mr *MockIHeimdallClientMockRecorder) Close() *gomock.Call { } // FetchLatestCheckpoint mocks base method. -func (m *MockIHeimdallClient) FetchLatestCheckpoint() (*checkpoint.Checkpoint, error) { +func (m *MockIHeimdallClient) FetchLatestCheckpoint(arg0 context.Context) (*checkpoint.Checkpoint, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchLatestCheckpoint") + ret := m.ctrl.Call(m, "FetchLatestCheckpoint", arg0) ret0, _ := ret[0].(*checkpoint.Checkpoint) ret1, _ := ret[1].(error) return ret0, ret1 } // FetchLatestCheckpoint indicates an expected call of FetchLatestCheckpoint. -func (mr *MockIHeimdallClientMockRecorder) FetchLatestCheckpoint() *gomock.Call { +func (mr *MockIHeimdallClientMockRecorder) FetchLatestCheckpoint(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchLatestCheckpoint", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchLatestCheckpoint)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchLatestCheckpoint", reflect.TypeOf((*MockIHeimdallClient)(nil).FetchLatestCheckpoint), arg0) } // Span mocks base method. -func (m *MockIHeimdallClient) Span(arg0 uint64) (*span.HeimdallSpan, error) { +func (m *MockIHeimdallClient) Span(arg0 context.Context, arg1 uint64) (*span.HeimdallSpan, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Span", arg0) + ret := m.ctrl.Call(m, "Span", arg0, arg1) ret0, _ := ret[0].(*span.HeimdallSpan) ret1, _ := ret[1].(error) return ret0, ret1 } // Span indicates an expected call of Span. -func (mr *MockIHeimdallClientMockRecorder) Span(arg0 interface{}) *gomock.Call { +func (mr *MockIHeimdallClientMockRecorder) Span(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Span", reflect.TypeOf((*MockIHeimdallClient)(nil).Span), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Span", reflect.TypeOf((*MockIHeimdallClient)(nil).Span), arg0, arg1) } // StateSyncEvents mocks base method. -func (m *MockIHeimdallClient) StateSyncEvents(arg0 uint64, arg1 int64) ([]*clerk.EventRecordWithTime, error) { +func (m *MockIHeimdallClient) StateSyncEvents(arg0 context.Context, arg1 uint64, arg2 int64) ([]*clerk.EventRecordWithTime, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StateSyncEvents", arg0, arg1) + ret := m.ctrl.Call(m, "StateSyncEvents", arg0, arg1, arg2) ret0, _ := ret[0].([]*clerk.EventRecordWithTime) ret1, _ := ret[1].(error) return ret0, ret1 } // StateSyncEvents indicates an expected call of StateSyncEvents. -func (mr *MockIHeimdallClientMockRecorder) StateSyncEvents(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockIHeimdallClientMockRecorder) StateSyncEvents(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateSyncEvents", reflect.TypeOf((*MockIHeimdallClient)(nil).StateSyncEvents), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateSyncEvents", reflect.TypeOf((*MockIHeimdallClient)(nil).StateSyncEvents), arg0, arg1, arg2) }