refactor(l1): deduplicate the account-update loop in Store - #7181
refactor(l1): deduplicate the account-update loop in Store#7181ilitteri wants to merge 1 commit into
Store#7181Conversation
apply_account_updates_from_trie_batch and apply_account_updates_from_trie_with_witness ran the same per-update algorithm, copy-pasted. Extract it into a private apply_account_updates_from_trie_inner and turn both public functions into thin wrappers with unchanged signatures. The variants' only real differences stay behind an Option<&mut StorageTries>: None opens a fresh storage trie per account (batch); Some caches tries in the map keyed by unhashed address, reusing the logged trie on hit and wrapping newly opened ones in TrieLogger::open_trie so witness recording spans updates. Hashed addresses unify on hash_address_fixed, the same keccak digest the witness copy re-wrapped with H256::from_slice.
|
🤖 Claude Code ReviewThis confirms the CI checks (fmt/clippy/tests) already passed per the PR description, and the logic is a faithful behavior-preserving merge. The code looks correct. Review: refactor(l1): deduplicate the account-update loop in
|
🤖 Codex Code ReviewNo correctness, security, or performance findings in the diff. This reads as a straight refactor: both entry points now share the same trie-update logic, and the Minor readability note:
I could not run Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
Lines of code reportTotal lines added: Detailed view |
|
Superseded by #7184: renaming the head branch closed this PR (GitHub closes PRs whose head branch is renamed); the same commits continue there under the new branch name. |
Motivation
Store::apply_account_updates_from_trie_batchandStore::apply_account_updates_from_trie_with_witnesswere two hand-maintained copies of the same per-update algorithm: remove the account ifupdate.removed; decode-or-default theAccountState; resetstorage_rootonremoved_storage; copy nonce/balance/code_hash and collectcode_updates; applyadded_storageto a storage trie andcollect_changes_since_last_hash; insert the account; finally collect state changes into anAccountUpdatesList. Any future change to the update semantics had to be made twice, and the copies had already drifted cosmetically (hashed-address type, blank-line style), which obscured that the only real difference is how the storage trie is acquired.Description
Extracts the loop into a private
apply_account_updates_from_trie_inner(&self, state_trie, account_updates, storage_tries: Option<&mut StorageTries>)and turns both public functions into thin wrappers with their exact original signatures. Net −42 lines incrates/storage/store.rs(+52/−94), no behavior change.The two variants' only genuine differences are preserved verbatim behind the
Option:None(batch): a fresh trie is opened per account viaopen_storage_trieat the account's currentstorage_root, exactly as before.Some(map)(witness): the entry API on the map keyed by unhashedupdate.address.Occupiedreuses the cached(TrieWitness, Trie)— the cached trie's in-memory state intentionally wins overaccount_state.storage_root, including ignoring anEMPTY_TRIE_HASHreset fromremoved_storage.Vacantopens the trie after theremoved_storagereset, wraps it inTrieLogger::open_trie, and inserts it into the map so witness nodes for storage tries first touched during update application are recorded; the mutated map is returned to the caller as before.hash_address_fixed(H256). The witness copy usedhash_address(Vec<u8>) and converted back withH256::from_slice; both are the same keccak digest of the address, so trie keys andstorage_updateskeys are byte-identical.state_rootpassed toopen_storage_trieremains the single pre-loophash_no_commitsnapshot, not recomputed per iteration.Option<&mut StorageTries>is reborrowed per iteration withas_deref_mut(), and the batch arm uses a deferred-initlocal_trieso both arms unify as&mut Trie.Public API, RLP encoding, DB schema, and
STORE_SCHEMA_VERSIONare untouched. No tests were deleted or modified.If this change were wrong, one of these would have to be true:
hash_addressandhash_address_fixedwould have to produce different bytes for some address — they are bothkeccak(address.to_fixed_bytes())(store.rs), one asVec<u8>, one asH256.removed_storageroot — it cannot: theOccupiedarm never consultedaccount_state.storage_rootbefore this change either.StorageTriesby hashed address — callers inblockchain.rspopulate and drain it keyed by unhashedAddress, which this change keeps.How to test
Results on this branch:
cargo fmt— clean (diff confined to the two functions).cargo clippy --workspace --all-targets -- -D warnings— clean.cargo test -p ethrex-storage— 91 passed, 0 failed (+ doctests).cargo test -p ethrex-blockchain— 58 passed, 0 failed (covers the batch and witness call sites inblockchain.rs).Checklist
Storeschema change, soSTORE_SCHEMA_VERSIONis untouched (private helper extraction only; public API, RLP and DB layout identical).