LCOV - code coverage report
Current view: top level - src/common - bip352.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 93.8 % 320 300
Test Date: 2026-10-11 07:59:32 Functions: 86.7 % 30 26
Branches: 58.6 % 454 266

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2023 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 <common/bip352.h>
       6                 :             : 
       7                 :             : #include <addresstype.h>
       8                 :             : #include <bech32.h>
       9                 :             : #include <chainparams.h>
      10                 :             : #include <coins.h>
      11                 :             : #include <hash.h>
      12                 :             : #include <key.h>
      13                 :             : #include <primitives/transaction.h>
      14                 :             : #include <pubkey.h>
      15                 :             : #include <script/interpreter.h>
      16                 :             : #include <script/script.h>
      17                 :             : #include <script/sign.h>
      18                 :             : #include <script/solver.h>
      19                 :             : #include <script/verify_flags.h>
      20                 :             : #include <secp256k1.h>
      21                 :             : #include <secp256k1_extrakeys.h>
      22                 :             : #include <secp256k1_silentpayments.h>
      23                 :             : #include <span.h>
      24                 :             : #include <streams.h>
      25                 :             : #include <tinyformat.h>
      26                 :             : #include <uint256.h>
      27                 :             : #include <util/strencodings.h>
      28                 :             : 
      29                 :             : #include <algorithm>
      30                 :             : #include <cassert>
      31                 :             : #include <cstddef>
      32                 :             : #include <optional>
      33                 :             : #include <span>
      34                 :             : #include <type_traits>
      35                 :             : #include <utility>
      36                 :             : 
      37                 :             : namespace bip352 {
      38                 :             : 
      39                 :          29 : PrevoutsSummary::PrevoutsSummary(const secp256k1_silentpayments_prevouts_summary& prevouts_summary)
      40                 :          29 :   : m_prevouts_summary{std::make_unique<secp256k1_silentpayments_prevouts_summary>(prevouts_summary)} {}
      41                 :             : 
      42                 :          87 : PrevoutsSummary::PrevoutsSummary(PrevoutsSummary&&) noexcept = default;
      43                 :           0 : PrevoutsSummary& PrevoutsSummary::operator=(PrevoutsSummary&&) noexcept = default;
      44                 :             : 
      45                 :         116 : PrevoutsSummary::~PrevoutsSummary() = default;
      46                 :             : 
      47                 :          28 : const secp256k1_silentpayments_prevouts_summary* PrevoutsSummary::Get() const
      48                 :             : {
      49                 :          28 :     return m_prevouts_summary.get();
      50                 :             : }
      51                 :             : 
      52                 :         135 : std::optional<SilentPaymentsDestination> SilentPaymentsDestination::From(
      53                 :             :     const CPubKey& scan_pubkey,
      54                 :             :     const CPubKey& spend_pubkey,
      55                 :             :     uint8_t version,
      56                 :             :     std::span<const unsigned char> extension_data
      57                 :             : ) {
      58         [ -  + ]:         135 :     if (version >= 31) return std::nullopt;
      59   [ +  +  -  + ]:         135 :     if (version == 0 && !extension_data.empty()) {
      60                 :             :         // V0 address has no extension data
      61                 :           0 :         return std::nullopt;
      62                 :             :     }
      63   [ +  +  +  - ]:         135 :     if (!scan_pubkey.IsFullyValid() || !scan_pubkey.IsCompressed()) return std::nullopt;
      64   [ +  +  +  - ]:         133 :     if (!spend_pubkey.IsFullyValid() || !spend_pubkey.IsCompressed()) return std::nullopt;
      65                 :         132 :     return SilentPaymentsDestination(version, scan_pubkey, spend_pubkey, extension_data);
      66                 :             : }
      67                 :             : 
      68                 :         133 : util::Expected<SilentPaymentsDestination, std::string> DecodeSilentPaymentsAddress(
      69                 :             :     const std::string& str, const CChainParams& params)
      70                 :             : {
      71                 :         133 :     static constexpr size_t SILENT_PAYMENTS_V0_DATA_SIZE = 66;
      72                 :         133 :     static constexpr size_t SP_PUBKEYS_SIZE = 2 * CPubKey::COMPRESSED_SIZE;
      73                 :             : 
      74                 :         133 :     const auto dec = bech32::Decode(str, bech32::CharLimit::SILENT_PAYMENTS);
      75         [ +  + ]:         133 :     if (dec.encoding != bech32::Encoding::BECH32M) {
      76         [ +  - ]:          15 :         return util::Unexpected{"Silent Payments address must use Bech32m checksum"};
      77                 :             :     }
      78         [ +  + ]:         118 :     if (dec.hrp != params.SilentPaymentsHRP()) {
      79         [ +  - ]:          50 :         return util::Unexpected{strprintf("Invalid or unsupported prefix for Silent Payments address (expected %s, got %s).", params.SilentPaymentsHRP(), dec.hrp)};
      80                 :             :     }
      81         [ -  + ]:          93 :     if (dec.data.empty()) {
      82         [ #  # ]:           0 :         return util::Unexpected{"Empty Bech32 data section"};
      83                 :             :     }
      84                 :          93 :     std::vector<unsigned char> data;
      85   [ +  -  -  + ]:        6206 :     if (!ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, dec.data.begin() + 1, dec.data.end())) {
      86         [ #  # ]:           0 :         return util::Unexpected{"Invalid padding in Silent payments address (Bech32m data section)"};
      87                 :             :     }
      88   [ -  +  +  + ]:          93 :     if (data.size() < SILENT_PAYMENTS_V0_DATA_SIZE) {
      89         [ +  - ]:           2 :         return util::Unexpected{strprintf("Silent payments data payload is too small (expected at least %d, got %d).", SILENT_PAYMENTS_V0_DATA_SIZE, data.size())};
      90                 :             :     }
      91         [ +  + ]:          92 :     const uint8_t version = dec.data[0];
      92         [ +  + ]:          92 :     if (version >= 31) {
      93         [ +  - ]:           2 :         return util::Unexpected{strprintf("This implementation only supports Silent payments addresses v0 through v30 (got %d).", version)};
      94                 :             :     }
      95   [ +  +  -  + ]:          91 :     if (version == 0 && data.size() != SILENT_PAYMENTS_V0_DATA_SIZE) {
      96         [ #  # ]:           0 :         return util::Unexpected{strprintf("Silent payments version is v0 but data is not the correct size (expected %d, got %d).", SILENT_PAYMENTS_V0_DATA_SIZE, data.size())};
      97                 :             :     }
      98                 :          91 :     CPubKey scan_pubkey{data.begin(), data.begin() + CPubKey::COMPRESSED_SIZE};
      99                 :          91 :     CPubKey spend_pubkey{data.begin() + CPubKey::COMPRESSED_SIZE, data.begin() + 2 * CPubKey::COMPRESSED_SIZE};
     100   [ -  +  +  - ]:          91 :     std::span<unsigned char> extension_data{data.data() + SP_PUBKEYS_SIZE, data.size() - SP_PUBKEYS_SIZE};
     101         [ +  - ]:          91 :     auto sp_dest = SilentPaymentsDestination::From(scan_pubkey, spend_pubkey, version, extension_data);
     102         [ +  + ]:          91 :     if (!sp_dest) {
     103         [ +  - ]:           3 :         return util::Unexpected{"Invalid Silent payments address"};
     104                 :             :     }
     105         [ +  - ]:         176 :     return *sp_dest;
     106                 :         317 : }
     107                 :             : 
     108         [ +  - ]:        2376 : SilentPaymentsLabel::SilentPaymentsLabel(const secp256k1_silentpayments_label& label) {
     109         [ +  - ]:        2376 :     m_label = std::make_unique<secp256k1_silentpayments_label>(label);
     110         [ +  - ]:        2376 :     int ret = secp256k1_silentpayments_recipient_label_serialize(secp256k1_context_static, m_vch, m_label.get());
     111         [ -  + ]:        2376 :     assert(ret);
     112                 :        2376 : }
     113                 :             : 
     114                 :           1 : std::optional<SilentPaymentsLabel> SilentPaymentsLabel::FromBytes(std::span<const unsigned char, CPubKey::COMPRESSED_SIZE> vch)
     115                 :             : {
     116                 :           1 :     secp256k1_silentpayments_label label_obj;
     117         [ -  + ]:           1 :     if (!secp256k1_silentpayments_recipient_label_parse(secp256k1_context_static, &label_obj, vch.data())) {
     118                 :           0 :         return std::nullopt;
     119                 :             :     }
     120                 :           1 :     return SilentPaymentsLabel(label_obj);
     121                 :             : }
     122                 :             : 
     123                 :        8850 : SilentPaymentsLabel::SilentPaymentsLabel(SilentPaymentsLabel&&) noexcept = default;
     124         [ #  # ]:           0 : SilentPaymentsLabel& SilentPaymentsLabel::operator=(SilentPaymentsLabel&&) noexcept = default;
     125                 :       11226 : SilentPaymentsLabel::~SilentPaymentsLabel() = default;
     126                 :             : 
     127                 :           0 : SilentPaymentsLabel::SilentPaymentsLabel(const SilentPaymentsLabel& label)
     128                 :           0 :     : m_label{std::make_unique<secp256k1_silentpayments_label>(*label.m_label)}
     129                 :             : {
     130                 :           0 :     memcpy(m_vch, label.m_vch, CPubKey::COMPRESSED_SIZE);
     131                 :           0 : }
     132                 :           0 : SilentPaymentsLabel& SilentPaymentsLabel::operator=(const SilentPaymentsLabel& label) {
     133         [ #  # ]:           0 :     if (this != &label) {
     134                 :           0 :         m_label = std::make_unique<secp256k1_silentpayments_label>(*label.m_label);
     135                 :           0 :         memcpy(m_vch, label.m_vch, CPubKey::COMPRESSED_SIZE);
     136                 :             :     }
     137                 :           0 :     return *this;
     138                 :             : }
     139                 :             : 
     140                 :          15 : const secp256k1_silentpayments_label* SilentPaymentsLabel::Get() const {
     141                 :          15 :     return m_label.get();
     142                 :             : }
     143                 :             : 
     144                 :         126 : std::optional<PubKey> GetPubKeyFromInput(const CTxIn& txin, const CScript& spk)
     145                 :             : {
     146                 :         126 :     std::vector<std::vector<unsigned char>> solutions;
     147         [ +  - ]:         126 :     const TxoutType type = Solver(spk, solutions);
     148                 :             : 
     149         [ +  + ]:         126 :     if (type == TxoutType::WITNESS_V1_TAPROOT) {
     150                 :          22 :         const auto& stack = txin.scriptWitness.stack;
     151         [ -  + ]:          22 :         if (stack.empty()) return std::nullopt;
     152   [ +  -  +  + ]:          22 :         const bool has_annex = !stack.back().empty() && stack.back()[0] == ANNEX_TAG;
     153   [ -  +  +  + ]:          22 :         const size_t effective_size = stack.size() - (has_annex ? 1 : 0);
     154                 :             : 
     155         [ +  + ]:          22 :         if (effective_size > 1) {
     156                 :             :             // BIP-352: skip script-path spends using NUMS-H internal key.
     157                 :             :             // Validate control block size before checking internal key.
     158         [ -  + ]:           4 :             const auto& control = stack[effective_size - 1];
     159         [ -  + ]:           4 :             if (control.size() < TAPROOT_CONTROL_BASE_SIZE ||
     160   [ +  -  +  - ]:           4 :                 control.size() > TAPROOT_CONTROL_MAX_SIZE ||
     161         [ -  + ]:           4 :                 (control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE != 0) {
     162                 :           0 :                 return std::nullopt;
     163                 :             :             }
     164         [ +  - ]:           4 :             if (std::equal(WitnessV1Taproot::NUMS_H.begin(), WitnessV1Taproot::NUMS_H.end(), control.begin() + 1)) {
     165                 :           4 :                 return std::nullopt;
     166                 :             :             }
     167                 :             :         }
     168                 :             : 
     169         [ -  + ]:          18 :         XOnlyPubKey key{solutions[0]};
     170   [ +  -  -  + ]:          18 :         if (!key.IsFullyValid()) return std::nullopt;
     171                 :          18 :         return PubKey{key};
     172                 :             :     }
     173                 :             : 
     174         [ +  + ]:         104 :     if (type == TxoutType::WITNESS_V0_KEYHASH) {
     175                 :          15 :         const auto& stack = txin.scriptWitness.stack;
     176         [ -  + ]:          15 :         if (stack.empty()) return std::nullopt;
     177         [ -  + ]:          15 :         CPubKey key{stack.back()};
     178   [ +  +  +  -  :          15 :         if (!key.IsCompressed() || !key.IsFullyValid()) return std::nullopt;
                   +  - ]
     179                 :          13 :         return PubKey{key};
     180                 :             :     }
     181                 :             : 
     182         [ +  + ]:          89 :     if (type == TxoutType::PUBKEYHASH) {
     183                 :             :         // For P2PKH public key extraction, BIP-352 states: "The receiver MUST parse the scriptSig
     184                 :             :         // for the public key, even if the scriptSig does not match the template specified. This is
     185                 :             :         // to address the third-party malleability of P2PKH scriptSigs."
     186                 :             :         //
     187                 :             :         // Find the preimage of the pubkey hash by iterating through the scriptSig data pushes and
     188                 :             :         // comparing the Hash160 of each 33-byte (compressed) push against the output script. In
     189                 :             :         // standard spends the pubkey is the last push, but due to malleability we can't rely on
     190                 :             :         // that (e.g. `<dummy> OP_DROP` inserted anywhere). Evaluating the scriptSig with a dummy
     191                 :             :         // signature checker isn't safe either, as a malleated scriptSig could then leave a
     192                 :             :         // different pubkey on top of the stack than real validation would.
     193         [ -  + ]:          83 :         const uint160 pubkey_hash{solutions[0]};
     194         [ -  + ]:         166 :         auto pc = txin.scriptSig.begin();
     195   [ +  +  +  + ]:         360 :         while (pc < txin.scriptSig.end()) {
     196                 :         174 :             opcodetype opcode;
     197                 :         174 :             std::vector<unsigned char> pubkey_candidate;
     198   [ +  -  -  + ]:         174 :             if (!txin.scriptSig.GetOp(pc, opcode, pubkey_candidate)) {
     199                 :           0 :                 return std::nullopt;
     200                 :             :             }
     201   [ -  +  +  +  :         174 :             if (pubkey_candidate.size() == 33 && Hash160(pubkey_candidate) == pubkey_hash) {
                   +  - ]
     202         [ -  + ]:          77 :                 CPubKey key{pubkey_candidate};
     203   [ +  -  +  - ]:          77 :                 if (key.IsFullyValid()) return PubKey{key};
     204                 :             :             }
     205                 :         174 :         }
     206                 :           6 :         return std::nullopt;
     207                 :             :     }
     208                 :             : 
     209         [ +  - ]:           6 :     if (type == TxoutType::SCRIPTHASH) {
     210                 :             :         // P2SH-P2WPKH only: eval scriptSig, verify redeem script is P2WPKH. Unlike P2PKH above,
     211                 :             :         // evaluating is safe here, as consensus (BIP-141) requires the scriptSig to be a single
     212                 :             :         // push of the redeem script.
     213                 :           6 :         std::vector<std::vector<unsigned char>> stack;
     214   [ +  -  -  + ]:           6 :         if (!EvalScript(stack, txin.scriptSig, SCRIPT_VERIFY_NONE, DUMMY_CHECKER, SigVersion::BASE)) {
     215                 :           0 :             return std::nullopt;
     216                 :             :         }
     217         [ -  + ]:           6 :         if (stack.empty()) return std::nullopt;
     218                 :           6 :         CScript redeem{stack.back().begin(), stack.back().end()};
     219   [ +  -  +  + ]:           6 :         if (Solver(redeem, solutions) != TxoutType::WITNESS_V0_KEYHASH) return std::nullopt;
     220         [ -  + ]:           4 :         if (txin.scriptWitness.stack.empty()) return std::nullopt;
     221         [ -  + ]:           4 :         CPubKey key{txin.scriptWitness.stack.back()};
     222   [ +  +  +  -  :           4 :         if (!key.IsCompressed() || !key.IsFullyValid()) return std::nullopt;
                   +  - ]
     223                 :           2 :         return PubKey{key};
     224                 :           6 :     }
     225                 :             : 
     226                 :           0 :     return std::nullopt;
     227                 :         126 : }
     228                 :             : 
     229                 :          30 : static std::optional<PrevoutsSummary> CreateInputPubkeysTweak(
     230                 :             :     const std::vector<CPubKey>& plain_pubkeys,
     231                 :             :     const std::vector<XOnlyPubKey>& taproot_pubkeys,
     232                 :             :     const COutPoint& smallest_outpoint)
     233                 :             : {
     234                 :          30 :     secp256k1_silentpayments_prevouts_summary prevouts_summary;
     235                 :          30 :     std::vector<secp256k1_pubkey> plain_pubkey_objs;
     236                 :          30 :     std::vector<secp256k1_pubkey*> plain_pubkey_ptrs;
     237   [ -  +  +  - ]:          30 :     plain_pubkey_objs.reserve(plain_pubkeys.size());
     238   [ -  +  +  - ]:          30 :     plain_pubkey_ptrs.reserve(plain_pubkeys.size());
     239         [ +  + ]:          77 :     for (const CPubKey& pubkey : plain_pubkeys) {
     240   [ +  -  +  - ]:          47 :         bool ret = secp256k1_ec_pubkey_parse(secp256k1_context_static,
     241         [ +  - ]:          47 :             &plain_pubkey_objs.emplace_back(), pubkey.data(), pubkey.size());
     242                 :             :         // This pubkey is expected to be valid because GetPubKeyFromInput()
     243                 :             :         // already called IsFullyValid() before including it here
     244         [ -  + ]:          47 :         assert(ret);
     245         [ +  - ]:          47 :         plain_pubkey_ptrs.push_back(&plain_pubkey_objs.back());
     246                 :             :     }
     247                 :             : 
     248                 :          30 :     std::vector<secp256k1_xonly_pubkey> taproot_pubkey_objs;
     249                 :          30 :     std::vector<secp256k1_xonly_pubkey*> taproot_pubkey_ptrs;
     250   [ -  +  +  - ]:          30 :     taproot_pubkey_objs.reserve(taproot_pubkeys.size());
     251   [ -  +  +  - ]:          30 :     taproot_pubkey_ptrs.reserve(taproot_pubkeys.size());
     252         [ +  + ]:          39 :     for (const XOnlyPubKey& pubkey : taproot_pubkeys) {
     253   [ +  -  +  - ]:           9 :         bool ret = secp256k1_xonly_pubkey_parse(secp256k1_context_static,
     254         [ +  - ]:           9 :             &taproot_pubkey_objs.emplace_back(), pubkey.data());
     255                 :             :         // This xonlypubkey is expected to be valid because
     256                 :             :         // GetPubKeyFromInput() already called IsFullyValid()
     257                 :             :         // before including it here
     258         [ -  + ]:           9 :         assert(ret);
     259         [ +  - ]:           9 :         taproot_pubkey_ptrs.push_back(&taproot_pubkey_objs.back());
     260                 :             :     }
     261                 :             : 
     262                 :          30 :     std::array<std::byte, 36> smallest_outpoint_ser;
     263         [ +  - ]:          30 :     SpanWriter{smallest_outpoint_ser} << smallest_outpoint;
     264         [ -  + ]:          30 :     bool ret = secp256k1_silentpayments_recipient_prevouts_summary_create(secp256k1_context_static,
     265                 :             :         &prevouts_summary,
     266         [ +  - ]:          30 :         UCharCast(smallest_outpoint_ser.data()),
     267         [ +  - ]:          30 :         taproot_pubkey_ptrs.data(), taproot_pubkey_ptrs.size(),
     268         [ -  + ]:          30 :         plain_pubkey_ptrs.data(), plain_pubkey_ptrs.size()
     269                 :          30 :     );
     270         [ +  + ]:          30 :     if (!ret) return std::nullopt;
     271         [ +  - ]:          58 :     return PrevoutsSummary(prevouts_summary);
     272                 :          30 : }
     273                 :             : 
     274                 :          32 : util::Expected<PrevoutsSummary, PrevoutsSummaryError> GetSilentPaymentsPrevoutsSummary(const std::vector<CTxIn>& vin, const std::map<COutPoint, Coin>& coins)
     275                 :             : {
     276                 :             :     // Extract the keys from the inputs
     277                 :             :     // or skip if no valid inputs
     278                 :          32 :     std::vector<CPubKey> pubkeys;
     279                 :          32 :     std::vector<XOnlyPubKey> xonly_pubkeys;
     280                 :          32 :     std::vector<COutPoint> tx_outpoints;
     281         [ +  + ]:          97 :     for (const CTxIn& txin : vin) {
     282                 :          66 :         const auto coin_it = coins.find(txin.prevout);
     283         [ -  + ]:          66 :         if (coin_it == coins.end()) return util::Unexpected(PrevoutsSummaryError::MISSING_COIN);
     284         [ +  - ]:          66 :         const Coin& coin = coin_it->second;
     285                 :          66 :         int witness_version{0};
     286                 :          66 :         std::vector<unsigned char> witness_program;
     287                 :             :         // BIP352 v0 skips transactions spending future witness versions.
     288   [ +  -  +  +  :          66 :         if (coin.out.scriptPubKey.IsWitnessProgram(witness_version, witness_program) && witness_version > 1) {
                   +  + ]
     289                 :           1 :             return util::Unexpected(PrevoutsSummaryError::NOT_ELIGIBLE);
     290                 :             :         }
     291         [ +  - ]:          65 :         tx_outpoints.emplace_back(txin.prevout);
     292         [ +  - ]:          65 :         auto pubkey = GetPubKeyFromInput(txin, coin.out.scriptPubKey);
     293         [ +  + ]:          65 :         if (pubkey.has_value()) {
     294         [ +  - ]:         122 :             std::visit([&pubkeys, &xonly_pubkeys](auto&& pubkey) {
     295                 :             :                 using T = std::decay_t<decltype(pubkey)>;
     296                 :             :                 if constexpr (std::is_same_v<T, CPubKey>) {
     297                 :          48 :                     pubkeys.push_back(pubkey);
     298                 :             :                 } else if constexpr (std::is_same_v<T, XOnlyPubKey>) {
     299                 :           9 :                     xonly_pubkeys.push_back(pubkey);
     300                 :             :                 }
     301                 :             :             }, *pubkey);
     302                 :             :         }
     303                 :          66 :     }
     304   [ -  +  -  +  :          31 :     if (pubkeys.size() + xonly_pubkeys.size() == 0) return util::Unexpected(PrevoutsSummaryError::NOT_ELIGIBLE);
                   +  + ]
     305                 :          30 :     auto smallest_outpoint = std::min_element(tx_outpoints.begin(), tx_outpoints.end(), BIP352Comparator());
     306         [ +  - ]:          30 :     auto tweak = CreateInputPubkeysTweak(pubkeys, xonly_pubkeys, *smallest_outpoint);
     307         [ +  + ]:          30 :     if (!tweak.has_value()) return util::Unexpected(PrevoutsSummaryError::NOT_ELIGIBLE);
     308                 :          29 :     return std::move(*tweak);
     309                 :          62 : }
     310                 :             : 
     311                 :          29 : static std::optional<std::vector<secp256k1_xonly_pubkey>> CreateOutputs(
     312                 :             :     const std::vector<SilentPaymentsDestination>& recipients,
     313                 :             :     const std::vector<CKey>& plain_keys,
     314                 :             :     const std::vector<KeyPair>& taproot_keypairs,
     315                 :             :     const COutPoint& smallest_outpoint
     316                 :             : ) {
     317                 :          29 :     bool ret;
     318                 :          29 :     std::vector<const secp256k1_keypair *> taproot_keypair_ptrs;
     319                 :          29 :     std::vector<const unsigned char *> plain_key_ptrs;
     320   [ -  +  +  - ]:          29 :     taproot_keypair_ptrs.reserve(taproot_keypairs.size());
     321   [ -  +  +  - ]:          29 :     plain_key_ptrs.reserve(plain_keys.size());
     322                 :             : 
     323                 :          29 :     std::vector<secp256k1_silentpayments_recipient> recipient_objs;
     324                 :          29 :     std::vector<const secp256k1_silentpayments_recipient *> recipient_ptrs;
     325   [ -  +  +  - ]:          29 :     recipient_objs.reserve(recipients.size());
     326   [ -  +  +  - ]:          29 :     recipient_ptrs.reserve(recipients.size());
     327                 :             : 
     328                 :          29 :     std::vector<secp256k1_xonly_pubkey> generated_outputs;
     329                 :          29 :     std::vector<secp256k1_xonly_pubkey *> generated_output_ptrs;
     330   [ -  +  +  - ]:          29 :     generated_outputs.reserve(recipients.size());
     331   [ -  +  +  - ]:          29 :     generated_output_ptrs.reserve(recipients.size());
     332                 :             : 
     333   [ -  +  +  + ]:        2391 :     for (size_t i = 0; i < recipients.size(); i++) {
     334                 :        2362 :         secp256k1_silentpayments_recipient recipient_obj;
     335         [ +  - ]:        2362 :         ret = secp256k1_ec_pubkey_parse(secp256k1_context_static, &recipient_obj.scan_pubkey, recipients[i].GetScanPubKey().data(), recipients[i].GetScanPubKey().size());
     336         [ -  + ]:        2362 :         assert(ret);
     337         [ +  - ]:        2362 :         ret = secp256k1_ec_pubkey_parse(secp256k1_context_static, &recipient_obj.spend_pubkey, recipients[i].GetSpendPubKey().data(), recipients[i].GetSpendPubKey().size());
     338         [ -  + ]:        2362 :         assert(ret);
     339                 :        2362 :         recipient_obj.index = i;
     340         [ +  - ]:        2362 :         recipient_objs.push_back(recipient_obj);
     341         [ +  - ]:        2362 :         recipient_ptrs.push_back(&recipient_objs[i]);
     342                 :             : 
     343                 :        2362 :         secp256k1_xonly_pubkey generated_output{};
     344         [ +  - ]:        2362 :         generated_outputs.push_back(generated_output);
     345         [ +  - ]:        2362 :         generated_output_ptrs.push_back(&generated_outputs[i]);
     346                 :             :     }
     347                 :             : 
     348         [ +  + ]:          74 :     for (const auto& key : plain_keys) {
     349         [ -  + ]:          45 :         if (!key.IsValid()) return std::nullopt;
     350         [ +  - ]:          45 :         plain_key_ptrs.push_back(UCharCast(key.begin()));
     351                 :             :     }
     352         [ +  + ]:          38 :     for (const auto& keypair : taproot_keypairs) {
     353         [ -  + ]:           9 :         if (!keypair.IsValid()) return std::nullopt;
     354         [ +  - ]:           9 :         taproot_keypair_ptrs.push_back(keypair.GetSecpKeypair());
     355                 :             :     }
     356                 :             : 
     357                 :             :     // Serialize the outpoint
     358                 :          29 :     std::array<std::byte, 36> smallest_outpoint_ser;
     359         [ +  - ]:          29 :     SpanWriter{smallest_outpoint_ser} << smallest_outpoint;
     360                 :             : 
     361   [ -  +  +  -  :          58 :     ret = secp256k1_silentpayments_sender_create_outputs(GetSecp256k1SignContext(),
                   +  - ]
     362                 :             :         generated_output_ptrs.data(),
     363                 :             :         recipient_ptrs.data(), recipient_ptrs.size(),
     364         [ -  + ]:          29 :         UCharCast(smallest_outpoint_ser.data()),
     365         [ -  + ]:          29 :         taproot_keypair_ptrs.data(), taproot_keypair_ptrs.size(),
     366         [ -  + ]:          29 :         plain_key_ptrs.data(), plain_key_ptrs.size()
     367                 :             :     );
     368         [ +  + ]:          29 :     if (!ret) return std::nullopt;
     369                 :          27 :     return generated_outputs;
     370                 :          29 : }
     371                 :             : 
     372                 :          29 : std::optional<std::map<size_t, WitnessV1Taproot>> GenerateSilentPaymentsTaprootDestinations(const std::map<size_t, SilentPaymentsDestination>& sp_dests, const std::vector<CKey>& plain_keys, const std::vector<KeyPair>& taproot_keys, const COutPoint& smallest_outpoint)
     373                 :             : {
     374         [ -  + ]:          29 :     if (sp_dests.empty()) return std::map<size_t, WitnessV1Taproot>();
     375                 :             : 
     376         [ -  + ]:          29 :     assert(!smallest_outpoint.IsNull());
     377   [ +  +  -  + ]:          29 :     assert(!plain_keys.empty() || !taproot_keys.empty());
     378                 :             : 
     379                 :          29 :     bool ret;
     380         [ +  - ]:          29 :     std::map<size_t, WitnessV1Taproot> tr_dests;
     381                 :          29 :     std::vector<SilentPaymentsDestination> recipients;
     382         [ +  - ]:          29 :     recipients.reserve(sp_dests.size());
     383   [ +  -  +  + ]:        2391 :     for (const auto& [_, addr] : sp_dests) {
     384         [ +  - ]:        2362 :         recipients.push_back(addr);
     385                 :             :     }
     386         [ +  - ]:          29 :     auto outputs = CreateOutputs(recipients, plain_keys, taproot_keys, smallest_outpoint);
     387                 :             :     // This will fail if any input pubkey is null or
     388                 :             :     // inputs were maliciously crafted to sum to zero
     389         [ +  + ]:          29 :     if (!outputs) return std::nullopt;
     390   [ -  +  -  + ]:          27 :     assert(sp_dests.size() == outputs->size());
     391                 :          27 :     size_t output_i{0};
     392   [ +  -  +  + ]:          64 :     for (const auto& [i, _] : sp_dests) {
     393                 :          37 :         unsigned char xonly_pubkey_bytes[32];
     394   [ +  -  +  - ]:          37 :         ret = secp256k1_xonly_pubkey_serialize(secp256k1_context_static, xonly_pubkey_bytes, &outputs.value()[output_i]);
     395         [ -  + ]:          37 :         assert(ret);
     396         [ +  - ]:          37 :         tr_dests[i] = WitnessV1Taproot{XOnlyPubKey{xonly_pubkey_bytes}};
     397                 :          37 :         output_i++;
     398                 :             :     }
     399                 :          27 :     return tr_dests;
     400                 :          29 : }
     401                 :             : 
     402                 :     5397493 : static const unsigned char* LabelLookupCallback(const unsigned char* key, const void* context) {
     403                 :     5397493 :     auto label_context = static_cast<const LabelTweakMap*>(context);
     404                 :     5397493 :     auto it = label_context->find(std::span<const unsigned char, CPubKey::COMPRESSED_SIZE>{key, CPubKey::COMPRESSED_SIZE});
     405         [ +  + ]:     5397493 :     if (it != label_context->end()) {
     406                 :        2333 :         return it->second.begin();
     407                 :             :     }
     408                 :             :     return nullptr;
     409                 :             : }
     410                 :             : 
     411                 :          42 : static std::pair<SilentPaymentsLabel, uint256> CreateLabel(const CKey& scan_key, const uint32_t m) {
     412                 :          42 :     secp256k1_silentpayments_label label_obj;
     413                 :          42 :     unsigned char label_tweak[32];
     414         [ +  - ]:          84 :     bool ret = secp256k1_silentpayments_recipient_label_create(GetSecp256k1SignContext(), &label_obj, label_tweak, UCharCast(scan_key.data()), m);
     415         [ -  + ]:          42 :     assert(ret);
     416                 :          42 :     return {SilentPaymentsLabel(label_obj), uint256{label_tweak}};
     417                 :             : }
     418                 :             : 
     419                 :          15 : static CPubKey CreateLabeledSpendPubKey(const CPubKey& spend_pubkey, const SilentPaymentsLabel& label) {
     420                 :          15 :     secp256k1_pubkey spend_obj, labeled_spend_obj;
     421                 :          15 :     bool ret = secp256k1_ec_pubkey_parse(secp256k1_context_static, &spend_obj, spend_pubkey.data(), spend_pubkey.size());
     422         [ -  + ]:          15 :     assert(ret);
     423                 :          15 :     ret = secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(secp256k1_context_static, &labeled_spend_obj, &spend_obj, label.Get());
     424         [ -  + ]:          15 :     assert(ret);
     425                 :          15 :     size_t pubkeylen = CPubKey::COMPRESSED_SIZE;
     426                 :          15 :     CPubKey labeled_spend_pubkey;
     427                 :          15 :     ret = secp256k1_ec_pubkey_serialize(secp256k1_context_static, (unsigned char*)labeled_spend_pubkey.begin(), &pubkeylen, &labeled_spend_obj, SECP256K1_EC_COMPRESSED);
     428         [ -  + ]:          15 :     assert(ret);
     429                 :          15 :     return labeled_spend_pubkey;
     430                 :             : }
     431                 :             : 
     432                 :          29 : SilentPaymentsReceiver::SilentPaymentsReceiver(const CKey& scan_key, const CPubKey& spend_pubkey,
     433   [ +  -  +  - ]:          29 :     const LabelTweakMap& labels) : m_scan_key(scan_key), m_spend_pubkey(spend_pubkey), m_labels(labels)
     434                 :             : {
     435         [ +  - ]:          58 :     m_change_it = m_labels.emplace(CreateLabel(scan_key, 0)).first;
     436         [ +  - ]:          29 :     m_spend_pubkey_obj = std::make_unique<secp256k1_pubkey>();
     437         [ +  - ]:          29 :     int ret = secp256k1_ec_pubkey_parse(secp256k1_context_static, m_spend_pubkey_obj.get(), m_spend_pubkey.data(), m_spend_pubkey.size());
     438         [ -  + ]:          29 :     assert(ret);
     439                 :          29 : }
     440                 :             : 
     441                 :          29 : SilentPaymentsReceiver::~SilentPaymentsReceiver() = default;
     442                 :             : 
     443                 :           2 : const LabelTweakMap& SilentPaymentsReceiver::GetLabels() const {
     444                 :           2 :     return m_labels;
     445                 :             : }
     446                 :             : 
     447                 :          15 : SilentPaymentsDestination SilentPaymentsReceiver::BuildLabeledDestination(const SilentPaymentsLabel& label) const {
     448                 :          15 :     CPubKey labeled_spend_pubkey = CreateLabeledSpendPubKey(m_spend_pubkey, label);
     449                 :          15 :     auto dest{SilentPaymentsDestination::From(m_scan_key.GetPubKey(), labeled_spend_pubkey)};
     450         [ -  + ]:          15 :     assert(dest);
     451         [ +  - ]:          15 :     return *dest;
     452                 :          15 : }
     453                 :             : 
     454                 :          13 : SilentPaymentsDestination SilentPaymentsReceiver::GenerateLabeledAddress(uint32_t m) {
     455         [ -  + ]:          13 :     assert(m >= 1);
     456         [ +  - ]:          13 :     auto it = m_labels.emplace(CreateLabel(m_scan_key, m)).first;
     457                 :          13 :     return BuildLabeledDestination(it->first);
     458                 :             : }
     459                 :             : 
     460                 :           2 : SilentPaymentsDestination SilentPaymentsReceiver::GetChangeDestination() const {
     461                 :           2 :     return BuildLabeledDestination(m_change_it->first);
     462                 :             : }
     463                 :             : 
     464                 :          28 : std::optional<std::vector<SilentPaymentsOutput>> SilentPaymentsReceiver::Scan(
     465                 :             :     const PrevoutsSummary& prevouts_summary,
     466                 :             :     const std::vector<XOnlyPubKey>& tx_outputs
     467                 :             : ) const {
     468                 :          28 :     bool ret;
     469                 :          28 :     std::vector<secp256k1_silentpayments_found_output> found_output_objs;
     470                 :          28 :     std::vector<secp256k1_silentpayments_found_output *> found_output_ptrs;
     471                 :          28 :     std::vector<secp256k1_xonly_pubkey> tx_output_objs;
     472                 :          28 :     std::vector<const secp256k1_xonly_pubkey *> tx_output_ptrs;
     473   [ -  +  +  - ]:          28 :     found_output_objs.reserve(tx_outputs.size());
     474   [ -  +  +  - ]:          28 :     found_output_ptrs.reserve(tx_outputs.size());
     475   [ -  +  +  - ]:          28 :     tx_output_objs.reserve(tx_outputs.size());
     476   [ -  +  +  - ]:          28 :     tx_output_ptrs.reserve(tx_outputs.size());
     477                 :             : 
     478         [ -  + ]:          28 :     assert(m_scan_key.IsValid());
     479         [ -  + ]:          28 :     assert(m_spend_pubkey_obj);
     480                 :             : 
     481         [ +  + ]:        2391 :     for (const XOnlyPubKey& tx_output : tx_outputs) {
     482                 :        2363 :         secp256k1_xonly_pubkey tx_output_obj;
     483         [ +  - ]:        2363 :         ret = secp256k1_xonly_pubkey_parse(secp256k1_context_static, &tx_output_obj, tx_output.data());
     484         [ +  + ]:        2363 :         if (!ret) {
     485                 :             :             // It is possible that a P2TR output encodes an invalid x-only pubkey.
     486                 :           1 :             continue;
     487                 :             :         }
     488         [ +  - ]:        2362 :         tx_output_objs.push_back(tx_output_obj);
     489         [ +  - ]:        2362 :         tx_output_ptrs.push_back(&tx_output_objs.back());
     490         [ +  - ]:        2362 :         found_output_objs.emplace_back();
     491         [ +  - ]:        2363 :         found_output_ptrs.push_back(&found_output_objs.back());
     492                 :             :     }
     493         [ -  + ]:          28 :     if (tx_output_ptrs.empty()) return std::vector<SilentPaymentsOutput>{};
     494                 :             : 
     495                 :             :     // Scan the outputs!
     496                 :          28 :     uint32_t n_found_outputs = 0;
     497   [ +  -  -  + ]:          56 :     ret = secp256k1_silentpayments_recipient_scan_outputs(secp256k1_context_static,
     498                 :             :         found_output_ptrs.data(), &n_found_outputs,
     499         [ +  - ]:          28 :         tx_output_ptrs.data(), tx_output_ptrs.size(),
     500                 :             :         UCharCast(m_scan_key.begin()),
     501                 :             :         prevouts_summary.Get(),
     502                 :          28 :         m_spend_pubkey_obj.get(),
     503                 :             :         LabelLookupCallback,
     504         [ +  - ]:          28 :         &m_labels
     505                 :             :     );
     506         [ -  + ]:          28 :     if (!ret) return std::nullopt;
     507                 :             : 
     508                 :          28 :     std::vector<SilentPaymentsOutput> outputs;
     509         [ +  + ]:        2384 :     for (size_t i = 0; i < n_found_outputs; i++) {
     510                 :        2356 :         SilentPaymentsOutput sp_output;
     511         [ +  - ]:        2356 :         ret = secp256k1_xonly_pubkey_serialize(secp256k1_context_static, sp_output.output.begin(), &found_output_objs[i].output);
     512         [ -  + ]:        2356 :         assert(ret);
     513                 :        2356 :         sp_output.tweak = uint256{found_output_objs[i].tweak};
     514         [ +  + ]:        2356 :         if (found_output_objs[i].found_with_label) {
     515         [ +  - ]:        2333 :             sp_output.label = SilentPaymentsLabel(found_output_objs[i].label);
     516                 :             :         }
     517         [ +  - ]:        2356 :         outputs.emplace_back(std::move(sp_output));
     518                 :        2356 :     }
     519                 :          28 :     return outputs;
     520                 :          56 : }
     521                 :             : }; // namespace bip352
        

Generated by: LCOV version 2.0-1