Skip to content

fix(state_sync)!: sync state version - #1540

Merged
sdbondi merged 1 commit into
tari-project:developmentfrom
sdbondi:consensus-shard-state-version
Aug 13, 2025
Merged

fix(state_sync)!: sync state version#1540
sdbondi merged 1 commit into
tari-project:developmentfrom
sdbondi:consensus-shard-state-version

Conversation

@sdbondi

@sdbondi sdbondi commented Aug 13, 2025

Copy link
Copy Markdown
Member

Description

fix(state_sync)!: sync state version

Motivation and Context

The shard state version is now synchronised across validators. However, this invariant is not enforced by consensus.
A future PR (or the change to the JMT #1472) may enforce this in future.

This is the first part that enables clients to periodically sync state changes using a (shard, state_version) tuple.

How Has This Been Tested?

Manually by deleting a validator node's data and restarting it, observing sync logs and checking the database against the sync node

What process can a PR reviewer use to test or verify this change?

Sync should work as before

Breaking Changes

  • None
  • Requires data directory to be deleted
  • Other - Please specify

Summary by CodeRabbit

  • New Features

    • State transitions now include a version, enabling more robust syncing and auditing.
    • State sync processes per-version batches with a defined maximum batch size, improving performance and stability.
  • Refactor

    • Streamlined state-sync flow with clearer progress and completion logging.
  • Chores

    • RPC protocol updated; legacy state-sync messages removed. Ensure peers are upgraded for compatibility.

@coderabbitai

coderabbitai Bot commented Aug 13, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a per-instance configurable batch size for state sync, introduces a public STATE_SYNC_MAX_BATCH_SIZE, refactors state sync to process transitions grouped by state_version, updates proto to include state_version and removes legacy sync messages, and plumbs state_version through conversions, storage models, reader/writer, and processing.

Changes

Cohort / File(s) Summary
State sync task configuration
applications/tari_validator_node/src/p2p/rpc/service_impl.rs, applications/tari_validator_node/src/p2p/rpc/state_sync_task.rs
StateSyncTask now accepts/stores a batch_size; service passes STATE_SYNC_MAX_BATCH_SIZE to constructor; internal buffering and retrieval use instance batch_size.
RPC protocol and conversions
crates/p2p/proto/rpc.proto, crates/p2p/src/conversions/rpc.rs
Proto: remove VnStateSyncRequest/Response; add state_version (uint64) to StateTransition. Conversions updated to read/write state_version; internal StateTransition gains public state_version field.
State sync processing refactor
crates/rpc_state_sync/src/state_sync.rs
Refactor to group incoming transitions by state_version using BTreeMap; enforce STATE_SYNC_MAX_BATCH_SIZE; process and commit per state_version; update persisted version tracking and root validation; logging adjusted.
Storage model and RocksDB plumbing
crates/storage/src/consensus_models/state_transition.rs, crates/state_store_rocksdb/src/column_families/state_transition.rs, crates/state_store_rocksdb/src/reader.rs, crates/state_store_rocksdb/src/writer.rs
Add state_version to public StateTransition and persisted StateTransitionModelData; reader populates state_version; writer records shard state_version for Up/Down transitions.
RPC constants
crates/validator_node_rpc/src/lib.rs
Add public constant STATE_SYNC_MAX_BATCH_SIZE: usize = 100.

Sequence Diagram(s)

sequenceDiagram
    participant VN as Validator Node
    participant SST as StateSyncTask
    participant RPC as Peer RPC Server
    participant RSS as RpcStateSync
    participant SS as State Store

    VN->>SST: Start sync_state(max_batch_size)
    SST->>RPC: Request state transitions (batched)
    RPC-->>SST: Return transitions (<= MAX_BATCH_SIZE)
    SST->>RSS: Process batch

    loop For each state_version group
        RSS->>RSS: Validate transitions, build tree changes
        RSS->>SS: Apply SubstateTreeChange (batch)
        RSS->>SS: Persist new state_version
    end

    RSS->>SS: Validate state root vs checkpoint
    SST-->>VN: Sync complete / error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I thump my paw: new versions hop,
Batches sized to neatly stop.
We group by rings of state and shard,
Then stash the roots in burrowed yard.
Proto sheds its older coat—
A sleeker sync; I twitch and gloat. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
crates/state_store_rocksdb/src/column_families/state_transition.rs (1)

26-26: Persisting state_version: good change; document semantics and make the upgrade path explicit

The addition of Version and the new state_version field is aligned with the PR goal. However, because ValueCodec uses DefaultCodec over a serde-serialized struct, adding a non-optional field changes on-disk encoding and will fail to deserialize older records. The PR notes a breaking change; consider making the upgrade path explicit at runtime (e.g., a friendly startup check) or bumping the CF name to prevent silent partial reads from mixed versions.

Also, please document the semantics of state_version to reduce confusion with substate version.

Apply this diff to add doc comments:

 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct StateTransitionModelData {
     pub substate_address: SubstateAddress,
     pub transition: StateTransitionType,
+    /// Global state tree (JMT) version at which this transition was applied.
+    /// Used to group and order transitions during state sync. Not the substate's own version.
     pub state_version: Version,
 }

Also applies to: 33-38

crates/validator_node_rpc/src/lib.rs (1)

28-29: Expose intent and usage via doc comments; consider making this configurable

The constant is reasonable as a guardrail. Follow-up: consider deriving this from node config/CLI to allow operators to tune based on environment.

Apply this diff to clarify intent:

-pub const STATE_SYNC_MAX_BATCH_SIZE: usize = 100;
+/// Hard cap for transitions per state-version batch streamed over RPC.
+/// Guardrail for memory usage and message size; consider making this configurable.
+pub const STATE_SYNC_MAX_BATCH_SIZE: usize = 100;
applications/tari_validator_node/src/p2p/rpc/service_impl.rs (1)

57-57: Wiring the per-instance batch size is correct; consider sourcing from config

Passing STATE_SYNC_MAX_BATCH_SIZE through to StateSyncTask::new matches the new API and intent. As a follow-up, consider plumbing this from node configuration to make it tunable without a code change.

Also applies to: 391-397

crates/p2p/proto/rpc.proto (1)

221-225: Add clarification comment for state_version to avoid confusion with substate version

The new field is well-placed and backward-compatible. A short comment will help distinguish it from SubstateData.version.

Apply this diff:

 message StateTransition {
   StateTransitionId id = 1;
   SubstateUpdate update = 2;
-  uint64 state_version = 3;
+  // JMT state version at which this transition was committed.
+  // Not to be confused with SubstateData.version (the substate's version).
+  uint64 state_version = 3;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be20c5a and 92b3731.

📒 Files selected for processing (10)
  • applications/tari_validator_node/src/p2p/rpc/service_impl.rs (2 hunks)
  • applications/tari_validator_node/src/p2p/rpc/state_sync_task.rs (3 hunks)
  • crates/p2p/proto/rpc.proto (1 hunks)
  • crates/p2p/src/conversions/rpc.rs (2 hunks)
  • crates/rpc_state_sync/src/state_sync.rs (6 hunks)
  • crates/state_store_rocksdb/src/column_families/state_transition.rs (2 hunks)
  • crates/state_store_rocksdb/src/reader.rs (1 hunks)
  • crates/state_store_rocksdb/src/writer.rs (2 hunks)
  • crates/storage/src/consensus_models/state_transition.rs (1 hunks)
  • crates/validator_node_rpc/src/lib.rs (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
applications/tari_validator_node/src/p2p/rpc/state_sync_task.rs (1)
crates/storage/src/consensus_models/state_transition.rs (1)
  • get_n_after (41-48)
crates/rpc_state_sync/src/state_sync.rs (2)
crates/storage/src/consensus_models/state_transition.rs (3)
  • shard (105-107)
  • new (73-75)
  • epoch (101-103)
crates/p2p/src/conversions/rpc.rs (9)
  • try_from (30-43)
  • try_from (58-68)
  • try_from (84-90)
  • try_from (107-116)
  • try_from (143-152)
  • try_from (160-175)
  • try_from (193-195)
  • try_from (213-226)
  • value (219-223)
crates/state_store_rocksdb/src/writer.rs (1)
crates/storage/src/consensus_models/state_transition.rs (3)
  • seq (109-111)
  • new (73-75)
  • shard (105-107)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: test
  • GitHub Check: machete
  • GitHub Check: clippy
  • GitHub Check: check nightly
🔇 Additional comments (21)
crates/state_store_rocksdb/src/reader.rs (1)

1649-1654: LGTM: state_version is plumbed through StateTransition construction

Correctly propagates the persisted state_version into the in-memory StateTransition.

crates/storage/src/consensus_models/state_transition.rs (2)

12-12: LGTM! Import added for Version type.

The addition of Version to the imports from tari_state_tree is correct and necessary for the new state_version field.


19-19: Breaking change: New required field in StateTransition struct.

The addition of the state_version field is a breaking change that requires all existing code creating StateTransition instances to be updated. This aligns with the PR objective of synchronizing state versions across validators.

applications/tari_validator_node/src/p2p/rpc/state_sync_task.rs (4)

24-24: LGTM! Field added for configurable batch size.

The addition of batch_size field allows for dynamic configuration of the batch size, replacing the hardcoded constant approach.


33-33: LGTM! Constructor signature updated correctly.

The constructor now accepts batch_size as a parameter and properly stores it in the struct.

Also applies to: 40-40


45-45: LGTM! Buffer capacity uses instance field.

The buffer initialization now correctly uses the instance's batch_size field instead of a compile-time constant.


97-97: LGTM! Batch retrieval uses instance field.

The call to StateTransition::get_n_after now correctly uses self.batch_size for dynamic batch sizing.

crates/p2p/src/conversions/rpc.rs (2)

170-174: LGTM! Proto-to-internal conversion handles state_version.

The conversion correctly populates the new state_version field from the proto message.


183-183: LGTM! Internal-to-proto conversion handles state_version.

The conversion correctly writes the state_version field to the proto message.

crates/state_store_rocksdb/src/writer.rs (4)

1232-1232: LGTM! State version correctly added to transition data.

The state_version is properly included in the StateTransitionModelData for UP transitions.


1280-1285: LGTM! Consistent state version handling for DOWN transitions.

The implementation correctly retrieves and includes the state version for DOWN transitions, maintaining consistency with UP transitions.


1288-1288: LGTM! State version included in DOWN transition data.

The state_version is properly added to the StateTransitionModelData for DOWN transitions.


1219-1224: Default Fallback for New Shard State Versions is Expected

The call to optional()?.unwrap_or_default() will return 0 for shards that haven’t been seen before, which is the intended “initial version” behavior. No error or warning is necessary here. If you’d like visibility into that case, you can add a debug! log immediately before or after the fallback, but no change is required to handle this as an error.

crates/rpc_state_sync/src/state_sync.rs (8)

4-7: LGTM! Required imports added for new functionality.

The addition of BTreeMap for grouping transitions by state version is appropriate.


61-61: LGTM! Batch size constant imported.

The import of STATE_SYNC_MAX_BATCH_SIZE replaces the hardcoded local constant.


208-214: Good defensive programming: Batch size validation added.

The check against STATE_SYNC_MAX_BATCH_SIZE prevents potential DoS attacks by limiting the number of transitions that can be processed in a single batch.


219-227: Excellent refactoring: Transitions grouped by state version.

The use of BTreeMap to group transitions by state_version ensures ordered processing and aligns with the PR objective of synchronizing state versions. The error handling with try_fold is properly implemented.


230-233: LGTM! Clear loop termination with informative logging.

The pop_first() usage ensures ordered processing, and the informative log message helps with debugging.


338-338: LGTM! State root calculation uses persisted version.

The state root calculation correctly uses maybe_persisted_state_version to verify against the checkpoint.


354-354: LGTM! Success logging shows final state version.

The log message correctly reports the final persisted state version.


328-329: I can’t find a SpreadPrefixStateTree type in the repository. It looks like the implementation in question is StateTree<S, M> in crates/state_tree/src/tree.rs, which correctly:

  • Accepts both current_version: Option<Version> and next_version: Version.
  • Delegates to calculate_substate_changes, which calls into jmt.batch_put_value_set(..., current_version, next_version).
  • Inserts new nodes and records stale ones tagged with next_version.

This matches the intended version‐transition logic in JellyfishMerkleTree’s batch API. No special handling appears missing.

Conclusion: The batch_put_substate_changes implementation already handles version transitions correctly; no further changes needed.

@sdbondi
sdbondi merged commit c5ab420 into tari-project:development Aug 13, 2025
12 checks passed
@sdbondi
sdbondi deleted the consensus-shard-state-version branch August 13, 2025 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants