/ src / bench / sign_transaction.cpp
sign_transaction.cpp
  1  // Copyright (c) 2023-present The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  #include <addresstype.h>
  6  #include <bench/bench.h>
  7  #include <coins.h>
  8  #include <key.h>
  9  #include <primitives/transaction.h>
 10  #include <pubkey.h>
 11  #include <script/interpreter.h>
 12  #include <script/script.h>
 13  #include <script/sign.h>
 14  #include <script/signingprovider.h>
 15  #include <span.h>
 16  #include <test/util/random.h>
 17  #include <uint256.h>
 18  #include <util/translation.h>
 19  
 20  #include <cassert>
 21  #include <map>
 22  #include <vector>
 23  
 24  enum class InputType {
 25      P2WPKH, // segwitv0, witness-pubkey-hash (ECDSA signature)
 26      P2TR,   // segwitv1, taproot key-path spend (Schnorr signature)
 27  };
 28  
 29  static void SignTransactionSingleInput(benchmark::Bench& bench, InputType input_type)
 30  {
 31      ECC_Context ecc_context{};
 32  
 33      FlatSigningProvider keystore;
 34      std::vector<CScript> prev_spks;
 35  
 36      // Create a bunch of keys / UTXOs to avoid signing with the same key repeatedly
 37      for (int i = 0; i < 32; i++) {
 38          CKey privkey = GenerateRandomKey();
 39          CPubKey pubkey = privkey.GetPubKey();
 40          CKeyID key_id = pubkey.GetID();
 41          keystore.keys.emplace(key_id, privkey);
 42          keystore.pubkeys.emplace(key_id, pubkey);
 43  
 44          // Create specified locking script type
 45          CScript prev_spk = [&]() {
 46              switch (input_type) {
 47              case InputType::P2WPKH:
 48                  return GetScriptForDestination(WitnessV0KeyHash(pubkey));
 49              case InputType::P2TR:
 50                  return GetScriptForDestination(WitnessV1Taproot(XOnlyPubKey{pubkey}));
 51              } // no default case, so the compiler can warn about missing cases
 52              assert(false);
 53          }();
 54          prev_spks.push_back(prev_spk);
 55      }
 56  
 57      // Simple 1-input tx with artificial outpoint
 58      // (note that for the purpose of signing with SIGHASH_ALL we don't need any outputs)
 59      COutPoint prevout{/*hashIn=*/Txid::FromUint256(uint256::ONE), /*nIn=*/1337};
 60      CMutableTransaction unsigned_tx;
 61      unsigned_tx.vin.emplace_back(prevout);
 62  
 63      // Benchmark.
 64      int iter = 0;
 65      bench.minEpochIterations(100).run([&] {
 66          CMutableTransaction tx{unsigned_tx};
 67          std::map<COutPoint, Coin> coins;
 68          const CScript& prev_spk = prev_spks[(iter++) % prev_spks.size()];
 69          coins[prevout] = Coin(CTxOut(10000, prev_spk), /*nHeightIn=*/100, /*fCoinBaseIn=*/false);
 70          std::map<int, bilingual_str> input_errors;
 71          bool complete = SignTransaction(tx, &keystore, coins, {.sighash_type = SIGHASH_ALL}, input_errors);
 72          assert(complete);
 73      });
 74  }
 75  
 76  static void SignTransactionECDSA(benchmark::Bench& bench)   { SignTransactionSingleInput(bench, InputType::P2WPKH); }
 77  static void SignTransactionSchnorr(benchmark::Bench& bench) { SignTransactionSingleInput(bench, InputType::P2TR);   }
 78  
 79  static void SignSchnorrTapTweakBenchmark(benchmark::Bench& bench, bool use_null_merkle_root)
 80  {
 81      FastRandomContext rng;
 82      ECC_Context ecc_context{};
 83  
 84      auto key = GenerateRandomKey();
 85      auto msg = rng.rand256();
 86      auto merkle_root = use_null_merkle_root ? uint256() : rng.rand256();
 87      auto aux = rng.rand256();
 88      std::vector<unsigned char> sig(64);
 89  
 90      bench.minEpochIterations(100).run([&] {
 91          bool success = key.SignSchnorr(msg, sig, &merkle_root, aux);
 92          assert(success);
 93      });
 94  }
 95  
 96  static void SignSchnorrWithMerkleRoot(benchmark::Bench& bench)
 97  {
 98      SignSchnorrTapTweakBenchmark(bench, /*use_null_merkle_root=*/false);
 99  }
100  
101  static void SignSchnorrWithNullMerkleRoot(benchmark::Bench& bench)
102  {
103      SignSchnorrTapTweakBenchmark(bench, /*use_null_merkle_root=*/true);
104  }
105  
106  BENCHMARK(SignTransactionECDSA);
107  BENCHMARK(SignTransactionSchnorr);
108  BENCHMARK(SignSchnorrWithMerkleRoot);
109  BENCHMARK(SignSchnorrWithNullMerkleRoot);