-
Notifications
You must be signed in to change notification settings - Fork 15
feat(toolkit): cache wallet state to eliminate genesis replay #483
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Introduce wallet state caching abstraction to eliminate genesis replay on toolkit startup. This commit adds: - WalletStateStorage trait for pluggable cache backends - WalletStateCache and WalletSnapshot types for serialization - InMemory implementation for testing - Placeholder files for Redb and PostgreSQL backends The cache is keyed by chain_id (block 1 hash) and wallet_id (hash of wallet public keys) to ensure correct cache matching. Refs: PM-21139
Add persistent WalletStateStorage backend using redb embedded database: - RedbBackend stores wallet cache in local file (wallet_state_cache table) - BSON serialization via Serde wrapper (same pattern as FetchStorage) - Supports sharing database file with FetchStorage via from_database() - Includes comprehensive tests for roundtrip, delete, overwrite, persistence The Redb backend enables single-instance toolkit deployments to persist wallet state across sessions. Refs: PM-21139
Add persistent WalletStateStorage backend using PostgreSQL: - PostgresBackend stores wallet cache in wallet_state_cache table - BSON serialization (consistent with FetchStorage pattern) - Supports sharing connection pool with FetchStorage via with_pool() - Separate block_height column for efficient height-only queries - Includes created_at/updated_at timestamps for observability The PostgreSQL backend enables multi-instance toolkit deployments to share wallet state cache across processes. Refs: PM-21139
Add cache_helpers module with functions to serialize/deserialize LedgerContext state for wallet state caching: - serialize_ledger_state/deserialize_ledger_state using mn_ledger_serialize - create_cache_from_context: captures LedgerState and wallet seed hashes - restore_context_from_cache: restores LedgerContext from cached state - Wallet state is initialized fresh and rebuilt during block replay The key optimization is caching LedgerState (full blockchain state), which is the main reconstruction bottleneck. Wallet-specific state (shielded coins, dust) is quickly rebuilt by replaying only blocks since the checkpoint. Refs: PM-21139
Add --wallet-cache CLI argument and WalletCacheConfig enum: - WalletCacheConfig enum with Disabled/InMemory/Redb/Postgres variants - FromStr implementation for parsing CLI arguments - wallet_cache_config parser function in cli_parsers - --wallet-cache arg on Source struct (default: disabled, env: MN_WALLET_CACHE) The wallet cache is disabled by default to maintain backward compatibility. Users can enable caching with --wallet-cache=redb:wallet.db or similar. Refs: PM-21139
Add build_context_with_cache function for cache-enabled context building: - Attempts to restore LedgerContext from cache if available - Only replays blocks since the cached checkpoint - Saves updated cache after processing new blocks - Supports Redb and PostgreSQL backends via WalletCacheConfig Helper functions: - compute_wallet_id_for_seeds: derive wallet identity from seeds - save_context_to_cache: persist context state to configured backend The caching is opt-in via the --wallet-cache CLI argument. When enabled, subsequent runs will skip replaying already-processed blocks. Refs: PM-21139
Add comprehensive unit tests for wallet state caching: - WalletCacheConfig parsing tests (disabled, inmemory, redb, postgres) - InMemory storage tests (cache miss, hit, delete) - RedbBackend tests (roundtrip, delete, overwrite, persistence) - cache_helpers tests (block context roundtrip, seed hashing) Note: Integration tests requiring a running node are deferred to the e2e test suite. Refs: PM-21139
ozgb
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this implementation, serializing/deserializing entire wallets states could lead to reduced performance on long-running networks as these states will get very large, and would be unwieldy in the case of loading many wallets from cache
An alternative would be using ledger storage - this would mean we're not duplicating data, and would improves read and write speeds and memory usage when loading and saving items from/to the cache.
The disadvantage of using ledger storage would be that each toolkit instance must have exclusive access to it's own ledger storage backend.
The implementation in this PR has an advantage of simplicity compared to the ledger storage route - to reduce the code size, I'd suggest re-using the fetch cache instead of adding a new separate wallet cache
…into feat/toolkit-wallet-state-cache
- Move wallet_state_storage module into fetcher/wallet_state_cache - Add WalletStateCaching trait implemented by InMemory, RedbBackend, and PostgresBackend - Add zstd compression for cached wallet state in file/postgres backends - Remove separate --wallet-cache CLI argument (now uses --fetch-cache) - Replace all expects with proper error handling (log warnings, return None for reads, early return for writes) - Net reduction of ~850 lines through code reuse
…into feat/toolkit-wallet-state-cache








Summary
Implement wallet state caching to eliminate genesis replay bottleneck in the toolkit. This feature dramatically reduces startup time for subsequent toolkit runs by persisting
LedgerStateacross sessions.Key changes:
WalletStateStoragetrait andWalletStateCachetypes for cache abstractionRedbBackendfor file-based persistence (single-instance)PostgresBackendfor multi-instance deploymentscache_helpersmodule for LedgerContext serialization/deserialization--wallet-cacheCLI argument (disabled by default)build_context_with_cache()functionUsage:
How it works:
Test plan
Checklist
cargo fmtTicket: https://shielded.atlassian.net/browse/PM-21139