starknet_committer,starknet_patricia,starknet_patricia_storage: hoist per-node db key prefix in index-layout serialization#14852
Conversation
… per-node db key prefix out of index-layout serialization loop Follow-up performance review of #14850, scoped to the same hot path (Patricia trie node key construction). In index-layout storage tries, `IndexLayoutStarknetStorageValue::get_static_prefix` rebuilds a fresh `Vec<u8>` from the contract address for every single node (inner and leaf alike), even though all nodes serialized in one `FilledTree::serialize` call, or deleted in one `serialize_deleted_nodes` iteration, share the same contract address. For a contract with thousands of modified storage slots in a block, this recomputes and heap-allocates an identical 32-byte prefix thousands of times instead of once. Add `HasDynamicPrefix::static_prefix_hint`, a per-type hook that returns the prefix without needing an instance when it doesn't depend on node data (`Some`, via the blanket `HasStaticPrefix` impl — this covers index layout) or `None` when it does (facts layout's inner-vs-leaf branching, left untouched). `FilledTreeImpl::serialize` computes the hint once per call and threads it through `NodeLayout::get_db_object`; `serialize_deleted_nodes` computes it once per contract in its existing loop. `create_db_key` now borrows its prefix instead of taking it by value, since callers with a precomputed prefix no longer need to move it per node. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQNuoKFwFs933ZC5oRvcAC
Covers the correctness contract of HasDynamicPrefix::static_prefix_hint: that the hint matches the per-instance get_prefix, and that two objects sharing a key_context but differing in unrelated data produce identical db keys through both the hoisted and per-instance paths. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQNuoKFwFs933ZC5oRvcAC
PR SummaryMedium Risk Overview Adds
Reviewed by Cursor Bugbot for commit 911cc8f. Bugbot is set up for automated code reviews on this repo. Configure here. |
Summary
Follow-up performance review of #14850 (which removed a redundant
.to_vec()copy increate_db_key), scoped to the same hot path: Patricia trie DB key construction, which runs once per node per block during block commitment.In the index-layout storage backend,
IndexLayoutStarknetStorageValue::get_static_prefix(crates/starknet_committer/src/db/index_db/leaves.rs) rebuilds a fresh 32-byteVec<u8>from a contract's address for every node in that contract's storage trie (inner nodes and leaves alike) — even though every node serialized in oneFilledTree::serializecall, or deleted in oneserialize_deleted_nodesiteration, shares the same contract address. For a contract with thousands of modified storage slots in a block, this recomputes and heap-allocates an identical prefix thousands of times instead of once.Facts layout doesn't have this problem — its prefix depends on node type (inner vs. leaf), not just on the key context — so it's left untouched.
Change
starknet_patricia_storage::db_object: addedHasDynamicPrefix::static_prefix_hint(key_context) -> Option<DbKeyPrefix>, a hook that returns the prefix without an instance when it depends only onkey_context(Some, via the blanketHasStaticPrefiximpl — covers index layout) orNonewhen it's node-data-dependent (facts layout's manualHasDynamicPrefiximpl doesn't get the blanket override, so it staysNone, and itsget_db_objectignores the hint — unchanged behavior).starknet_patricia::db_layout+filled_tree::tree:NodeLayout::get_db_objectgained aprefix_hint: Option<&DbKeyPrefix>parameter;FilledTreeImpl::serializecomputes the hint once before its per-node loop and reuses it for every node.starknet_committer::db::index_db::db:IndexNodeLayout::get_db_objectuses the hint when present;serialize_deleted_nodes(a separate per-contract loop) now computes the prefix once per contract instead of once per node index.create_db_keynow borrows its prefix (&DbKeyPrefix) instead of taking it by value, since callers with a precomputed prefix no longer need to move it per node; all call sites updated.starknet_patricia_storage::db_object_test) directly pinning the invariant the optimization relies on: the hoisted hint equals the per-instance prefix, and two objects sharing a key context but differing in data produce identical db keys via both paths.Test plan
cargo build -p starknet_patricia_storage -p starknet_patricia -p starknet_committer -p starknet_transaction_proverSEED=0 cargo test -p starknet_patricia_storage -p starknet_patricia -p starknet_committer -p starknet_transaction_prover— all green, with two known-unrelated exceptions confirmed pre-existing on a cleanorigin/maincheckout:starknet_transaction_prover'stest_compiled_class_v1_to_casm_round_tripfails in this sandbox because it needs to download the Cairo 1 compiler over a network the sandbox doesn't have; reproduces identically onorigin/main.starknet_patricia_storage'stest_storage_concurrent_access::case_2_mdbx_storageuses a hardcoded/tmp/test_mdbx_storagepath with no cleanup between runs; it only fails when the suite is re-run repeatedly against leftover on-disk state from a prior run in the same environment. Passes cleanly otherwise.unset CI && scripts/rust_fmt.sh— no diffdb_object_test.rsunit tests above and confirmed no blocking issues; a second independent Opus pass reached the same verdict.🤖 Generated with Claude Code
Generated by Claude Code