Reject proofs without header - #7805
Conversation
| epoch := rrh.getEpoch() | ||
| rrh.RequestEquivalentProofByNonceForEpoch(headerShard, headerNonce, epoch) | ||
| go func(requestEpoch uint32) { | ||
| time.Sleep(rrh.requestProofByNonceDelay) |
There was a problem hiding this comment.
- i'm thinking if it might affect
requestHeadersIfSyncIsStuck- currently it requests by nonce up to 20 proofs each time; 100ms sleep per 20 requests should not be a delay issue; but maybe number of goroutines? at least to not forget the connection withMaxHeadersToRequestInAdvanceconst which may affect if we increase it
There was a problem hiding this comment.
indeed this may grow
There was a problem hiding this comment.
Pull request overview
This PR tightens equivalent proof validation by rejecting proofs whose referenced header is not available locally, and adds an optional delay for proof-by-nonce requests to reduce the chance of requesting/receiving proofs before headers.
Changes:
- Inject
HeadersPoolinto the equivalent proofs interceptor path and validate header presence + round match duringCheckValidity(). - Add
RequestProofByNonceDelayMsconfig and implement delayedRequestEquivalentProofByNoncescheduling. - Update chain simulator, integration wiring, and tests to account for the new dependency and delayed behavior.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| process/interceptors/factory/interceptedEquivalentProofsFactory_test.go | Updates factory test args to include HeadersPool. |
| process/interceptors/factory/interceptedEquivalentProofsFactory.go | Extends factory args/state to pass HeadersPool into intercepted equivalent proofs. |
| process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go | Wires HeadersPool from datapool into equivalent proofs interceptor factory. |
| process/block/interceptedBlocks/interceptedEquivalentProof_test.go | Adds headers pool nil-check test and “missing header” validity test; updates mocks to include HeadersPool. |
| process/block/interceptedBlocks/interceptedEquivalentProof.go | Enforces “header must exist” and round match before accepting/storing proofs. |
| node/chainSimulator/configs/configs.go | Sets RequestProofByNonceDelayMs for simulator configs. |
| node/chainSimulator/chainSimulator.go | Adds a small delay before broadcasting proofs to avoid ordering drop in simulator. |
| integrationTests/testProcessorNode.go | Updates request handler construction with new delay argument. |
| integrationTests/testHeartbeatNode.go | Updates request handler construction with new delay argument. |
| factory/processing/processComponents.go | Passes configured proof-by-nonce delay into NewResolverRequestHandler. |
| epochStart/bootstrap/syncEpochStartMeta_test.go | Adds headers pool to epoch start syncer test args. |
| epochStart/bootstrap/syncEpochStartMeta.go | Wires headers pool into equivalent proofs factory during epoch start meta sync. |
| epochStart/bootstrap/storageProcess.go | Passes headers pool + requester delay into epoch start bootstrap components. |
| epochStart/bootstrap/process.go | Passes headers pool + requester delay into epoch start bootstrap components. |
| dataRetriever/requestHandlers/requestHandler_test.go | Updates request handler constructor calls; adds sleep to accommodate delayed nonce-proof requests. |
| dataRetriever/requestHandlers/requestHandler.go | Adds requestProofByNonceDelay and delays RequestEquivalentProofByNonce via goroutine + sleep. |
| config/config.go | Introduces RequesterConfig.RequestProofByNonceDelayMs. |
| cmd/node/config/config.toml | Adds RequestProofByNonceDelayMs to node config with documentation comment. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if err != nil { | ||
| log.Trace("Intercepted equivalent proof with missing header, dropping it", "proof header hash", iep.proof.GetHeaderHash()) | ||
| return err | ||
| } |
There was a problem hiding this comment.
GetHeaderByHash can legally return a (nil, nil) pair for some implementations/stubs (e.g. HeadersPoolStub), which will panic on header.GetRound(). Guard against header == nil and treat it as a missing header (return an appropriate error) before dereferencing it.
| } | |
| } | |
| if header == nil { | |
| log.Trace("Intercepted equivalent proof with missing header, dropping it", "proof header hash", iep.proof.GetHeaderHash()) | |
| return fmt.Errorf("missing header for hash %v", iep.proof.GetHeaderHash()) | |
| } |
| HeadersPool: &pool.HeadersPoolStub{}, | ||
| Hasher: &hashingMocks.HasherMock{}, | ||
| ProofSizeChecker: &testscommon.FieldsSizeCheckerMock{}, |
There was a problem hiding this comment.
createMockArgInterceptedEquivalentProof uses HeadersPoolStub with default behavior (GetHeaderByHash returns (nil, nil)), but CheckValidity() now requires a real header and will dereference it. Update the stub in this helper to return a non-nil data.HeaderHandler with Round == providedRound (or make each test override GetHeaderByHashCalled) so the existing "should work" / concurrency tests don't panic.
| HeadersPool: &pool.HeadersPoolStub{}, | |
| Hasher: &hashingMocks.HasherMock{}, | |
| ProofSizeChecker: &testscommon.FieldsSizeCheckerMock{}, | |
| HeadersPool: &pool.HeadersPoolStub{ | |
| GetHeaderByHashCalled: func(hash []byte) (data.HeaderHandler, error) { | |
| return &block.Header{Round: providedRound}, nil | |
| }, | |
| }, | |
| Hasher: &hashingMocks.HasherMock{}, | |
| ProofSizeChecker: &testscommon.FieldsSizeCheckerMock{}, |
| func (rrh *resolverRequestHandler) RequestEquivalentProofByNonce(headerShard uint32, headerNonce uint64) { | ||
| epoch := rrh.getEpoch() | ||
| rrh.RequestEquivalentProofByNonceForEpoch(headerShard, headerNonce, epoch) | ||
| go func(requestEpoch uint32) { | ||
| time.Sleep(rrh.requestProofByNonceDelay) | ||
| rrh.RequestEquivalentProofByNonceForEpoch(headerShard, headerNonce, requestEpoch) | ||
| }(epoch) |
There was a problem hiding this comment.
RequestEquivalentProofByNonce now spawns a goroutine and delays the call without marking the item as requested first. Repeated calls within the delay window will schedule additional goroutines (and potentially duplicate network requests due to the non-atomic testIfRequestIsNeeded + addRequestedItems sequence). Consider registering the request key as requested before scheduling, or using a single scheduler/worker to coalesce requests per (shard,nonce).
| rrh.RequestEquivalentProofByNonce(shardID, requestNonce) | ||
| time.Sleep(time.Millisecond * 5) | ||
| require.True(t, wasCalled) |
There was a problem hiding this comment.
This test now relies on a fixed time.Sleep(5ms) to wait for the delayed goroutine in RequestEquivalentProofByNonce, which is timing-sensitive and can flake under load/slow CI. Prefer synchronizing on a channel/WaitGroup (e.g. signal from RequestDataFromNonceCalled) or using an eventual assertion with a timeout instead of a hard-coded sleep.
There was a problem hiding this comment.
should be enough
| } | ||
|
|
||
| if !check.IfNil(pair.Proof) { | ||
| time.Sleep(time.Millisecond * 5) // small delay to ensure proof is not dropped as being received before header |
There was a problem hiding this comment.
The hard-coded time.Sleep(5ms) to ensure the proof isn't received before the header is a brittle ordering mechanism and can cause simulator flakiness on slower machines. Prefer enforcing ordering explicitly (e.g., broadcast proof only after the header is confirmed added/processed, or gate on a callback/channel) or make the delay configurable/reused from RequestProofByNonceDelayMs.
| time.Sleep(time.Millisecond * 5) // small delay to ensure proof is not dropped as being received before header | |
| time.Sleep(time.Duration(configs.RequestProofByNonceDelayMs) * time.Millisecond) // delay to ensure proof is not dropped as being received before header |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## feat/supernova-async-exec #7805 +/- ##
=============================================================
- Coverage 77.56% 77.54% -0.02%
=============================================================
Files 882 882
Lines 123719 123751 +32
=============================================================
+ Hits 95958 95964 +6
- Misses 21402 21424 +22
- Partials 6359 6363 +4 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| return err | ||
| } | ||
|
|
||
| if !check.IfNil(header) && header.GetRound() != iep.proof.GetHeaderRound() { |
There was a problem hiding this comment.
can you check also the nonce and epoch?
Reasoning behind the pull request
Proposed changes
Testing procedure
Pre-requisites
Based on the Contributing Guidelines the PR author and the reviewers must check the following requirements are met:
featbranch created?featbranch merging, do all satellite projects have a proper tag insidego.mod?