Skip to content

fix(validator): bump gobdk to hot-fix with per-CheckSig signature cache#842

Merged
oskarszoon merged 3 commits into
bsv-blockchain:mainfrom
oskarszoon:fix/validator-bump-gobdk-checker-cache
May 11, 2026
Merged

fix(validator): bump gobdk to hot-fix with per-CheckSig signature cache#842
oskarszoon merged 3 commits into
bsv-blockchain:mainfrom
oskarszoon:fix/validator-bump-gobdk-checker-cache

Conversation

@oskarszoon

Copy link
Copy Markdown
Contributor

Summary

Pins `github.com/bitcoin-sv/bdk/module/gobdk` to a pseudo-version of the hot-fix on top of `v1.2.3` (bitcoin-sv/bdk PR #40) that adds a per-instance `CachingScriptChecker` overriding `CheckSig` to short-circuit identical signature verifications within one `EvalScript` run.

Adds a regression test that drives a known-stuck testnet tx through the validator end-to-end against production policy settings and asserts the verifier returns in bounded time with `nil` (accepted as valid).

Background

Validators stalled for hours on testnet block 1,451,505 trying to verify tx `7bc9a3408dd0c87b835c887a0bce22c20788fc3c4b953929d4367656d80acab5`. The tx spends a 218-sat output whose locking script is 490,001 bytes of:

```
(OP_2DUP OP_CHECKSIGVERIFY) * 245,000 + OP_CHECKSIG
```

Each `(OP_2DUP, OP_CHECKSIGVERIFY)` pair leaves the stack at `[sig, pubkey]`, so the script performs 245,001 identical ECDSA verifications, each preceded by a full `SignatureHash` SHA256 stream over the 490 KB scriptCode. Without a cache that is hours of single-core CPU. The cgo entry point `_Cfunc_ScriptEngine_VerifyScript` is uninterruptible from Go so it presents to operators as a hang.

Block 1,451,505 is part of testnet's hardcoded checkpoint set (`go-chaincfg/params.go`) — the script is consensus-valid and the verifier must return `SCRIPT_ERR_OK`. Confirmed reproduced against `gobdk` v1.2.2 / v1.2.3 / v1.2.4 and against the pure-Go `gobt` verifier — not a library version regression.

The fix

`bitcoin-sv/bdk` PR #40 adds `bsv::CachingScriptChecker`:

  • Per-instance subclass of `TransactionSignatureChecker`, overrides `CheckSig`.
  • Cache key `(FNV1a(sig), FNV1a(pubkey))` plus a scriptCode-content fingerprint via `memcmp` against a stashed `std::vector<uint8_t>`. Within one `EvalScript` run on a script with no `OP_CODESEPARATOR` (effectively all modern BSV scripts) the scriptCode bytes are invariant, so the memcmp pass discovers identity cheaply without re-hashing.
  • On `OP_CODESEPARATOR` (scriptCode content changes) the cache is dropped and the new bytes stashed — correctness preserved.

First `CheckSig` does one full sighash plus one ECDSA verify; remaining 245,000 calls hit the in-memory map and return in O(ns) each. Verify time collapses from hours to ~70 s on macOS arm64 / similar on Linux x86_64.

The hot-fix branched off the BDK `release/1.2.3` tag rather than `master` so it can ship as `v1.2.3.1` (or merge forward) without entangling with the in-flight v1.2.4 upgrade in #839. PR #41 in the BDK repo is the master / v1.2.4 port of the same fix.

Why not wire the existing `CachingTransactionSignatureChecker` from `script/sigcache.cpp`?

Investigated; rejected. Three reasons (full discussion in BDK PR #40):

  1. Wrong cache layer. SV Node's `CachingTransactionSignatureChecker::VerifySignature` is called by `TransactionSignatureChecker::CheckSig` after the full `SignatureHash` has already been computed. The 245,001 × 490 KB SHA256 still happens; net saving for this tx would be ~12 s (just the ECDSA work).
  2. Build-list blast radius. `sigcache.cpp` is excluded from BDK's minimal source list and wiring it pulls in `gArgs` / `util.cpp` (application-only).
  3. Global state. `signatureCache` is file-static in `sigcache.cpp` and initialised from `gArgs` — cannot be initialised externally without an upstream BSV source patch.

Changes

  • `go.mod`: bump `github.com/bitcoin-sv/bdk/module/gobdk` from `v1.2.3` to the hot-fix pseudo-version `v1.2.4-0.20260511121643-5ab3fd5b627d`. The require block carries a multi-line comment documenting the pin rationale and the DO NOT bump to v1.2.4 until BDK PR Remove Distributor from RPC service #41 merges constraint.
  • `go.sum`: corresponding checksum update.
  • `services/validator/ScriptVerifierGoBDK_test.go`: new `Test_ScriptVerificationGoBDK_StuckTx_ProductionPolicy` loading the real fixture, driving the verifier under production policy, asserting nil return within a 3 minute deadline. Helpers `runVerifyWithDeadline` and `loadStuckTx7bc9a340Fixture` factor the deadline-bounded run and fixture loading.
  • `services/validator/testdata/`: three new files (tx hex, prev-locking-script hex, JSON metadata) capturing the production fixture. The prev-locking-script hex was extracted from the raw parent tx supplied by an operator with access to the production utxo external store — WhatsOnChain truncates this script to 50,000 bytes in its API/UI, which is why earlier investigations did not match the production cgo arg size (5,390,351 bytes).

Test plan

  • `go vet ./services/validator/...` — clean
  • `go test -count=1 -v -run Test_ScriptVerificationGoBDK_StuckTx_ProductionPolicy ./services/validator/ -timeout 300s` — passes in ~70 s on macOS arm64, verifier returns nil error
  • CI runs the new test as part of the validator package
  • Reviewer to spot-check the gobdk pseudo-version pin and the `DO NOT bump to v1.2.4` comment in `go.mod`

Follow-up

When BDK PR #41 merges and a tagged v1.2.4 release ships with the same hot-fix, a separate PR can bump to that and drop the warning comment.

Pins github.com/bitcoin-sv/bdk/module/gobdk to a pseudo-version of the
hot-fix on top of v1.2.3 (bitcoin-sv/bdk PR bsv-blockchain#40) that adds a per-instance
CachingScriptChecker overriding CheckSig to short-circuit identical
signature verifications within one EvalScript run.

Without the hot-fix the validator stalls for hours on testnet block
1,451,505 / tx 7bc9a3408dd0c87b835c887a0bce22c20788fc3c4b953929d4367656d80acab5.
The tx spends a 218-sat output whose locking script is 490,001 bytes of
(OP_2DUP OP_CHECKSIGVERIFY) * 245,000 + OP_CHECKSIG. Each (OP_2DUP,
OP_CHECKSIGVERIFY) pair leaves the stack at [sig, pubkey] so the script
performs 245,001 identical ECDSA verifications, each preceded by a full
SignatureHash SHA256 stream over the 490 KB scriptCode. Without the
cache that is hours of CPU at 100% on a single core; the cgo entry
point is uninterruptible from Go so it presents to operators as a hang
in _Cfunc_ScriptEngine_VerifyScript.

Block 1,451,505 is in testnet's hardcoded checkpoint set, so the script
must verify as valid — the fix preserves consensus semantics and only
collapses redundant work. With the hot-fix the verifier completes in
~70 s on macOS arm64 / similar on Linux x86_64 and returns nil
(accepted as valid).

Test_ScriptVerificationGoBDK_StuckTx_ProductionPolicy in
services/validator/ScriptVerifierGoBDK_test.go regresses the fix
end-to-end:
  - Loads the real testnet tx hex from
    services/validator/testdata/stuck-tx-7bc9a340.hex
  - Wires the real 490 KB previous-output locking script from
    services/validator/testdata/stuck-tx-7bc9a340-prev-locking-script-0.hex
    onto the input (WhatsOnChain truncates the script in its API/UI,
    so the fixture was extracted from the raw parent tx supplied by an
    operator with access to the production utxo external store)
  - Drives the verifier against production-grade PolicySettings
  - Asserts VerifyScript returns nil within a 3 minute deadline

The go.mod entry carries a hard-coded warning: do not bump gobdk to
v1.2.4 (or later) until bitcoin-sv/bdk PR bsv-blockchain#41 — the master/v1.2.4 port
of the same hot-fix — has been merged. Without bsv-blockchain#41 the upstream v1.2.4
release ships without the cache and re-introduces the multi-hour stall.
@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

🤖 Claude Code Review

Status: Complete

Critical Finding

[Critical] Dependency pinned to unmerged code: The go.mod pins github.com/bitcoin-sv/bdk/module/gobdk to pseudo-version v1.2.4-0.20260511121643-5ab3fd5b627d, which references commit 5ab3fd5b627d from bitcoin-sv/bdk PR #40. However, PR #40 is closed but not merged (merged: false). This means:

  1. The code being pinned was rejected/closed and never merged into the BDK repository
  2. The commit may not be available long-term if the branch is deleted
  3. The PR description states "DO NOT bump to gobdk v1.2.4 (or later) until bitcoin-sv/bdk PR Remove Distributor from RPC service #41 has merged", but PR Remove Distributor from RPC service #41 is also still open and unmerged

Recommendation: Before merging this PR, verify that the BDK fix has been properly merged (either PR #40 or #41) and pin to the official merged commit or tagged release instead of an unmerged branch commit.

Additional Observations

  • Documentation accuracy: The PR description is comprehensive but describes test files that were added in commit 1 and removed in commit 2. The final diff contains only dependency changes, which is correct.
  • Comment clarity: The multi-line comment in go.mod lines 15-21 clearly explains the hot-fix rationale and warns against premature upgrades.
  • Pseudo-version format: The pseudo-version syntax is technically correct for a Go module reference.

@oskarszoon oskarszoon requested review from ctnguyen and icellan May 11, 2026 12:37
@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Benchmark Comparison Report

Baseline: main (unknown)

Current: PR-842 (bb59880)

Summary

  • Regressions: 0
  • Improvements: 0
  • Unchanged: 142
  • Significance level: p < 0.05
All benchmark results (sec/op)
Benchmark Baseline Current Change p-value
_NewBlockFromBytes-4 1.961µ 1.669µ ~ 0.200
SplitSyncedParentMap_SetIfNotExists/256_buckets-4 61.59n 61.67n ~ 0.700
SplitSyncedParentMap_SetIfNotExists/16_buckets-4 61.43n 61.74n ~ 0.100
SplitSyncedParentMap_SetIfNotExists/1_bucket-4 61.68n 61.85n ~ 0.700
SplitSyncedParentMap_ConcurrentSetIfNotExists/256_buckets... 30.37n 29.73n ~ 0.100
SplitSyncedParentMap_ConcurrentSetIfNotExists/16_buckets_... 53.18n 51.63n ~ 0.100
SplitSyncedParentMap_ConcurrentSetIfNotExists/1_bucket_pa... 108.5n 106.8n ~ 0.100
MiningCandidate_Stringify_Short-4 264.4n 263.0n ~ 0.400
MiningCandidate_Stringify_Long-4 1.914µ 1.889µ ~ 0.100
MiningSolution_Stringify-4 983.1n 977.4n ~ 0.200
BlockInfo_MarshalJSON-4 1.755µ 1.739µ ~ 0.100
NewFromBytes-4 127.9n 146.0n ~ 0.100
Mine_EasyDifficulty-4 60.77µ 62.00µ ~ 0.700
Mine_WithAddress-4 6.824µ 6.745µ ~ 0.200
DiskTxMap_SetIfNotExists-4 3.759µ 3.679µ ~ 0.400
DiskTxMap_SetIfNotExists_Parallel-4 3.486µ 3.565µ ~ 0.700
DiskTxMap_ExistenceOnly-4 311.5n 311.2n ~ 1.000
Queue-4 193.0n 196.7n ~ 0.100
AtomicPointer-4 4.896n 4.704n ~ 0.100
ReorgOptimizations/DedupFilterPipeline/Old/10K-4 864.3µ 853.4µ ~ 0.100
ReorgOptimizations/DedupFilterPipeline/New/10K-4 833.0µ 816.3µ ~ 0.100
ReorgOptimizations/AllMarkFalse/Old/10K-4 109.3µ 115.4µ ~ 0.200
ReorgOptimizations/AllMarkFalse/New/10K-4 62.63µ 62.99µ ~ 0.100
ReorgOptimizations/HashSlicePool/Old/10K-4 59.35µ 63.77µ ~ 0.700
ReorgOptimizations/HashSlicePool/New/10K-4 11.93µ 11.91µ ~ 1.000
ReorgOptimizations/NodeFlags/Old/10K-4 5.313µ 5.387µ ~ 0.100
ReorgOptimizations/NodeFlags/New/10K-4 1.831µ 1.833µ ~ 0.800
ReorgOptimizations/DedupFilterPipeline/Old/100K-4 10.311m 9.713m ~ 0.100
ReorgOptimizations/DedupFilterPipeline/New/100K-4 10.083m 9.979m ~ 1.000
ReorgOptimizations/AllMarkFalse/Old/100K-4 1.136m 1.158m ~ 0.200
ReorgOptimizations/AllMarkFalse/New/100K-4 677.9µ 690.1µ ~ 0.100
ReorgOptimizations/HashSlicePool/Old/100K-4 695.6µ 634.5µ ~ 0.100
ReorgOptimizations/HashSlicePool/New/100K-4 337.4µ 309.9µ ~ 0.700
ReorgOptimizations/NodeFlags/Old/100K-4 57.52µ 58.06µ ~ 0.700
ReorgOptimizations/NodeFlags/New/100K-4 20.22µ 20.01µ ~ 1.000
TxMapSetIfNotExists-4 51.88n 51.64n ~ 0.700
TxMapSetIfNotExistsDuplicate-4 38.10n 38.21n ~ 0.700
ChannelSendReceive-4 644.4n 600.8n ~ 0.100
BlockAssembler_AddTx-4 0.02698n 0.02620n ~ 1.000
AddNode-4 11.68 11.12 ~ 0.400
AddNodeWithMap-4 11.96 10.56 ~ 0.400
DirectSubtreeAdd/4_per_subtree-4 74.88n 73.29n ~ 0.400
DirectSubtreeAdd/64_per_subtree-4 40.91n 41.70n ~ 0.100
DirectSubtreeAdd/256_per_subtree-4 39.83n 39.82n ~ 0.700
DirectSubtreeAdd/1024_per_subtree-4 38.62n 38.39n ~ 0.100
DirectSubtreeAdd/2048_per_subtree-4 38.16n 38.09n ~ 0.500
SubtreeProcessorAdd/4_per_subtree-4 330.6n 326.1n ~ 0.600
SubtreeProcessorAdd/64_per_subtree-4 319.4n 317.7n ~ 0.700
SubtreeProcessorAdd/256_per_subtree-4 322.9n 318.7n ~ 0.400
SubtreeProcessorAdd/1024_per_subtree-4 310.2n 311.4n ~ 0.100
SubtreeProcessorAdd/2048_per_subtree-4 311.5n 312.0n ~ 0.400
SubtreeProcessorRotate/4_per_subtree-4 316.4n 312.6n ~ 0.100
SubtreeProcessorRotate/64_per_subtree-4 319.5n 311.9n ~ 0.100
SubtreeProcessorRotate/256_per_subtree-4 313.1n 310.0n ~ 0.100
SubtreeProcessorRotate/1024_per_subtree-4 312.9n 308.8n ~ 0.200
SubtreeNodeAddOnly/4_per_subtree-4 87.68n 87.62n ~ 0.400
SubtreeNodeAddOnly/64_per_subtree-4 64.93n 64.85n ~ 0.700
SubtreeNodeAddOnly/256_per_subtree-4 63.97n 63.98n ~ 1.000
SubtreeNodeAddOnly/1024_per_subtree-4 63.48n 63.46n ~ 1.000
SubtreeCreationOnly/4_per_subtree-4 146.5n 146.9n ~ 1.000
SubtreeCreationOnly/64_per_subtree-4 545.4n 546.9n ~ 1.000
SubtreeCreationOnly/256_per_subtree-4 1.930µ 1.902µ ~ 0.100
SubtreeCreationOnly/1024_per_subtree-4 6.252µ 6.237µ ~ 0.400
SubtreeCreationOnly/2048_per_subtree-4 11.54µ 11.39µ ~ 0.100
SubtreeProcessorOverheadBreakdown/64_per_subtree-4 315.1n 312.8n ~ 0.100
SubtreeProcessorOverheadBreakdown/1024_per_subtree-4 312.0n 314.6n ~ 0.400
ParallelGetAndSetIfNotExists/1k_nodes-4 650.2µ 649.8µ ~ 1.000
ParallelGetAndSetIfNotExists/10k_nodes-4 1.761m 1.787m ~ 0.700
ParallelGetAndSetIfNotExists/50k_nodes-4 9.342m 8.970m ~ 0.100
ParallelGetAndSetIfNotExists/100k_nodes-4 17.88m 18.41m ~ 0.100
SequentialGetAndSetIfNotExists/1k_nodes-4 697.1µ 685.5µ ~ 0.100
SequentialGetAndSetIfNotExists/10k_nodes-4 3.442m 3.373m ~ 0.400
SequentialGetAndSetIfNotExists/50k_nodes-4 12.72m 12.73m ~ 1.000
SequentialGetAndSetIfNotExists/100k_nodes-4 24.48m 24.40m ~ 0.100
ProcessOwnBlockSubtreeNodesParallel/1k_nodes-4 720.5µ 721.7µ ~ 0.400
ProcessOwnBlockSubtreeNodesParallel/10k_nodes-4 4.685m 4.729m ~ 0.400
ProcessOwnBlockSubtreeNodesParallel/100k_nodes-4 21.68m 21.98m ~ 0.400
ProcessOwnBlockSubtreeNodesSequential/1k_nodes-4 751.1µ 748.9µ ~ 0.700
ProcessOwnBlockSubtreeNodesSequential/10k_nodes-4 6.732m 6.718m ~ 1.000
ProcessOwnBlockSubtreeNodesSequential/100k_nodes-4 47.98m 47.30m ~ 0.100
CalcBlockWork-4 507.4n 519.8n ~ 1.000
CalculateWork-4 683.4n 690.3n ~ 0.700
BuildBlockLocatorString_Helpers/Size_10-4 1.319µ 1.340µ ~ 0.700
BuildBlockLocatorString_Helpers/Size_100-4 15.60µ 15.34µ ~ 1.000
BuildBlockLocatorString_Helpers/Size_1000-4 125.2µ 126.9µ ~ 0.400
CatchupWithHeaderCache-4 104.4m 104.3m ~ 1.000
_BufferPoolAllocation/16KB-4 3.354µ 4.031µ ~ 0.200
_BufferPoolAllocation/32KB-4 9.131µ 8.210µ ~ 0.700
_BufferPoolAllocation/64KB-4 14.16µ 16.61µ ~ 0.100
_BufferPoolAllocation/128KB-4 28.23µ 29.11µ ~ 0.100
_BufferPoolAllocation/512KB-4 109.8µ 114.7µ ~ 0.100
_BufferPoolConcurrent/32KB-4 19.27µ 18.87µ ~ 0.400
_BufferPoolConcurrent/64KB-4 28.22µ 30.16µ ~ 0.100
_BufferPoolConcurrent/512KB-4 147.6µ 148.4µ ~ 0.100
_SubtreeDeserializationWithBufferSizes/16KB-4 621.3µ 620.8µ ~ 1.000
_SubtreeDeserializationWithBufferSizes/32KB-4 616.9µ 621.3µ ~ 1.000
_SubtreeDeserializationWithBufferSizes/64KB-4 609.4µ 612.3µ ~ 0.400
_SubtreeDeserializationWithBufferSizes/128KB-4 610.8µ 621.7µ ~ 0.200
_SubtreeDeserializationWithBufferSizes/512KB-4 631.3µ 632.7µ ~ 0.700
_SubtreeDataDeserializationWithBufferSizes/16KB-4 36.52m 36.25m ~ 0.100
_SubtreeDataDeserializationWithBufferSizes/32KB-4 36.02m 36.32m ~ 0.100
_SubtreeDataDeserializationWithBufferSizes/64KB-4 35.80m 36.35m ~ 0.100
_SubtreeDataDeserializationWithBufferSizes/128KB-4 36.10m 36.06m ~ 1.000
_SubtreeDataDeserializationWithBufferSizes/512KB-4 35.83m 35.97m ~ 0.700
_PooledVsNonPooled/Pooled-4 646.6n 742.1n ~ 0.100
_PooledVsNonPooled/NonPooled-4 6.719µ 6.711µ ~ 0.400
_MemoryFootprint/Current_512KB_32concurrent-4 6.759µ 6.929µ ~ 0.700
_MemoryFootprint/Proposed_32KB_32concurrent-4 9.710µ 10.380µ ~ 0.100
_MemoryFootprint/Alternative_64KB_32concurrent-4 9.179µ 10.492µ ~ 0.100
SubtreeSizes/10k_tx_4_per_subtree-4 1.336m 1.342m ~ 0.400
SubtreeSizes/10k_tx_16_per_subtree-4 331.2µ 315.8µ ~ 0.200
SubtreeSizes/10k_tx_64_per_subtree-4 75.37µ 75.38µ ~ 1.000
SubtreeSizes/10k_tx_256_per_subtree-4 18.78µ 18.73µ ~ 0.700
SubtreeSizes/10k_tx_512_per_subtree-4 9.397µ 9.324µ ~ 0.400
SubtreeSizes/10k_tx_1024_per_subtree-4 4.692µ 4.676µ ~ 0.700
SubtreeSizes/10k_tx_2k_per_subtree-4 2.333µ 2.322µ ~ 1.000
BlockSizeScaling/10k_tx_64_per_subtree-4 74.32µ 74.07µ ~ 0.700
BlockSizeScaling/10k_tx_256_per_subtree-4 18.66µ 18.83µ ~ 0.200
BlockSizeScaling/10k_tx_1024_per_subtree-4 4.689µ 4.711µ ~ 0.100
BlockSizeScaling/50k_tx_64_per_subtree-4 394.8µ 390.3µ ~ 0.700
BlockSizeScaling/50k_tx_256_per_subtree-4 95.11µ 95.15µ ~ 0.700
BlockSizeScaling/50k_tx_1024_per_subtree-4 23.22µ 23.17µ ~ 0.400
SubtreeAllocations/small_subtrees_exists_check-4 159.2µ 156.4µ ~ 0.400
SubtreeAllocations/small_subtrees_data_fetch-4 172.0µ 169.5µ ~ 0.400
SubtreeAllocations/small_subtrees_full_validation-4 327.8µ 331.9µ ~ 0.100
SubtreeAllocations/medium_subtrees_exists_check-4 9.263µ 9.330µ ~ 0.700
SubtreeAllocations/medium_subtrees_data_fetch-4 10.06µ 10.01µ ~ 0.400
SubtreeAllocations/medium_subtrees_full_validation-4 19.24µ 19.09µ ~ 0.700
SubtreeAllocations/large_subtrees_exists_check-4 2.265µ 2.232µ ~ 0.600
SubtreeAllocations/large_subtrees_data_fetch-4 2.424µ 2.444µ ~ 0.700
SubtreeAllocations/large_subtrees_full_validation-4 4.801µ 4.810µ ~ 1.000
_prepareTxsPerLevel-4 387.3m 387.1m ~ 1.000
_prepareTxsPerLevelOrdered-4 4.675m 4.403m ~ 0.400
_prepareTxsPerLevel_Comparison/Original-4 401.4m 393.9m ~ 0.200
_prepareTxsPerLevel_Comparison/Optimized-4 4.598m 4.338m ~ 0.100
StoreBlock_Sequential/BelowCSVHeight-4 326.2µ 319.7µ ~ 1.000
StoreBlock_Sequential/AboveCSVHeight-4 332.3µ 329.9µ ~ 1.000
GetUtxoHashes-4 256.1n 258.6n ~ 1.000
GetUtxoHashes_ManyOutputs-4 47.51µ 43.92µ ~ 0.100
_NewMetaDataFromBytes-4 242.7n 239.5n ~ 0.200
_Bytes-4 624.9n 642.5n ~ 0.100
_MetaBytes-4 573.1n 577.9n ~ 0.700

Threshold: >10% with p < 0.05 | Generated: 2026-05-11 13:48 UTC

The original commit added a Test_ScriptVerificationGoBDK_StuckTx_ProductionPolicy
regression test that loaded the real testnet block 1,451,505 fixture and
asserted the verifier returns within a deadline. The test is a faithful
end-to-end reproducer but takes ~70 s on macOS arm64 and ~3 min on
Linux x86_64 even with the gobdk hot-fix wired in — too much wall-clock
to spend on every CI run.

Synthetic alternatives that fit a < 1 s with-cache / > 60 s without-cache
budget are not feasible with the current per-CheckSig memcmp identity
check in the gobdk cache: the per-iter memcmp cost scales linearly with
scriptCode size, capping the speedup ratio against the without-cache
SignatureHash at ~15 x. No (N, scriptCode) combination fits both
budgets.

The fixture file naming also did not follow the rest of the codebase's
hash-based <txid>.hex / <hash>.bin convention.

Drop the test and the three testdata files from this PR so the change is
just the go.mod bump. The previous commit on this branch retains the
test in git history; revisit later either with a cheaper-identity gobdk
cache (would enable a smaller synthetic regression) or as an opt-in
benchmark target outside the default CI test set.
Drop the dangling reference to the regression test (removed in the
previous commit) and the overspecified production-tx details. Keep the
two pieces of information a future bumper actually needs: which BDK PR
landed the hot-fix, and the constraint that v1.2.4 should not be
adopted until bitcoin-sv/bdk PR bsv-blockchain#41 has merged.
oskarszoon added a commit that referenced this pull request May 11, 2026
…he (#842)

Pins github.com/bitcoin-sv/bdk/module/gobdk to a pseudo-version of the
hot-fix on top of v1.2.3 (bitcoin-sv/bdk PR #40) that adds a per-instance
CachingScriptChecker overriding CheckSig to short-circuit identical
signature verifications within one EvalScript run.

Without the hot-fix the validator stalls for hours on testnet block
1,451,505 / tx 7bc9a3408dd0c87b835c887a0bce22c20788fc3c4b953929d4367656d80acab5,
whose 490 KB locking script performs 245,001 identical ECDSA verifications.
The cgo entry point is uninterruptible from Go so it presents to operators
as a hang in _Cfunc_ScriptEngine_VerifyScript. With the hot-fix the
verifier completes in ~70 s.

Do not bump gobdk to v1.2.4 (or later) until bitcoin-sv/bdk PR #41 — the
master/v1.2.4 port of the same hot-fix — has been merged.
@sonarqubecloud

Copy link
Copy Markdown

@oskarszoon oskarszoon merged commit b6405b0 into bsv-blockchain:main May 11, 2026
25 checks passed
@oskarszoon oskarszoon self-assigned this May 12, 2026
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.

3 participants