Skip to content

fix(consensus/XDPoS): keep only full verifications in the verified-header cache - #2570

Open
gzliudan wants to merge 1 commit into
XinFinOrg:dev-upgradefrom
gzliudan:fix-verified-header-cache
Open

gzliudan wants to merge 1 commit into
XinFinOrg:dev-upgradefrom
gzliudan:fix-verified-header-cache

Conversation

@gzliudan

@gzliudan gzliudan commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

XDPoS_v1.verifiedHeaders and XDPoS_v2.verifiedHeaders remember a header as verified, keyed by its hash, and every caller answers nil from the cache before running a single check. The fullVerify flag that selects how much of the verification runs is not part of that key, so a header admitted by a reduced check was remembered as if it had passed all of them - and the next caller asking for full verification was answered from the cache without ever looking at the validator signature, the double validation of v1, or the masternode set a v2 epoch-switch block claims for itself.

Both engines now only remember a header when the full verification passed. Consulting the cache under a reduced check stays correct, because passing the full set covers the reduced one.

 	err := x.verifyHeader(chain, header, parents, fullVerify)
-	if err == nil {
+	if err == nil && fullVerify {
 		x.verifiedHeaders.Add(header.Hash(), struct{}{})
 	}

Behaviour and risk

  • The change only narrows what may be stored; cache hits under a reduced check are untouched, so it can only cause more verification work, never less - the fail-safe direction.
  • The cost is that a header previously admitted by a reduced check is verified again when a full verification is requested for it. The set is small (a pruned sidechain segment, a reorg replay, the testnet shouldDisableFullVerify path), and the cache is an LRU bounded by utils.InMemorySnapshots.

Tests

consensus/XDPoS/engines/engine_v1/verify_header_test.go: TestVerifiedHeadersOnlyRemembersFullVerification turns on SkipV1Validation so that both levels succeed without a chain to read, which makes the verification level the only difference between the two calls - the reduced call must leave no entry behind, the full one must.

Removing the && fullVerify guard makes it fail with a reduced verification must not be remembered as a full one.

The v2 side is the symmetric change and has no dedicated test: building a header that passes the whole v2 verification is too expensive for a test of the cache.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 283664fc-6a3d-4abb-a2c0-e78c4914f087

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The v1 testnet override still allows an effectively reduced verification result to enter the cache.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR prevents reduced XDPoS header verification results from being cached as fully verified.

Changes:

  • Restricts v1 and v2 cache insertion to full verification.
  • Adds a v1 regression test for reduced and full verification caching.
File summaries
File Description
engine_v1/engine.go Guards verified-header cache insertion.
engine_v1/verify_header_test.go Tests cache behavior by verification level.
engine_v2/verifyHeader.go Applies the equivalent v2 cache guard.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread consensus/XDPoS/engines/engine_v1/engine.go Outdated
@gzliudan
gzliudan force-pushed the fix-verified-header-cache branch 2 times, most recently from 85f16c1 to e3062ec Compare September 15, 2026 04:33
…ader cache

verifiedHeaders remembers a header as verified, keyed by its hash, and both
the single-header and the batch path answer nil from it before running any
check. The flag that selects full verification is not part of that key, so a
header admitted by a reduced check was remembered as if it had passed all of
them, and the next caller to ask for full verification was answered from the
cache without looking at the validator signature, the double validation of v1
or the masternode set a v2 epoch-switch block claims for itself.

Only remember a header when the full verification passed. Consulting the cache
under a reduced check stays correct, because passing the full set covers it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants