/ 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: prev_spk = GetScriptForDestination(WitnessV0KeyHash(pubkey)); break;
 48          case InputType::P2TR:   prev_spk = GetScriptForDestination(WitnessV1Taproot(XOnlyPubKey{pubkey})); break;
 49          default: assert(false);
 50          }
 51          prev_spks.push_back(prev_spk);
 52      }
 53  
 54      // Simple 1-input tx with artificial outpoint
 55      // (note that for the purpose of signing with SIGHASH_ALL we don't need any outputs)
 56      COutPoint prevout{/*hashIn=*/Txid::FromUint256(uint256::ONE), /*nIn=*/1337};
 57      CMutableTransaction unsigned_tx;
 58      unsigned_tx.vin.emplace_back(prevout);
 59  
 60      // Benchmark.
 61      int iter = 0;
 62      bench.minEpochIterations(100).run([&] {
 63          CMutableTransaction tx{unsigned_tx};
 64          std::map<COutPoint, Coin> coins;
 65          const CScript& prev_spk = prev_spks[(iter++) % prev_spks.size()];
 66          coins[prevout] = Coin(CTxOut(10000, prev_spk), /*nHeightIn=*/100, /*fCoinBaseIn=*/false);
 67          std::map<int, bilingual_str> input_errors;
 68          bool complete = SignTransaction(tx, &keystore, coins, SIGHASH_ALL, input_errors);
 69          assert(complete);
 70      });
 71  }
 72  
 73  static void SignTransactionECDSA(benchmark::Bench& bench)   { SignTransactionSingleInput(bench, InputType::P2WPKH); }
 74  static void SignTransactionSchnorr(benchmark::Bench& bench) { SignTransactionSingleInput(bench, InputType::P2TR);   }
 75  
 76  static void SignSchnorrTapTweakBenchmark(benchmark::Bench& bench, bool use_null_merkle_root)
 77  {
 78      FastRandomContext rng;
 79      ECC_Context ecc_context{};
 80  
 81      auto key = GenerateRandomKey();
 82      auto msg = rng.rand256();
 83      auto merkle_root = use_null_merkle_root ? uint256() : rng.rand256();
 84      auto aux = rng.rand256();
 85      std::vector<unsigned char> sig(64);
 86  
 87      bench.minEpochIterations(100).run([&] {
 88          bool success = key.SignSchnorr(msg, sig, &merkle_root, aux);
 89          assert(success);
 90      });
 91  }
 92  
 93  static void SignSchnorrWithMerkleRoot(benchmark::Bench& bench)
 94  {
 95      SignSchnorrTapTweakBenchmark(bench, /*use_null_merkle_root=*/false);
 96  }
 97  
 98  static void SignSchnorrWithNullMerkleRoot(benchmark::Bench& bench)
 99  {
100      SignSchnorrTapTweakBenchmark(bench, /*use_null_merkle_root=*/true);
101  }
102  
103  BENCHMARK(SignTransactionECDSA);
104  BENCHMARK(SignTransactionSchnorr);
105  BENCHMARK(SignSchnorrWithMerkleRoot);
106  BENCHMARK(SignSchnorrWithNullMerkleRoot);