Skip to content

feat: Deferred block proving#1725

Open
sergerad wants to merge 102 commits intonextfrom
sergerad-deferred-block-proving
Open

feat: Deferred block proving#1725
sergerad wants to merge 102 commits intonextfrom
sergerad-deferred-block-proving

Conversation

@sergerad
Copy link
Collaborator

@sergerad sergerad commented Mar 2, 2026

Context

We are adding deferred (asynchronous) block proving for the node, as described #1592. Currently, block proving happens synchronously during apply_block, which means block commitment is blocked until the proof is generated.

Blocks will now exhibit committed (not yet proven) and proven states. A committed block is already part of the canonical chain and fully usable. Clients that require proof-level finality can opt into it via the new finality parameter on SyncChainMmr.

Changes

  • Schema: Added proving_inputs BLOB to the block_headers table, with partial index for querying proven (proving_inputs = NULL) blocks. Added proven_in_sequence BOOLEAN column to track whether a block and all its ancestors have been proven, with a partial index for efficient lookups.
  • Block proof file storage: Block proofs are stored as files via BlockStore (following the existing block file pattern) rather than as BLOBs in SQLite.
  • DB queries: Added select_block_proving_inputs (returns deserialized BlockProofRequest), clear_block_proving_inputs, select_proven_not_in_sequence_blocks, mark_blocks_as_proven_in_sequence, select_unproven_blocks, and select_latest_proven_in_sequence_block_num. Block proving business logic (tip walking, contiguity detection) lives in Db::mark_proven_and_advance_sequence in db/mod.rs, composing these queries within a single transaction.
  • Decoupled proving from apply_block: The BlockProofRequest is now serialized and persisted alongside the block during apply_block. Removed the ApplyBlockData struct, replacing it with a &SignedBlock parameter plus notes and proving_inputs.
  • Proof scheduler: Added a background task (proof_scheduler.rs) that drives deferred proving. It queries unproven blocks on startup (restart recovery), listens for new block commits via a watch channel, and proves blocks concurrently up to a configurable limit. Because blocks may be proven out of order, the scheduler maintains a BTreeSet of proven-but-not-yet-in-sequence blocks and advances a contiguous watermark as gaps fill, updating the proven_in_sequence column for exactly the newly in-sequence blocks.
  • Finality on SyncChainMmr: Added a Finality enum (COMMITTED, PROVEN) to the protobuf and a finality field on SyncChainMmrRequest. When PROVEN finality is requested, the effective tip is the highest contiguously proven block.

@sergerad sergerad changed the title Sergerad deferred block proving feat: Deferred block proving Mar 2, 2026
@sergerad sergerad marked this pull request as ready for review March 2, 2026 23:42
@sergerad sergerad requested review from Mirko-von-Leipzig, bobbinth and drahnr and removed request for Mirko-von-Leipzig March 2, 2026 23:43
Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

Looks good! Thank you! I left some comments inline - most are pretty small and some should be left for follow-up PRs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe not for this PR, but it would be great to add doc comments (maybe at the module level), explaining how we save raw block data. I believe currently it is something like this:

  • Raw block data is saved under [store_dir]/[epoch]/block_[block_num].dat file.
  • For proven blocks, the proof data is saved under [store_dir]/epoch[epoch_num]/proof_[block_num].dat file.

Where epoch_num is the 16 most significant bits of the block number, and both epoch_num and block_num are formatted as hex strings.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

pub async fn apply_block(
&self,
signed_block: SignedBlock,
proving_inputs: Option<BlockProofRequest>,
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably explain this in the doc comment above.

Also, not from this PR, but is the TODO on line 42 still relevant?

Comment on lines 501 to +507
BlockRange block_range = 1;

// The finality level to use when clamping the upper bound of the block range.
//
// When set to FINALITY_COMMITTED (default), the upper bound is clamped to the chain tip.
// When set to FINALITY_PROVEN, the upper bound is clamped to the latest proven block.
Finality finality = 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

I still think it would be good to make finality and block_to mutually exclusive - but I would leave this for a follow-up PR.

To illustrate the idea, in Rust, it would look something like this:

struct SyncChainMmrRequest {
    block_from: u32,
    block_to: UpperBlockBound,
}

// the name is just illustrative
enum UpperBlockBound {
    Block(u32),
    LastCommitted,
    LastProven,
}

@sergerad sergerad requested a review from bobbinth March 26, 2026 01:20
Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

Looks good! Thank you! I left a few comments inline. The main one is about encapsulating more logic in the mark_block_proven operation and making it "atomic".

@sergerad sergerad requested a review from bobbinth March 26, 2026 22:14
@sergerad sergerad requested a review from bobbinth March 27, 2026 03:22
Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

Looks good! Thank you! I left a couple more small comments inline.

Also, I didn't review in depth the test code and some of the tokio-related scheduling code - so, another set of eyes on that would be good.

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.

5 participants