fix(smt): bind insert result before debug_assert to keep side effect#4
Open
TaprootFreak wants to merge 1 commit intoZeroSync:masterfrom
Open
fix(smt): bind insert result before debug_assert to keep side effect#4TaprootFreak wants to merge 1 commit intoZeroSync:masterfrom
TaprootFreak wants to merge 1 commit intoZeroSync:masterfrom
Conversation
`debug_assert_eq!`/`debug_assert_ne!` expand to
`if cfg!(debug_assertions) { assert!(...) }`. In release builds
`cfg!(debug_assertions)` is `false`, so the entire expression is
eliminated and its argument is never evaluated — including any side
effects.
`SparseMerkleTree::insert` wrapped two mutating `HashMap::insert`
calls inside `debug_assert!` macros. In release this meant
`leaf_values.insert(...)` and the root-node `nodes.insert(...)` were
never executed: the tree state was never updated, `tree.root()` always
returned `ZERO_HASH`, and any code that compared the post-insert root
against an externally computed root (e.g. via `NonInclusionProof::insert`)
would diverge.
Reproduction on master:
cargo test -p zkcoins-program --lib merkle::sparse_merkle_tree
# 10/10 pass
cargo test --release -p zkcoins-program --lib merkle::sparse_merkle_tree
# 8/10 fail, including test_verify_and_insert and
# test_verify_and_insert_sibling
Fix: bind the `HashMap::insert` result to a local first, then run the
assertion on the local. The mutation now always runs; the assertion
remains debug-only as originally intended.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SparseMerkleTree::insertwraps two mutatingHashMap::insertcalls insidedebug_assert!macros. In release builds these calls are silently dropped, so the tree state is never updated.Mechanism
debug_assert_eq!/debug_assert_ne!expand to:Under
--releasethe workspace has no[profile.release]override, sodebug_assertionsisfalse. The compiler eliminatesif false { ... }entirely — the argument expression, including any side effects, is never evaluated.The current code in
program/src/merkle/sparse_merkle_tree.rs:In release, neither
HashMap::insertruns. Result:leaf_valuesstays empty, the root node mapping stays empty,tree.root()always returnsZERO_HASH. Any consistency check that compares the post-insert root against an externally computed root (e.g. viaNonInclusionProof::insert) diverges.Reproduction
On
master(a91939f), no other changes needed:The eight failing tests cover both inclusion and non-inclusion paths, including
test_verify_and_insertandtest_verify_and_insert_sibling.CI (
prove.yml) only builds the SP1 program and does not runcargo testin either profile, which explains why this has gone undetected.Standalone illustration
For anyone who wants to see the macro behavior outside this codebase:
cargo runprintslen = 1.cargo run --releaseprintslen = 0.Fix
Bind the
HashMap::insertresult to a local before the assertion. Mutation runs in both profiles; the assertion stays debug-only as originally intended.Diff: 8 insertions, 4 deletions, both in
program/src/merkle/sparse_merkle_tree.rs.Test plan
cargo test -p zkcoins-program --lib merkle::sparse_merkle_tree— 10/10 passcargo test --release -p zkcoins-program --lib merkle::sparse_merkle_tree— 10/10 pass (was 2/10)