LCOV - code coverage report
Current view: top level - src/common - bip352.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 0.0 % 316 0
Test Date: 2026-10-07 06:11:03 Functions: 0.0 % 30 0
Branches: 0.0 % 444 0

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

Generated by: LCOV version 2.5.0-full