[zero] perf(zebra-consensus): cache successful transparent script verification - #45
[zero] perf(zebra-consensus): cache successful transparent script verification#45aphelionz wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
Reintroduces a process-global cache in zebra-consensus so successful transparent script verification performed during mempool admission can be reused during subsequent block verification (v5+ only, keyed by WtxId), reducing repeated script-interpreter/FFI work.
Changes:
- Add a bounded, randomly-evicting transparent script verification cache (
transaction/script_cache.rs) keyed byWtxId, and consult/insert it from the transparent script-check pipeline. - Wire the cache into both block and mempool transaction verification paths, including bypass for spends of unmined mempool outputs.
- Add extensive tests and a Criterion benchmark to validate and measure cache hits/misses.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| zebra/zebra-consensus/src/transaction.rs | Integrates script-cache keying and hit/miss behavior into transparent script verification checks for block/mempool verification. |
| zebra/zebra-consensus/src/transaction/script_cache.rs | New bounded, process-global cache for successful transparent script verifications with keyed random replacement + metrics/test hooks. |
| zebra/zebra-consensus/src/transaction/tests.rs | Adds cache-focused integration tests covering key soundness (twins, v4 exclusion, poisoning resistance, cross-upgrade behavior, mempool→block reuse). |
| zebra/zebra-consensus/Cargo.toml | Adds siphasher dependency and registers the new script benchmark target. |
| zebra/zebra-consensus/benches/script.rs | New benchmark measuring per-input script verification cost and end-to-end block verification miss vs hit. |
| zebra/Cargo.toml | Adds siphasher to workspace dependencies. |
| zebra/Cargo.lock | Locks siphasher into the dependency graph for zebra-consensus. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
zebra/zebra-consensus/src/transaction/script_cache.rs:168
victim_indexcallsself.siphasher.hash(&key.as_bytes()), butSipHasher13is typically used via thestd::hash::HasherAPI (stateful), not via a purehash()helper. If this is using theHasherimplementation, reusing a singleSipHasher13instance would make victim selection depend on prior calls and risks thread-safety issues; ifhash()is not available, this won’t compile. Prefer storing the SipHash key/seed and constructing a freshSipHasher13pervictim_indexcall (or cloning/resetting per call) to get a deterministic PRF output for justkey.
/// The slot a full cache replaces when inserting `key`.
fn victim_index(&self, key: &WtxId) -> usize {
// Casts are lossless: `capacity` is a usize, and the modulus keeps
// the result below it.
(self.siphasher.hash(&key.as_bytes()) % self.capacity as u64) as usize
}
0ddc4d4 to
5ad9173
Compare
Path-gated informational job (not a required check): runs the script-cache and UTXO-lookup criterion benches on PRs and main pushes that touch the benched crates, and posts the timings to the step summary. Bench steps are guarded on their bench files existing, so this lands before #45/#46 and activates as they merge. rust-cache persists target/criterion, so criterion also reports a change estimate against the previous cached run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5ad9173 to
f4f43f0
Compare
…ification Block verification re-runs every input script already verified at mempool admission; zcashd skips the repeat via its script and signature caches, and zebra has had no reuse since upstream #10494. Remember each verified transaction by its WtxId (v5+ only: the ZIP-244 id commits to the branch id, the outpoints, and the scriptSigs), evict randomly via keyed siphash, and skip only the per-input script checks on a hit. Check, verification, and insert are co-located in one function; transactions spending unmined mempool outputs bypass the cache (zebra#10346). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
f4f43f0 to
9d30ba9
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
zebra/zebra-consensus/src/transaction/tests.rs:4221
- The panic message is inverted: this branch runs when the mock input is not a
PrevOut, but the message says it is.
let (input, known_utxos) = uniquely_sourced(0xA4, input, known_utxos);
let transparent::Input::PrevOut {
outpoint, sequence, ..
} = input
else {
panic!("mock input is a PrevOut");
| let transparent::Input::PrevOut { | ||
| outpoint, | ||
| unlock_script, | ||
| sequence, | ||
| } = input | ||
| else { | ||
| panic!("mock input is a PrevOut"); | ||
| }; |
Reintroduces transparent script verification reuse: a transaction verified at mempool admission is not re-verified in the block that mines it (zebra lost this in upstream #10494). This is the caching half of #34, redesigned per its review; the overlapping-UTXO-lookups half is #46.
WtxIdalone. The ZIP-244 txid commits to the consensus branch id and to the outpoints, which pin the immutable prevout scripts and values; the authorizing-data digest commits to the scriptSigs, so a CVE-2026-34377 same-txid twin misses. v1-v4 ids don't commit to the branch id and are never cached.siphasher, already in the dependency tree and vet-exempted); unguessable seed in production, fixed seed in tests. One deviation from the review's sketch: the victim is the hash of an insert counter, not of the incoming key, so the choice is uniform over the slots (a per-key victim would be direct-mapped, and two hot colliding keys would evict each other forever).make_transparent_input_and_output_checks(renamed: it builds deferred checks, it doesn't verify).Benchmarks (
cargo bench -p zebra-consensus --bench script; a 1001-input P2SH consolidation with a real ECDSA signature on every input, so a miss pays the interpreter, ZIP-244 sighash, and signature verification per input):The miss/hit gap is smaller than the serial number because the verifier spreads input checks across cores; a 4-vCPU host sits correspondingly closer to the serial figure.
145/145
cargo nextest run -p zebra-consensus;fmt,clippy -D warnings, andcargo vet --lockedclean.🤖 Generated with Claude Code