mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-16 16:32:47 +00:00
Merge bitcoin/bitcoin#34472: bench: add script verification benchmark for P2TR key path spends
d339884f1dfac3749c6214ae896b4354cf9ee28e bench: add script verification benchmark for P2TR key path spends (Sebastian Falbesoner) dd93362a1d874eb761ed7026956a490b5ed0472b bench: simplify script verification benchmark, generalize signing (Sebastian Falbesoner) Pull request description: We currently benchmark Schnorr signature verification only in the context of block validation ([`ConectBlock*`](8bb77f348e/src/bench/connectblock.cpp (L107)) benchmarks), but not individually for single inputs [1]. This PR adds a script verification benchmark for P2TR key path spends accordingly, by generalizing the already existing one for P2WPKH spends. This should make it easier to quantify potential performance improvements like e.g. https://github.com/bitcoin-core/secp256k1/pull/1777, which allows to plug in our HW-optimized SHA256 functions to be used in libsecp256k1 (see the linked example commitf68bef06d9). IIRC from last CoreDev, the main speedup from this is expected for ECDSA signing though (as this involves quite a lot of hashing), but it still makes sense to have verification benchmarks available for both signature types as well. (An alternative way could be to add benchmarks for the signing/verifying member functions `CKey::Sign{,Schnorr}`, `CPubKey::Verify` and `XOnlyPubKey::VerifySchnorr` directly, if we prefer that.) [1] this claim can be practically verified by putting an `assert(false);` into `XOnlyPubKey::VerifySchnorr`: the three benchmarks crashing are `ConnectBlockAllSchnorr`, `ConnectBlockMixedEcdsaSchnorr` and `SignTransactionSchnorr` (as signing includes verification) ACKs for top commit: furszy: ACK d339884f1dfac3749c6214ae896b4354cf9ee28e sedited: Re-ACK d339884f1dfac3749c6214ae896b4354cf9ee28e w0xlt: ACK d339884f1dfac3749c6214ae896b4354cf9ee28e Tree-SHA512: efd20444984bdf1dab4d3d876fdbe2a3a838d7cebc0e31e26683009b81afe4dab8611e2c28c87e46fe8b7e305895c93e461b7934a5aaf293f72b19488b9cec60
This commit is contained in:
commit
9df4f9d100
@ -2,9 +2,10 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <addresstype.h>
|
||||
#include <bench/bench.h>
|
||||
#include <hash.h>
|
||||
#include <key.h>
|
||||
#include <policy/policy.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <pubkey.h>
|
||||
#include <script/interpreter.h>
|
||||
@ -12,44 +13,53 @@
|
||||
#include <span.h>
|
||||
#include <test/util/transaction_utils.h>
|
||||
#include <uint256.h>
|
||||
#include <util/translation.h>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#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{};
|
||||
|
||||
const script_verify_flags flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
|
||||
const int witnessversion = 0;
|
||||
// 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();
|
||||
|
||||
// Key pair.
|
||||
CKey key;
|
||||
static const std::array<unsigned char, 32> vchKey = {
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
|
||||
}
|
||||
};
|
||||
key.Set(vchKey.begin(), vchKey.end(), false);
|
||||
CPubKey pubkey = key.GetPubKey();
|
||||
uint160 pubkeyHash;
|
||||
CHash160().Write(pubkey).Finalize(pubkeyHash);
|
||||
FlatSigningProvider keystore;
|
||||
keystore.keys.emplace(key_id, privkey);
|
||||
keystore.pubkeys.emplace(key_id, pubkey);
|
||||
|
||||
// Script.
|
||||
CScript scriptPubKey = CScript() << witnessversion << ToByteVector(pubkeyHash);
|
||||
CScript scriptSig;
|
||||
CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
const CMutableTransaction& txCredit = BuildCreditingTransaction(scriptPubKey, 1);
|
||||
CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, CScriptWitness(), CTransaction(txCredit));
|
||||
CScriptWitness& witness = txSpend.vin[0].scriptWitness;
|
||||
witness.stack.emplace_back();
|
||||
key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SigVersion::WITNESS_V0), witness.stack.back());
|
||||
witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL));
|
||||
witness.stack.push_back(ToByteVector(pubkey));
|
||||
// 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, 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.
|
||||
bench.run([&] {
|
||||
@ -58,14 +68,17 @@ static void VerifyScriptBench(benchmark::Bench& bench)
|
||||
txSpend.vin[0].scriptSig,
|
||||
txCredit.vout[0].scriptPubKey,
|
||||
&txSpend.vin[0].scriptWitness,
|
||||
flags,
|
||||
MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL),
|
||||
STANDARD_SCRIPT_VERIFY_FLAGS,
|
||||
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;
|
||||
@ -87,5 +100,6 @@ static void VerifyNestedIfScript(benchmark::Bench& bench)
|
||||
});
|
||||
}
|
||||
|
||||
BENCHMARK(VerifyScriptBench);
|
||||
BENCHMARK(VerifyScriptP2WPKH);
|
||||
BENCHMARK(VerifyScriptP2TR);
|
||||
BENCHMARK(VerifyNestedIfScript);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user