Skip to content

Consensus: skip Spark state updates during VerifyDB reconnect - #1903

Open
reubenyap wants to merge 2 commits into
masterfrom
claude/pr-1902-review-5oe0a4
Open

Consensus: skip Spark state updates during VerifyDB reconnect#1903
reubenyap wants to merge 2 commits into
masterfrom
claude/pr-1902-review-5oe0a4

Conversation

@reubenyap

Copy link
Copy Markdown
Member

PR intention

Fix a pre-existing -checklevel=4 startup crash/corruption found while reviewing #1902.

CVerifyDB level 3 disconnects recent blocks against a throwaway coins view and level 4 reconnects them through ConnectBlock, but global Spark state and the Spark entries of the block index are never rolled back in the process (DisconnectBlock with pfClean performs "no real disconnect", and DisconnectTipSpark is never invoked). Level-4 reconnection therefore re-runs ConnectBlockSpark against state that still contains those blocks:

  • a block with Spark mints double-counts the coin group size and mint metadata, or aborts on assert(coinGroup.lastBlock->nHeight <= index->nHeight) in AddMintsToStateAndBlockIndex when a later block already holds mints of the same group;
  • a block with a Spark spend fails the CheckLTag used-lTag check, so VerifyDB reports an unconnectable block and the node refuses to start;
  • Spark name expiry is replayed at historical heights, and the rewritten block-index Spark entries can later be flushed to disk.

The new regression test reproduces the abort on current master (assertion failure in spark/state.cpp AddMintsToStateAndBlockIndex during level-4 reconnection) and passes with the fix.

Code changes brief

  • Thread an isVerifyDB flag through ConnectBlock (default false) and pass it from CVerifyDB::VerifyDB's level-4 reconnection loop.
  • Skip ConnectBlockSpark when the flag is set. Stateful Spark checks are meaningless against un-rewound state, while the stateless per-transaction Spark checks still run through CheckBlock/CheckTransaction inside ConnectBlock, whose lTag check already tolerates recorded lTags outside tip connection. Real block connection (ConnectTip, TestBlockValidity) is unchanged.
  • Add spark_state_tests/verifydb_preserves_spark_state: builds two Spark mint blocks and a spend block, runs -checklevel=4-style verification via CVerifyDB, and asserts the mint map, spend map, and coin-group bookkeeping come out untouched.
  • Separately fix the stale comment on spark::Coin::getHash() claiming type and v are excluded from the hash; they are part of the serialization it hashes.

Verified locally on Linux: test_firo --run_test=spark_state_tests,spark_tests,spark_mintspend passes (25 cases, no errors detected). With the validation change reverted to master, the new test aborts exactly as a -checklevel=4 node would.

Independent of #1902 and textually non-conflicting: the hunks touch different regions of validation.cpp and this PR does not modify spark/state.cpp logic. If both land, #1902's active-state gate in ConnectBlockSpark simply never sees a VerifyDB reconnection anymore, which is compatible with its intent.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LphSq9aU2w13j6DWs7m6Ry


Generated by Claude Code

claude added 2 commits August 14, 2026 13:26
CVerifyDB check level 3 disconnects recent blocks against a throwaway
coins view and level 4 reconnects them through ConnectBlock, but global
Spark state and the Spark entries of the block index are never rolled
back in the process. Re-running ConnectBlockSpark on such a reconnect
fails the used-lTag check for any block containing a Spark spend, and
for mint blocks double-counts coin group sizes and mint metadata or
trips the coin group ordering assertion, so -checklevel=4 either
refuses to start, aborts, or corrupts in-memory Spark state that can
later be flushed to the block index on disk.

Thread an isVerifyDB flag through ConnectBlock and skip the Spark
connect logic when reconnecting under VerifyDB. The stateless Spark
checks still run through CheckBlock and per-transaction validation,
whose lTag check already tolerates recorded lTags outside tip
connection.

Add a regression test that runs -checklevel=4 style verification over
Spark mint and spend blocks and asserts the global Spark state comes
out untouched.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LphSq9aU2w13j6DWs7m6Ry
getHash() hashes the coin's full serialization, which covers the type
byte and, for mint-type coins, the value v. The old comment claimed
type and v were excluded.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LphSq9aU2w13j6DWs7m6Ry
@codeant-ai

codeant-ai Bot commented Aug 14, 2026

Copy link
Copy Markdown

User [email protected] does not have a PR Review subscription.

Go to Team management and add this email to the PR Review subscription.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cc2ac457-a533-4b52-bfed-cbb79dfd5505

📥 Commits

Reviewing files that changed from the base of the PR and between daf3f0c and 2557181.

📒 Files selected for processing (4)
  • src/libspark/coin.h
  • src/test/spark_state_test.cpp
  • src/validation.cpp
  • src/validation.h

Summary by CodeRabbit

  • Bug Fixes

    • Improved database verification to preserve Spark state, including mint and spend counts and coin-group metadata.
    • Prevented verification processes from incorrectly reapplying Spark state during block reconnection.
    • Clarified coin hash documentation to reflect serialized coin contents.
  • Tests

    • Added integration coverage confirming Spark state remains unchanged after database verification.

Walkthrough

The change prevents VerifyDB block reconnects from reapplying Spark state. It adds an integration test for state preservation and updates the Coin hash documentation.

Changes

Spark state preservation

Layer / File(s) Summary
VerifyDB connection path
src/validation.h, src/validation.cpp
ConnectBlock accepts isVerifyDB. VerifyDB sets it during reconnects, so Spark state connection is skipped.
Spark state preservation test
src/test/spark_state_test.cpp
The test verifies that level-4 VerifyDB preserves mint counts, spend counts, and coin-group metadata.

Coin hash documentation

Layer / File(s) Summary
Coin hash comment
src/libspark/coin.h
The comment states that mint-coin hashes include the coin type and value.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 25571

This change prevents Spark state from being replayed during VerifyDB reconnection and adds regression coverage; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: levoncrypto, navidr

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: skipping Spark state updates during VerifyDB reconnection.
Description check ✅ Passed The description includes the required intention and code changes sections with detailed problem, solution, tests, and scope information.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/pr-1902-review-5oe0a4

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.

@coderabbitai
coderabbitai Bot requested review from levoncrypto and navidR August 14, 2026 13:36

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2557181731

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/validation.cpp

bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck)
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck, bool isVerifyDB)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Propagate VerifyDB mode through Spark transaction checks

When the verified range contains a Spark-name registration followed by a transfer, the live name manager still holds the transferred address, but this new flag only suppresses ConnectBlockSpark: ConnectBlock continues calling CheckTransaction(..., false, ...), which reaches CheckSparkNameTx and rejects the historical registration as “name already exists” because its original address differs from the current one. Thus -checklevel=4 can still report an unconnectable block and prevent startup; the VerifyDB mode must also bypass checks that depend on the unrewound Spark-name state while retaining the context-independent Spark checks.

Useful? React with 👍 / 👎.

Comment thread src/validation.cpp
// Spark entries of the block index are never rolled back. Re-running the
// Spark connect logic on such a reconnect double-counts mints and fails the
// used-lTag check against state that still contains this block, so skip it.
if (!isVerifyDB && !spark::ConnectBlockSpark(state, chainparams, pindex, &block, fJustCheck))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate Spark index metadata during VerifyDB reconnects

When the block index is missing or has corrupted Spark metadata, this blanket skip makes level-4 verification report success without comparing the Spark data reconstructed into block.sparkTxInfo with pindex->sparkMintedCoins, spentLTags, or sparkSetHash. For example, a missing spentLTags entry remains absent from the global state built at startup, so the node can subsequently treat an already-spent linking tag as unused even after the operator runs -checklevel=4; VerifyDB should validate the reconstructed entries without applying them to the live state rather than omitting this phase entirely.

Useful? React with 👍 / 👎.

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.

2 participants