bench: add script verification benchmark for P2TR key path spends
What changed, and why it matters
This commit adds a new performance benchmark to Bitcoin Core. It does not change any production code that runs the Bitcoin network; it only adds a test that measures how fast the software can verify a specific kind of modern Bitcoin transaction (a Taproot/P2TR key-path spend).
No security action needed. This is a benign benchmark-only addition. Normal code-review approval is sufficient.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change modifies src/bench/verify_script.cpp to refactor an existing P2WPKH script-verification benchmark into a reusable helper and adds a second benchmark case for P2TR (segwit v1, Taproot key-path) spends. It updates the signing path to use XOnlyPubKey for Taproot and passes PrecomputedTransactionData to the signature checker. No consensus, networking, wallet, or RPC code is touched.
Changed components
src/bench/verify_script.cppInspect captured patch +31 / −18
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index b8a71dd4..1ef6a361 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -20,36 +20,45 @@
#include <cstdint>
#include <vector>
-// Microbenchmark for verification of a basic P2WPKH script. Can be easily
-// modified to measure performance of other types of scripts.
-static void VerifyScriptBench(benchmark::Bench& bench)
+enum class ScriptType {
+ P2WPKH, // segwitv0, witness-pubkey-hash (ECDSA signature)
+ P2TR, // segwitv1, taproot key-path spend (Schnorr signature)
+};
+
+// Microbenchmark for verification of standard scripts.
+static void VerifyScriptBench(benchmark::Bench& bench, ScriptType script_type)
{
ECC_Context ecc_context{};
// Create deterministic key material needed for output script creation / signing
+ CKey privkey;
+ privkey.Set(uint256::ONE.begin(), uint256::ONE.end(), /*fCompressedIn=*/true);
+ CPubKey pubkey = privkey.GetPubKey();
+ CKeyID key_id = pubkey.GetID();
+
FlatSigningProvider keystore;
- CPubKey pubkey;
- {
- CKey privkey;
- privkey.Set(uint256::ONE.begin(), uint256::ONE.end(), /*fCompressedIn=*/true);
- pubkey = privkey.GetPubKey();
- CKeyID key_id = pubkey.GetID();
- keystore.keys.emplace(key_id, privkey);
- keystore.pubkeys.emplace(key_id, pubkey);
- }
+ keystore.keys.emplace(key_id, privkey);
+ keystore.pubkeys.emplace(key_id, pubkey);
- // Create crediting and spending transactions
- CScript scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkey));
- const CMutableTransaction& txCredit = BuildCreditingTransaction(scriptPubKey, 1);
+ // Create crediting and spending transactions with provided input type
+ CTxDestination dest;
+ switch (script_type) {
+ case ScriptType::P2WPKH: dest = WitnessV0KeyHash(pubkey); break;
+ case ScriptType::P2TR: dest = WitnessV1Taproot(XOnlyPubKey{pubkey}); break;
+ default: assert(false);
+ }
+ const CMutableTransaction& txCredit = BuildCreditingTransaction(GetScriptForDestination(dest), 1);
CMutableTransaction txSpend = BuildSpendingTransaction(/*scriptSig=*/{}, /*scriptWitness=*/{}, CTransaction(txCredit));
- // Sign spending transaction
+ // Sign spending transaction, precompute transaction data
+ PrecomputedTransactionData txdata;
{
std::map<COutPoint, Coin> coins;
coins[txSpend.vin[0].prevout] = Coin(txCredit.vout[0], /*nHeightIn=*/100, /*fCoinBaseIn=*/false);
std::map<int, bilingual_str> input_errors;
bool complete = SignTransaction(txSpend, &keystore, coins, SIGHASH_ALL, input_errors);
assert(complete);
+ txdata.Init(txSpend, /*spent_outputs=*/{txCredit.vout[0]});
}
// Benchmark.
@@ -60,13 +69,16 @@ static void VerifyScriptBench(benchmark::Bench& bench)
txCredit.vout[0].scriptPubKey,
&txSpend.vin[0].scriptWitness,
STANDARD_SCRIPT_VERIFY_FLAGS,
- MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL),
+ MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue, txdata, MissingDataBehavior::ASSERT_FAIL),
&err);
assert(err == SCRIPT_ERR_OK);
assert(success);
});
}
+static void VerifyScriptP2WPKH(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2WPKH); }
+static void VerifyScriptP2TR(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2TR); }
+
static void VerifyNestedIfScript(benchmark::Bench& bench)
{
std::vector<std::vector<unsigned char>> stack;
@@ -88,5 +100,6 @@ static void VerifyNestedIfScript(benchmark::Bench& bench)
});
}
-BENCHMARK(VerifyScriptBench);
+BENCHMARK(VerifyScriptP2WPKH);
+BENCHMARK(VerifyScriptP2TR);
BENCHMARK(VerifyNestedIfScript);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.