Skip to content

script: cache successful P2MR PQC signature checks - #162

Open
kiwidream wants to merge 6 commits into
1.x.xfrom
kiwidream/cache-p2mr-signature-checks
Open

kiwidream wants to merge 6 commits into
1.x.xfrom
kiwidream/cache-p2mr-signature-checks

Conversation

@kiwidream

@kiwidream kiwidream commented Sep 15, 2026

Copy link
Copy Markdown
Member

Summary

Cache successful P2MR transaction-signature (SLH-DSA) checks in the existing signature cache so mempool admission's policy pass and consensus pass verify each PQC signature once instead of twice. Closes #153.

  • CachingTransactionSignatureChecker now overrides VerifyPQCSignature with the same shape as the ECDSA and Schnorr overrides: look up a salted entry ('P' domain, keyed by sighash, public key and raw signature bytes), return true on a hit, otherwise call the primitive verifier and insert only a verified success when the checker is in store mode. The cache is the same bounded cuckoo cache with the same budget, lazy-discard consumption and minimum allocation; no separate store is added.
  • Nothing upstream of the primitive seam changes: signature length and hash-type checks, P2MR sighash computation, legacy and v2 validation-weight charging, the two admission passes, full-script cache keys and flags, cache sizing, and data-signature validation (which calls the primitive directly) are untouched. No consensus rule, validity or activation change.
  • A test-only observer pointer on SignatureCache (null by default, never installed by production code; one relaxed atomic load beside a SHA256 and an SLH-DSA verify) lets tests observe each cache lookup and the verified result after a miss. A separate scoped counter at CPQCPubKey::Verify records actual backend invocations, so extra verification on cache hits cannot hide behind unchanged cache events. The backend and validity checks remain unchanged. The new txvalidationcache_tests/pqc_policy_consensus_reuse admits fresh P2MR spends through the real ProcessTransaction and asserts the ordered event log: one miss and one verified insert in the policy pass, then a hit in the consensus pass (two verifications instead of four for a two-input spend). It also documents the non-evidence cases: a repeated test_accept shows a lone policy hit because the consensus pass is a full-script cache hit, and a duplicate submission is rejected before any script check.
  • New pqc_sigcache_tests cover key composition and domain separation, failures never populating the cache, cold/warm/uncached equivalence over the P2MR witness-vector corpus including error codes, unchanged weight budgets at the legacy and v2 boundaries, store/consume semantics with seeded reclamation, and the shared bounded budget.
  • New benchmark checkinputs_p2mr_pqc.cpp times the two-pass admission pattern per fresh transaction (pre-signed pool, fixed epochs so no transaction is ever rechecked).

Testing

  • Built locally (Debug, tests and benchmarks enabled).
  • At 79c2bd51b2, 186 cases pass across pqc_tests, txvalidationcache_tests, pqc_sigcache_tests, cuckoocache_tests, script_p2mr_tests, script_tests, and transaction_tests.
  • Docker lint passes on a clean clone of 79c2bd51b2.
  • Thirteen cache mutation variants were caught at 337bfa668f; a redundant-verification-on-hit mutation is also caught by the final primitive counter. Each restored named test passes. The full unit suite passed before the counter follow-up.
  • Not run: package admission, real block-connection consumption, end-to-end admission timing, and quiet-host timing. Independent adversarial review is pending and will follow PR creation.

Measurements

These measurements were collected at 337bfa668f, before the primitive-counter follow-up; its overhead has not been measured.

Three interleaved baseline/candidate pairs on an Apple M5 Max (18 cores, macOS 26.4, Apple clang 17, Release -O2, default 16 MiB signature cache). The baseline compiles out the override from the candidate tree. The benchmark times two script-check passes on distinct pre-signed transactions; it does not time full mempool admission. The observer was detached.

Signatures per transaction Baseline run medians (ms/tx) Candidate run medians (ms/tx) Median paired ratio
1 4.071, 3.996, 3.594 2.070, 1.984, 1.808 0.503 (0.496–0.508)
5 24.289, 21.872, 21.905 10.173, 9.010, 9.288 0.419 (0.412–0.424)

All five-signature pairs were noisy: other builds were active and load rose during baseline arms. Observer overhead was not isolated above noise. No production or block-connection speedup is claimed.

Target Branch

  • This PR targets main or a maintainer-requested release branch such as 0.1.x. (Targets 1.x.x as requested by the maintainer.)

Risk / Review Notes

  • Consensus, script, crypto, wallet, P2P, release, CI, or security-sensitive behavior changed.
  • No consensus, script, crypto, wallet, P2P, release, CI, or security-sensitive behavior changed.

Notes: consensus-adjacent script code. The change reuses a verified primitive result only when sighash, public key and signature bytes are identical, within a distinct salted domain; validity outcomes and script errors are shown equal for uncached, cold and warm checks across the witness-vector corpus, and weight budgets are equal at both weight-rule boundaries. Block connection consumes entries lazily exactly as for ECDSA and Schnorr.

Docs / Process Impact

Choose exactly one:

  • I updated public docs because this PR changes user-visible behavior, integration guidance, release/process guidance, or expected validation.
  • No public docs update needed. Reason: internal validation cache behaviour; no interface, option or default changes.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Review follow-up

  • Restore header-defined PQC verification and counter state so the shared kernel does not depend on bitcoin_common.
  • Check middle and final bytes of the signature, public key, and sighash. All three prefix-truncation mutations fail; restored tests pass.
  • Validation: 186 targeted regression tests; shared kernel and chainstate executable built with wallet disabled and reduced exports on macOS; Docker lint passed for the committed tree.

kiwidream and others added 3 commits September 15, 2026 15:01
Add a PQC domain to SignatureCache and a VerifyPQCSignature override in
CachingTransactionSignatureChecker, following the Schnorr pattern. Entries
are SHA256(nonce || 'P' || 31 zero bytes || sighash || pubkey || signature)
in the existing shared cuckoo cache. Only a verified success is inserted,
and only when the checker stores; non-store lookups keep the lazy
erase semantics. Interpreter checks, validation-weight charges, script
flags, the full-script cache and data signatures are unchanged.

Add a null-by-default, test-only PQCSignatureCacheObserver so tests can
prove that a fresh mempool admission verifies each PQC signature once in
the policy pass and reuses it in the consensus pass.

Tests: pqc_sigcache_tests covers cache-key components and domain
separation, failures never populating the cache (including cold/warm
equivalence over the witness-vector corpus), unchanged P2MR errors and
weight budgets on warm caches, store/consume and reclamation, and the
shared budget. txvalidationcache_tests/pqc_policy_consensus_reuse checks
the observer event log for real ProcessTransaction admissions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Time the mempool admission script-check pattern (standard flags without
full-script storing, then consensus flags with storing) for distinct,
pre-signed P2MR CHECKSIGPQC spends against one long-lived validation
cache with default sizes. Signing and pool construction happen before
timing, and epochs and iterations are fixed so no transaction is checked
twice. A low-priority variant attaches a counting observer to bound the
test-only observer cost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-16T20:19:24.897968Z 3ec75ff New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 79c2bd51b2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/crypto/pqc.h Outdated
Keep the verifier and its scoped counter header-defined so consensus builds do not depend on bitcoin_common. Exercise middle and final bytes of all cache-key inputs to detect prefix truncation.
Pre-allocate capacity for the per-tuple cache entries so the reclamation
test does not grow the vector inside its fill loop.
Pick up the Qt wallet shutdown test rework so CI no longer evaluates the removed wall-clock bound.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant