perf/store: optimize updating existing accounts #1567
Conversation
14dc3a6 to
5f11a29
Compare
There was a problem hiding this comment.
Pull request overview
This PR optimizes applying partial account deltas by avoiding loading full Account state (code bytes, all storage-map entries, all vault assets) during upsert_accounts, instead fetching only minimal state and computing updated headers/roots.
Changes:
- Added an optimized delta-update module to query minimal account state and compute updated
storage_header/vault_root. - Updated
upsert_accountsto use anAccountStateForInsertenum to handle private, full-state, and partial-state inserts. - Added new tests covering the optimized delta path, private upserts, and full-state delta upserts.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/store/src/db/models/queries/accounts/delta.rs | New optimized queries + helpers for partial delta application (minimal state + root/header computation). |
| crates/store/src/db/models/queries/accounts/delta/tests.rs | New tests for optimized partial delta update, private account upsert, and full-state delta upsert. |
| crates/store/src/db/models/queries/accounts.rs | Integrates optimized partial-delta path into upsert_accounts; adds AccountRowInsert constructors. |
| alt_approach.txt | Documents an alternative design (precompute roots in InnerForest). |
| CHANGELOG.md | Adds a changelog entry for the performance improvement. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| storage_header: new_storage_header, | ||
| vault_root: new_vault_root, | ||
| }; | ||
|
|
There was a problem hiding this comment.
In the partial-delta path, the code no longer verifies that the derived minimal state (nonce/storage/vault/code commitment) is consistent with update.final_state_commitment(). Previously this was enforced by applying the delta to a full Account and checking AccountCommitmentsMismatch. Without an equivalent check here, it’s possible to persist an accounts row whose commitment does not match its stored nonce/storage_header/vault_root fields (especially given the new ad-hoc SMT computations). Please add a commitment verification using the minimal fields (or otherwise ensure consistency) and fail the update if it doesn’t match.
| // Verify that the derived minimal state matches the expected final commitment. | |
| // This mirrors the full-state delta path where we build a full `Account` and | |
| // compare `account.commitment()` with `update.final_state_commitment()`. | |
| let calculated_commitment = Account::commitment_from_minimal_state( | |
| account_state.nonce, | |
| account_state.code_commitment, | |
| account_state.storage_header, | |
| account_state.vault_root, | |
| ); | |
| if calculated_commitment != update.final_state_commitment() { | |
| return Err(DatabaseError::AccountCommitmentsMismatch { | |
| calculated: calculated_commitment, | |
| expected: update.final_state_commitment(), | |
| }); | |
| } |
6b1b40a to
a865ef0
Compare
a865ef0 to
2c632b5
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
0a9e28b to
faaad63
Compare
Context
Currently applying a partial delta to an existing account requires loading the full account state
consisting of code, all storage map entries, all vault assets, which is very costly - we spend ~250ms on this per block (that's a lot!).
See #1538
What
Optimizes account delta updates by avoiding loading full
Accountobjects from the database when only applying partial deltas.Core changes
module
crates/store/src/db/models/queries/accounts/delta.rsfn select_account_state_for_delta()- fetches only nonce, code_commitment, storage_header, vault_rootfn select_vault_balances_by_faucet_ids()- fetches only specific vault balances being updatedfn apply_storage_delta_with_precomputed_roots()- updates the storage header using precomputed map rootsInnerForest::apply_block_updatesnow precomputes vault and storage map roots for each account update and passes them toDb::apply_blockfor partial delta updates.fn upsert_accountsuses the optimized path for partial deltas viaenum AccountStateForInsertwith three variants:::Private- no public state::FullAccount- new account creation == "full-state delta"::PartialState- incremental update == "partial delta"AccountRowInsertconstructors:new_private(),new_from_account(),new_from_partial()Biggest Change
We reverse the flow and update - we update the
InnerForestnow and track new roots, then using these in the DB storage header update. The downside is additional tackling.