Skip to content

Commit 07167e9

Browse files
committed
Merge bitcoin/bitcoin#35038: bench: add script verification benchmark for P2TR script-path spends
ec0d923 bench: improve `VerifyNestedIfScript` benchmark precision (make stack clearing untimed) (David Gumberg) 56f3ad9 bench: add script verification benchmark for P2TR script-path spends (Sebastian Falbesoner) Pull request description: Similarly as #34472 already did for key-path spends, this PR adds a benchmark for P2TR script-path spends. So far we don't have a benchmark on master yet that exercises the verification of taproot commitments ([`VerifyTaprootCommitment`](https://github.com/bitcoin/bitcoin/blob/2ea8612051b5f0b8574a2cfe7d946f5671ef885a/src/script/interpreter.cpp#L1903)). Note that the tapscript leaf intentionally includes a single OP_CHECKSIG as it likely reflects the real world best. Spending tapscript leafs without any signature checks don't make much sense (they could be trivially tampered with and thus stolen by miners), and doing more than one signature check seems the exception rather than the rule. The primary motivation for this PR is to evaluate how potential secp256k1 changes in pubkey tweaking (e.g. [#1843](bitcoin-core/secp256k1#1843)) may impact script verification performance. ACKs for top commit: davidgumberg: reACK ec0d923 l0rinc: ACK ec0d923 sedited: ACK ec0d923 Tree-SHA512: 6fa1f2c336d6332b4f2d22173279ee29ad3ec5e5431109913a6978fef32e22a34d3247729ac9092bfdbbcd8f9dfcad8da50bc4ede2cb7efc1f93d6a744ddf41b
2 parents 2bc8fef + ec0d923 commit 07167e9

1 file changed

Lines changed: 39 additions & 15 deletions

File tree

src/bench/verify_script.cpp

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,20 @@
2222

2323
enum class ScriptType {
2424
P2WPKH, // segwitv0, witness-pubkey-hash (ECDSA signature)
25-
P2TR, // segwitv1, taproot key-path spend (Schnorr signature)
25+
P2TR_KeyPath, // segwitv1, taproot key-path spend (Schnorr signature)
26+
P2TR_ScriptPath, // segwitv1, taproot script-path spend (Tapscript leaf with a single OP_CHECKSIG)
2627
};
2728

29+
static size_t ExpectedWitnessStackSize(ScriptType script_type)
30+
{
31+
switch (script_type) {
32+
case ScriptType::P2WPKH: return 2; // [pubkey, signature]
33+
case ScriptType::P2TR_KeyPath: return 1; // [signature]
34+
case ScriptType::P2TR_ScriptPath: return 3; // [signature, tapscript, control block]
35+
} // no default case, so the compiler can warn about missing cases
36+
assert(false);
37+
}
38+
2839
// Microbenchmark for verification of standard scripts.
2940
static void VerifyScriptBench(benchmark::Bench& bench, ScriptType script_type)
3041
{
@@ -34,6 +45,7 @@ static void VerifyScriptBench(benchmark::Bench& bench, ScriptType script_type)
3445
CKey privkey;
3546
privkey.Set(uint256::ONE.begin(), uint256::ONE.end(), /*fCompressedIn=*/true);
3647
CPubKey pubkey = privkey.GetPubKey();
48+
XOnlyPubKey xonly_pubkey{pubkey};
3749
CKeyID key_id = pubkey.GetID();
3850

3951
FlatSigningProvider keystore;
@@ -44,7 +56,14 @@ static void VerifyScriptBench(benchmark::Bench& bench, ScriptType script_type)
4456
const auto dest{[&]() -> CTxDestination {
4557
switch (script_type) {
4658
case ScriptType::P2WPKH: return WitnessV0KeyHash(pubkey);
47-
case ScriptType::P2TR: return WitnessV1Taproot(XOnlyPubKey{pubkey});
59+
case ScriptType::P2TR_KeyPath: return WitnessV1Taproot(xonly_pubkey);
60+
case ScriptType::P2TR_ScriptPath:
61+
TaprootBuilder builder;
62+
builder.Add(0, CScript() << ToByteVector(xonly_pubkey) << OP_CHECKSIG, TAPROOT_LEAF_TAPSCRIPT);
63+
builder.Finalize(XOnlyPubKey::NUMS_H); // effectively unspendable key-path
64+
const auto output{builder.GetOutput()};
65+
keystore.tr_trees.emplace(output, builder);
66+
return output;
4867
} // no default case, so the compiler can warn about missing cases
4968
assert(false);
5069
}()};
@@ -54,16 +73,18 @@ static void VerifyScriptBench(benchmark::Bench& bench, ScriptType script_type)
5473
// Sign spending transaction, precompute transaction data
5574
PrecomputedTransactionData txdata;
5675
{
57-
std::map<COutPoint, Coin> coins;
58-
coins[txSpend.vin[0].prevout] = Coin(txCredit.vout[0], /*nHeightIn=*/100, /*fCoinBaseIn=*/false);
76+
const std::map<COutPoint, Coin> coins{
77+
{txSpend.vin[0].prevout, Coin(txCredit.vout[0], /*nHeightIn=*/100, /*fCoinBaseIn=*/false)}
78+
};
5979
std::map<int, bilingual_str> input_errors;
60-
bool complete = SignTransaction(txSpend, &keystore, coins, SIGHASH_ALL, input_errors);
61-
assert(complete);
80+
assert(SignTransaction(txSpend, &keystore, coins, SIGHASH_ALL, input_errors));
81+
// Weak sanity check on witness data to ensure we produced the intended spending type
82+
assert(txSpend.vin[0].scriptWitness.stack.size() == ExpectedWitnessStackSize(script_type));
6283
txdata.Init(txSpend, /*spent_outputs=*/{txCredit.vout[0]});
6384
}
6485

6586
// Benchmark.
66-
bench.run([&] {
87+
bench.unit("script").run([&] {
6788
ScriptError err;
6889
bool success = VerifyScript(
6990
txSpend.vin[0].scriptSig,
@@ -78,7 +99,8 @@ static void VerifyScriptBench(benchmark::Bench& bench, ScriptType script_type)
7899
}
79100

80101
static void VerifyScriptP2WPKH(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2WPKH); }
81-
static void VerifyScriptP2TR(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2TR); }
102+
static void VerifyScriptP2TR_KeyPath(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2TR_KeyPath); }
103+
static void VerifyScriptP2TR_ScriptPath(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2TR_ScriptPath); }
82104

83105
static void VerifyNestedIfScript(benchmark::Bench& bench)
84106
{
@@ -93,14 +115,16 @@ static void VerifyNestedIfScript(benchmark::Bench& bench)
93115
for (int i = 0; i < 100; ++i) {
94116
script << OP_ENDIF;
95117
}
96-
bench.run([&] {
97-
auto stack_copy = stack;
98-
ScriptError error;
99-
bool ret = EvalScript(stack_copy, script, 0, BaseSignatureChecker(), SigVersion::BASE, &error);
100-
assert(ret);
101-
});
118+
bench.unit("script").epochIterations(1)
119+
.setup([&] { stack.clear(); })
120+
.run([&] {
121+
ScriptError error;
122+
const bool ret{EvalScript(stack, script, /*flags=*/0, BaseSignatureChecker(), SigVersion::BASE, &error)};
123+
assert(ret && error == SCRIPT_ERR_OK);
124+
});
102125
}
103126

104127
BENCHMARK(VerifyScriptP2WPKH);
105-
BENCHMARK(VerifyScriptP2TR);
128+
BENCHMARK(VerifyScriptP2TR_KeyPath);
129+
BENCHMARK(VerifyScriptP2TR_ScriptPath);
106130
BENCHMARK(VerifyNestedIfScript);

0 commit comments

Comments
 (0)