Skip to content

Conversation

@lowhung
Copy link
Collaborator

@lowhung lowhung commented Dec 5, 2025

Description

Implements bootstrap of accounts state module from snapshot data, enabling the accounts state to be initialized from a ledger snapshot rather than syncing from genesis.

Key changes:

  • Pre-process mark/set/go epoch snapshots in the snapshot publisher before sending to accounts_state
  • Load stake addresses, pools, DReps, pots, and epoch snapshots during bootstrap
  • Unified Snapshot type moved to acropolis_common - eliminates duplicate type definitions
  • Added RegistrationChange and RegistrationChangeKind types to common for tracking stake address registration changes

Related Issue(s)

Completes #388

How was this tested?

  • Manual testing with startup.method = "snapshot" (make run)
  • Verified bootstrap logs show loaded accounts, pools, and epoch snapshots
  • Verified REST API returns data for bootstrapped stake addresses
➜ curl http://127.0.0.1:4340/accounts/stake1780j8ewwgup2vsx558zpn6jmryqt0pdevuqfh4dsfjmmgkg29pmrw
{
  "utxo_value": 0,
  "rewards": 3516252,
  "delegated_spo": "pool1uj4u73qgtprqre78q75fq2vkcrpfrcdreqcqkvn6u0m2k6nk2yp",
  "delegated_drep": null
}

Checklist

  • My code builds and passes local tests
  • I added/updated tests for my changes, where applicable
  • I updated documentation (if applicable)
  • CI is green for this PR

Impact / Side effects

  • utxo_value is not populated for stake addresses from snapshot (would require UTxO aggregation - known limitation)
  • No breaking changes
  • Added imbl dependency to acropolis_common

Reviewer notes / Areas to focus

  • Unified Snapshot type in common/src/types.rs now includes new(), from_raw(), and helper methods
  • Conversion logic from raw snapshot data to processed SnapshotsContainer in mark_set_go.rs

@lowhung lowhung marked this pull request as draft December 5, 2025 00:58
@alexwoods alexwoods mentioned this pull request Dec 5, 2025
4 tasks
@lowhung lowhung linked an issue Dec 9, 2025 that may be closed by this pull request
6 tasks
- Add BootstrapSnapshots type to pre-process raw snapshot data in publisher
- Pass processed snapshots to accounts_state via AccountsBootstrapMessage
- Simplify accounts_state bootstrap flow to match epochs_state pattern
- Extract Arc ownership to avoid cloning large snapshot data
- Add stake address stats logging for verification

Note: utxo_value not populated from snapshot (requires UTxO aggregation)
@lowhung lowhung marked this pull request as ready for review December 9, 2025 22:34
@lowhung lowhung force-pushed the lowhung/388-bootstrap-accounts-module branch from 0fe69ed to 5a16940 Compare December 10, 2025 02:23
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request implements bootstrap functionality for the accounts state module, enabling Acropolis to initialize from a Cardano ledger snapshot instead of syncing from genesis. The implementation follows the existing pattern established by epochs_state, with snapshot data being pre-processed by the snapshot publisher before being sent to accounts_state for initialization.

Key changes:

  • Pre-processing of mark/set/go epoch snapshots in snapshot_bootstrapper before sending to accounts_state
  • New AccountsBootstrapMessage containing all data needed to initialize accounts state (stake addresses, pools, DReps, pots, and epoch snapshots)
  • Bootstrap flow in accounts_state that loads state from the message during startup when using snapshot method
  • Migration of Pots type to common for sharing between modules
  • Refactoring of snapshot parsing to use native types (PoolRegistration, StakeAddress) instead of intermediate string representations
  • Cleanup of DRepKeyHash naming (previously inconsistently named DrepKeyHash)

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
modules/accounts_state/src/accounts_state.rs Adds snapshot subscription and bootstrap waiting logic, handling AccountsBootstrapMessage
modules/accounts_state/src/state.rs Implements bootstrap() method to load accounts, pools, DReps, pots, and epoch snapshots from message
modules/accounts_state/src/snapshot.rs Adds from_bootstrap() method to convert pre-processed BootstrapSnapshot to internal Snapshot format
modules/snapshot_bootstrapper/src/publisher.rs Implements AccountsCallback to publish processed bootstrap data; updates PoolCallback to send SPOState
common/src/snapshot/mark_set_go.rs Adds BootstrapSnapshots types and from_raw() method to pre-process raw CBOR data into usable format
common/src/snapshot/streaming_snapshot.rs Major refactoring: removes intermediate string types, adds wrapper types for clean CBOR decoding, implements AccountsCallback
common/src/snapshot/pool_params.rs Deleted - replaced by using PoolRegistration directly with new snapshot decoders
common/src/messages.rs Adds AccountsBootstrapMessage for passing pre-processed snapshot data
common/src/types.rs Adds Pots struct (migrated from accounts_state) and fixes DRepKeyHash naming
common/src/stake_addresses.rs Changes AccountState.stake_address from String to StakeAddress, adds Deserialize derives
common/src/ledger_state.rs Adds SPOState::new() and extend() helper methods
modules/spo_state/src/spo_state.rs Updates snapshot subscription to loop and handle multiple message types
modules/governance_state/src/conway_voting_test.rs Fixes import order for DRepKeyHash (was DrepKeyHash)
modules/accounts_state/Cargo.toml Removes unused serde dependency
docs/streaming-snapshot-parser.md Updates documentation to reflect AccountsCallback rename
common/examples/test_streaming_parser.rs Updates example to work with new callback signatures and types

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 574 to 580
match variant {
0 => Ok(DRepCredential::AddrKeyhash(d.decode_with(ctx)?)),
1 => Ok(DRepCredential::ScriptHash(d.decode_with(ctx)?)),
0 => Ok(LocalDRepCredential(DRepCredential::ScriptHash(
d.decode_with(ctx)?,
))),
1 => Ok(LocalDRepCredential(DRepCredential::AddrKeyHash(
d.decode_with(ctx)?,
))),
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

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

Critical bug: The variant order is reversed compared to the Credential enum definition. According to common/src/types.rs line 942-947, the Credential enum has ScriptHash as variant 0 and AddrKeyHash as variant 1. This decoder has them swapped, which will cause all DRep credentials to be decoded with the wrong type (scripts as keys and keys as scripts), corrupting the data.

The correct mapping should be:

  • variant 0 → DRepCredential::ScriptHash
  • variant 1 → DRepCredential::AddrKeyHash

Copilot uses AI. Check for mistakes.
@lowhung lowhung force-pushed the lowhung/388-bootstrap-accounts-module branch from 72d28ef to 6fd31a4 Compare December 10, 2025 03:23
This commit introduces several improvements to the snapshot parsing
logic:

- Simplify snapshot parsing by extracting common parsing logic
- Add more descriptive logging for snapshot parsing
- Create a new `ParsedSnapshotsContainer` to represent fully parsed
  snapshots
- Reduce verbosity in logging and error handling
);

// Show snapshots info if available
if let Some(snapshots_info) = &metadata.snapshots {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I wasn't finding this information particularly useful, but happy to re-stage it if wanted.

// Publish SPDD after bootstrap if bootstrap occurred
if let Some(block_info) = bootstrap_block_info {
let state = history.lock().await.get_current_state();
let spdd = state.generate_spdd();
Copy link
Collaborator Author

@lowhung lowhung Dec 10, 2025

Choose a reason for hiding this comment

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

This is an area of concern for me with this implementation. Would love some thoughts on whether we generate the spdd after the bootstrap here? As we discussed, utxos haven't been read and the address deltas have not been sent either.

We already have what is required to generate an spdd from the SnapshotSPO objects we send though. So maybe we don't even go through the stake_address map for this.
#456

impl Snapshot {
/// Create a new snapshot from current stake address state (used at epoch boundary)
#[allow(clippy::too_many_arguments)]
pub fn new(
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I lifted all of this from accounts_state/snapshot.rs. I feel bad adding even more into this type.rs file though.

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.

Bootstrap Accounts Module

2 participants