LCOV - code coverage report
Current view: top level - src/common - bip352.h (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 0.0 % 15 0
Test Date: 2026-10-07 06:11:03 Functions: 0.0 % 2 0
Branches: 0.0 % 8 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                 :             : #ifndef BITCOIN_COMMON_BIP352_H
       6                 :             : #define BITCOIN_COMMON_BIP352_H
       7                 :             : 
       8                 :             : #include <addresstype.h>
       9                 :             : #include <attributes.h>
      10                 :             : #include <compat/byteswap.h>
      11                 :             : #include <key.h>
      12                 :             : #include <primitives/transaction.h>
      13                 :             : #include <pubkey.h>
      14                 :             : #include <uint256.h>
      15                 :             : #include <util/expected.h>
      16                 :             : 
      17                 :             : #include <array>
      18                 :             : #include <compare>
      19                 :             : #include <cstdint>
      20                 :             : #include <cstring>
      21                 :             : #include <functional>
      22                 :             : #include <map>
      23                 :             : #include <memory>
      24                 :             : #include <optional>
      25                 :             : #include <span>
      26                 :             : #include <string>
      27                 :             : #include <variant>
      28                 :             : #include <vector>
      29                 :             : 
      30                 :             : struct secp256k1_silentpayments_label;
      31                 :             : struct secp256k1_silentpayments_prevouts_summary;
      32                 :             : struct secp256k1_pubkey;
      33                 :             : class CChainParams;
      34                 :             : class CScript;
      35                 :             : class Coin;
      36                 :             : 
      37                 :             : namespace bip352 {
      38                 :             : 
      39                 :             : using PubKey = std::variant<CPubKey, XOnlyPubKey>;
      40                 :             : 
      41                 :             : class PrevoutsSummary
      42                 :             : {
      43                 :             : private:
      44                 :             :     std::unique_ptr<secp256k1_silentpayments_prevouts_summary> m_prevouts_summary;
      45                 :             : 
      46                 :             : public:
      47                 :             :     PrevoutsSummary(const secp256k1_silentpayments_prevouts_summary& prevouts_summary);
      48                 :             :     PrevoutsSummary(PrevoutsSummary&&) noexcept;
      49                 :             :     PrevoutsSummary& operator=(PrevoutsSummary&&) noexcept;
      50                 :             :     ~PrevoutsSummary();
      51                 :             : 
      52                 :             :     // Delete copy constructors
      53                 :             :     PrevoutsSummary(const PrevoutsSummary&) = delete;
      54                 :             :     PrevoutsSummary& operator=(const PrevoutsSummary&) = delete;
      55                 :             : 
      56                 :             :     const secp256k1_silentpayments_prevouts_summary* Get() const LIFETIMEBOUND;
      57                 :             : };
      58                 :             : 
      59                 :             : struct BIP352Comparator {
      60                 :           0 :     bool operator()(const COutPoint& a, const COutPoint& b) const {
      61                 :             :         // BIP352 defines the "smallest outpoint" based on a lexicographic
      62                 :             :         // sort of the outpoints, using the 36-byte serialization:
      63                 :             :         // <txid, 32-bytes little-endian>:<vout, 4-bytes little-endian>
      64         [ #  # ]:           0 :         if (a.hash != b.hash) return a.hash < b.hash;
      65                 :           0 :         return internal_bswap_32(a.n) < internal_bswap_32(b.n);
      66                 :             :     }
      67                 :             : };
      68                 :             : 
      69                 :           0 : struct SilentPaymentsDestination
      70                 :             : {
      71                 :             : private:
      72                 :             :     uint8_t m_version;
      73                 :             :     CPubKey m_scan_pubkey;
      74                 :             :     CPubKey m_spend_pubkey;
      75                 :             :     std::vector<unsigned char> m_extension_data;
      76                 :             : 
      77                 :           0 :     SilentPaymentsDestination(
      78                 :             :         uint8_t version,
      79                 :             :         const CPubKey& scan_pubkey,
      80                 :             :         const CPubKey& spend_pubkey,
      81                 :             :         std::span<const unsigned char> extension_data = {}
      82                 :           0 :     ) : m_version(version), m_scan_pubkey(scan_pubkey),
      83                 :           0 :         m_spend_pubkey(spend_pubkey),
      84                 :           0 :         m_extension_data(extension_data.begin(), extension_data.end()) {};
      85                 :             : public:
      86                 :             :     static std::optional<SilentPaymentsDestination> From(
      87                 :             :         const CPubKey& scan_pubkey,
      88                 :             :         const CPubKey& spend_pubkey,
      89                 :             :         uint8_t version = 0,
      90                 :             :         std::span<const unsigned char> extension_data = {}
      91                 :             :     );
      92                 :             : 
      93                 :             :     uint8_t GetVersion() const { return m_version; }
      94                 :             :     const CPubKey& GetScanPubKey() const { return m_scan_pubkey; }
      95                 :             :     const CPubKey& GetSpendPubKey() const { return m_spend_pubkey; }
      96                 :             :     std::span<const unsigned char> GetExtensionData() const { return m_extension_data; }
      97                 :             : 
      98                 :             :     bool operator==(const SilentPaymentsDestination&) const = default;
      99                 :             : };
     100                 :             : 
     101                 :             : //! Decode a BIP352 "sp1..." address. Returns the destination, or an error message on failure.
     102                 :             : util::Expected<SilentPaymentsDestination, std::string> DecodeSilentPaymentsAddress(
     103                 :             :     const std::string& str, const CChainParams& params);
     104                 :             : 
     105                 :             : class SilentPaymentsLabel {
     106                 :             : private:
     107                 :             :     std::unique_ptr<secp256k1_silentpayments_label> m_label;
     108                 :             :     unsigned char m_vch[CPubKey::COMPRESSED_SIZE];
     109                 :             : 
     110                 :             :     //! Parses raw bytes into a fully valid label
     111                 :             :     //! returns std::nullopt if vch is not a validly-encoded label.
     112                 :             :     static std::optional<SilentPaymentsLabel> FromBytes(std::span<const unsigned char, CPubKey::COMPRESSED_SIZE> vch);
     113                 :             : 
     114                 :             : public:
     115                 :             :     SilentPaymentsLabel(const secp256k1_silentpayments_label& label);
     116                 :             : 
     117                 :             :     SilentPaymentsLabel(SilentPaymentsLabel&&) noexcept;
     118                 :             :     SilentPaymentsLabel& operator=(SilentPaymentsLabel&&) noexcept;
     119                 :             :     SilentPaymentsLabel(const SilentPaymentsLabel&);
     120                 :             :     SilentPaymentsLabel& operator=(const SilentPaymentsLabel&);
     121                 :             : 
     122                 :             :     ~SilentPaymentsLabel();
     123                 :             : 
     124                 :             :     friend bool operator==(const SilentPaymentsLabel& a, const SilentPaymentsLabel& b) {
     125                 :             :         return memcmp(a.m_vch,  b.m_vch, CPubKey::COMPRESSED_SIZE) == 0;
     126                 :             :     }
     127                 :           0 :     friend bool operator<(const SilentPaymentsLabel& a, const SilentPaymentsLabel& b) {
     128   [ #  #  #  #  :           0 :         return memcmp(a.m_vch,  b.m_vch, CPubKey::COMPRESSED_SIZE) < 0;
                   #  # ]
     129                 :             :     }
     130                 :             :     friend bool operator>(const SilentPaymentsLabel& a, const SilentPaymentsLabel& b) {
     131                 :             :         return b < a;
     132                 :             :     }
     133                 :             :     //! Transparent comparisons against a raw 33-byte compressed label key, so a
     134                 :             :     //! LabelTweakMap can be looked up by key bytes (e.g. from a secp256k1 callback)
     135                 :             :     //! without constructing a fully-parsed SilentPaymentsLabel.
     136                 :           0 :     friend bool operator<(const SilentPaymentsLabel& a, std::span<const unsigned char, CPubKey::COMPRESSED_SIZE> b) {
     137                 :           0 :         return memcmp(a.m_vch, b.data(), CPubKey::COMPRESSED_SIZE) < 0;
     138                 :             :     }
     139                 :           0 :     friend bool operator<(std::span<const unsigned char, CPubKey::COMPRESSED_SIZE> a, const SilentPaymentsLabel& b) {
     140                 :           0 :         return memcmp(a.data(), b.m_vch, CPubKey::COMPRESSED_SIZE) < 0;
     141                 :             :     }
     142                 :             : 
     143                 :             :     template <typename Stream>
     144                 :             :     void Serialize(Stream& s) const
     145                 :             :     {
     146                 :             :         s << std::span{m_vch, CPubKey::COMPRESSED_SIZE};
     147                 :             :     }
     148                 :             : 
     149                 :             :     template <typename Stream>
     150                 :             :     static std::optional<SilentPaymentsLabel> Unserialize(Stream& s)
     151                 :             :     {
     152                 :             :         std::array<unsigned char, CPubKey::COMPRESSED_SIZE> vch;
     153                 :             :         s >> std::span{vch};
     154                 :             :         return FromBytes(std::span{vch});
     155                 :             :     }
     156                 :             : 
     157                 :             :     const secp256k1_silentpayments_label* Get() const LIFETIMEBOUND;
     158                 :             : };
     159                 :             : 
     160                 :           0 : struct SilentPaymentsOutput {
     161                 :             :     XOnlyPubKey output;
     162                 :             :     uint256 tweak;
     163                 :             :     std::optional<SilentPaymentsLabel> label;
     164                 :             : };
     165                 :             : 
     166                 :             : /**
     167                 :             :  * @brief Get the public key from an input.
     168                 :             :  *
     169                 :             :  * Get the public key from a silent payments eligible input. This requires knowledge of the prevout
     170                 :             :  * scriptPubKey to determine the type of input and whether or not it is eligible for silent payments.
     171                 :             :  *
     172                 :             :  * If the input is not eligible for silent payments, the input is skipped (indicated by returning a nullopt).
     173                 :             :  *
     174                 :             :  * @param txin                    The transaction input.
     175                 :             :  * @param spk                     The scriptPubKey of the prevout.
     176                 :             :  * @return The public key, or nullopt if not found.
     177                 :             :  */
     178                 :             : std::optional<PubKey> GetPubKeyFromInput(const CTxIn& txin, const CScript& spk);
     179                 :             : 
     180                 :             : /**
     181                 :             :  * @brief Generate silent payments taproot destinations.
     182                 :             :  *
     183                 :             :  * Given a set of silent payments destinations, generate the requested number of outputs. If a silent payment
     184                 :             :  * destination is repeated, this indicates multiple outputs are requested for the same recipient. The silent payment
     185                 :             :  * destinations are passed in a map where the key indicates their desired position in the final tx.vout array.
     186                 :             :  *
     187                 :             :  * @param sp_dests                            The silent payments destinations.
     188                 :             :  * @param plain_keys                          The private keys for non-taproot inputs.
     189                 :             :  * @param taproot_keys                        The keypairs for taproot inputs.
     190                 :             :  * @param smallest_outpoint                   The smallest_outpoint from the transaction inputs.
     191                 :             :  * @pre smallest_outpoint is not null, and at least one of plain_keys or taproot_keys is non-empty.
     192                 :             :  * @return The generated silent payments taproot destinations or std::nullopt if the set of provided inputs is invalid:
     193                 :             :  *         - The size of any group (i.e. recipients sharing the same scan public key)
     194                 :             :  *           exceeds the protocol limit SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT.
     195                 :             :  *         - There is an invalid key in the set.
     196                 :             :  *         - The inputs sum to zero.
     197                 :             :  */
     198                 :             : 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);
     199                 :             : 
     200                 :             : enum class PrevoutsSummaryError {
     201                 :             :     //! A prevout referenced by an input in `vin` has no corresponding entry in `coins`.
     202                 :             :     MISSING_COIN,
     203                 :             :     //! This transaction is not eligible to be scanned for silent payments outputs: either none
     204                 :             :     //! of its inputs yielded a public key eligible for silent payments (e.g. an input spends an
     205                 :             :     //! unknown segwit version), or the eligible inputs' public keys summed to the point at infinity.
     206                 :             :     NOT_ELIGIBLE,
     207                 :             : };
     208                 :             : 
     209                 :             : /**
     210                 :             :  * @brief Get silent payments public data from transaction inputs.
     211                 :             :  *
     212                 :             :  * Get the necessary data from the transaction inputs to be able to scan the transaction outputs for silent payments outputs.
     213                 :             :  * This requires knowledge of the prevout scriptPubKey, which is passed via `coins`.
     214                 :             :  *
     215                 :             :  * This function returns the public key sum and the input hash separately and is intended to be used by the wallet when scanning
     216                 :             :  * a transaction.
     217                 :             :  *
     218                 :             :  * If there are no eligible inputs, or one of the inputs spends an unknown segwit version (i.e > 1), this transaction is not
     219                 :             :  * eligible to be scanned for silent payments outputs; see PrevoutsSummaryError.
     220                 :             :  *
     221                 :             :  * @param vin                        The transaction inputs.
     222                 :             :  * @param coins                      The coins (potentially) spent in this transaction.
     223                 :             :  * @return util::Expected<PrevoutsSummary, PrevoutsSummaryError> The silent payments public data, or the reason it could not be computed.
     224                 :             :  */
     225                 :             : util::Expected<PrevoutsSummary, PrevoutsSummaryError> GetSilentPaymentsPrevoutsSummary(const std::vector<CTxIn>& vin, const std::map<COutPoint, Coin>& coins);
     226                 :             : 
     227                 :             : using LabelTweakMap = std::map<SilentPaymentsLabel, uint256, std::less<>>;
     228                 :             : 
     229                 :             : /**
     230                 :             :  * @brief A silent payments recipient's scanning identity.
     231                 :             :  *
     232                 :             :  * Bundles a recipient's scan key, spend public key, and known labels (always including the change
     233                 :             :  * label) into a single self-contained object capable of scanning transactions and deriving the
     234                 :             :  * recipient's change destination.
     235                 :             :  *
     236                 :             :  * The change label is created automatically on construction if one is not supplied through
     237                 :             :  * the labels map; additional labels are registered on creation with GenerateLabeledAddress().
     238                 :             :  */
     239                 :             : class SilentPaymentsReceiver {
     240                 :             : private:
     241                 :             :     CKey m_scan_key;
     242                 :             :     CPubKey m_spend_pubkey;
     243                 :             :     std::unique_ptr<secp256k1_pubkey> m_spend_pubkey_obj;
     244                 :             :     LabelTweakMap m_labels;
     245                 :             :     LabelTweakMap::const_iterator m_change_it;
     246                 :             : 
     247                 :             :     SilentPaymentsDestination BuildLabeledDestination(const SilentPaymentsLabel& label) const;
     248                 :             : 
     249                 :             : public:
     250                 :             :     SilentPaymentsReceiver(const CKey& scan_key, const CPubKey& spend_pubkey, const LabelTweakMap& labels = {});
     251                 :             :     ~SilentPaymentsReceiver();
     252                 :             : 
     253                 :             :     // Default move would leave m_scan_key and m_labels empty while
     254                 :             :     // m_spend_pubkey remains fully populated, leaving the
     255                 :             :     // SilentPaymentsReceiver object in an inconsistent state.
     256                 :             :     SilentPaymentsReceiver(SilentPaymentsReceiver&&) = delete;
     257                 :             :     SilentPaymentsReceiver& operator=(SilentPaymentsReceiver&&) = delete;
     258                 :             : 
     259                 :             :     SilentPaymentsReceiver(const SilentPaymentsReceiver&) = delete;
     260                 :             :     SilentPaymentsReceiver& operator=(const SilentPaymentsReceiver&) = delete;
     261                 :             : 
     262                 :             :     /**
     263                 :             :      * @brief Get this recipient's registered labels, including the change label.
     264                 :             :      *
     265                 :             :      * @return const LabelTweakMap& Each label mapped to its scalar tweak.
     266                 :             :      */
     267                 :             :     const LabelTweakMap& GetLabels() const;
     268                 :             : 
     269                 :             :     /**
     270                 :             :      * @brief Register label `m` (e.g. for a labeled sub-address to hand out to a payer) and generate
     271                 :             :      * the resulting address.
     272                 :             :      *
     273                 :             :      * Derives the label from this recipient's own scan key and `m`, registers it so Scan() can
     274                 :             :      * recognize outputs sent to it, and returns the address to hand out.
     275                 :             :      *
     276                 :             :      * @param m                        An integer m, greater than 0 (m = 0 is reserved for the change label).
     277                 :             :      * @return SilentPaymentsDestination The labeled destination, with `B_spend -> B_spend + label`.
     278                 :             :      */
     279                 :             :     SilentPaymentsDestination GenerateLabeledAddress(uint32_t m);
     280                 :             : 
     281                 :             :     /**
     282                 :             :      * @brief Get this recipient's silent payments change destination.
     283                 :             :      *
     284                 :             :      * @return SilentPaymentsDestination The destination to use for this recipient's own change outputs,
     285                 :             :      *         i.e. `B_spend -> B_spend + change_label`.
     286                 :             :      */
     287                 :             :     SilentPaymentsDestination GetChangeDestination() const;
     288                 :             : 
     289                 :             :     /**
     290                 :             :      * @brief Scan a transaction for silent payments outputs.
     291                 :             :      *
     292                 :             :      * Scan the transaction for silent payments outputs intended for this recipient. The output, shared
     293                 :             :      * secret tweak, and (optionally) label public key is returned for each output found. If the output
     294                 :             :      * was sent to a labeled address, the label tweak is added to the shared secret tweak. The shared
     295                 :             :      * secret tweak is needed to spend the output, by adding it to the spend secret key. If no outputs
     296                 :             :      * are found, this transaction does not contain silent payments outputs for this recipient.
     297                 :             :      *
     298                 :             :      * @param prevouts_summary                   The silent payments public data.
     299                 :             :      * @param tx_outputs                         The taproot output public keys.
     300                 :             :      * @return The found outputs or std::nullopt in the case of an error.
     301                 :             :      */
     302                 :             :     std::optional<std::vector<SilentPaymentsOutput>> Scan(const PrevoutsSummary& prevouts_summary, const std::vector<XOnlyPubKey>& tx_outputs) const;
     303                 :             : };
     304                 :             : }; // namespace bip352
     305                 :             : #endif // BITCOIN_COMMON_BIP352_H
        

Generated by: LCOV version 2.5.0-full