/tmp/bitcoin/src/common/bip352.h
Line | Count | Source |
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 | 63 | 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 | 63 | if (a.hash != b.hash) return a.hash < b.hash; |
65 | 20 | return internal_bswap_32(a.n) < internal_bswap_32(b.n); |
66 | 63 | } |
67 | | }; |
68 | | |
69 | | 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 | | SilentPaymentsDestination( |
78 | | uint8_t version, |
79 | | const CPubKey& scan_pubkey, |
80 | | const CPubKey& spend_pubkey, |
81 | | std::span<const unsigned char> extension_data = {} |
82 | 132 | ) : m_version(version), m_scan_pubkey(scan_pubkey), |
83 | 132 | m_spend_pubkey(spend_pubkey), |
84 | 132 | 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 | 5 | uint8_t GetVersion() const { return m_version; } |
94 | 4.76k | const CPubKey& GetScanPubKey() const { return m_scan_pubkey; } |
95 | 4.79k | const CPubKey& GetSpendPubKey() const { return m_spend_pubkey; } |
96 | 5 | std::span<const unsigned char> GetExtensionData() const { return m_extension_data; } |
97 | | |
98 | 235 | 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 | 1 | friend bool operator==(const SilentPaymentsLabel& a, const SilentPaymentsLabel& b) { |
125 | 1 | return memcmp(a.m_vch, b.m_vch, CPubKey::COMPRESSED_SIZE) == 0; |
126 | 1 | } |
127 | 37 | friend bool operator<(const SilentPaymentsLabel& a, const SilentPaymentsLabel& b) { |
128 | 37 | return memcmp(a.m_vch, b.m_vch, CPubKey::COMPRESSED_SIZE) < 0; |
129 | 37 | } |
130 | 0 | friend bool operator>(const SilentPaymentsLabel& a, const SilentPaymentsLabel& b) { |
131 | 0 | return b < a; |
132 | 0 | } |
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 | 5.39M | friend bool operator<(const SilentPaymentsLabel& a, std::span<const unsigned char, CPubKey::COMPRESSED_SIZE> b) { |
137 | 5.39M | return memcmp(a.m_vch, b.data(), CPubKey::COMPRESSED_SIZE) < 0; |
138 | 5.39M | } |
139 | 532k | friend bool operator<(std::span<const unsigned char, CPubKey::COMPRESSED_SIZE> a, const SilentPaymentsLabel& b) { |
140 | 532k | return memcmp(a.data(), b.m_vch, CPubKey::COMPRESSED_SIZE) < 0; |
141 | 532k | } |
142 | | |
143 | | template <typename Stream> |
144 | | void Serialize(Stream& s) const |
145 | 1 | { |
146 | 1 | s << std::span{m_vch, CPubKey::COMPRESSED_SIZE}; |
147 | 1 | } |
148 | | |
149 | | template <typename Stream> |
150 | | static std::optional<SilentPaymentsLabel> Unserialize(Stream& s) |
151 | 1 | { |
152 | 1 | std::array<unsigned char, CPubKey::COMPRESSED_SIZE> vch; |
153 | 1 | s >> std::span{vch}; |
154 | 1 | return FromBytes(std::span{vch}); |
155 | 1 | } |
156 | | |
157 | | const secp256k1_silentpayments_label* Get() const LIFETIMEBOUND; |
158 | | }; |
159 | | |
160 | | 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 |