Skip to content

SIP-5 draft: block hash + state root commitment consistency (Bug B fix)#2

Merged
satyakwok merged 2 commits into
mainfrom
feat/sip-5-bug-b-block-hash-state-root
May 31, 2026
Merged

SIP-5 draft: block hash + state root commitment consistency (Bug B fix)#2
satyakwok merged 2 commits into
mainfrom
feat/sip-5-bug-b-block-hash-state-root

Conversation

@satyakwok
Copy link
Copy Markdown
Member

@satyakwok satyakwok commented May 31, 2026

Draft SIP for Bug B fix (2026-05-31 testnet cascade). See sips/sip-5.md for full text. Approaches B1 (speculative apply, recommended) vs B4 (drop state_root from hash). Activation plan, rollback, review questions inside. Related: sentrix-labs/sentrix#751 (Bug B), #750 (Bug A), #752 (defense check).

Summary by CodeRabbit

  • Bug Fixes

    • Fork-gated consensus fix to prevent stored blocks from becoming inconsistent between block hash and claimed state root; includes recommended approach for proposers/validators and an alternative trade-off.
  • Documentation

    • Added SIP-5: motivation, activation/migration and rollback plans, compatibility guarantees, security considerations, open questions, and a reference implementation sketch.

Discovered 2026-05-31 testnet h=5,817,132 cascade incident. After Bug A
(off-trie state drift, #750) caused state divergence at h=5,817,131, the
block at h=5,817,132 had internally-inconsistent block.hash vs
justification.block_hash.

Root cause: block_executor.rs:1561-1563 stamps state_root post-apply +
recomputes block.hash. Embedded justification.block_hash stays at the
pre-recompute hash. Latent — chain progression works on stored block.hash;
strict_justification fork inactive by default so signature recovery never
runs against the stale j.block_hash.

This SIP evaluates two architectural fixes:

- B1 (recommended): speculative apply at propose time. Proposer pre-applies
  block to compute state_root, includes in proposal, validators verify
  before voting. Block hash committed with real state_root from broadcast.
  Justification refs same hash throughout lifecycle. Cost: 2x apply CPU.

- B4 (alternative): drop state_root from Block::calculate_hash. State_root
  becomes metadata verified separately. Cheaper but removes state_root from
  hash chain — safety downgrade.

Recommendation: B1. Tendermint-style chains pay the 2x apply cost; safety
of state_root in hash chain is worth preserving.

Activation plan: fork-gated via BLOCK_HASH_WITH_REAL_STATE_ROOT_HEIGHT.
Mainnet default u64::MAX. Testnet activates at SIP merge time + 50K blocks
buffer. Bake 1+ week before mainnet activation height chosen.

Related: #750 (Bug A), #751 (Bug B), #752 (defense check, gated).
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

SIP-5 adds a specification to fix a consensus bug where stored blocks can have inconsistent block.hash vs block.justification.block_hash; it recommends fork-gated B1 (proposer speculative-apply to set real state_root and recompute hash, validator dual verification) and documents B4 alternative, activation, rollback, and a reference implementation sketch.

Changes

Cohort / File(s) Summary
SIP-5 specification
sips/sip-5.md
Adds SIP-5: metadata, abstract, motivation, recommended fix B1 (proposer speculative-apply, recompute block.hash, validator verification), alternative B4, add_block_impl changes, fork predicate and activation/rollback plan, reference speculative-apply API wiring, security considerations, and references/license.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Poem

🐰 A rabbit nibbles through a forked code tree,
Rewrites state roots so blocks agree;
It hops, it applies, then recomputes with care,
Consensus now tidy, no mismatched pair —
✨ Hop on, little fix, we’ll keep you rare.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'SIP-5 draft: block hash + state root commitment consistency (Bug B fix)' clearly and specifically summarizes the main change: a new SIP-5 document addressing a consensus bug (Bug B) related to block hash and state root consistency.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sip-5-bug-b-block-hash-state-root

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sips/sip-5.md`:
- Line 179: Update the spec text for the function signature
Blockchain::speculative_apply_for_state_root(&block) -> SentrixResult<[u8; 32]>
to use "txs" instead of "tx's" so the description reads "clones the trie +
applies block txs + returns root WITHOUT mutating chain state" and ensure any
other occurrences in the same sentence or nearby lines use the same "txs"
wording for consistency.
- Around line 113-120: Specify the fork activation predicate explicitly and use
it everywhere: define that new rules apply when block.height >=
BLOCK_HASH_WITH_REAL_STATE_ROOT_HEIGHT (or choose > if preferred) and then
update all occurrences in the SIP (proposer, validator, apply sections and the
text around lines 145-153 and the paragraphs describing pre/post-fork behavior)
to reference that canonical predicate; ensure wording is exact (e.g., "When
block.height >= BLOCK_HASH_WITH_REAL_STATE_ROOT_HEIGHT, proposer must pre-apply
and validators must accept hashes with real state root") so there is a single
authoritative rule used throughout.
- Around line 82-93: Add an explicit verification that the received block's
declared hash matches a locally computed hash: after deserializing the proposed
Block in the BftMessage::Propose handler (main.rs) compute the hash via the
block's hash function (e.g. block.calculate_hash() or equivalent) and compare it
to block.hash; if block.hash is missing or differs, log a warning and
reject/return (do not prevote) before proceeding to the state_root check
(bc_read.speculative_apply_for_state_root and block.state_root handling).
- Around line 99-100: Remove the recomputation/reassignment lines that set
last.state_root and last.hash (the L1561-1563 pattern and siblings) because the
proposer-supplied block already carries state_root and hash; in add_block (and
any validator code that currently does computed_root ==
block.state_root.unwrap()) replace the unwrap usage with an explicit
validation/reject branch that returns an error if block.state_root is None or
malformed and only compares when present (i.e., check block.state_root.is_some()
then compare computed_root to the contained value and reject on mismatch), and
ensure any places referencing last.calculate_hash() do not overwrite proposer
values but instead verify consistency.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b73cd2a-47c9-4211-87c3-2f31dc35000c

📥 Commits

Reviewing files that changed from the base of the PR and between 6dc0609 and 0d05799.

📒 Files selected for processing (1)
  • sips/sip-5.md

Comment thread sips/sip-5.md
Comment thread sips/sip-5.md Outdated
Comment thread sips/sip-5.md
Comment thread sips/sip-5.md Outdated
@satyakwok satyakwok self-assigned this May 31, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sips/sip-5.md (1)

220-222: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include validator block.hash recomputation in the reference-implementation checklist.

The normative spec now requires validators to verify both hash claim and state_root claim, but the skeleton only lists state_root verification on the validator path. Please add an explicit checklist item for recomputing block.calculate_hash() and rejecting on mismatch so implementers don’t accidentally ship only half the validation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sips/sip-5.md` around lines 220 - 222, The checklist in the reference
implementation omits validator-side hash recomputation: update the validator
path notes to explicitly require recomputing block.calculate_hash() and
rejecting proposals when it mismatches the proposer’s claimed block.hash in
addition to verifying block.state_root; specifically mention adding this check
in the BftMessage::Propose handler (the validator path) and ensure
add_block_impl behavior around block.state_root and legacy recompute still
documents that validators must recompute and compare block.calculate_hash()
unless block.state_root was provided and post-fork rules exempt recompute.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@sips/sip-5.md`:
- Around line 220-222: The checklist in the reference implementation omits
validator-side hash recomputation: update the validator path notes to explicitly
require recomputing block.calculate_hash() and rejecting proposals when it
mismatches the proposer’s claimed block.hash in addition to verifying
block.state_root; specifically mention adding this check in the
BftMessage::Propose handler (the validator path) and ensure add_block_impl
behavior around block.state_root and legacy recompute still documents that
validators must recompute and compare block.calculate_hash() unless
block.state_root was provided and post-fork rules exempt recompute.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a48f54db-a019-4f34-9ebc-e793d9d08366

📥 Commits

Reviewing files that changed from the base of the PR and between 0d05799 and d402266.

📒 Files selected for processing (1)
  • sips/sip-5.md

@satyakwok satyakwok merged commit 680010a into main May 31, 2026
4 checks passed
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.

1 participant