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
|