From ef46b64c9732be5ab45c31914b6e281f40a81a09 Mon Sep 17 00:00:00 2001 From: soloking1412 Date: Thu, 23 Jul 2026 00:45:57 +0530 Subject: [PATCH 1/5] docs: add silent payments ELIP draft --- Cargo.lock | 1 + docs/elip-silent-payments.mediawiki | 531 ++ lwk_test_util/src/lib.rs | 5 + ...bip0352_send_and_receive_test_vectors.json | 5729 +++++++++++++++++ lwk_wollet/Cargo.toml | 5 + lwk_wollet/src/error.rs | 3 + lwk_wollet/src/lib.rs | 5 + lwk_wollet/src/silent_payments/address.rs | 273 + lwk_wollet/src/silent_payments/hashes.rs | 115 + lwk_wollet/src/silent_payments/inputs.rs | 376 ++ lwk_wollet/src/silent_payments/keys.rs | 160 + lwk_wollet/src/silent_payments/mod.rs | 163 + lwk_wollet/src/silent_payments/scan.rs | 482 ++ lwk_wollet/src/silent_payments/send.rs | 255 + .../src/silent_payments/test_vectors.rs | 225 + lwk_wollet/src/silent_payments/wollet.rs | 237 + lwk_wollet/tests/silent_payments.rs | 526 ++ 17 files changed, 9091 insertions(+) create mode 100644 docs/elip-silent-payments.mediawiki create mode 100644 lwk_test_util/test_data/bip0352_send_and_receive_test_vectors.json create mode 100644 lwk_wollet/src/silent_payments/address.rs create mode 100644 lwk_wollet/src/silent_payments/hashes.rs create mode 100644 lwk_wollet/src/silent_payments/inputs.rs create mode 100644 lwk_wollet/src/silent_payments/keys.rs create mode 100644 lwk_wollet/src/silent_payments/mod.rs create mode 100644 lwk_wollet/src/silent_payments/scan.rs create mode 100644 lwk_wollet/src/silent_payments/send.rs create mode 100644 lwk_wollet/src/silent_payments/test_vectors.rs create mode 100644 lwk_wollet/src/silent_payments/wollet.rs create mode 100644 lwk_wollet/tests/silent_payments.rs diff --git a/Cargo.lock b/Cargo.lock index 326b17978..87fd503db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2997,6 +2997,7 @@ dependencies = [ "aes-gcm-siv", "age", "base64 0.21.7", + "bech32 0.11.0", "bip39", "bitcoincore-rpc", "cbc", diff --git a/docs/elip-silent-payments.mediawiki b/docs/elip-silent-payments.mediawiki new file mode 100644 index 000000000..f572393c1 --- /dev/null +++ b/docs/elip-silent-payments.mediawiki @@ -0,0 +1,531 @@ +
+  ELIP: ?
+  Layer: Wallet
+  Title: Silent Payments for Elements
+  Author: Maheswaran Velmurugan 
+  Comments-Summary: No comments yet.
+  Comments-URI: https://github.com/ElementsProject/elips/wiki/Comments:ELIP-????
+  Status: Draft
+  Type: Standards Track
+  Created: 2026-07-23
+  License: BSD-3-Clause
+
+ +==Introduction== + +===Abstract=== + +This document describes how [https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki BIP-352] silent payments can be used on Elements based chains, where transaction outputs are confidential. + +A silent payment address is a static address that can be published without giving up privacy. +The sender derives a fresh taproot output from the address and from the inputs it is spending, and only the receiver can recognize it. +Nothing is added to the transaction, so the receiver has to scan the chain. + +Key derivation is unchanged from BIP-352. +What Elements adds is blinding: an output is confidential, so the sender must blind it to a key that the receiver is able to compute back. +This document defines that key as a tagged hash of the same ECDH shared secret used to derive the output key, so that no data other than the transaction itself is needed to receive a payment. + +It also defines the address encoding, which is the BIP-352 encoding with an Elements specific human readable part, the rules that decide which inputs contribute to the shared secret on a chain that has peg-ins and issuances, and the client and server side of the "Tweak Server" model of the [https://github.com/silent-payments/BIP0352-index-server-specification BIP-352 index server specification], which is how a wallet is expected to scan. + +===Copyright=== + +This document is licensed under the 3-clause BSD license. + +===Motivation=== + +Receiving on Elements based chains today means either reusing an address or handing out a fresh one for every payment. + +Address reuse is bad for privacy: confidential transactions hide the amount and the asset, but not the scriptPubKey, so anybody can see that two payments went to the same script, and anybody who has been given that address can watch it. +This is not a corner case: it is what happens whenever an address is published, embedded in a donation page, or printed on an invoice that is paid more than once. + +Handing out a fresh address per payment avoids the problem but requires an interaction with the receiver at payment time, which is not always possible, and it puts the burden of gap limits and address recovery on the wallet. + +Silent payments remove the interaction: one static address, one output per payment, no on chain link between payments. +The cost is that the receiver has to scan, which is why this document also specifies how a wallet scans without downloading every block. + +Elements cannot use BIP-352 as is, because a BIP-352 output is a plain taproot output while an Elements output is normally confidential. +A confidential output is blinded to a public key chosen by the sender: unless the receiver can derive the corresponding secret key it cannot unblind the output, and an output it cannot unblind is an output it cannot spend +Knowing the secret key of an output is not enough to spend it on Elements: to build a valid transaction the wallet needs the asset and value blinding factors of the outputs it is spending, which are recovered by rewinding the range proof with the blinding secret key. +. +This document fills exactly that gap, and resolves the ambiguities that a chain with peg-ins, issuances and explicit fee outputs introduces in the BIP-352 rules. + +==Design== + +===Overview=== + +Three things are added to BIP-352, everything else is unchanged: + +# A '''blinding key derivation'''. The sender blinds the output to blinding_keyk·G, where blinding_keyk is a tagged hash of the ECDH shared secret and the output counter. The receiver derives the same key while scanning and unblinds the output. +# An '''address encoding''' with an Elements specific human readable part, so that a Bitcoin silent payment address cannot be paid on Liquid and vice versa. +# '''Input eligibility rules''' for the inputs that only exist on Elements: peg-ins never contribute their public key, and the outpoint of an input is serialized with the peg-in and issuance flags cleared. + +For receiving, this document specifies the "Tweak Server" model of the BIP-352 index server specification: the wallet asks a server for the per transaction tweak data of each block, derives locally the scripts it would be paid to, and only downloads a block when a compact block filter says that one of those scripts is in it. +The wallet never shares its keys with the server. + +===Drawbacks=== + +Deriving the blinding key from the shared secret means that whoever knows the scan secret key can unblind every payment received by that wallet, not just detect it. +On Bitcoin, sharing the scan key with a scanning service reveals which outputs belong to the wallet; on Elements it would also reveal every amount and asset. +This is the reason why this document specifies the Tweak Server model, in which the scan key never leaves the wallet, and why the "Remote Scanner" model of the index server specification is '''not''' recommended here. + +The sender can unblind the outputs it creates. This is inherent to confidential transactions: the sender picks the ephemeral key used to blind the output, so it already knows the amount and the asset of what it sends, whatever the blinding key derivation is. + +Scanning cost is proportional to the number of blocks since the wallet birthday, not to the number of payments received. A wallet restored from a backup that only contains the seed must scan from the taproot activation height. + +Labels have to be backed up, or payments received on a labelled address are not detected on recovery. This is the same drawback as BIP-352. + +==Specification== + +===Notation=== + +All the arithmetic is on the secp256k1 curve, with generator G and group order n. + +{| class="wikitable" +! Notation !! Meaning +|- +| serP(P) || the 33 byte compressed serialization of the point P +|- +| ser32(i) || the 4 byte big endian serialization of the integer i +|- +| ser256(x) || the 32 byte big endian serialization of the scalar x +|- +| hashtag(x) || the [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#design BIP-340 tagged hash] of x with tag tag +|} + +Inside a formula, || denotes concatenation. + +An operation that yields 0 or a value not smaller than n, or that yields the point at infinity, MUST be treated as a failure wherever it appears below. + +===Keys=== + +A silent payment wallet is defined by two key pairs: + +* the '''scan key''' bscan, Bscan = bscan·G, used to detect the payments; +* the '''spend key''' bspend, Bspend = bspend·G, used to spend them. + +A watch only wallet holds bscan and Bspend: it detects and unblinds the payments but cannot spend them. + +When the keys are derived from a BIP-32 seed, the derivation of BIP-352 is used, with the Elements coin type: + +
+  scan_key:  m / 352' / coin_type' / account' / 1' / 0
+  spend_key: m / 352' / coin_type' / account' / 0' / 0
+
+ +where coin_type is 1776 on Liquid and 1 on any test network, as in [https://github.com/satoshilabs/slips/blob/master/slip-0044.md SLIP-44]. + +===Address encoding=== + +A silent payment address is encoded as in BIP-352: bech32m +Bech32m and not bech32, for every version, unlike segwit addresses. This is what BIP-352 does. +, with a version character followed by the payload, and a maximum length of 1023 characters. + +The payload of a version 0 address is exactly 66 bytes: + +
+  ser_P(B_scan) || ser_P(B_m)
+
+ +where Bm is the spend public key, tweaked by a label if the address is labelled (see [[#Labels|Labels]]). + +The human readable part identifies the chain: + +{| class="wikitable" +! Chain !! HRP !! Example prefix +|- +| Liquid || lqsp || lqsp1q +|- +| Liquid testnet || tlqsp || tlqsp1q +|- +| Elements regtest || elsp || elsp1q +|} + +A wallet MUST reject an address whose human readable part is not the one of the chain it is operating on. + +A wallet MUST reject version 31 (l), which BIP-352 reserves to signal a backward incompatible change. +For a version between 1 and 30 a wallet MUST accept a payload of 66 bytes or more and use the first 66 bytes, ignoring the rest; for version 0 the payload MUST be exactly 66 bytes. + +===Transaction eligibility=== + +A transaction can pay to a silent payment address, and MUST be scanned by a receiver, if and only if all of the following hold: + +* it has at least one output that is a taproot output; +* it has at least one eligible input, as defined below, and the public keys of its eligible inputs do not sum to the point at infinity; +* none of its inputs spends an output whose script is a witness program of version greater than 1 +As in BIP-352: skipping the transactions that spend a script this version of the protocol does not understand leaves a clean upgrade path, since a wallet will never have to scan the same transaction once with the rules of this document and once with the rules of a future one. +. + +A sender MUST NOT select an input that spends a witness program of version greater than 1, and MUST use a sighash flag that commits to all the inputs: if the inputs change after the outputs have been derived, the receiver computes a different shared secret and the payment is lost. + +===Input eligibility=== + +An input is '''eligible''' if it contributes its public key to the shared secret. The rules of BIP-352 apply, with the peg-in rule added. + +An input is eligible if and only if the output it spends has one of the following scripts, and the corresponding public key is compressed or x-only: + +{| class="wikitable" +! Spent script !! Public key +|- +| P2TR || the output key, read from the spent scriptPubKey, lifted with even Y +|- +| P2WPKH || the last element of the witness +|- +| P2SH-P2WPKH || the last element of the witness, if the scriptSig is a single push of a P2WPKH redeem script +|- +| P2PKH || the 33 byte push in the scriptSig whose HASH160 is the one committed in the spent script +The scriptSig of a P2PKH input can be malleated by anybody, so the public key cannot simply be taken from the last push: it is found by scanning the scriptSig backwards for a push that hashes to the committed value. + +|} + +An input is '''not''' eligible if: + +* it is a '''peg-in''' input. The output it spends is on the Bitcoin chain, so a wallet scanning Elements cannot read the script that would tell how to extract the key. Its outpoint is still taken into account when looking for the smallest one. +* it spends a P2TR output through a script path whose control block reveals the BIP-341 NUMS point H = lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0) as internal key, in which case nobody knows the corresponding secret key. The annex, if present, is removed before looking at the control block. +* its public key is uncompressed. +* it spends anything else, in particular a P2WSH or a bare P2SH output. + +An input carrying an asset issuance or reissuance is eligible under the rules above: an issuance does not change how the input is spent. + +===Outpoint serialization=== + +BIP-352 commits to the smallest outpoint of the transaction. On Elements the vout field of an input carries, in its two most significant bits, the peg-in and issuance flags +See CTxIn in Elements: the flags are set when the transaction is serialized and are stripped when it is parsed, so a wallet that hashes the parsed outpoint and a wallet that hashes the raw bytes would compute different values. +. + +An outpoint MUST be serialized as: + +
+  txid (32 bytes, as in the transaction) || vout (4 bytes, little endian)
+
+ +where vout is the index '''with the peg-in flag (230) and the issuance flag (231) cleared''', that is the value obtained after parsing the transaction. +The vout of a coinbase input, 0xffffffff, is not masked, as it carries no flags. + +outpointL is the lexicographically smallest of the serializations of '''all''' the outpoints of the transaction, including the ones of inputs that are not eligible. + +===Shared secret=== + +Let A = A1 + ... + Ai be the sum of the public keys of the eligible inputs of the transaction, and + +
+  input_hash = hash_BIP0352/Inputs(outpoint_L || ser_P(A))
+
+ +The shared secret between the sender, which knows a = a1 + ... + ai, and the receiver, which knows bscan, is + +
+  ecdh_shared_secret = input_hash · a · B_scan       (sender)
+                     = input_hash · b_scan · A       (receiver)
+
+ +A transaction with no eligible input, or whose eligible input keys sum to the point at infinity, cannot pay to a silent payment address. +An intermediate sum equal to zero is not a failure, only the final one is. + +The receiver does not need A and input_hash separately: input_hash · A, called the '''tweak data''' of the transaction, is enough, which is what makes the light client model of [[#Receiving|Receiving]] possible. + +===Output derivation=== + +For every output paid to the same Bscan in a transaction, with a counter k starting at 0: + +
+  t_k = hash_BIP0352/SharedSecret(ser_P(ecdh_shared_secret) || ser_32(k))
+  P_k = B_m + t_k·G
+
+ +The scriptPubKey of the output is Pk encoded as a taproot output: + +
+  OP_1 OP_PUSHBYTES_32 
+
+ +Pk is the output key itself: the BIP-341 tweak is '''not''' applied +This is what BIP-352 does, and it is why a received output cannot be described by an eltr() descriptor, see [[#Representation of a received output|Representation of a received output]]. +. + +A transaction MUST NOT contain more than Kmax = 2323 outputs paying to the same Bscan. + +===Blinding key derivation=== + +This is the part that Elements adds. + +The output MUST be blinded to the public key Ck = ck·G, where + +
+  c_k = hash_Silent-Payment-Blinding-Key/1.0(ser_P(ecdh_shared_secret) || ser_32(k))
+
+ +with the same k used to derive Pk. + +The sender blinds the output as usual, using Ck as the blinding public key of the receiver, which is the same as paying to the confidential address made of Ck and of the scriptPubKey above. +The receiver, once it has detected the output, derives ck and unblinds the output by rewinding its range proof, recovering the asset, the value and the blinding factors. + +A sender MUST blind the output. +A receiver MUST also detect an output that is not blinded +An explicit output is valid on Elements, and it is spendable by the receiver without any unblinding. Detecting it costs nothing, since detection only looks at the scriptPubKey, and not detecting it would make the funds unrecoverable. +, and SHOULD warn that the amount and the asset of that payment are public. + +===Labels=== + +Labels are unchanged from BIP-352. For an integer m ≥ 0: + +
+  label_tweak_m = hash_BIP0352/Label(ser_256(b_scan) || ser_32(m))
+  B_m           = B_spend + label_tweak_m·G
+
+ +B0 is reserved for the change of the wallet and MUST NOT be published. + +A wallet detects a payment to a labelled address only if it knows the label, so the set of labels in use is part of the wallet backup. + +===Sending=== + +To pay a set of recipients: + +# Choose the inputs of the transaction. Every eligible input contributes its secret key ai; for a P2TR input whose public key has odd Y, ai is negated. +# Compute outpointL over all the inputs, then a, A, input_hash and, for each distinct Bscan among the recipients, ecdh_shared_secret. +# Group the recipients by Bscan. Within a group, derive one output per recipient entry with k = 0, 1, .... Paying the same address twice is allowed and produces two different outputs. +# Blind each output to Ck and finish the transaction as usual. + +The set of inputs MUST be final before the outputs are derived, because the output scripts commit to it. +A wallet that selects inputs to cover an amount therefore has to select the inputs first and derive the outputs after, and it MUST re-derive the outputs if the input set changes +This is the same constraint as in BIP-352 and the reason why [https://github.com/bitcoin/bips/blob/master/bip-0375.mediawiki BIP-375] adds silent payment fields to PSBT. Nothing prevents an equivalent PSET extension, which this document leaves to a future proposal: a wallet that builds and signs the whole transaction, which is the common case, does not need it. +. + +The fee output of the transaction, which has an empty scriptPubKey, is never a silent payment output. + +===Receiving=== + +A receiver detects the payments of a transaction from its tweak data and its outputs, without any other information about the sender. + +Given the tweak data of a transaction, the receiver computes ecdh_shared_secret = bscan · tweak_data and then, for k = 0, 1, ... up to Kmax: + +# Compute tk and Pk = Bspend + tk·G. +# If a taproot output of the transaction, not already matched, has x-only(Pk) as output key, it is a payment to the address of this wallet. +# Otherwise, for each taproot output not already matched, with output key O, compute label = O' − Pk for O' equal to lift_x(O) and to −lift_x(O). If label is label_tweakm·G for a known label m, it is a payment to the address labelled m, and the tweak of the output is tk + label_tweakm. +# If no output matched, stop. Otherwise remove the matched output, derive its blinding key ck, unblind it, increment k and repeat. + +The decision to continue MUST be based on whether an output matched cryptographically, not on whether the wallet considers it spendable or worth its dust threshold. + +The secret key spending an output found with tweak t is + +
+  d = (b_spend + t) mod n
+
+ +and the output is spent as a taproot key path spend with d, without applying the BIP-341 tweak. + +===Scanning with a Tweak Server=== + +A wallet is expected to obtain the tweak data from an index server implementing the "Tweak Server" stack of the [https://github.com/silent-payments/BIP0352-index-server-specification BIP-352 index server specification]. +The wallet does not share its keys with the server, and the server does not learn which transactions the wallet is interested in. + +====Server==== + +For every transaction that satisfies [[#Transaction eligibility|Transaction eligibility]], the server computes and stores its tweak data serP(input_hash · A), 33 bytes. +A server needs the outputs spent by a transaction to do so, which is why a wallet cannot compute this from the block alone. + +The server MUST index from the height at which taproot activated, which on Liquid is block 1,663,200 +Block 1aea43f7205bd02c03b081f3c6de4604756bc1a37ae2ca6e34b1936137756870, mined on 2022-01-16. No transaction before it can have a taproot output, so no transaction before it can pay to a silent payment address. +. + +The following endpoints are defined, as a profile of the index server specification: + +GET /getinfo + +
+{
+  "version": "1.0.0",
+  "network": "liquidv1" | "liquidtestnet" | "elementsregtest",
+  "block_height": 3984398,
+  "dust_limit": 0,
+  "filter_spent": "optional"
+}
+
+ +dust_limit MUST be 0. +Amounts on Elements are confidential, so a server cannot tell a dust output from any other one, and the dust filtering of the index server specification does not apply. + +GET /tweaks/:blockheight, with the optional query parameter filterSpent + +
+{
+  "height": 3984398,
+  "hash": "…",
+  "dust_limit": 0,
+  "filter_spent": 0,
+  "tweaks": ["03398173f560782d934ddf4f5a291c47fd0866d6e26a97a7407b810e1873e34777", …]
+}
+
+ +One tweak per silent payment eligible transaction of the block. +When filterSpent is set, the server omits the transactions all of whose taproot outputs have already been spent, which a wallet that is catching up after a long time can use to skip outputs it would only receive to see them spent +Spending is visible on Elements even though amounts are not, so this optimization applies unchanged. +. + +GET /filters/:blockheight + +
+{
+  "height": 3984398,
+  "hash": "…",
+  "filter_type": 0,
+  "filter": "…"
+}
+
+ +The BIP-158 basic filter of the block, as computed by Elements +Elements computes the basic filter as Bitcoin does: it contains the scriptPubKey of every output that is neither empty nor an OP_RETURN, and the scriptPubKey of every output spent by the block. Fee outputs have an empty scriptPubKey and are therefore not in the filter, and confidential outputs are, since a scriptPubKey is never confidential. +, keyed as in BIP-158 on the hash of the block. +A wallet MAY obtain the same filter from any other source, for instance from an Elements node run with -blockfilterindex=basic. + +A server MAY implement GET /compute-index/:blockheight as defined in the index server specification, which returns the txid of each eligible transaction along with its tweak. It is not needed by the algorithm below. + +====Client==== + +For each block from the wallet birthday to the tip: + +# GET /tweaks/:height. +# For each tweak, derive the '''candidate scripts''': the taproot script of P0, plus one for each known label m, of P0 + label_tweakm·G. +# Test all the candidate scripts of the block against the block filter. If none matches, the block contains no payment for this wallet: continue from the next height. +# Otherwise download the block. For each tweak whose candidate script is the scriptPubKey of an output of a transaction of the block, run the algorithm of [[#Receiving|Receiving]] on that transaction with that tweak, and record the outputs found. +# Record the height as scanned. + +A wallet MUST re-scan the blocks that a reorganization disconnected, and MUST drop the outputs it had found in them. + +A payment is detected only once it is in a block: the tweak of a transaction in the mempool is not served by a tweak server, and asking for it would tell the server which transactions the wallet is interested in. + +The candidate scripts only cover k = 0, but a transaction paying to this wallet always contains the k = 0 output, so a payment is never missed; the transaction is then scanned for all the values of k. +The tweaks do not have to be matched to a transaction by position: a tweak is matched to the transaction that contains one of its candidate scripts. + +A wallet SHOULD verify the tweaks it receives, by recomputing them from the block it has downloaded whenever it has the spent outputs it needs, and SHOULD compare the filter against the block it downloaded. +A server that omits a tweak makes the wallet miss a payment; it cannot make the wallet accept an output that is not its own, because every output is verified against the keys of the wallet before being recorded. + +Since the tweak data of a block is 33 bytes per eligible transaction, and Liquid produces one block per minute, a wallet that scans every block downloads at most 33 · t · 1440 bytes per day, where t is the number of eligible transactions per block: with the 6.5 transactions per block measured on Liquid around height 3,984,390, and in the worst case in which all of them are eligible, that is about 310 kB per day, plus one filter per block. + +===Representation of a received output=== + +A received output is an ordinary confidential taproot output, described by: + +* its scriptPubKey, +* its blinding secret key ck, needed to unblind it, +* its tweak t, needed to spend it, together with bspend. + +A wallet MUST persist the outputs it discovers, or it has to scan again from the birthday. + +An [https://github.com/ElementsProject/ELIPs/blob/main/elip-0150.mediawiki ELIP-150] CT descriptor cannot describe such an output today: the descriptor ct(ck,eltr(Pk)) would apply the BIP-341 tweak to Pk and produce a different script. +A rawtr() equivalent for Elements, that is a key spend only taproot descriptor that uses its key as the output key, would make ct(ck,elrawtr(Pk)) the natural representation of a received output; adding it is left to a separate proposal. + +==Rationale== + +'''Why derive the blinding key from the shared secret?''' +The alternative is to publish a third key in the address and blind to it, or to a tweak of it. +It would not hide anything more from the sender, which picks the ephemeral key used to blind the output and therefore knows the amount it is sending in any case, and it would make the address longer and incompatible with the BIP-352 encoding. +It would, however, separate the ability to detect a payment from the ability to unblind it, which is only useful in a model where the scan key is shared with a third party. This document recommends against that model, so the simpler derivation was chosen. + +'''Why a separate tag instead of reusing tk?''' +Using tk as the blinding key would tie two unrelated secrets together: anybody learning the blinding key of an output, for instance because the receiver revealed it to prove a payment +Revealing the blinding key of an output is the usual way to prove to a third party what was received, since it lets them unblind that single output. +, would also learn the tweak, and with the spend public key they could compute the output key. A separate tagged hash keeps the two independent. + +'''Why a different human readable part?''' +A silent payment address does not commit to a chain in any other way. +Paying a Bitcoin address on Liquid, or the reverse, would create an output that the receiver cannot detect and that the sender cannot recover. +The prefixes are the Elements address prefixes, lq, tlq and el, with sp appended. + +'''Why are peg-ins not eligible?''' +The output a peg-in spends lives on the Bitcoin chain: the scanning wallet would need Bitcoin data to know which of the rules of [[#Input eligibility|Input eligibility]] applies, which defeats the purpose of a light client. +Their outpoints are still part of outpointL, because they are part of the transaction and both sides can read them from it. + +'''Why clear the flags in the outpoint?''' +The alternative, hashing the outpoint as it appears in the serialized transaction, would make the value depend on whether the input carries an issuance, which is unrelated to the payment, and would be easy to get wrong: a wallet naturally works with parsed transactions, where the flags have already been stripped. +The same choice makes the derivation identical to BIP-352 for the transactions that have no peg-in and no issuance. + +'''Why the Tweak Server model?''' +As explained in [[#Drawbacks|Drawbacks]], on Elements the scan key unblinds as well as detects, so the models of the index server specification in which the scan key is shared with a service would give that service every amount and every asset of the wallet. The Tweak Server model does not share any key. + +'''Why only taproot outputs?''' +As in BIP-352. A silent payment output is a fresh key that has never been published, so there is nothing to gain from a script, and restricting to one output type keeps the scanning work of the receiver proportional to the taproot outputs of a block. + +'''Interaction with discounted fees.''' +A silent payment output is an ordinary confidential taproot output, so [https://github.com/ElementsProject/ELIPs/blob/main/elip-0200.mediawiki ELIP-200] discounted fees apply to it unchanged. + +==Backwards compatibility== + +This document does not change consensus rules and does not require any change to Elements: it describes how existing outputs are derived and found. +A silent payment transaction is an ordinary transaction, and it is indistinguishable from any other transaction with taproot outputs. + +A wallet that does not implement this document cannot pay to a silent payment address: the address is a new format, and parsing it fails, so funds cannot be sent to a script that nobody can spend. + +A wallet that implements this document can receive from any sender that does, and can spend the outputs it receives with any signer able to produce a taproot key path signature for a given secret key. + +Scanning requires the tweak data of every transaction, which no Elements node exposes today. +Until an index server is available, a wallet can compute the tweak data itself from the full blocks and from the outputs those blocks spend, which is only practical for a node or for a wallet with a short scanning range. + +==Test vectors== + +The BIP-352 [https://github.com/bitcoin/bips/blob/master/bip-0352/send_and_receive_test_vectors.json send and receive test vectors] apply unchanged to everything that is not blinding: the address encoding, the input eligibility, the tweak data, the output derivation and the labels. +An implementation SHOULD run them, encoding the addresses with the Bitcoin human readable part for the purpose of the comparison. + +The vectors below cover what is specific to this document. + +===Keys and addresses=== + +All of them are derived from the BIP-39 mnemonic + +
+abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
+
+ +with an empty passphrase, account 0. + +* Liquid, m/352'/1776'/0'/{1,0}'/0 +** scan secret key: e4284c50d48a373098d44638e3c9d6f6eca770aaa54562eb8cdc0cff8cd3b550 +** spend secret key: ca29645fc7167f2810c909fa52bcd177b82aa3bfbe999ef579a6607926dc7f93 +** address: lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcqc9sw44xujvc7ejg4w5lt4zxpvc3fk446qdcrrmsgg9cnax8ujj7spnvlwq +** change address, label 0: lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcqmujrcapnql6np3dpdjwswdwdcs0h3rs637agjsaeac6thmlcfpyy2eg0eh +** address labelled 1: lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcq3789yp3lv5nvyyvhucjttyanypmzdg7qpadykuz7xd9zenvh6dwylcw0sj +* Liquid testnet, m/352'/1'/0'/{1,0}'/0 +** scan secret key: 38658693c017c46fd6b8bb94b8766c123cd5baf6026338305b6f59f82b36f9c0 +** spend secret key: 9fd37137e760930c7208fa905e991c78c522689d237a220b2820c3ddb4c745a8 +** address: tlqsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq5rxzzunfck6d45va2jcqxk429agt3e4klf3vzmcgp3zqthryhhqgu2k23l +** change address, label 0: tlqsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq4y0sngs4cjaw6cxuy7p6rlslx6h54lmppe9kx4r2ylf63tkem26qqt6u7t +** address labelled 1: tlqsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzqknp3yhxtxhcvkczk4vfvdx0z8pfff3n78nelgppy4l2sss3famcgq974m5 +* Elements regtest, m/352'/1'/0'/{1,0}'/0, same keys as testnet +** address: elsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq5rxzzunfck6d45va2jcqxk429agt3e4klf3vzmcgp3zqthryhhqgd4lgkm +** change address, label 0: elsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq4y0sngs4cjaw6cxuy7p6rlslx6h54lmppe9kx4r2ylf63tkem26q35n7e0 +** address labelled 1: elsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzqknp3yhxtxhcvkczk4vfvdx0z8pfff3n78nelgppy4l2sss3famcg36hhus + +===A payment=== + +A transaction on Liquid spending one P2WPKH input and paying to the Liquid address above. + +Given: + +* input outpoint: f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16:0 +* input secret key: 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b +* input public key: 02552c630b64b54bf50210c9e253d38bd4949c72e22873500f6285c2bede312a84 +* spent scriptPubKey: 0014db3f00d429f2715383cc594258ec11d6de526697 +* recipient: lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcqc9sw44xujvc7ejg4w5lt4zxpvc3fk446qdcrrmsgg9cnax8ujj7spnvlwq + +Expected: + +* tweak data, input_hash · A: 03398173f560782d934ddf4f5a291c47fd0866d6e26a97a7407b810e1873e34777 +* output tweak, t0: a4c41218034595e818fce768dded0e52409abfe23501fb94e6787c32debad2bd +* output scriptPubKey: 512092a9d712661ac4c1ebd5e3953a5af8a60037fb69e7c559ecacbce41a050262d7 +* blinding secret key, c0: f01c7948615e9ead2e612cf325857a605346bdd240896263ec888d2d4b7d1e27 +* blinding public key, C0: 0368c38c6542751c6c8778c31f60043d3dd5efa3dd03ccc1dff7b7c4ab6ae3afb8 +* confidential address of the output: lq1pqd5v8rr9gf63cmy80rp37cqy857atmarm5pueswl77muf2m2uwhm3y4f6ufxvxkyc84atcu48fd03fsqxlakne79t8k2e08yrgzsyckhf2sstv683ytv +* secret key spending the output, bspend + t0: 6eed7677ca5c151029c5f16330a9dfcb3e1686bb4452fa4ea04c7e1f3561110f + +==Reference implementation== + +[https://github.com/Blockstream/lwk Liquid Wallet Kit], in the silent_payments module of the lwk_wollet crate. +It implements the address encoding, the key derivation, the input eligibility rules, the sender and receiver derivations and the blinding key derivation, and it is checked against both the BIP-352 test vectors and the vectors of this document. + +==Footnotes== + + + +==Acknowledgments== + +Silent payments were designed by Ruben Somsen and Josie Baker in BIP-352; this document only adapts their work to confidential transactions. diff --git a/lwk_test_util/src/lib.rs b/lwk_test_util/src/lib.rs index b176a2fcf..478164587 100644 --- a/lwk_test_util/src/lib.rs +++ b/lwk_test_util/src/lib.rs @@ -136,6 +136,11 @@ pub fn serve_http_response( format!("http://{addr}") } +/// The BIP352 send and receive test vectors, as published in the BIPs repository +pub fn bip352_test_vectors() -> &'static str { + include_str!("../test_data/bip0352_send_and_receive_test_vectors.json") +} + pub fn liquid_block_1() -> Block { let raw = include_bytes!( "../test_data/afafbbdfc52a45e51a3b634f391f952f6bdfd14ef74b34925954b4e20d0ad639.raw" diff --git a/lwk_test_util/test_data/bip0352_send_and_receive_test_vectors.json b/lwk_test_util/test_data/bip0352_send_and_receive_test_vectors.json new file mode 100644 index 000000000..3a189757d --- /dev/null +++ b/lwk_test_util/test_data/bip0352_send_and_receive_test_vectors.json @@ -0,0 +1,5729 @@ +[ + { + "comment": "Simple send: two inputs", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac" + } + }, + "private_key": "93f5ed907ad5b2bdbbdcb5d9116ebc0a4e1f92f910d5260237fa45a9408aad16" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1" + ] + ], + "shared_secrets": [ + "028158aff7d61ea66b2fa7f555bc3c5937d1debbde16423d630f9aa7943e14d80d" + ], + "input_private_key_sum": "7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac" + } + } + } + ], + "outputs": [ + "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "f438b40179a3c4262de12986c0e6cce0634007cdc79c1dcd3e20b9ebc2e7eef6", + "pub_key": "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1", + "signature": "74f85b856337fbe837643b86f462118159f93ac4acc2671522f27e8f67b079959195ccc7a5dbee396d2909f5d680d6e30cda7359aa2755822509b70d6b0687a1" + } + ], + "tweak": "024ac253c216532e961988e2a8ce266a447c894c781e52ef6cee902361db960004", + "shared_secret": "028158aff7d61ea66b2fa7f555bc3c5937d1debbde16423d630f9aa7943e14d80d", + "input_pub_key_sum": "032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee" + } + } + ] + }, + { + "comment": "Simple send: two inputs, order reversed", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac" + } + }, + "private_key": "93f5ed907ad5b2bdbbdcb5d9116ebc0a4e1f92f910d5260237fa45a9408aad16" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1" + ] + ], + "shared_secrets": [ + "028158aff7d61ea66b2fa7f555bc3c5937d1debbde16423d630f9aa7943e14d80d" + ], + "input_private_key_sum": "7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac" + } + } + } + ], + "outputs": [ + "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "f438b40179a3c4262de12986c0e6cce0634007cdc79c1dcd3e20b9ebc2e7eef6", + "pub_key": "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1", + "signature": "74f85b856337fbe837643b86f462118159f93ac4acc2671522f27e8f67b079959195ccc7a5dbee396d2909f5d680d6e30cda7359aa2755822509b70d6b0687a1" + } + ], + "tweak": "024ac253c216532e961988e2a8ce266a447c894c781e52ef6cee902361db960004", + "shared_secret": "028158aff7d61ea66b2fa7f555bc3c5937d1debbde16423d630f9aa7943e14d80d", + "input_pub_key_sum": "032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee" + } + } + ] + }, + { + "comment": "Simple send: two inputs from the same transaction", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 3, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 7, + "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac" + } + }, + "private_key": "93f5ed907ad5b2bdbbdcb5d9116ebc0a4e1f92f910d5260237fa45a9408aad16" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6" + ] + ], + "shared_secrets": [ + "03aa707f7b5e94b448abd28aa217e3d7a7cc6bb07f1a8d07be4de91bf7b1417469" + ], + "input_private_key_sum": "7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 3, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 7, + "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac" + } + } + } + ], + "outputs": [ + "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "4851455bfbe1ab4f80156570aa45063201aa5c9e1b1dcd29f0f8c33d10bf77ae", + "pub_key": "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6", + "signature": "10332eea808b6a13f70059a8a73195808db782012907f5ba32b6eae66a2f66b4f65147e2b968a1678c5f73d57d5d195dbaf667b606ff80c8490eac1f3b710657" + } + ], + "tweak": "03aeea547819c08413974e2ab2b12212e007166bb2058f88b009e082b9b4914a58", + "shared_secret": "03aa707f7b5e94b448abd28aa217e3d7a7cc6bb07f1a8d07be4de91bf7b1417469", + "input_pub_key_sum": "032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee" + } + } + ] + }, + { + "comment": "Simple send: two inputs from the same transaction, order reversed", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 7, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 3, + "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac" + } + }, + "private_key": "93f5ed907ad5b2bdbbdcb5d9116ebc0a4e1f92f910d5260237fa45a9408aad16" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38" + ] + ], + "shared_secrets": [ + "03054f5c84b07182ba2a2e10a35e088778f95c04f059f4574b024c372eb8ce5468" + ], + "input_private_key_sum": "7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 7, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 3, + "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac" + } + } + } + ], + "outputs": [ + "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "ab0c9b87181bf527879f48db9f14a02233619b986f8e8f2d5d408ce68a709f51", + "pub_key": "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38", + "signature": "398a9790865791a9db41a8015afad3a47d60fec5086c50557806a49a1bc038808632b8fe679a7bb65fc6b455be994502eed849f1da3729cd948fc7be73d67295" + } + ], + "tweak": "024cad5180a093d3af0f49f586bdf37f890920178e68e80561ed53351d0fa499ad", + "shared_secret": "03054f5c84b07182ba2a2e10a35e088778f95c04f059f4574b024c372eb8ce5468", + "input_pub_key_sum": "032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee" + } + } + ] + }, + { + "comment": "Outpoint ordering byte-lexicographically vs. vout-integer", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 1, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 256, + "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac" + } + }, + "private_key": "93f5ed907ad5b2bdbbdcb5d9116ebc0a4e1f92f910d5260237fa45a9408aad16" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "a85ef8701394b517a4b35217c4bd37ac01ebeed4b008f8d0879f9e09ba95319c" + ] + ], + "shared_secrets": [ + "02cb25a6e7c9b7c6d550e0413da63834678465b5e80853a51d0335d318296ac182" + ], + "input_private_key_sum": "7ed265a6dac7aba8508a32d6d6b84c7f1dbd0a0941dd01088d69e8d556345f86", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 1, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 256, + "scriptSig": "48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac" + } + } + } + ], + "outputs": [ + "a85ef8701394b517a4b35217c4bd37ac01ebeed4b008f8d0879f9e09ba95319c" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "c8ac0292997b5bca98b3ebd99a57e253071137550f270452cd3df8a3e2266d36", + "pub_key": "a85ef8701394b517a4b35217c4bd37ac01ebeed4b008f8d0879f9e09ba95319c", + "signature": "c036ee38bfe46aba03234339ae7219b31b824b52ef9d5ce05810a0d6f62330dedc2b55652578aa5bdabf930fae941acd839d5a66f8fce7caa9710ccb446bddd1" + } + ], + "tweak": "031f9a80d0938cf980b51f7cc4fad713d49037f430646dff129c0570d75a40d8f0", + "shared_secret": "02cb25a6e7c9b7c6d550e0413da63834678465b5e80853a51d0335d318296ac182", + "input_pub_key_sum": "032562c1ab2d6bd45d7ca4d78f569999e5333dffd3ac5263924fd00d00dedc4bee" + } + } + ] + }, + { + "comment": "Single recipient: multiple UTXOs from the same public key", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566" + ] + ], + "shared_secrets": [ + "02f6b40ff17f4010fe732ac4b0f2f211281aa09c9a5fb41f1c151ec2606fee9ec2" + ], + "input_private_key_sum": "d5b8f02cbfe3f1d5295af9fb8a9320e859e9cb07115856486ab1a4e4fb89a621", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + } + ], + "outputs": [ + "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "f032695e2636619efa523fffaa9ef93c8802299181fd0461913c1b8daf9784cd", + "pub_key": "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566", + "signature": "f238386c5d5e5444f8d2c75aabbcb28c346f208c76f60823f5de3b67b79e0ec72ea5de2d7caec314e0971d3454f122dda342b3eede01b3857e83654e36b25f76" + } + ], + "tweak": "0319949463fc6a2368d999a2a6a2bcb2dbf64a2ac6e00b3ba5659780c860a6d9e0", + "shared_secret": "02f6b40ff17f4010fe732ac4b0f2f211281aa09c9a5fb41f1c151ec2606fee9ec2", + "input_pub_key_sum": "03e40664e222ba71e29b80efc907fa22a3c6c64f45e89dbb8511dc7a3712b0a186" + } + } + ] + }, + { + "comment": "Single recipient: taproot only inputs with even y-values", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b", + "prevout": { + "scriptPubKey": { + "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140bd1e708f92dbeaf24a6b8dd22e59c6274355424d62baea976b449e220fd75b13578e262ab11b7aa58e037f0c6b0519b66803b7d9decaa1906dedebfb531c56c1", + "prevout": { + "scriptPubKey": { + "hex": "5120782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + } + }, + "private_key": "fc8716a97a48ba9a05a98ae47b5cd201a25a7fd5d8b73c203c5f7b6b6b3b6ad7" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb" + ] + ], + "shared_secrets": [ + "02de9719785c6d09f71571dadf44bca59edba2af3e689c65cbc3bb5a4a387732ef" + ], + "input_private_key_sum": "e7638ebfda3ab3849a5707e240a6627671f7f6e609bf172691cf1e9780e51d47", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "02782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b", + "prevout": { + "scriptPubKey": { + "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140bd1e708f92dbeaf24a6b8dd22e59c6274355424d62baea976b449e220fd75b13578e262ab11b7aa58e037f0c6b0519b66803b7d9decaa1906dedebfb531c56c1", + "prevout": { + "scriptPubKey": { + "hex": "5120782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + } + } + } + ], + "outputs": [ + "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "3fb9ce5ce1746ced103c8ed254e81f6690764637ddbc876ec1f9b3ddab776b03", + "pub_key": "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb", + "signature": "c5acd25a8f021a4192f93bc34403fd8b76484613466336fb259c72d04c169824f2690ca34e96cee86b69f376c8377003268fda56feeb1b873e5783d7e19bcca5" + } + ], + "tweak": "02dc59cc8e8873b65c1dd5c416d4fbeb647372c329bd84a70c05b310e222e2c183", + "shared_secret": "02de9719785c6d09f71571dadf44bca59edba2af3e689c65cbc3bb5a4a387732ef", + "input_pub_key_sum": "038180a2125f9d6dd116e1a6139be4d72fd5057dab6aaabaa5654817c11baeb3ba" + } + } + ] + }, + { + "comment": "Single recipient: taproot only with mixed even/odd y-values", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b", + "prevout": { + "scriptPubKey": { + "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "", + "txinwitness": "01400a4d0dca6293f40499394d7eefe14a1de11e0e3454f51de2e802592abf5ee549042a1b1a8fb2e149ee9dd3f086c1b69b2f182565ab6ecf599b1ec9ebadfda6c5", + "prevout": { + "scriptPubKey": { + "hex": "51208c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4" + } + }, + "private_key": "1d37787c2b7116ee983e9f9c13269df29091b391c04db94239e0d2bc2182c3bf" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1" + ] + ], + "shared_secrets": [ + "030e7f5ca4bf109fc35c8c2d878f756c891ac04c456cc5f0b05fcec4d3b2b1beb2" + ], + "input_private_key_sum": "cda4ff9a3480e1fbfc6edd61b222f280f9baa0652002c1ffdb612efcc45d2ff2", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "028c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b", + "prevout": { + "scriptPubKey": { + "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "", + "txinwitness": "01400a4d0dca6293f40499394d7eefe14a1de11e0e3454f51de2e802592abf5ee549042a1b1a8fb2e149ee9dd3f086c1b69b2f182565ab6ecf599b1ec9ebadfda6c5", + "prevout": { + "scriptPubKey": { + "hex": "51208c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4" + } + } + } + ], + "outputs": [ + "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "f5382508609771068ed079b24e1f72e4a17ee6d1c979066bf1d4e2a5676f09d4", + "pub_key": "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1", + "signature": "ff65833b8fd1ed3ef9d0443b4f702b45a3f2dd457ba247687e8207745c3be9d2bdad0ab3f07118f8b2efc6a04b95f7b3e218daf8a64137ec91bd2fc67fc137a5" + } + ], + "tweak": "03b990f5b1d90ea8fd4bdd5c856a9dfe17035d196958062e2c6cb4c99e413f3548", + "shared_secret": "030e7f5ca4bf109fc35c8c2d878f756c891ac04c456cc5f0b05fcec4d3b2b1beb2", + "input_pub_key_sum": "020f0ab50f420ab1249bc2a21659c607f2873400853035aad0ca6d0ded04d62623" + } + } + ] + }, + { + "comment": "Single recipient: taproot input with even y-value and non-taproot input", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b", + "prevout": { + "scriptPubKey": { + "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "463044021f24e010c6e475814740ba24c8cf9362c4db1276b7f46a7b1e63473159a80ec30221008198e8ece7b7f88e6c6cc6bb8c86f9f00b7458222a8c91addf6e1577bcf7697e2103e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9148cbc7dfe44f1579bff3340bbef1eddeaeb1fc97788ac" + } + }, + "private_key": "8d4751f6e8a3586880fb66c19ae277969bd5aa06f61c4ee2f1e2486efdf666d3" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0" + ] + ], + "shared_secrets": [ + "021cd92ff153e638d0a97bcd11fafc81c321b111f5ba1efff593371b7b688efdd3" + ], + "input_private_key_sum": "7823ca0d4895515315a8e3bf602c080b6b732117272429e94751eb9b13a01943", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b", + "prevout": { + "scriptPubKey": { + "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "463044021f24e010c6e475814740ba24c8cf9362c4db1276b7f46a7b1e63473159a80ec30221008198e8ece7b7f88e6c6cc6bb8c86f9f00b7458222a8c91addf6e1577bcf7697e2103e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9148cbc7dfe44f1579bff3340bbef1eddeaeb1fc97788ac" + } + } + } + ], + "outputs": [ + "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "b40017865c79b1fcbed68896791be93186d08f47e416b289b8c063777e14e8df", + "pub_key": "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0", + "signature": "d1edeea28cf1033bcb3d89376cabaaaa2886cbd8fda112b5c61cc90a4e7f1878bdd62180b07d1dfc8ffee1863c525a0c7b5bcd413183282cfda756cb65787266" + } + ], + "tweak": "0233c2a447b8b244e4ffcfb59fe365eaa3bb22288b31e2113b9998861f40d4d6da", + "shared_secret": "021cd92ff153e638d0a97bcd11fafc81c321b111f5ba1efff593371b7b688efdd3", + "input_pub_key_sum": "031ecda9c64faaa6cd57c9f3d7c62bcfc0763c2627ed8dc0e2c3018e9ff37a0bf0" + } + } + ] + }, + { + "comment": "Single recipient: taproot input with odd y-value and non-taproot input", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "01400a4d0dca6293f40499394d7eefe14a1de11e0e3454f51de2e802592abf5ee549042a1b1a8fb2e149ee9dd3f086c1b69b2f182565ab6ecf599b1ec9ebadfda6c5", + "prevout": { + "scriptPubKey": { + "hex": "51208c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4" + } + }, + "private_key": "1d37787c2b7116ee983e9f9c13269df29091b391c04db94239e0d2bc2182c3bf" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "463044021f24e010c6e475814740ba24c8cf9362c4db1276b7f46a7b1e63473159a80ec30221008198e8ece7b7f88e6c6cc6bb8c86f9f00b7458222a8c91addf6e1577bcf7697e2103e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9148cbc7dfe44f1579bff3340bbef1eddeaeb1fc97788ac" + } + }, + "private_key": "8d4751f6e8a3586880fb66c19ae277969bd5aa06f61c4ee2f1e2486efdf666d3" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a" + ] + ], + "shared_secrets": [ + "03d9437eb3676cf5cc00feebe68bc44c4567332e4b89788dec9eceb3779054442b" + ], + "input_private_key_sum": "700fd97abd324179e8bcc72587bbd9a40b43f67535ce95a0b80175b2dc73a314", + "input_pub_keys": [ + "028c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4", + "03e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "01400a4d0dca6293f40499394d7eefe14a1de11e0e3454f51de2e802592abf5ee549042a1b1a8fb2e149ee9dd3f086c1b69b2f182565ab6ecf599b1ec9ebadfda6c5", + "prevout": { + "scriptPubKey": { + "hex": "51208c8d23d4764feffcd5e72e380802540fa0f88e3d62ad5e0b47955f74d7b283c4" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "463044021f24e010c6e475814740ba24c8cf9362c4db1276b7f46a7b1e63473159a80ec30221008198e8ece7b7f88e6c6cc6bb8c86f9f00b7458222a8c91addf6e1577bcf7697e2103e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9148cbc7dfe44f1579bff3340bbef1eddeaeb1fc97788ac" + } + } + } + ], + "outputs": [ + "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "a2f9dd05d1d398347c885d9c61a64d18a264de6d49cea4326bafc2791d627fa7", + "pub_key": "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a", + "signature": "96038ad233d8befe342573a6e54828d863471fb2afbad575cc65271a2a649480ea14912b6abbd3fbf92efc1928c036f6e3eef927105af4ec1dd57cb909f360b8" + } + ], + "tweak": "02d4e4f2c4cdb71c9c39a700a9ee1a0fc05b98362a441183f5770af7d6e2b3038c", + "shared_secret": "03d9437eb3676cf5cc00feebe68bc44c4567332e4b89788dec9eceb3779054442b", + "input_pub_key_sum": "03bc118b1c8178915b716d6137633722c71adfe721551ec7b3938054691de6a2b9" + } + } + ] + }, + { + "comment": "Multiple outputs: multiple outputs, same recipient", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + }, + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ] + ], + "shared_secrets": [ + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413" + ], + "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "d97e442d110c0bdd31161a7bb6e7862e038d02a09b1484dfbb463f2e0f7c9230", + "pub_key": "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "signature": "29bd25d0f808d7fcd2aa6d5ed206053899198397506c301b218a9e47a3d7070af03e903ff718978d50d1b6b9af8cc0e313d84eda5d5b1e8e85e5516d630bbeb9" + }, + { + "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a", + "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912" + } + ], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + } + ] + }, + { + "comment": "Multiple outputs: multiple outputs, multiple recipients", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + }, + { + "address": "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn", + "scan_pub_key": "02062d49ffc02787d586c608dfbec184aa91a6597d97b463ea5c6babd9d17a95a3", + "spend_pub_key": "0381eb9a9a9ec739d527c1631b31b421566f5c2a47b4ab5b1f6a686dfb68eab716" + }, + { + "address": "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn", + "scan_pub_key": "02062d49ffc02787d586c608dfbec184aa91a6597d97b463ea5c6babd9d17a95a3", + "spend_pub_key": "0381eb9a9a9ec739d527c1631b31b421566f5c2a47b4ab5b1f6a686dfb68eab716" + } + ] + }, + "expected": { + "outputs": [ + [ + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ] + ], + "shared_secrets": [ + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "03dd5fd04d3c8863be750a1bd7474df06161461d38d3ce1397a5c78cee112cdcd2", + "03dd5fd04d3c8863be750a1bd7474df06161461d38d3ce1397a5c78cee112cdcd2" + ], + "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ], + "key_material": { + "spend_priv_key": "9902c3c56e84002a7cd410113a9ab21d142be7f53cf5200720bb01314c5eb920", + "scan_priv_key": "060b751d7892149006ed7b98606955a29fe284a1e900070c0971f5fb93dbf422" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn" + ], + "outputs": [ + { + "priv_key_tweak": "72cd082cccb633bf85240a83494b32dc943a4d05647a6686d23ad4ca59c0ebe4", + "pub_key": "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", + "signature": "38745f3d9f5eef0b1cfb17ca314efa8c521efab28a23aa20ec5e3abb561d42804d539906dce60c4ee7977966184e6f2cab1faa0e5377ceb7148ec5218b4e7878" + }, + { + "priv_key_tweak": "2f17ea873a0047fc01ba8010fef0969e76d0e4283f600d48f735098b1fee6eb9", + "pub_key": "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "signature": "c26f4e3cf371b90b840f48ea0e761b5ec31883ed55719f9ef06a90e282d85f565790ab780a3f491bc2668cc64e944dca849d1022a878cdadb8d168b8da4a6da3" + } + ], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "03dd5fd04d3c8863be750a1bd7474df06161461d38d3ce1397a5c78cee112cdcd2", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + } + ] + }, + { + "comment": "Receiving with labels: label with even parity", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjex54dmqmmv6rw353tsuqhs99ydvadxzrsy9nuvk74epvee55drs734pqq", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "0259352add837b6686e8d22b87017814a46b3ad308702167c65bd5c8599cd28d1c" + } + ] + }, + "expected": { + "outputs": [ + [ + "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a" + ] + ], + "shared_secrets": [ + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413" + ], + "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [ + 2, + 3, + 1001337 + ] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjex54dmqmmv6rw353tsuqhs99ydvadxzrsy9nuvk74epvee55drs734pqq", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqsg59z2rppn4qlkx0yz9sdltmjv3j8zgcqadjn4ug98m3t6plujsq9qvu5n", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgq7c2zfthc6x3a5yecwc52nxa0kfd20xuz08zyrjpfw4l2j257yq6qgnkdh5" + ], + "outputs": [ + { + "priv_key_tweak": "51d4e9d0d482b5700109b4b2e16ff508269b03d800192a043d61dca4a0a72a52", + "pub_key": "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a", + "signature": "c30fa63bad6f0a317f39a773a5cbf0b0f8193c71dfebba05ee6ae4ed28e3775e6e04c3ea70a83703bb888122855dc894cab61692e7fd10c9b3494d479a60785e" + } + ], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + } + ] + }, + { + "comment": "Receiving with labels: label with odd parity", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqsg59z2rppn4qlkx0yz9sdltmjv3j8zgcqadjn4ug98m3t6plujsq9qvu5n", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "0208a144a18433a83f633c822c1bf5ee4c8c8e24601d6ca75e20a7dc57a0ff9280" + } + ] + }, + "expected": { + "outputs": [ + [ + "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c" + ] + ], + "shared_secrets": [ + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413" + ], + "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [ + 2, + 3, + 1001337 + ] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjex54dmqmmv6rw353tsuqhs99ydvadxzrsy9nuvk74epvee55drs734pqq", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqsg59z2rppn4qlkx0yz9sdltmjv3j8zgcqadjn4ug98m3t6plujsq9qvu5n", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgq7c2zfthc6x3a5yecwc52nxa0kfd20xuz08zyrjpfw4l2j257yq6qgnkdh5" + ], + "outputs": [ + { + "priv_key_tweak": "6024ae214876356b8d917716e7707d267ae16a0fdb07de2a786b74a7bbcddead", + "pub_key": "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c", + "signature": "a86d554d0d6b7aa0907155f7e0b47f0182752472fffaeddd68da90e99b9402f166fd9b33039c302c7115098d971c1399e67c19e9e4de180b10ea0b9d6f0db832" + } + ], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + } + ] + }, + { + "comment": "Receiving with labels: large label integer", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgq7c2zfthc6x3a5yecwc52nxa0kfd20xuz08zyrjpfw4l2j257yq6qgnkdh5", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "03d85092bbe3468f684ce1d8a2a66ebec96a9e6e09e7110720a5d5faa4aa7880d0" + } + ] + }, + "expected": { + "outputs": [ + [ + "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951" + ] + ], + "shared_secrets": [ + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413" + ], + "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [ + 2, + 3, + 1001337 + ] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjex54dmqmmv6rw353tsuqhs99ydvadxzrsy9nuvk74epvee55drs734pqq", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqsg59z2rppn4qlkx0yz9sdltmjv3j8zgcqadjn4ug98m3t6plujsq9qvu5n", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgq7c2zfthc6x3a5yecwc52nxa0kfd20xuz08zyrjpfw4l2j257yq6qgnkdh5" + ], + "outputs": [ + { + "priv_key_tweak": "e336b92330c33030285ce42e4115ad92d5197913c88e06b9072b4a9b47c664a2", + "pub_key": "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951", + "signature": "c9e80dd3bdd25ca2d352ce77510f1aed37ba3509dc8cc0677f2d7c2dd04090707950ce9dd6c83d2a428063063aff5c04f1744e334f661f2fc01b4ef80b50f739" + } + ], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + } + ] + }, + { + "comment": "Multiple outputs with labels: un-labeled and labeled address; same recipient", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "03a6739499dc667d308baefea4de0c4a85cc72aece181bc05712d3919662610ff1" + }, + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ], + [ + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" + ] + ], + "shared_secrets": [ + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413" + ], + "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [ + 1 + ] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj" + ], + "outputs": [ + { + "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914", + "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75" + }, + { + "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a", + "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912" + } + ], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + } + ] + }, + { + "comment": "Multiple outputs with labels: multiple outputs for labeled address; same recipient", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "03a6739499dc667d308baefea4de0c4a85cc72aece181bc05712d3919662610ff1" + }, + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "03a6739499dc667d308baefea4de0c4a85cc72aece181bc05712d3919662610ff1" + } + ] + }, + "expected": { + "outputs": [ + [ + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c" + ] + ], + "shared_secrets": [ + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413" + ], + "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [ + 1 + ] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj" + ], + "outputs": [ + { + "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914", + "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75" + }, + { + "priv_key_tweak": "9d5fd3b91cac9ddfea6fc2e6f9386f680e6cee623cda02f53706306c081de87f", + "pub_key": "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "signature": "db0dfacc98b6a6fcc67cc4631f080b1ca38c60d8c397f2f19843f8f95ec91594b24e47c5bd39480a861c1209f7e3145c440371f9191fb96e324690101eac8e8e" + } + ], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + } + ] + }, + { + "comment": "Multiple outputs with labels: un-labeled, labeled, and multiple outputs for labeled address; same recipients", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + }, + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "03a6739499dc667d308baefea4de0c4a85cc72aece181bc05712d3919662610ff1" + }, + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "0244baa5cf5db444a9e922832ff2c88716b566a85d62e8235aebd91884d4f64942" + }, + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "0244baa5cf5db444a9e922832ff2c88716b566a85d62e8235aebd91884d4f64942" + } + ] + }, + "expected": { + "outputs": [ + [ + "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa" + ], + [ + "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa" + ], + [ + "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701" + ], + [ + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b" + ], + [ + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b" + ], + [ + "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", + "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa" + ], + [ + "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b" + ], + [ + "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b" + ], + [ + "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed", + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b" + ], + [ + "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ], + [ + "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e", + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" + ], + [ + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b" + ] + ], + "shared_secrets": [ + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413" + ], + "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [ + 1, + 1337 + ] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5" + ], + "outputs": [ + { + "priv_key_tweak": "4e3352fbe0505c25e718d96007c259ef08db34f8c844e4ff742d9855ff03805a", + "pub_key": "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "signature": "6eeae1ea9eb826e3d0e812f65937100e0836ea188c04f36fabc4981eda29de8d3d3529390a0a8b3d830f7bca4f5eae5994b9788ddaf05ad259ffe26d86144b4b" + }, + { + "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914", + "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75" + }, + { + "priv_key_tweak": "bf709f98d4418f8a67e738154ae48818dad44689cd37fbc070891a396dd1c633", + "pub_key": "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", + "signature": "42a19fd8a63dde1824966a95d65a28203e631e49bf96ca5dae1b390e7a0ace2cc8709c9b0c5715047032f57f536a3c80273cbecf4c05be0b5456c183fa122c06" + }, + { + "priv_key_tweak": "736f05e4e3072c3b8656bedef2e9bf54cbcaa2b6fe5320d3e86f5b96874dda71", + "pub_key": "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "signature": "2e61bb3d79418ecf55f68847cf121bfc12d397b39d1da8643246b2f0a9b96c3daa4bfe9651beb5c9ce20e1f29282c4566400a4b45ee6657ec3b18fdc554da0b4" + } + ], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + } + ] + }, + { + "comment": "Single recipient: use silent payments for sender change", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + }, + { + "address": "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqlv6saelkk5snl4wfutyxrchpzzwm8rjp3z6q7apna59z9huq4x754e5atr", + "scan_pub_key": "03b4cc0b090b6f49a684558852db60ee5eb1c5f74352839c3d18a8fc04ef7354e0", + "spend_pub_key": "03ecd43b9fdad484ff57278b21878b844276ce390622d03dd0cfb4288b7e02a6f5" + } + ] + }, + "expected": { + "outputs": [ + [ + "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ] + ], + "shared_secrets": [ + "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "037d12c02c3aed482658a28b8d1be030dac1daf995551491d74c00543af98572fb" + ], + "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ], + "key_material": { + "spend_priv_key": "b8f87388cbb41934c50daca018901b00070a5ff6cc25a7e9e716a9d5b9e4d664", + "scan_priv_key": "11b7a82e06ca2648d5fded2366478078ec4fc9dc1d8ff487518226f229d768fd" + }, + "labels": [ + 0 + ] + }, + "expected": { + "addresses": [ + "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqauj52ymtc4xdkmx3tgyhrsemg2g3303xk2gtzfy8h8ejet8fz8jcw23zua", + "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqlv6saelkk5snl4wfutyxrchpzzwm8rjp3z6q7apna59z9huq4x754e5atr" + ], + "outputs": [ + { + "priv_key_tweak": "80cd767ed20bd0bb7d8ea5e803f8c381293a62e8a073cf46fb0081da46e64e1f", + "pub_key": "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff", + "signature": "7fbd5074cf1377273155eefafc7c330cb61b31da252f22206ac27530d2b2567040d9af7808342ed4a09598c26d8307446e4ed77079e6a2e61fea736e44da5f5a" + } + ], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "037d12c02c3aed482658a28b8d1be030dac1daf995551491d74c00543af98572fb", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + }, + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a", + "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912" + } + ], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + } + ] + }, + { + "comment": "Single recipient: taproot input with NUMS point", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "0440c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b22205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5ac21c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac00150", + "prevout": { + "scriptPubKey": { + "hex": "5120da6f0595ecb302bbe73e2f221f05ab10f336b06817d36fd28fc6691725ddaa85" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140bd1e708f92dbeaf24a6b8dd22e59c6274355424d62baea976b449e220fd75b13578e262ab11b7aa58e037f0c6b0519b66803b7d9decaa1906dedebfb531c56c1", + "prevout": { + "scriptPubKey": { + "hex": "5120782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + } + }, + "private_key": "fc8716a97a48ba9a05a98ae47b5cd201a25a7fd5d8b73c203c5f7b6b6b3b6ad7" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 1, + "scriptSig": "", + "txinwitness": "0340268d31a9276f6380107d5321cafa6d9e8e5ea39204318fdc8206b31507c891c3bbcea3c99e2208d73bd127a8e8c5f1e45a54f1bd217205414ddb566ab7eda0092220e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85dac21c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0", + "prevout": { + "scriptPubKey": { + "hex": "51200a3c9365ceb131f89b0a4feb6896ebd67bb15a98c31eaa3da143bb955a0f3fcb" + } + }, + "private_key": "8d4751f6e8a3586880fb66c19ae277969bd5aa06f61c4ee2f1e2486efdf666d3" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d" + ] + ], + "shared_secrets": [ + "036f040608cd1e5ee79c54e78bea85904c895591f547beae080d0c5f6946c2730d" + ], + "input_private_key_sum": "fc8716a97a48ba9a05a98ae47b5cd201a25a7fd5d8b73c203c5f7b6b6b3b6ad7", + "input_pub_keys": [ + "02782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "0440c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b22205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5ac21c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac00150", + "prevout": { + "scriptPubKey": { + "hex": "5120da6f0595ecb302bbe73e2f221f05ab10f336b06817d36fd28fc6691725ddaa85" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140bd1e708f92dbeaf24a6b8dd22e59c6274355424d62baea976b449e220fd75b13578e262ab11b7aa58e037f0c6b0519b66803b7d9decaa1906dedebfb531c56c1", + "prevout": { + "scriptPubKey": { + "hex": "5120782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 1, + "scriptSig": "", + "txinwitness": "0340268d31a9276f6380107d5321cafa6d9e8e5ea39204318fdc8206b31507c891c3bbcea3c99e2208d73bd127a8e8c5f1e45a54f1bd217205414ddb566ab7eda0092220e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85dac21c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0", + "prevout": { + "scriptPubKey": { + "hex": "51200a3c9365ceb131f89b0a4feb6896ebd67bb15a98c31eaa3da143bb955a0f3fcb" + } + } + } + ], + "outputs": [ + "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "3ddec3232609d348d6b8b53123b4f40f6d4f5398ca586f087b0416ec3b851496", + "pub_key": "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d", + "signature": "d7d06e3afb68363031e4eb18035c46ceae41bdbebe7888a4754bc9848c596436869aeaecff0527649a1f458b71c9ceecec10b535c09d01d720229aa228547706" + } + ], + "tweak": "02213b872c9a6ee28a0d861384a1b3e3ec7257f4855ed09b4323e3899f3b028989", + "shared_secret": "036f040608cd1e5ee79c54e78bea85904c895591f547beae080d0c5f6946c2730d", + "input_pub_key_sum": "02782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + } + } + ] + }, + { + "comment": "Pubkey extraction from malleated p2pkh", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 1, + "scriptSig": "0075473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 2, + "scriptSig": "5163473045022100e7d26e77290b37128f5215ade25b9b908ce87cc9a4d498908b5bb8fd6daa1b8d022002568c3a8226f4f0436510283052bfb780b76f3fe4aa60c4c5eb118e43b187372102e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d67483046022100c0d3c851d3bd562ae93d56bcefd735ea57c027af46145a4d5e9cac113bfeb0c2022100ee5b2239af199fa9b7aa1d98da83a29d0a2cf1e4f29e2f37134ce386d51c544c2102ad0f26ddc7b3fcc340155963b3051b85289c1869612ecb290184ac952e2864ec68", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914c82c5ec473cbc6c86e5ef410e36f9495adcf979988ac" + } + }, + "private_key": "72b8ae09175ca7977f04993e651d88681ed932dfb92c5158cdf0161dd23fda6e" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "4612cdbf845c66c7511d70aab4d9aed11e49e48cdb8d799d787101cdd0d53e4f" + ] + ], + "shared_secrets": [ + "034773b97ccad9791cb4213964ff9896ccd6581ee69345de5d114786d9d86b03a2" + ], + "input_private_key_sum": "610e0f75fd05e5e80e088b57af0a46da06cb0700c0c5907aa6d29c6b4ce46348", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "02e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 1, + "scriptSig": "0075473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 2, + "scriptSig": "5163473045022100e7d26e77290b37128f5215ade25b9b908ce87cc9a4d498908b5bb8fd6daa1b8d022002568c3a8226f4f0436510283052bfb780b76f3fe4aa60c4c5eb118e43b187372102e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d67483046022100c0d3c851d3bd562ae93d56bcefd735ea57c027af46145a4d5e9cac113bfeb0c2022100ee5b2239af199fa9b7aa1d98da83a29d0a2cf1e4f29e2f37134ce386d51c544c2102ad0f26ddc7b3fcc340155963b3051b85289c1869612ecb290184ac952e2864ec68", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914c82c5ec473cbc6c86e5ef410e36f9495adcf979988ac" + } + } + } + ], + "outputs": [ + "4612cdbf845c66c7511d70aab4d9aed11e49e48cdb8d799d787101cdd0d53e4f" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "10bde9781def20d7701e7603ef1b1e5e71c67bae7154818814e3c81ef5b1a3d3", + "pub_key": "4612cdbf845c66c7511d70aab4d9aed11e49e48cdb8d799d787101cdd0d53e4f", + "signature": "6137969f810e9e8ef6c9755010e808f5dd1aed705882e44d7f0ae64eb0c509ec8b62a0671bee0d5914ac27d2c463443e28e999d82dc3d3a4919f093872d947bb" + } + ], + "tweak": "028d6617f9bfe08604beb2188f4eebec923f5f8cc436fa6d14e4256e49bc32e7c8", + "shared_secret": "034773b97ccad9791cb4213964ff9896ccd6581ee69345de5d114786d9d86b03a2", + "input_pub_key_sum": "038b0d201fe111bdc0e6953772bd02a41959d25d5b2f66bcbe348af27bbdd42735" + } + } + ] + }, + { + "comment": "P2PKH and P2WPKH Uncompressed Keys are skipped", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b974104782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c3799373233387c5343bf58e23269e903335b958a12182f9849297321e8d710e49a8727129cab", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9144b92ac4ac6fe6212393894addda332f2e47a315688ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 1, + "scriptSig": "", + "txinwitness": "02473045022100e7d26e77290b37128f5215ade25b9b908ce87cc9a4d498908b5bb8fd6daa1b8d022002568c3a8226f4f0436510283052bfb780b76f3fe4aa60c4c5eb118e43b187374104e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d6fe8190e189be57d0d5bcd17dbcbcd04c9b4a1c5f605b10d5c90abfcc0d12884", + "prevout": { + "scriptPubKey": { + "hex": "00140423f731a07491364e8dce98b7c00bda63336950" + } + }, + "private_key": "72b8ae09175ca7977f04993e651d88681ed932dfb92c5158cdf0161dd23fda6e" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" + ] + ], + "shared_secrets": [ + "0295a54c359da5b2640601ddbedb26e040cb97b6a3432e60b76d1258e85f72fa64" + ], + "input_private_key_sum": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b974104782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c3799373233387c5343bf58e23269e903335b958a12182f9849297321e8d710e49a8727129cab", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9144b92ac4ac6fe6212393894addda332f2e47a315688ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 1, + "scriptSig": "", + "txinwitness": "02473045022100e7d26e77290b37128f5215ade25b9b908ce87cc9a4d498908b5bb8fd6daa1b8d022002568c3a8226f4f0436510283052bfb780b76f3fe4aa60c4c5eb118e43b187374104e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d6fe8190e189be57d0d5bcd17dbcbcd04c9b4a1c5f605b10d5c90abfcc0d12884", + "prevout": { + "scriptPubKey": { + "hex": "00140423f731a07491364e8dce98b7c00bda63336950" + } + } + } + ], + "outputs": [ + "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "688fa3aeb97d2a46ae87b03591921c2eaf4b505eb0ddca2733c94701e01060cf", + "pub_key": "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6", + "signature": "72e7ad573ac23255d4651d5b0326a200496588acb7a4894b22092236d5eda6a0a9a4d8429b022c2219081fefce5b33795cae488d10f5ea9438849ed8353624f2" + } + ], + "tweak": "02b04034f00da0678507d1345b7d56fecef825a1151f9dc7d8ca6946452a9e1f43", + "shared_secret": "0295a54c359da5b2640601ddbedb26e040cb97b6a3432e60b76d1258e85f72fa64", + "input_pub_key_sum": "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + } + } + ] + }, + { + "comment": "Skip invalid P2SH inputs", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "16001419c2f3ae0ca3b642bd3e49598b8da89f50c14161", + "txinwitness": "02483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "prevout": { + "scriptPubKey": { + "hex": "a9148629db5007d5fcfbdbb466637af09daf9125969387" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 1, + "scriptSig": "1600144b92ac4ac6fe6212393894addda332f2e47a3156", + "txinwitness": "02473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b974104782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c3799373233387c5343bf58e23269e903335b958a12182f9849297321e8d710e49a8727129cab", + "prevout": { + "scriptPubKey": { + "hex": "a9146c9bf136fbb7305fd99d771a95127fcf87dedd0d87" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 2, + "scriptSig": "00493046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d601483045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b97014c695221025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be52103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c3799373233382102e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d53ae", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "a9141044ddc6cea09e4ac40fbec2ba34ad62de6db25b87" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" + ] + ], + "shared_secrets": [ + "0295a54c359da5b2640601ddbedb26e040cb97b6a3432e60b76d1258e85f72fa64" + ], + "input_private_key_sum": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "16001419c2f3ae0ca3b642bd3e49598b8da89f50c14161", + "txinwitness": "02483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "prevout": { + "scriptPubKey": { + "hex": "a9148629db5007d5fcfbdbb466637af09daf9125969387" + } + } + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 1, + "scriptSig": "1600144b92ac4ac6fe6212393894addda332f2e47a3156", + "txinwitness": "02473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b974104782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c3799373233387c5343bf58e23269e903335b958a12182f9849297321e8d710e49a8727129cab", + "prevout": { + "scriptPubKey": { + "hex": "a9146c9bf136fbb7305fd99d771a95127fcf87dedd0d87" + } + } + }, + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 2, + "scriptSig": "00493046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d601483045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b97014c695221025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be52103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c3799373233382102e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d53ae", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "a9141044ddc6cea09e4ac40fbec2ba34ad62de6db25b87" + } + } + } + ], + "outputs": [ + "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "688fa3aeb97d2a46ae87b03591921c2eaf4b505eb0ddca2733c94701e01060cf", + "pub_key": "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6", + "signature": "72e7ad573ac23255d4651d5b0326a200496588acb7a4894b22092236d5eda6a0a9a4d8429b022c2219081fefce5b33795cae488d10f5ea9438849ed8353624f2" + } + ], + "tweak": "02b04034f00da0678507d1345b7d56fecef825a1151f9dc7d8ca6946452a9e1f43", + "shared_secret": "0295a54c359da5b2640601ddbedb26e040cb97b6a3432e60b76d1258e85f72fa64", + "input_pub_key_sum": "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + } + } + ] + }, + { + "comment": "Recipient ignores unrelated outputs", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b", + "prevout": { + "scriptPubKey": { + "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn", + "scan_pub_key": "02062d49ffc02787d586c608dfbec184aa91a6597d97b463ea5c6babd9d17a95a3", + "spend_pub_key": "0381eb9a9a9ec739d527c1631b31b421566f5c2a47b4ab5b1f6a686dfb68eab716" + } + ] + }, + "expected": { + "outputs": [ + [ + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8" + ] + ], + "shared_secrets": [ + "03dd5fd04d3c8863be750a1bd7474df06161461d38d3ce1397a5c78cee112cdcd2" + ], + "input_private_key_sum": "ee55616ce5a93e508f03f21949ecbe70a2a0b107b6e1df5d98b4e4da4adaca1b", + "input_pub_keys": [ + "025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "", + "txinwitness": "0140c459b671370d12cfb5acee76da7e3ba7cc29b0b4653e3af8388591082660137d087fdc8e89a612cd5d15be0febe61fc7cdcf3161a26e599a4514aa5c3e86f47b", + "prevout": { + "scriptPubKey": { + "hex": "51205a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [], + "tweak": "0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8", + "shared_secret": "038efbcbc1b0938fba3bf59fea1219a3c54b6d6f9107560da05001407adc13f413", + "input_pub_key_sum": "03853f51bef283502181e93238c8708ae27235dc51ae45a0c4053987c52fc6428b" + } + } + ] + }, + { + "comment": "No valid inputs, sender generates no outputs", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d641045a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5c61836c9b1688ba431f7ea3039742251f62f0dca3da1bee58a47fa9b456c2d52", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914460e8b41545d2dbe7e0671f0f573e2232814260a88ac" + } + }, + "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b974104782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c3799373233387c5343bf58e23269e903335b958a12182f9849297321e8d710e49a8727129cab", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9144b92ac4ac6fe6212393894addda332f2e47a315688ac" + } + }, + "private_key": "0378e95685b74565fa56751b84a32dfd18545d10d691641b8372e32164fad66a" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [] + ], + "shared_secrets": [ + null + ], + "input_pub_keys": [] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d641045a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5c61836c9b1688ba431f7ea3039742251f62f0dca3da1bee58a47fa9b456c2d52", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a914460e8b41545d2dbe7e0671f0f573e2232814260a88ac" + } + } + }, + { + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b974104782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c3799373233387c5343bf58e23269e903335b958a12182f9849297321e8d710e49a8727129cab", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9144b92ac4ac6fe6212393894addda332f2e47a315688ac" + } + } + } + ], + "outputs": [ + "782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "e0ec4f64b3fa2e463ccfcf4e856e37d5e1e20275bc89ec1def9eb098eff1f85d" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [], + "tweak": null, + "shared_secret": null + } + } + ] + }, + { + "comment": "Input keys sum up to zero / point at infinity: sending fails, receiver skips tx", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "3a286147b25e16ae80aff406f2673c6e565418c40f45c071245cdebc8a94174e", + "vout": 0, + "scriptSig": "", + "txinwitness": "024730440220085003179ce1a3a88ce0069aa6ea045e140761ab88c22a26ae2a8cfe983a6e4602204a8a39940f0735c8a4424270ac8da65240c261ab3fda9272f6d6efbf9cfea366012102557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "prevout": { + "scriptPubKey": { + "hex": "00149d9e24f9fab4e35bf1a6df4b46cb533296ac0792" + } + }, + "private_key": "a6df6a0bb448992a301df4258e06a89fe7cf7146f59ac3bd5ff26083acb22ceb" + }, + { + "txid": "3a286147b25e16ae80aff406f2673c6e565418c40f45c071245cdebc8a94174e", + "vout": 1, + "scriptSig": "", + "txinwitness": "0247304402204586a68e1d97dd3c6928e3622799859f8c3b20c3c670cf654cc905c9be29fdb7022043fbcde1689f3f4045e8816caf6163624bd19e62e4565bc99f95c533e599782c012103557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "prevout": { + "scriptPubKey": { + "hex": "00149860538b5575962776ed0814ae222c7d60c72d7b" + } + }, + "private_key": "592095f44bb766d5cfe20bda71f9575ed2df6b9fb9addc7e5fdffe0923841456" + } + ], + "recipients": [ + { + "address": "sp1qqtrqglu5g8kh6mfsg4qxa9wq0nv9cauwfwxw70984wkqnw2uwz0w2qnehen8a7wuhwk9tgrzjh8gwzc8q2dlekedec5djk0js9d3d7qhnq6lqj3s", + "scan_pub_key": "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", + "spend_pub_key": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" + } + ] + }, + "expected": { + "outputs": [ + [] + ], + "shared_secrets": [ + null + ], + "input_pub_keys": [ + "02557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "03557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "3a286147b25e16ae80aff406f2673c6e565418c40f45c071245cdebc8a94174e", + "vout": 0, + "scriptSig": "", + "txinwitness": "024730440220085003179ce1a3a88ce0069aa6ea045e140761ab88c22a26ae2a8cfe983a6e4602204a8a39940f0735c8a4424270ac8da65240c261ab3fda9272f6d6efbf9cfea366012102557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "prevout": { + "scriptPubKey": { + "hex": "00149d9e24f9fab4e35bf1a6df4b46cb533296ac0792" + } + } + }, + { + "txid": "3a286147b25e16ae80aff406f2673c6e565418c40f45c071245cdebc8a94174e", + "vout": 1, + "scriptSig": "", + "txinwitness": "0247304402204586a68e1d97dd3c6928e3622799859f8c3b20c3c670cf654cc905c9be29fdb7022043fbcde1689f3f4045e8816caf6163624bd19e62e4565bc99f95c533e599782c012103557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "prevout": { + "scriptPubKey": { + "hex": "00149860538b5575962776ed0814ae222c7d60c72d7b" + } + } + } + ], + "outputs": [ + "0000000000000000000000000000000000000000000000000000000000000000" + ], + "key_material": { + "spend_priv_key": "0000000000000000000000000000000000000000000000000000000000000001", + "scan_priv_key": "0000000000000000000000000000000000000000000000000000000000000002" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqtrqglu5g8kh6mfsg4qxa9wq0nv9cauwfwxw70984wkqnw2uwz0w2qnehen8a7wuhwk9tgrzjh8gwzc8q2dlekedec5djk0js9d3d7qhnq6lqj3s" + ], + "outputs": [], + "tweak": null, + "shared_secret": null + } + } + ] + }, + { + "comment": "Input keys intermediate sum is zero but final sum is non-zero", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "3a286147b25e16ae80aff406f2673c6e565418c40f45c071245cdebc8a94174e", + "vout": 0, + "scriptSig": "", + "txinwitness": "0247304402203e5537fa8c876b3475e7efe4f1474b0f48b7a6e4169179db5de9bb5b55ad1bd10220200e06f8f4d29dbc48bbcdf90df3278e798ce6cbf3c9fbf90427599fde147867012102557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "prevout": { + "scriptPubKey": { + "hex": "00149d9e24f9fab4e35bf1a6df4b46cb533296ac0792" + } + }, + "private_key": "a6df6a0bb448992a301df4258e06a89fe7cf7146f59ac3bd5ff26083acb22ceb" + }, + { + "txid": "3a286147b25e16ae80aff406f2673c6e565418c40f45c071245cdebc8a94174e", + "vout": 1, + "scriptSig": "", + "txinwitness": "0247304402207fdad0faf46edc54f5a5c67d33b2fa8d3f1fdc869381fd96e659f9e0c470ab1e022044f0d973339618b18667cef9a6251817f7f431f7f2b252a8cb760ccb40e7d823012103557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "prevout": { + "scriptPubKey": { + "hex": "00149860538b5575962776ed0814ae222c7d60c72d7b" + } + }, + "private_key": "592095f44bb766d5cfe20bda71f9575ed2df6b9fb9addc7e5fdffe0923841456" + }, + { + "txid": "3a286147b25e16ae80aff406f2673c6e565418c40f45c071245cdebc8a94174e", + "vout": 2, + "scriptSig": "", + "txinwitness": "0247304402203e5537fa8c876b3475e7efe4f1474b0f48b7a6e4169179db5de9bb5b55ad1bd10220200e06f8f4d29dbc48bbcdf90df3278e798ce6cbf3c9fbf90427599fde147867012102557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "prevout": { + "scriptPubKey": { + "hex": "00149d9e24f9fab4e35bf1a6df4b46cb533296ac0792" + } + }, + "private_key": "a6df6a0bb448992a301df4258e06a89fe7cf7146f59ac3bd5ff26083acb22ceb" + } + ], + "recipients": [ + { + "address": "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "scan_pub_key": "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4", + "spend_pub_key": "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36" + } + ] + }, + "expected": { + "outputs": [ + [ + "7e88a7536c90770be4d2693a84ed03abe3fdcc5a29f96ec3433effec3b0c2194" + ] + ], + "shared_secrets": [ + "037dc4e5904ab4770dbdbb628860b54265fdbb7810b8afdf9f582fedaabfdebef0" + ], + "input_private_key_sum": "a6df6a0bb448992a301df4258e06a89fe7cf7146f59ac3bd5ff26083acb22ceb", + "input_pub_keys": [ + "02557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "03557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "02557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "3a286147b25e16ae80aff406f2673c6e565418c40f45c071245cdebc8a94174e", + "vout": 0, + "scriptSig": "", + "txinwitness": "0247304402203e5537fa8c876b3475e7efe4f1474b0f48b7a6e4169179db5de9bb5b55ad1bd10220200e06f8f4d29dbc48bbcdf90df3278e798ce6cbf3c9fbf90427599fde147867012102557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "prevout": { + "scriptPubKey": { + "hex": "00149d9e24f9fab4e35bf1a6df4b46cb533296ac0792" + } + } + }, + { + "txid": "3a286147b25e16ae80aff406f2673c6e565418c40f45c071245cdebc8a94174e", + "vout": 1, + "scriptSig": "", + "txinwitness": "0247304402207fdad0faf46edc54f5a5c67d33b2fa8d3f1fdc869381fd96e659f9e0c470ab1e022044f0d973339618b18667cef9a6251817f7f431f7f2b252a8cb760ccb40e7d823012103557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "prevout": { + "scriptPubKey": { + "hex": "00149860538b5575962776ed0814ae222c7d60c72d7b" + } + } + }, + { + "txid": "3a286147b25e16ae80aff406f2673c6e565418c40f45c071245cdebc8a94174e", + "vout": 2, + "scriptSig": "", + "txinwitness": "0247304402203e5537fa8c876b3475e7efe4f1474b0f48b7a6e4169179db5de9bb5b55ad1bd10220200e06f8f4d29dbc48bbcdf90df3278e798ce6cbf3c9fbf90427599fde147867012102557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975", + "prevout": { + "scriptPubKey": { + "hex": "00149d9e24f9fab4e35bf1a6df4b46cb533296ac0792" + } + } + } + ], + "outputs": [ + "7e88a7536c90770be4d2693a84ed03abe3fdcc5a29f96ec3433effec3b0c2194" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ], + "outputs": [ + { + "priv_key_tweak": "3d5b7a284108f93b9fe78f2c300d2ca9ef3b4e0cd0de673fc72990f2a4f417b9", + "pub_key": "7e88a7536c90770be4d2693a84ed03abe3fdcc5a29f96ec3433effec3b0c2194", + "signature": "f8d5222f1a682215c40ab677f2104606f2a0e6c5cb1a2d248d970fb61d4eadb31702353e6b41ea5a9b24817efa0eaf535552eeee8a794b662c3cf303b6c86672" + } + ], + "tweak": "039c68bacb7efbf2175d781822f460afc4839a5798fabb055b50d939231bf57bb6", + "shared_secret": "037dc4e5904ab4770dbdbb628860b54265fdbb7810b8afdf9f582fedaabfdebef0", + "input_pub_key_sum": "02557ef3e55b0a52489b4454c1169e06bdea43687a69c1f190eb50781644ab6975" + } + } + ] + }, + { + "comment": "Maximum per-group recipient limit K_max is exceeded (2324 matches): sending fails, receiver doesn't scan beyond limit", + "sending": [ + { + "given": { + "vin": [ + { + "txid": "3cde0e7ad8adf24a4e52fd18efafffb94f73f4c7facf2627a5b588ad1c9db87f", + "vout": 0, + "scriptSig": "", + "txinwitness": "01407ce01bc5ef2e4aae353e7f291fec32623d6a491b5421be6dccc70345f32ebae5baa6654c60c074e72da76fd06b343474370e56d3ec0413156ec6261757125ad4", + "prevout": { + "scriptPubKey": { + "hex": "5120bca87f72e604e8850064552bedf380ca4584227057efe12a6cc238470658aaa3" + } + }, + "private_key": "0000000000000000000000000000000000000000000000000000000000001337" + } + ], + "recipients": [ + { + "address": "sp1qqtnvg7hxjck9ag24naytgd7pjwsmevwh95ydwht58w3uh7uw0ta7kqmk4a3pm24z0yepw6aw27pku56md0hq5ace0l3p3jstkqaajwrsjqc5dde5", + "scan_pub_key": "02e6c47ae6962c5ea1559f48b437c193a1bcb1d72d08d75d743ba3cbfb8e7afbeb", + "spend_pub_key": "0376af621daaa27932176bae57836e535b6bee0a77197fe218ca0bb03bd9387090", + "count": 2324 + } + ] + }, + "expected": { + "outputs": [ + [] + ], + "shared_secrets": [ + null + ], + "input_private_key_sum": "0000000000000000000000000000000000000000000000000000000000001337", + "input_pub_keys": [ + "02bca87f72e604e8850064552bedf380ca4584227057efe12a6cc238470658aaa3" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ + { + "txid": "3cde0e7ad8adf24a4e52fd18efafffb94f73f4c7facf2627a5b588ad1c9db87f", + "vout": 0, + "scriptSig": "", + "txinwitness": "01407ce01bc5ef2e4aae353e7f291fec32623d6a491b5421be6dccc70345f32ebae5baa6654c60c074e72da76fd06b343474370e56d3ec0413156ec6261757125ad4", + "prevout": { + "scriptPubKey": { + "hex": "5120bca87f72e604e8850064552bedf380ca4584227057efe12a6cc238470658aaa3" + } + }, + "private_key": "0000000000000000000000000000000000000000000000000000000000001337" + } + ], + "outputs": [ + "5cfb6fd7a7e5d2af0fee276f2104ce2e5c86cde7955b98f2f1fb32811b448f2f", + "da290e0d79ead5cd2e5d150aac80dcab54dbd0e68f241a6c34dc30906eabd13d", + "d4a2b1458f0c8b20d756bde8e545560d7e7a65f6b4158f01ed34a913ba3a4603", + "24752e555b0edcca123f769113692224bf1e6db137cddeb7d69ae52e51144e9e", + "c911f722ef9a3594d6b85c585d4f3ed8dc437f850591dc627a0154723644114b", + "ef91ddaa83a0b748d638dc9be8c9771b049a04815f03967b7ee993bde6c260ab", + "ee4ed334fb38af95f52c6bbadcf2f491e646b9cc8ed31da67635f36c3cfdaba0", + "a17d61b4772e453b32782744902261e051510cbafc04f086f53480d1ceee1a9c", + "44d89f5e903739072e3aff8a3eb2f351520f77ff288ac8be277be3f28834bdd2", + "3eff02a4a04d66d63f6bc93a693f23db703f351c0a1ff1656b907ed348a9a35f", + "628c23a6e21e6a47b80274fc80e4decb98ff79b8040c7f9ea5972e4920b62b6d", + "e72190c0a5ea167c2663b18d378520b9228da61ebbc99c3fa2af39e01fb9501e", + "24183edbc5b80588e0e5b803e924e77fc1cab1afca54cfd285dad9bfc2a34f6d", + "c45bad25a223f0f617e8167f858037907e4454210a745f3b9798c8913646dfc6", + "9f9cec85011d7f718f96ee1a2940df21920a857cd1a545255579cb615f285a42", + "8c60891f9bc2f772efc51d44f7cc885161412835b6fd5aac3b0cf57c3c2f9154", + "85ac46a6ac9569ed67d1bc5342ed3b591af5e01581d1b09bcb2c71deb9b2c2c9", + "8a6bfe47e616c6854fc797a4af53ef51c942b99eae18f316594fc8eb8869ca05", + "ed5e1aa6a8b4e805273367b8ec3ade16ed6494270fc2169ba920b89770efe8eb", + "2448503c55a1a7035c069e1d5461b81774c43bf6061421fdd0157009bfe96a8a", + "d0dcb3d7b551eac8d31fceccb83df415c554860d5541dd08ff5fa0028ce5704f", + "f11a298b54b650613e0b0183552c9c9e42c3812e03cf713d351ef71106b6ee3f", + "49749206f23791f858c453cb60160d48710075bdbfd33907b0046d10f0b0295e", + "3fbfdb52cbf0ea9e6655a211748bf2ac2dc92ff3cfeeef0f0a26be9987016de0", + "d4a29144511981919cadbe8227fc68379e85d3fe399f177c2ae36c304b6ad6b0", + "c586cc196ff56f42396f426a0200afee3a6c41058d81f19caf6536ec0efd5605", + "c666994f027ceab025256af28d8ea6aec452fb8b0e1f69e7e13a4f3c5119f4ae", + "5c9216cc4529c3f9c62caf70b04f8354ce9ec2cabaef745092bea1b160328fda", + "d9aea54323503d78c026a8cd465c19a6df54b32ee866714d190e8260054361da", + "ba71f87ca1d74eee115d8e8a26293fe2c88d9d66cff185a7a079282f02f931ad", + "b8b930666b2df74c33dc8963221080c115246070e3fcd51f39bee71312db4fc4", + "b7f42d2f18a3ac61f570b64ae6864f7dbff774a370ba27358d76797e841d5304", + "d636d179055241a0f8bb7db193912da513a8999684d9829b266985a8083819a9", + "be9622aec125f9605bcee193a99ca27c16bc591122ff8b44c511a85e6f63ec9d", + "af5beb25776f84fadd6079c3ffd85775a35ad74096a58229e1d5076d836ca25d", + "75fb55ec31e304456f663c9a66321e67f9d9bd8f43749494acc5189ed57b0d4a", + "8dfebff33c514486d06eeb4ab8eb8471496556858c4382f73780620101a77f71", + "93bfd4e75706951de4a59c7f70c5d2ce4c617b4bde0b0b23ae046df096116726", + "b6eeca22f43bd62a4fcb3eee719e07d39ea703fa0707970fc197916f2efebbb5", + "0437c3971f73342e5c41178590e1cb1364cb855b2d20b7b6c1c1ed0e7da5b57b", + "d284c831cd9d52793977925ce3111b68f36d1ea456e46716158fe6947fb6d635", + "d95b8d2789fd06cc764d47cc7e0697404220ae8f938ef47dcc9a1bba299fbb9f", + "33599fb1fe313b5a2680b126bfee93cb097d938915d425a63e307e5834479b95", + "05ba86903fac2f02ef2e152072bc6067008ee8ee34ce55ad460801303373eaab", + "0b757802e56728b6d5b28e671a90de573d7251a47fc1bcd9319b617cac8e1463", + "b4a2451e725e4cbcdf47fc736139cfe29c847e2f75cbb7653d80c8522bd48a6c", + "8dd800c3a7cb1ee60f52db3bb69413c4c4247b0b134576552c675970f9903c9b", + "4de21129c6fdf5568040346bbdda272caaadd58218ec6f1f152518068dac4d4d", + "22f6927eae0c3717395df6483d756028839fbf0814f8cf0391159efa65dd2854", + "192d26fe0bc772c705c2657dd5c5033ac41ba408793e6c721f74c53747be16e8", + "9527b8e19570a16ce2d8dd3620dfd915e461bccc82340fe682f7f811b978814d", + "f84039ff95cd44f842d7cf6982a726957a89d90bb151a368e62770720d3efbee", + "b5b36dd84b352cddf715246d0463984b9d7894f50b566a20042c4d485df41946", + "592e1bd733a1095b9e5b770021769090df0c001260803fce9a9b761ba697a5e7", + "dd2fdbd6932d8b56d6ff91db3ed28f282a6ca01588ee5a552d061da215580411", + "82f95ef71d4d0b32e3f0cef89e6e90a8c271cd5536b38c507fc69b354ab51062", + "1800903b2d5f8483a12f0b98a5b35ed39d6cf8f4a52f495395f1816d6e3c139c", + "121a2c2ea70a6986af8bde71b7f75ac9f9e1189eeb9756395e7281702f01936d", + "8578df7e8ec4b068f64dca679310894afec095dbbedbefef5944c3a319809b8b", + "5cebde1dbb352ff62841d7c50c3c45cd0fd1b7b3ebe884a7cf29cef6686695ab", + "362f7a73f2620ea061586b5edbee01f9c026ae6e0d95131c570c44933fc41f10", + "3a19b5976236d44a3d1d6920b348fcef9ec830aac56aa672353c711471ebc36a", + "842c43c5293b03811165f1ff7e04c189ccd355fe08d37808a843a0995541c075", + "0ab1fdbf7117c88aed3f882d5f7dee8964c9246030086b9ee3e2ebed4eb2c731", + "7cf8ca4463d4bd500cc12623ac9d3984cc48e7fbb98bebb4a498c2fd66d54cde", + "631676f3369eb479686256cef33c4fb061c212cc215ab442d8f7837c039ddacc", + "2fff6de5386b65efe6062cc2242b692a1e811a083612ab79fa7f7517f1488767", + "3c7d8b7f2a56e22ade186a70a3039b4d4e4e3c0be6e661770b0a7b2ef18b178b", + "e030882188281a754c00706f621084f96869bf95bb5d005ccc7b2649fbfc747e", + "072cb899c3eb621240792a39bf727bf59398329748317d5ca1addfd8ee2f7e92", + "37cfa6b7214e99bbec6323802115b9615b10851ac85cdc26f213b9532fdefcb8", + "68b14b71ad6b5e2d41ef47fe1b9229a0b8297ede0812d1b9cb95aac185d1a5b8", + "652ef549580cf412bb217fb15b2a0a1db8feb3c79969b95364a80dd97d565f09", + "dd3504b7563a65115a050505bc88ee23a50e37c354773cac329355c16c43d839", + "6d7f05d6ee71459d2ea982f7a5cd4a251d7466d4892e6c8b6aaf45eec66472d7", + "14c28adbf9008d0cbcc978006b6c1ec7be51a134bd5587e94ee33844b62c1ffb", + "f9c20067f16feaaf3f8daa8456dd9334725ebdc8b49b4f74f5a02c38ef5f2804", + "e2c537948a01054b7a0d630952c2ddd395a3a265d618e22ee9d99d4597f82322", + "70e3a71945fee29350b5b10a8ee8dcbd19c2f734e22407bb6b50ec3acb1035a5", + "effbced848df72cd467b1ada7353b1ddc0b7f3f08190c0f2fc747f18303ed9f8", + "02d6eedc5abc295bcfbd4c30a8fa3406c8256d8e51f4714800b4aecb795a976b", + "92134c6158993a0390af92565e166418561c9b157989d02304b68c2ee1babe14", + "6065e3e3aff3e2b2f33bf4af9256e9368b8a303335b0587c8cc9e31cb9db42bd", + "732fd2ba9efabc6d79bd7404c1c6f1d5c1f14c9bfe02ee1320621824847935c2", + "209f9c958b5f4813b32205a44d05c83aa1009cb7b9c7343f0ca3717ae069defc", + "1d274fcb65b6d4c846e95030c37851a97d161f926ed88f6c4dec4ed7007fc23b", + "1d3787106cf47e1e9d08761a6c6f427ef6c07d4a571f23d340ae2d31d34b1d8e", + "5697a3f56a0a2a6bd6991ba05559df9b526d5c42e19604c398548a7cb0dec0b5", + "8c0f581f38238215f10f8017e3d234cbda9d94ca017bbd6b076ac8666014b3d3", + "78f061b0fc912fbbeb0d207b92528ad78ded55ec145c8f18133991847fb9cbe9", + "1e3555081a869f3aafa3d74d6b5afa942baace60869ecd4cd33635f84602b715", + "4f0e36ac0c4813db3fdf79d85ef0db89150dc1be2c879ba06299c049288ab8be", + "6b048c38924b7a1f970cdff081124b739202b703db5c17bafa4ad23a5bcb97e9", + "a5f5e9238aec20bd706262f64671ca2d05961f55ca383953d489edf857e4b2a4", + "a047b08e2af7ae5bfb9e54f98051ff7cd5e7dc88a25afacdd9a324526cd33c63", + "66ae2b9513a0e0ad125001bcd5f3a36e18b4ec009db4020ea4afc4951cb39a34", + "621d8269c8f93b6336f8b7480516d2191cfb15a1d9501463771d0fb70c1afa33", + "76b94079066bff52a077ba1d1ba8cdec4d6484d1ce2f7454b2b04aa3d9cf52ed", + "33c6a108f9d4fe054c085991d89d2a5c32b8f108c95b0e688c5763424c283e54", + "52575ed2ad84295a070a99e7785ff0e4dd32a93d3830be7f7c2811804f3df2f3", + "158dfe014c271c148b38459ef16b49cc3b5c6bdfe052795a6f6a6aacbb3e47b4", + "d471729cb231deeb2f1a67f91962ecfcb9e8c66f66b6b79fe1117d34ae86a7c0", + "e086b1a09ab5307d953e25366b1acf7c41047a926aa6a4d122c5a34e3e0d93d4", + "d541d825401a1734d9961fdb4f0e9ce7af220c6929648e0aa2e885cd28696343", + "a555af7a52ac07e681c247f1d6597f8240d28e76036bbf3b2be1f7cfab90dfd6", + "8598fe7a4a6c6fa865c0ca8e8e65c96301c80965e19e48f5d4ca29ac073551c7", + "70c9ac61ab3508a7419ead73503d8fe51b4ecba73a55d4b167cd4db6c9b0cb8a", + "e594eb83b38ad916176abff3f0ec16d7a814a461012c8f9bc8286274af3f1856", + "fa4e0df6df82f21050394fc2caaa8a8713b131d0d1f7d21eb92b56796095bf17", + "891711c535579b555105e5ec18ea0f2f610c6fe5126ff0f6a4257e99a24033c5", + "f192c9b2c49dab7515d405052f825abead30ef9a870ba3bdad98c1d6256be461", + "70c22f6ceb81744c83eb8dae69980f4583c75431457dbb14f5ecd6c9fae3e63d", + "7d563f44bdfcb2a10ee053f4c05b8d46fa90d44822775f97ee8cfbdeda7a4ca1", + "c09b3df69eaa232a9119e2fb805cdccacb658b2fb9c7f3a2106c8399aa928033", + "15b4b3d50fef72de80fef6e3351dae0f203902e4c4db8ce8d8737b67aed9104d", + "c9b7718633714b3ce1b489f13e53fc647be53cba7703f07a35a8815245b23ff3", + "2d344af9852fc2451ca5d2ed7155e074efd2c53ca9cbed7f48c74658967aeca6", + "c8b778b5c1fc378365d8b342915215117c3d365e12f61f3ccefbe4091879e373", + "1deb076e44c029027c1aa10000305c3eded5d401a036c21a4dd3d3455c0c33a1", + "d2d2dfe410cefbae2309e746690818e8d52dcd80ebd192cddaa00204b6833d05", + "d18a0a0bcc678908ff00b236296f0a955a9aa52a802ee4175849d0bcb3d6738a", + "7af51e08357e455e4dda86d60da75899b5ffce4bebc7f96037d1e404b1260ebe", + "65b930580f9738e1a7af2ac56faad5bdc6a417e75cb872b7f62513f4e8e36b33", + "71c8e88c7c478a4d919d0c80d351331102584d032d82aa25510801e18bb5d6b9", + "2166bad16a6e4a1f77a22b0fb13f7f11d618a123c3c077dbf1ed1504ed68cbb4", + "717a3a5903c2b12aea8be15599ea9864259a964c0139125e649fefbec763890e", + "0d042f5767f6cb2f94526b5b6da8ef986e4a5927eb5f734bdeb6c6ec3bb8211a", + "bb67d586774ae9f3653c609dbdd98f216315b54c0ce24ad9e359d0c2c21614a3", + "945b2962ec44c54ea25e3e35e32a2362c130fc3a9495b16871e8a2eadbe457e7", + "7d5324e5e158c82388cba0d497530c6de0c1ba97131688931c03dbfc60ca7541", + "21805a8f8d43aede3e927b80ef04924d388e96f3cc3947a0dbc1bd1b812539f0", + "27ec492fde5fc3d3784359d9a270b735c1820bc4a6f15ac87797d97de047a6b5", + "4fda2f855ab08a8d4dcba1874402e1662315c872e3ad55681fc4d6c5a89665a3", + "b0dc69204deaa17ac4e0f197f8b23211418b5d54f5bac55414445a66c57fd6f2", + "209262521ab45331639a57523f2e3bbf8250052b84fba81c50c990166a6f58b6", + "b02857949ebddecc58aaba80b9d635ae95cf71f4bfc5ea8fbe05fc55d4c73e96", + "5194133163b28182a261ce0473b1f3ab991a53a08d0e813d3bfb533cfaf1255b", + "c56fe98848fe79124ae1ee09d1b214ee87da51c1440134210c67fe15bf6d1bb0", + "851d949ac91f439ec84f07c481598728ca93acf79cb6505bd77ef776559275f1", + "eeccaf0cd29cb002e2140cffa9fe2c08c53ffd73b99167f4b5e6ab5c94481066", + "821f1058ae54732723937c5ae535cdc0a9ce673eec7fc02bd03c240c93aff24c", + "de836fd21ce91d4a6add661f5d259d0ade4e0ff927d03f67ab5d0b24bff37242", + "fdac0470c61d81d1498e4e0c6fc4ca5ccd02f727413e33c84478ebe844feba13", + "469b4f5af2b2377dc09e5824af17c57f1dd8fbe02294059e16641c1307eff125", + "b77348b40d52d531378dead61cf2eb6fafb4c89f85285a98cd4b6032c0e30a16", + "2cf9616aefee83eddb2df0dda1a3331185f0b772c3a8a62ea3ffbb325290f32b", + "2d23a9035ec627187108b9d13b24134d69ad7cb6f77d2c3ab939db17cc6f129c", + "4925a7008584e04fa769661dd9eb766fe2ea3b66622691249142ca155423f368", + "69090019b3c1635da9d9e4661f55863cb3ab561bcedf2632360745e62b06afc5", + "5cecb744c3e65889c22e14a58e42bb69d068c721f1c547c8c704fbc688c6c6e2", + "2f7371ba97f17c64c4c487742f83755ab61aeb1ee537bcd5f16beb4440d0233a", + "9d1e3dcad79b153296a67238771b91a92c430432a9cc9f7b2ad7667fd67b4de3", + "e9a34ca0067dbb6dbba48e736e1d32337cca5e93f2b76229fafef501979d84ae", + "4d5d36724a2baa0d73657a140493a9ba95e1de95cad74c9753eaad785ac18cbd", + "d135fe3d44b6b0875c1662ecfe33556be0f1b39bc8279dc8730d5e9e351f36ae", + "1fb8439b54ead68f72b7ba4fcaa5041f38d313d92826b57dbda299dab06ab716", + "b96e8d59a7c1006f15b0ac10cd35c08a8008e0c2ecba26d87db3f08c0f6fc991", + "73300dd04c53d270420023957c50410147ea4a2814085d3b98ef25d8d44afeed", + "6941323a74081c1ee28846c4ffe3b4023b29eb3a3b64d0d2dea0f47349fc2138", + "f7cebcfc608a982a3e11b3941abf482d8108a7d59c3719279563788173a2cc6c", + "b76c687ff464c50b58e4231379c5105470264de2826574e8d7fc58d6889d0753", + "f0148c800120bd34159684196bdfb34f08d1af93c74232dd9f59e2ca0a66aed4", + "51e0334c027b5e43e7cd557e301bceb0189903a189dadec2a11c1e2d675e3e9c", + "32a3f110862c9340106f2dbba85c8af5d7ed575635d55cf2af7637a59c83103d", + "b35bc38a430ebd8c7584d0c90bdaaa15aa252b3977fcac456646e839746f9c0a", + "87ea74b899c0b929e83bf31a8d146faea3d26140ea99a3d0d3e0be93a4d6eeb4", + "e222ed1dfcf6505fad77b9cf2e3e38d0ea86fe89a969b88e729ebdf2e6c63f0e", + "d5e92da54ae3432603ea96cd770f4da442f7dcbd0a4f2d82774a33675690297e", + "01e372d5dee08beefd03317d0897d5ef1c5126bbfe00c8967ec9aa05523c5e25", + "369df1d577f66ea1c93b58b770783a74606907713b14ad86b316319d94f84ac7", + "ef7e648860a8b3490440f2a8d541a268b4613f4a94c0dbf685df5973b55f0e88", + "f19f4b29d4d7b70abc4624c01a4ef7ab772bd4feae8cb33bb502f30a11dab29f", + "8ccbea64791275ea047a3648b092ed281acfa21f62a4b50ed111b954e534a697", + "d60a6b4846f218a8369db8deb45972760a1df33c7532d0ef1ae679ba0294216e", + "7aa4638be336fb77bc1ac5dfcd3f5ae002aeed8c42bb46475f718e3c278f0097", + "06a82a57d7951a5629a1153240f6b1f09c150f0b9b777d7c64a0a628e24a0bdc", + "d23d2ce73e24b434f2fe5a6eef2b88eaed4e57848cf357055da6914bb234851d", + "0dfbe791dfc86570004538c64195a7702e33ed121490fee5819537676a021932", + "8e2a510bc84d4600f25812994dfd5b4566c281a153e459cc9460c006ababd093", + "cf73a8cdabecf4323a194f3249cb5b1674037f779c747690eb0d9c9f703f7c8c", + "a8999928014db24e80a27cfb572eacf902b50cdac259b5be80229fe73260ce08", + "f2a13b90ffda09f999ca5d7f9a2cd171ac33ee2a9a889c70a2e2d454dee0ddeb", + "a5fa0377e644e743a38f0b427adefab90d218ba8e9e2894fc1f1f71cd815a92d", + "1a27dc6f452362eca3745218915d24726b72f5d6963a9c41a34b9b7a12aeedda", + "74657a8b361a7d235d94878cf2ac2cf3e18dbd76fa1a88440e3be7ac7a8dc54a", + "15059499248e579d66827fb6700d3c31ab3c1e824fd0b0de583668631f31abe1", + "21dab269f60f67bbce69447b7fec674662bbfe2bf1fb998e164d44e92086a89a", + "a266bfdd44a5969f46985337359fe5ebb6083b8a21cf310ebdf4a62aa16dcb89", + "612ebf41822f25ab638867c1232596a83de60c5bc1a62aea81464cd87c50a53e", + "7c7e8506946118e3af1f5c36f94fc5cddb945e6346a009d21934ea0a384778ac", + "82cc3dcfa10188e9cee8d84a60ab86cef7b564208734ebe8d1c6f4fd583e5466", + "7ec2dce8c901de5c5b5da218d4ca6c304c4a8691568631b1d9d625deab025e8a", + "baf8884aec756bba04cafe11f82e84d5721bc66689a59418e24dba0471e5ed9c", + "2033109b98dec88046390619d056fba7ab1e207591b049b5ca4e5ca7905a0736", + "04337d0bc9948530c0f6bd541442a14cb8da9342cbaae84ffd21f49a3a4ff9b5", + "95a390c9ff01592aefaab09d6409dd618263bd596d5134e024d2e04fc080f9d2", + "c314b11e8ab0957c8c11770b6dd3bf9ba8a281c312f0c25d0dbdb0dab2bf6176", + "9372c5731f8e38ca30845e277b499e77dd3f5fa442f5496c4bdde0512de1e922", + "84ff2e4e6c23f0953aec51c9d8642b1d71c653731d7585fdf021e045ebaa0c11", + "c4472b40a9ec2b01de30d79bafcff2cd4853edb77724a5ae02f4387597deaf53", + "de59785aaed498deb953dc239d06847baed05797fe3dd2420a5c6f35a4ddaa1b", + "9622558b5b744223868445c32fd92ff2ba0d9c56f5b34bbf9de7f3e26206a277", + "80887a104ef050b442514ff8292132f050bfda9b640a840c3258f01893e75f42", + "cbfb1b0ad1df2f84e6ddfa190362225f9306eb62a9f5541cc5deb5da781fa213", + "b01734822f1598f9edceacfe3d828203c629313c7ea0f51111f6b10474e070f9", + "a12cab7ec7e781e90a49f837e6efab253d896dab615aba3f6327b264df5b91be", + "ff54a2a3cae5ac505347c5577e010f713411b5401bff7491cebc18b34592683c", + "99529fea4b50b9b8bd91aee8f8a25cf88dd3612ede12610acb24e73a1fda35ae", + "679c55a753495d26c5ff8ee7e9dd2f6329fe76bc3bba1324f27ff5f3ecb7f8c5", + "c805e4ae5d2358af561cce2ab0ae13a83b68a80886fd11ce5e8200b0194a0085", + "5fbec47c66e62ecc3c483c440f0e39711f414dcc8c0ad72b17325bedb8bce577", + "c8e43b97b4aae188ac288e16f856f16e839e67ee149d8bc8f8bb6aa05a186704", + "ff4748fce9a654b3876905aef033f19030938880da461ae9e05bfe84698658ff", + "c6e39da9593ab92827ce0e81d29fd85003d110bb2e85d530769eaeb0b3fcb122", + "0a1cd405a53aeeb8c03e69eda64aca2dc6df3d599c41551e0e71067c2c3299d2", + "41a3b0883541b8e28e321b9cf326c99f07fe0bf8354e080539511dc27d382728", + "47f3ee76ee3a53b8b6c84de48dbfe40d4487c0f1e024be7ad735b4cb01b240e3", + "4605541e0a8b64ebfb694d092456455e366312df0fdb6edb975d5df6738d5f64", + "f140f701e73403c39925a8ec3bf6fc033df1de9c5d013a0b866b71808f77c3b2", + "0a9f71c5ea3656600032d72147b1fb795f1ec515eb0c0e9b142a51b63ccdd236", + "773a797fa4bb49ec882c2c972b71e509bcc094b89083340df1ebdd64a68f6045", + "bd004ac4dc73dd5304e33abe88de5b3889f233d1b27327b14814f2941f47131e", + "5c2b636057904e70179efc5366acf56b238780b1bd50d5733f3cfd06e4a894f8", + "30cd462a2b16211e365728808b2dea5591fdae5f8e7f816300440e255839d9aa", + "388cd1a37c37a2e7fc75da6363e894618c7e011f6db275cf64011312f3d377ed", + "f88beb132412107250f73babe32b820fe6b277b2cf21035bb4c36e7b60a9585a", + "c38eb4323358d7ae5f9aa8d459dee525427a9a490650246a2e8104d1346599dd", + "212947c08a738977407b769756f1f4d192eca956b07413bb3f702402aeff1307", + "c6bb090f1ea6e4350d6e71cba3c096adca356ce16d997f48dd081eaeeac50267", + "2da0a61f2510fe2fbafb40b27ba5aa8d0fac8254d7d69e475fdff6f3777b4d6a", + "785fc19dee1f3e84307dc836550f5ace30b672460267751e6fcd5e33ac46c296", + "370020c80d7e1f3f53566b5f51931a1297fa1a4efdc81ccf7c5ab72e2577ae40", + "259bc6129f4c136073173a83e0d5d5954203beb997ce140d8f69d7b950e0187c", + "11cb148948a9c92a2c962f6f2da0bc9a024ce63c12e62aab3f0693847c23ad0e", + "a09df031dc5d362e71b730b91a0e84677353406309aa06fce96f83eb77671b4c", + "046bd72f9bedc26c0ffa2bb7bb9679b7b1afc7c9cc72a0de2da2fd9e9f08837e", + "9ee8b7df8898711a797fd8816a32c181e641ae77f9d04f69fbccdcf5916cb668", + "98813c8870bd227cb607e97a296195e673829e66f6c18434e852f36719fe823e", + "5ff5c13d156c6c20c0018848eb761cc24a1739b1a2964e279e1841d587ce3441", + "11caa777f57c824055e6cfec2281deeccea4cc6a9a0cd971ec1b9ea7de49cbca", + "3a8e126522f84bb01239c8d1c51bada77fb0e3e76d51ca82245cbed04dcb7739", + "860a27fef1b6b537a6be688753599f84f29a2600f462bddb5180f0575cf10bf7", + "0bfb8295a95da4b08f084135ce5b221f4e5ea4cb7e63d96410e1b0161f0eb5ec", + "684b917015396fe12a0b84845c805f3e07534070e1a0410bd6319f40704fe392", + "5809b9f126cc43329050dec45c6ec7215c588ae85a0647a23816a73e2eee7e8b", + "21ce0014bae97d4599230abcf322b031d49fee0f48e578f173b317478f0098a2", + "79192c7dec85ace4a9cbb6daa82e76105d10e988ffabf1050a0b27e1eddc706b", + "9a4545f77055b0320345d7f147adae0552191e0b77ba53fcd82749b5ef45e334", + "e154afdc575fcfec7acc74e634eb767a87354b7e4c9277d6218174919f21fd7e", + "12c94ad4e5fccd6a8a24229298b1aee2efa0f231fb209aaa4581a5ae970e4636", + "cd089347c67b34ab0d80db569233b6f42792d9fbb08eddb769c5d2c06c03b78f", + "c95de3ac861cb2435096b17b6a6e96880331ccfe730b15fb14771ce6414fbf36", + "b355cc801fd8712315d0247517eb2ff1ffcf90ce66e786f66c0b70b05c3aac13", + "ead6b1e3c8ce5380567394714a8d3e71b739a128872a4310fd52ccc0cd015bca", + "b2aa10099a3d806f3b5fefc4cf5aade2c91e518a93a03eff4b4bfed37aa98bc1", + "ba727d2be80bdb3b47b860b7588a46ac93984db5de47ee999ad946aedc411a3b", + "5de5016468e7f8b981bd5f1b04b270eb7c5ba3665481b9f2215607137c5d6ecb", + "44a38331d4f01c48fd89cb306be7d40910f61e85c01efa64be56007e168eb704", + "876407f1f2289e2212c3f09fbdddd40e4d99331e618fe1fe76e54c0007146cca", + "3c12bc0d8d061735b0b03371c4869f9ebb423eb84ba6a80bdddf863601287190", + "35426258f74c650aac3cacfe5294871a989306eec1f93c85f34648d3e1e64b57", + "24b0f6118578f557278af2deaea6bcff73b7c1fc2c034f0529b804b5a3667f7c", + "a853095e6abb3da36dab40eba98107044c9bd706a59569c27df4956af6a2867a", + "7bc75c278a522c3f2fe32b83c4e5ac1f002b970424ea9534ecb70519e2ffe1f5", + "968a9fabed55c4ffe4df25e7aa61da56e67bb39f407d6c138d8949f0ebd2d654", + "c01b876bfce242254ed98ebe8999d5bce9d6aa8a0afcd643f192c9c58ba05697", + "0f25ddf33af868397a40586b9b29c759b5f48c5d5ecffd65c8025eae40cd455c", + "9b2aa176b8978b1a6ed7c1a7ea238fa59414085797fcc91a2f0bcd4ba2a56b17", + "454807437182e5bd112654fa4dd079fa0ec3d701f61075d1671c6300e77cc3a2", + "dfb43a5c2c31241a80a316403612fc16007a8bb04652eb3316bbcf1a0f8783ef", + "f0c2b72b9e3f3e0dda04e2c94ad826890cb8fa891b519b63a1646fb00690772b", + "8fc8c2fb737c13eee55751ce1dc798fa291d428858e1cd862eabd89507dd0f65", + "a84481a8966b29dcade6a3e58692ad8868a0b729eeeffa1e25cfc95f7b3912db", + "91c8e0ebbee601828719aa29d7664b5d94e3501f3778999c60a975ea74bb00f8", + "86862070933c5b4cbe7d1b29e15c728766079109dada7f51ab02b03796ffea61", + "2aca32ca5da86a14878e221dd132cf11e9f858a1694eb66c29cd245ed49a81c4", + "621a41b83f8e9299fa89f2f5245ccbe34ddf352e1270c6ef094161708f86edcf", + "23984583b0ab9321f11a5c054ca891c1b5940ca0ee0174a701ed91d8f7c52d33", + "69dc06a85efe0e51884bb0e8215d50ea2d2cd59ff9708079e90434bb2412b217", + "96bd0d2f1ff8360f4846e79d20a31b6eab3705f52fd737741bda0568e629acd0", + "7df4dd373b3fac18a0ec775c111b47f96484f3270a6d5d9b1455d4337c031b62", + "65b585500d0e4f021bf4f625c7f48bfb02d32e9e532ec9cbc0c264247f9c9446", + "28e2b11230c728131c2312f86434b934b1e34e985c1601fa17740b7820775fc1", + "f1eec6722e3a8a3626a3b9cb45ce9c80fd08bb7e71c38d7b637aa986c6206273", + "cd5c00f5aa138760efd51fabfba83c0964c47eaa2e54f95b952b437ed66a5a80", + "16557e57e3b29f120e52c5d9c08f07f93340bc79910a29953df8a4fcda6b745e", + "683fca73c707ae662b516a2c0e2793e6a764b990af2f2ce8c3151ea75e9379af", + "b77d4a94dbc5ce109210288aa0cd3edc83f671ae9a9172495e705ea902ba90e7", + "5506fe1e6126cabdbfdeb4cb59bf30bf21edc868046b263d692345649b3fd6c2", + "e94b83fb44f81d60f82a9844edb2559563ffc876bc584f4fc4e11049237ac60e", + "395cc58f6dc01018cd334bf6ac29b8f5ec3385ec325470b408bc2e8d9af11755", + "3463442e45c1c1df952e9760f91b99ea8a0fb604b203c27f03a955fea0381ceb", + "41f4c041586f3e7468fa44de1ea745e3d41b9942dc19cf35b5565978449a78dd", + "31b31a285469f516015a5045dd8434642b2f55f19373b9e0d5530e843deeccf7", + "943b282f0be57bc8c9b5ad4f47aadfc0a439af58522a4495b7cb36e0ad43a6ca", + "f1d7d6d9dc97dd43e3b76bc13c6973e31572fa1792335ce0387ca171236838b5", + "01cc0fadd0cbb9e8aaa0dd49291e39f3f325030943c8041178738bfb662a716a", + "d96ec821e29cbeeb8b9f25f7123c8d5d708159daf8acd12e7a800c72521a4efa", + "b5395de909f1cade846176880210f4a560ccd3180f8b70d2d4bf15325f196ddb", + "e447d8ecd54443d4674c84cc84763a18b9161c76399eadaa82dd74cf31535064", + "8f47c23e998213388a6abc7d3b5754336bd0eacf4eb545b0825abade1ae48feb", + "b2b553a0af737c3912207071469517222d3d3704c7c45bed01f3cae1b50c54d4", + "ebd2711b2330377b5d7fd2f39a720b896a7eef7d7f0ac2a1d0739002a1511a00", + "d62a3142e4c4a2c87e8014012b804805dfa6f7d5da1a270f7c3401479a3b2f51", + "0142c36fee839b588918bdadaf8c737f91d61b920dfc581e05335f6f2cbc3310", + "e729c42028460ee33d01a15c2e369048bb79f8ddfd70a22dccd43b1c58d41933", + "6fd227ca8a63cd5aae1cb590c6d3baccc3518306e22db68095de25f0ccb4b49f", + "80ece14b164adb418170ba9feaa4d352cfc6cb1e31e47b3239163d2cdb54eaf8", + "04205ac29beea2aad32008fe47c9acc02e3ebc9f66c16db685c8f0976f3562aa", + "071d3e6a4659c37fec52601fe99ccb4d2a2480f3a4a7bcd16f12ff21fe84a666", + "57f2fce9dc52bccdec3de4763a02e3700d13953003190d560a8ab306b81c1221", + "076c6ea3b84d7f2535d034e5371378e9345dee62947ffe4de3185d02a2152340", + "0e386ca6f2161a1c64a4072ce7a507e1131edbd90446723d84a41dcddaed4324", + "48042f945a825bc8cea4e8fef1da5c85b252054137adb5e123dbefbcc189db93", + "410d27dddbea610ff9c4e6354b187fe8a2f7c5b19c5563e2a1bca1a4b4b8c6f1", + "453ce5c10b3de7a4fd1b0bd5f96512d05fba41117299d588a4740585fc295590", + "677e34874e3dde27ba4a987778de7b1a64b6c63051f3cfc774ebd4c3fc605b7a", + "c9bfc40e1be4befc1b44e38b471167667f6b12457d5c82662ef3a0a0e9f66e2e", + "4cd169e13f4aca41b158924d9f1b972aa4d167d6d9c116e7094a7a06d96e6387", + "aad5a84ac08dcbcd06b4fd5a0c5d2c1af8eb0d6568f17b3d8cc47d064b72a3de", + "4d495f70907d93c7c1142990927f1a3d59e3218a34db881714250ea6e9aea84b", + "8c43d66f0d23b88c17d19aa01c64ab1d2af7c43704952326129b0dab4453cfcc", + "c12581dc707a3a6da47886aca1c2aabb5babcc0e0fb21808c27c6d5a939841db", + "2c63aef84a0c812b53e37656cc6d5e8f64f8e4f8552e4e502dbb1aa0319e13c1", + "86886a41f2639a791085b6a2be4f2ac4f76d951880eff5cafc722785ab4d64f7", + "73bb646c6363f96e1ecc19f5ddf7df417327b9089c1b70a867f799af03cb3d66", + "ebe518fccbdf53bfed56fd1f8f3b114b5849a8fb6e14ad7bf3f2a2baf29775c2", + "db82684a4b314c554fa6139c4c7ede0245f68b32a885c819efee12ab0c027414", + "5a985ff0be58aa5a46a0f4122cb70c7ebb937f7802a33a32d8262e63b3771796", + "938ffa6e22fccdc71d6ca1544cece54518b9fb75cac5736a0468420cb9f504b3", + "4726ef072888e9d5ad65342790b285c7ff024d60b084c32377eae6faf67db536", + "57292f406904048855148e3dc04cdb08ce05a58f174867de688b252e448096f0", + "ef8e25e005dda6d6cac7947006e1f688c743efacb7633c34ebbed7f87707a3d7", + "b90e5ada245515dc90c2fec028ce8359708a610927c3f9cb92bd93eff2fb59de", + "32ded7b174c9528307b8042692bd00480d31db9683c9476a4f50085aa6810893", + "532a40b49048d2695ab6ce15572377ea0befb57bf6311e5bc89a3b579561837f", + "587782fd3913fdd01f9f475f25e0704ab834d89fac46a0be1d88e54de6b50701", + "b91389a5cf8a1c10e890ee4c740fe9994d48e999b59a2c6320397a389ec7a316", + "6e737036b6c0c9d72a72c8545cc835d4cf7da70c5ea33f6c6003fb844565354f", + "2f239f75caba3909f2b6f059a782c2624a71b591a7e132ef72b4add3c9c2d738", + "95d4dd64bcc89d80efe84c9fb5d4a17ac7115d1b38a0ac0262e7687905eb1c4f", + "4487d612260e1a6c676d2967cedd84cc5adee2ac845ab2900aa193a1b86fade2", + "97edff35d1872b6a95f43606f084de35aef0292cd411541243bf7336a905a938", + "9001c5d627af968d68f12200662585921aab14619935c8be4775c457e2a40867", + "ba959223c1636c7b0d10b43b78c1821e7af6f6aee1f80337c555a470867c5ad8", + "9f8b4937db8172a5e01b31310cb3b74824910f9c52c356c17035d70173794089", + "1750c07b09dfc52f0cb0f09f4560095db135127cab4c511c525f560c42025abd", + "a51ee98093bf11eb0455cf106b3a4e027135e870e3e5fb212c42d62e190f153f", + "35501a82a47804cf7718d6bd2e4bb6fd67d7639984eade0c1232bc2c74588343", + "9c3192c2435a9cf815b717ecbc6c539cac03c7f582dbda889b39485895f3e174", + "ddf04cdfd5366d08b5886ef736dbf547a403f583cdd7c089f9f3c4239e554210", + "2675b30bcda3da6a2cb6837e614ada858c1bddbd42e6c3963eb220c290fbf967", + "dbd9134ad3f09406a76eba91323ab3ad2a4fc0bff4975b2cc480bd4403ac7560", + "2929520c43b14ce24a94bf56870520ddfe4d29bbcba0d0e1c4624292d8719832", + "f71513cd36ec057435881587013b2fea181cb57df1cd9798e9b4bc2b0457e778", + "b83971a9cbba7903239a1d2a5907351c61028b952d4dd207dd49f6fcc9abc13c", + "c3b7e679bd4d0dab1a2e7938b0d5cd8c28200c5da9534bda97ea0f1ea81c4aaf", + "77ccaeb09a35c5a1df5bece681062f6ce9fea08bbc2c2075ae235946e8e1d9d4", + "51001f07d1462cd9312b077be915dfd5225b85c3ba760e2a414275aba5fee655", + "ab0f6f8f5e58803093fff168c38de2710c2aff8b47661eaa3ae2a337c0e71ecf", + "96e9ce518bea74432b7aa4c857b2e2423a4b3f48275fc4b48f87caec87800b87", + "60c0b7a0ccbe7792b9332ad6072636d9790ff736e2d83a12f50e7dfec65a2680", + "38175abae6191a78d957e3c63121036688cb6b8c46d42d026551ad37a6aeccb1", + "48d82a8ce43a65f567584bfe7fb28cf8c84bc725c73413b8768d2e6688ae77f9", + "18621d94f2733bf9289e0353f066c53d95ef8dde449b2e160e28232caf17df74", + "e86c65449e6b871ac6886526107b8d7bfaca468ef270172576bd9f39fd208aad", + "c32bde208f9087259fab6b053db25b1b136b38276942e2420482855a5d7ad17a", + "dfb55a5293f934d40ff7c0a6749aad57806e571df185f592044e69e680e995a2", + "8c2a1c5eea4c8dad313bbc6beae0c4d94828fcb2a4877b19761e75c74fc10164", + "eed8a70bc0d94fdd32629373b50eb9ef830282b42dcb1d8b96fdb9b8a1797df9", + "2c4735a60cfae0273c4f309be579a51c6ebbed0200715a826048b1ee568d4934", + "f7e56418539529401d0780879ad1266aee0f0354da07486ceab82011a069deef", + "aca86fb3d7d9fb64e920ee5ddccfa78227a0a3a3e40bb3b275f114186e488c78", + "bd4b2c02f07305164010353383415b08f570fbf3fb297df74a38d6f6e3ae99ab", + "589908e264b0c91b1358f1a51f020daf473b7e4d5faf493b3625782866b476ff", + "70360b520b95938174c5b0c2400da3b047743e60d837bf567767732d748e991a", + "a54ed87456ed361b714da5c56fae7c4cfd92f8ac4a6e8e901a565241a39569dc", + "9df7afdf237605066f98cf95611f49f5c5f383a080c86dc3753390c5627efca1", + "6281424793e144590ddb68a85972c45272a5ca39227f9d24d0bfe78e2bb82580", + "13657e27b82c768942fab407deeb645ecfd958f506a6e02c95d119620cac86ec", + "cc67906e35cda000740c83f50b24b92bd04c58f2a0c45580e9a134c284580804", + "44e92cefa2c869560d185d18baafa780bf16f148d1a7ea186f05597b18783606", + "8ae261dd18679d8e203e7b4c8dd241a51634a84f83855b559f7ba55728d9c9de", + "0269bb4c3079709fb80f3aa451a895b639e3b7db83360a14f078f26816979dae", + "2faf079e75b3e9e0298fb15ef8c6d53715cba427c8c551e98d44a3c46a67414e", + "745a15e40bf06483cf58188b9c7df99e796754dc4bf3eb0547b11fd8016c5c66", + "70dbd06038f269ead55d560b7f960d24c7cb3a9c8864fe68c0daebf315cfcc9e", + "1dda0994af0142dc5f71646735ed3bbdb4f6eb40955836db16fc7524eff22ed7", + "492af127ff75fa55f94ed919386917778ed9c51e4cd193b75d82524c186a1b5d", + "5031aec77ba13f73b21e5cf92708f44c0edfd1081c89b4630a0ee3a50b91794b", + "859b7197bd1a5d6b8dbca9d6c96321e706708cba23866d44bf3f014deba6a4bf", + "3108f998d14696b3e945569ca9b47346b26f8a3700cdd444f545fd0a03968891", + "b7cde7af47f0df23419ccba57da19a5ff7645fa160e136d43c827f41b460decc", + "18cb7236904f9784d611eb67b53daa31efac1a8a9a7d2ff47dc34255ba276a57", + "1670703aaef6d8c722bdacacc31a80dcbac0326dcee64becba1339bcf630e330", + "6ae67bdc91167c7efd005be6881b58d24abde9f8c3a71e6aecdb8e6738f3d169", + "3efdadf9ad79dd00eeee1d80e40a8297254f249a9dde4b4d94b656d58338fc5f", + "d14ad9887a66067658afeb25a64866bf90debccb49cb8213184b807e9f1284aa", + "7f2bb055f5dd834bedc5ba9d63b3c83322e4325de7866b2af7ff8692bb2b52ec", + "729e0ab27d04bf1c14dc28c42ea56cd7fc1a8207ef207ac34e676577fc32006e", + "3aea585e172df036a10a0a3c26ebf4705972eb3efb5b61602231b8a2b2942b19", + "a3b01753e934a16eea44aed4a756b7582aa1b469ac462ccfe4c92268705591b4", + "d5af668caeda6f6af27a778359ce9196ba091cc04d9f9b3928f8f5203fc3fc88", + "a6319eb545257cd4c849e2cca4c6faabea9490eafa3723434800331f222b86fb", + "a4f001d05640267e07caac4cad7122771451f3f997caee435965cbd19e64a621", + "f852577eacf926a5db6921da93b4a78f00f57e06fc064ac02633976e35d9e00a", + "c48da9db46193fcda552476b8c08fe35a73ff1bc1950a41f3b1ec098d2f75a47", + "7b26f8d832edc5d6c025be9189216766b58ab27f59e7b253d0ece4d28f51e2d8", + "cc5b9121f74988f1bafbe4ebdb7296d2255a4af6f01f6967c7391568c32377ce", + "29fe88933391cedc886475d8aa7b68415a7f3e80e153d8837cacbaec3f47d8c1", + "392cf2ab942625fad754d738f33732d3172f4213a8606368330f72373ec99c64", + "b9ca5c8d1d64334e586ba1f0303d3fe802a03eca9c2aa12d2f57e79836d0b64a", + "c47d0f78a14f36ea4a9d1fdca74596a88f93dd9a0e746d97c91a79762420390e", + "74a1f274dda992663e03a2a10f29c84d41e2e202bf56e658c1a42ebed46db202", + "dfa9dcc132701d8b1f4332e2a6bc7faae3c7cf11ac0909a83c4c7e30fcbe7dcb", + "35d4395190d570f8288d255533eca2fd7bb13e720d6e8f5a7113094f97538c5f", + "70a16244e7480d092b617242786deee67056d07cb15365fe51d0e8ab1e8932a1", + "a26843f48f4f2a218b8f5b219147641ca236a1abd96905d8534350e570b164e2", + "5d9eed350175b4d1217b25e671c2f3c9868c8732b6dfa80e62e4a9bc7010dcf6", + "72db3913aa066e04662cc170f3363578fa47799b64561d3b9e84598b43ee8c26", + "e406db4deb9cc79c8eb7d658c382c2291668d7917d73c652c415f37a3f713b0f", + "a0faece933213756d22239d1a066c1730fe58b3e815df52c2de1fca102b03caf", + "9d6bc10478d9345260c53c40de93ccd7b1f344dc25e6e4cc8199e1ed9c6dc824", + "ff1a79b9d954943147a0b29d67ce6fcae1f9f8d5476c2b8eaf78ac575f95247c", + "ba31c30e4a28d5ad22a364042b7155e5f4ee008929f22eceb3fb7371d0c1c2a0", + "797bb553ef79e09eb1aa5400a889bcaa513583662c3708614e29b202cfc3b80d", + "3df07ae6890b2c332556c247ee91abf341c5dce226e38df09f7010b1c21615f6", + "30de2051725d9f0fc371cfa55aaded73b12e0668b1624775b657ae0f41ed8daf", + "035e448afdafd5069a783abcd6e268a82f43a52d9ea805c1141d329557d2029f", + "1cd59a8ac8c61f8f97cfcd1e43d633f41f0b3b3782e3deb3d0a9d80942b52077", + "b1bd938d8288167f4a985abf342f977cfd1c2f1da70c6037c9bdc4295a705726", + "418297a2f0c0e5c3d6b2c4d80824e1a909a61fce6c18bc937720eecb1ce3682f", + "06031df90b06aa84d58da9de78808f7b2cd7d563f41dbed683e74bcd7884720d", + "28961517d508b8e963f98272dcbe636af5f99e6fbf436d14b9b64273b17857a0", + "bac113e3e3572e90dad38b0c2b32deb78f9027e59b75db03d0fb86c49c2d6d9d", + "26a5e2f817b0596d95c8a82cbbfeec6971da2247ce1cfa52bc5098ebdfd3c084", + "ba7bae49a577e15e14568cec5f779c4700c8ecbd21bc8cfbd3aa56eb0948dd27", + "90bdd504836627c40d4c14d54da1b3dc5cf5b6b1cacfe2e220d1aa44850f6fba", + "02f1553ab9b4f4486fbee6f5735ffe0d9b305feae9a120df2ea311fde4cc6076", + "2a9ec46cca5184106d4df225de39422136c6cc995fb9d124bdd640ea45667a13", + "28a25bb686696fcfcdeed1a996ff4f8956dd4c41587d599eb78a83ab6b1358c8", + "422bb78c5107f8f0a5e1a253347a72894704cc4d8d1f60a49a49f8c5dbaaf298", + "74f3c43fcc95c26354129e18241a1e747c78975c4bfbb0b90a2b67c117dd85b5", + "8c0f5a2aeb81ef4e5805c1070bfb8667ae185b59705c3cab2aac50363327daed", + "87deb9c6b62b237a5f83b7333776baeebb4eaf804ef78b9ca2c62ab92388a697", + "42897cf080c2e405a77fd501360a7a86d0a1cb7d573f5fb07e094bb13a942f03", + "0d3b80df67851ee640b906d761fd95ad7e47d0186cdcddc59a10a22e011816ed", + "dd222577e63a176e688a09834ba7dfbc7d25af9cecda7b130e48a25fec558e2c", + "f6f7bd7315404bee4a9611e69fe5d3387de5e4577ac28267d04d69946f42f18c", + "03cce690c88d34689a3fa66b6f8c9da75747ed568e63fb20715def88e7228b38", + "d9a4eced4646f53c7840f817b4296b5bf16e7b3d5e906953b6302d7b8a848dd9", + "319741447aa9b6c819cab13368125a52f3c35d264249d5b4068d2f379010f4d6", + "0a0f3eab30a538a23ac52e4f251d383d418bccc96d5975446e093cb424e8c247", + "550942f02799dc998561d8695fdafc4554c5a89f12333403cfd7c4c9cb15bbec", + "1b0d7536d6774149b4ed8db577268f176e4c1f76e88c9f956d51577d7349cfa6", + "5d27bef5a988fb678c4e178b4b1063c4151c5d3780c1dff1ec2ca898f8f8cb5a", + "6bb4b71ce2a72c465e83586c39a2c1d2f77a47ecfca14b45af365a4ec29448ca", + "938e0c409a837260a9fc27ad5878f3b8936ca0e6219ecd3f9bd4bef6c402acd2", + "d8aea1404c3d5fca7289e2512982bda873437b43e7b8b2a912f383340f821c8f", + "1f4cd386950035ca3cec81cba5d6a506620863056da90c7dc9a5afe47d0d9dd1", + "dac066c881d458341c7db30fde7d9197e498ee577bb4f0e0e48fb859f3790143", + "09381cdc27896ab6c73c341673a61927099eaf2d300d3e00ea37ea6cecb262b8", + "65f65598a48e7b2e38efa51d454ef1f26be016c6a70181e26bf35921998671ea", + "98d991cab4e708f9281d1f56b1a507026a114ab0733ec2dd572bbeaa22fa4887", + "1c2a4e2bf0dc9a09e511baff42c297b321cb8da45c18ec50e4c39ca2465d7cd5", + "327c7a6d05d522536e61aa1fbe8fa19f781f915e20a66a647ea90abed8879fb4", + "cb46cc08d32b5bc7ae9480f30884c7670b8e8694d66d5438e5f8fe81785dcd1c", + "537fa4c41690903a48b7a632249144f154c5dc4686f51868df0159a0350a4ea5", + "75a5e4322582d7e240de8e8b0ab9ad73b7e283428537338b41b35107dd63ea2c", + "1f87054001c06863b66f55b3a897ac0d76f361908900b30e1d8ffa46742bfd16", + "22197f4735d51aa39ef48c88c4d60818245fca73b98df8b9669dc434c6ac8796", + "a2e14dc37362b89a5b85c57b3b48169adb2e6dd5360b8030de7a0e028a6db3b2", + "676f7706eb7a57397f6c9f69de04cb037d3a1045281b8ad8390244b9afc7a2d1", + "8defc187fb1f0d7bfea657619f9a020f303f0cede255b473d18817b500b94b75", + "be1166434669922a764541789705f9d86af0587241cf3da9e33a7983d048177b", + "c5adb9840c2cba41c6e49f9e97c3b78d8d0a1c4af88929e89af205f75f61cebe", + "a83b4a5b3282ab0a2dfa4b1620dd0e3ecefd89cd43fcee45a1a6e4760453e9a8", + "ae2117b3fadabe7e3d94153da7e68c6d7b371efc20373aa8e1d9326ed9a3b369", + "a0b5970a81570a1b2b4d058b32ba5f207273765be398986490ed5e9109d07863", + "9ea221fe02ecd3c974957ec24796afb677672fd1ca770a13ec9c3a94135ef06c", + "a8316aa74116d533973b74f528dac5bc257a3e9cfdeae39db7d8d32f38085f4e", + "efc5250bdfe42346a1d032ef4c7189cfeaf303af28a12b3be074daeea810b171", + "9aa7194fdc5f39e9ea59a13d04158913df4c87ed385b32a37f216c17abfc69c1", + "a17f4cce7a3aa86d9bd5d51f3c38f235fcc4b086a746030af939997d05e54fe5", + "98b049494b468b0c6cdb3cf382283110668d919005678e4e8a5550a892ce133b", + "1ff264905c3f040c3fcaa107b7df3c38ec0dc7d59ddf9ff8780d6aff0381da57", + "ef6077b9e143f01f8b500e2eb6b6ff02043ec00befeb78ee44db5f00d037bbf9", + "ca1af382997fd751c70fa1fea1cdee6ee1d2a22246463ed7867d56e57619aaa5", + "be8045324dcbd46d31a1b24be314d5f0ed7bd1d1ca67bf71ba5cc43043d5c9e3", + "70455e4b87d4b01585564a77a6a414a53338c05a984de6dcd14d7126306e22af", + "5bb2bfe0be18cf76e3f70ab3c6ef2816dd988074d43389a3c4479703d1a61041", + "3c95b917d6409a12360186d7c8839b33d6fb9a7f66f2e0e3a887601927d14043", + "0259cb83d1129316ee620361341d083c43690ae542e5fec01f887acaf2fb04b9", + "9aa9b67c720b3b2fe3eff8732fdc359ab15ed4fe1092e289fc27b7e55a9c5b50", + "1151155d7be1017544c0c2b7432ea1f67cf3191baee64771e9364f3bd1da3708", + "9c45b3560ce8419825ae84affed5223683fcccfd6e35247d1c03f4b988e8f48d", + "a3242aa0a01b82c6d5faf748c22a616b950ecd30ef0f72685aa5860f9320761c", + "0f07485d78018a506a69021080b41861f801ddad09666090e53fff3bff0cfa92", + "e819c861150099438a5ef2613bbc8e4c854cb8410521e9699fe90500a9127443", + "4e78cd42978462aaedb993b6a49a5ba1315519db360522a26add6a17c8adac8e", + "b21ef6dd5976ca60b6e335e78e479a655409da184e68c10e0b4010ff9d208c64", + "e8bd4c3f3e0a8345ae5b2b5748060b15ef0c7529fc3eff080bec1a9a77584592", + "3c8d2c54c7d5d826498d730a289d36f903720b7e2ce1f699ccfa152999b5a0fe", + "fb6e025eec367e2bf4b44af67ea674d767b77397d0568528e7fe3aa7574ae730", + "9236e92010421a2c1674eb0a8044ac2b215b605c0b94b903f3a2872d0fabd344", + "9ea25f2f9472292aba590e298fc5ff15c4cb22608b2f0f2ce1bf26e148920007", + "d4d18a3b2db8540a7bf72e0cb14b176919f995e70f0df607489d5adeedb0a932", + "e6f6e272b36731484782bcd4844087cf45678a05d8b84819f9d41f5376f0f8ca", + "5088a2b878336dd33c3a7c55c09aa70bfb1d34815f031d4b3e92402c7633b400", + "3d12743c540c798ec2587301a3fb2b4f0f6e1278e4bb15280c247272ffed0651", + "52c17f712792472ca80df50f7b9f96f51a6eb155c872ba9ef460aa6d4e10fe39", + "b97f17e90b9fc70044f8789379639e52f97707eac2863a35e9a19376555f52dc", + "19701f39368b26bd3c71a2f0630b5e89586f7249431843eb6c890045f7789280", + "49ea123cea990f6fb2029e8cbc9ff7ba7e049c32db1f6519306ab980d74d0d38", + "fd574068f0c0df88faf7b54341a310446c0ec94931bc459748e11c58435d6ea4", + "fdd6cc2c790b22471f03a32b906635b4028debfd691a0a57f847e15061a2904c", + "b7a173017905bf55dfe03c1640a4e808dd6a1d6bdaeb27722a7c839766b8465e", + "74b799aae53dafe2131b183068d8cf5fa8f2a3006c9bb9944aa493c7491b94cc", + "cef9c06a87ada326bb03e6a301f53fe5fe6d68df015b4d72f1965581545c7fd8", + "db390feffd9cdd7f8a34acafa77d80889a820c9275095e84b439ff5f05781ceb", + "57e17c1b71a226c7bf02687565e92689e5c966f5c864e64d9a621bb6757f4f2f", + "b8c8dc2624b8aa73874647bb89be2765fda8223fa7055796a59c7f4d1918e8ff", + "08a2553e3004a255c9a4135930bd9f8724423747135c0dfe45747285549e4ea2", + "4d4966d172bd8c367d828eb8a14f853999df1394fda96fdcdf5e41d70725ba21", + "23888052d915017c4b47b5307d25158fd13daa02bef1d4a2c11bfef79a851470", + "52a97f2b532139784f4a6a6250b2e61a3989a2c94534c2434041aefa13af2871", + "73c82b469fce6ce8b6b2828441bd31b66784b0994e57b82d331be9e03cdccca8", + "cdbb2bec43b864ec0d7caf5def1222c3cbe20c79d97c7642350dab42b5d1af3e", + "e047c075c048467400806f61489cc503dfcdffc73d4b9b6bfa470427f95127f2", + "82de0f386c63555542e8a34ece6dd42f2a4671d437e00aca7da175e7d18bbc85", + "fee469874ca3fb72f0871a24f410284c04311446f4c5f04480d7b3ceb4e78efa", + "830d8aa08ae29fa0941efc576cb59975d035139cc27213d0a04a0153e45207d4", + "f444da313f6e7bd9e27396f9a95129c83977e1462f86e7a9b5addb1a7ff30580", + "fa601478a412743bdf29bcf44ecd5fdedc09792c8e469a7399bc17c64e134585", + "cec5ef97aa6a39f3c146c5e3f955bc9028798591d4e38094f38f70e981566d58", + "6bbf46cb35bc96854a8fae0e20935f2ed32ab5a940e7891fa7f268d28ce5d549", + "955d9741503c2e5244f513e2c2430bf814201a2d7dc74e2e5c9c9aeed401ebc5", + "9f2e6590991e25b4fbcacbee5bf7381092cfcd7d4f3e258bf4b41b84102fc179", + "647c657c3c930537bc13047cf886869f5a0699fc52496ab30c997e5fea1b3dbd", + "9802afd3ddb4d5067fd0db7ead8c66ffb3ad1c29391ce56321c4b8f34174eeb5", + "60f782f178f30f3d9321017b2c6591f25a82f957ef1c704c6901fe2fc92ed76f", + "109992589b06b8eeba15fc2f6104140fec043b2031c8df3e9025fb094978ddaf", + "091adb2a123d0cbeae7dd399a39f4ca4a3b68086410e6c7aef0526bb2ad2fb82", + "64143c36d10d8d0659f9431dcf30cec804f4487452d5376639157354bb7ee637", + "9602ec038d85b2cc617dbdbbff0a3293ec62048f70b16a6b38702e26b99074a6", + "1c6b6508f0f0c772137ed1a53cb559fb1aa4b3690f6e1f6fd3ddbe5b74da7676", + "a4df5d4c393b255674bfd64aea4ae82da5175732d39591fa671a233004d2d0bf", + "928a67d956422b29189df8c6e88e345ed2cfc8ac941d06f15a7c24cf30bc5296", + "79abadbacc8e8bcf8930e8295dbf6626e3c2c71b8c4c6883318dd1c72e03ecba", + "ddfc0d445cc013734947279fdf7c6f0d474a622da65db4ab1c0c9cea7f60d0fd", + "61ce62f67c3526d4183f74ef0d457216c7ba9913c9a0243944c972cac6dbf42c", + "9cbe363f9bc69639efc73b9bc02264e29f7d0f8ea3b1f35ba16640a5767c5db2", + "d7a25bfdc1e8bfba013bbcdc8afe41d81511e15cece155e9be7bf1ce81af27c3", + "93f9632756d3b0d8bc72c4718dffaac5ef81630d6bd79364b7bbcc9ffbc302d5", + "2311829f9842404d9cc2243dd149bdb26d210031ea78dfb066e90381a392e641", + "cd08a8a153ded8be5d705390e3bf53cb56d7b8c20e0e08c39adb3689a68b9602", + "8d310a900176ade581ad42108605cc6bf867f21cc115e6abbf2a5e083c2bbae5", + "43c5cb40e94874eb80ec26b7df732cea255453f511992fd61ddbf4de739f3b35", + "a2a9abf1c835b824a9d311105a4c92b3d6ca8eb2761b45da7241f5879d05bcab", + "ec48aab645d64498938781156b6daffadc0ec9f3556b7e995c8dbd63299c3121", + "05f6c84dd8ff3468d8992d9dfcb4e31597f7a378828cc0108320c009ef16ceaf", + "d4d39f9d3724cad98bf1466577b79c4dcdca104ecb489e877fa8f44adb1b2ba2", + "b23a123b0bb6c11060658fd14786dd345ad2a2c699e4fdc7c8de79b1eba41fc1", + "f27d04707f4b1110ffd978f8610b2f094ac0f24c1dc819b59b70e1f64cd86a99", + "42895aa8dc4c0ca50b98c7157fbaafa53fc3d8a5221b660dc86837eed7e6d319", + "ea9f13a6e1cbe9c308bf85b50ddc729abe112c46d74b50ef53d0bd7ece84121d", + "e8d250858b5de70d138213342a6765948419b81b8e8ed65b3763b20ddf009fdf", + "827b1279e3ea50b1b7aeda4a7969cdf18bed45fa018e552190e8341d2b8be2a1", + "3584a32e4fa98cc72c35e0050fc00f46462b7286e9d6ad675c340a8d1e56fed2", + "196435304b5e92922c3453ad9f31a8d1b2821634ee6a29c07543b05eafd7b061", + "a86b7271d28b011a2749b302fcdbbf712f89d9f3efa24b9a169e97e67a57e594", + "d40ed1342bca6c536e736f1fa15aa5f5a01274b4208ac4690a4f8c293dd40b0c", + "513256a2ae5c50af825420724fadd88aa0a77d93f64e1425f768f33bc56bf862", + "83381a3fb9ec95f2fccc410ae48e0581ee4f492881cd0c20c90cf19823ffca7e", + "0d51b0210f527afde630887455f5d231425f8a8aab5d8567e5ccd49441f5effb", + "c584a68f8f6a5d8841d9b51fa9cff1a3aee73b1b4f8c8f378843c79ba5ce5902", + "b5396ef2974c2a1040b8e8ff023a82ed4ddbff2b69e5569249d139ec01dafaef", + "e52c0b2f5da5c4c5f68f4c1b7e3dc81e9572bcb6299d3386d54bf881c43dcec4", + "ba60878ab1ffd5e5b85d843ba049d669ec4d0094330147c3942a62f0394c32e5", + "e412b797b242a5619823b6b33008f45bb0b11bac0ff365b4f8c73c9f3fe6e590", + "50933f5388c97812aff9aa52db014d71556383c63c6b1b7ce294195c8efcb540", + "643ee4638b7d9c626522f9178bce2a53a875453252c46ee3ed4d524ad2710a4e", + "e72b8739dbff60185cfe3da8783fd9af56b7da9974610428a11adaf5c433b2c7", + "48c3cb8b5c7a90728fbc104f8b191a98119f8a2777c00f8c1000244013745527", + "34845f23b0bcf4e5e7fac5f82c059cd08d9bd7cabb11edbd62fdf1b40df95758", + "33bcac68cb4ae201b3d3fd356c62feac888ea3f7e0f59ff0dfa2fef97f1eb558", + "778d833969b28ccda7711e0f6e37345fa598ac1a9fd1c6a497dcfbb0d2a9d590", + "5fca36d19c656d347e71c9ca43df0d701852d9811b758d63b3ae6522dd4c0b68", + "74f0e0b61425240202486eb26a74e53bdf8460203352f1c1adf698be9c01dda5", + "d2b4e304a065b74641f42966a43398a96e85c14cb209ff06d18e6fdd8a30e1bd", + "df6401c07b51c72f6ea2654aa3069fb6bf0d8a3adbf00d926d534400d88733b0", + "3850acc410d21b19110d9f229c63d67e36e2484915693f21569b627b0f3395bb", + "6143ef6a8f8edfb6b40ad2314c0f9595fbed1f55fac3b8abbacd72f691da7780", + "b14ae396d6da8ee43a3eaf5e9a06333ce6515cbf1819bb5283d73a68cd39d2c2", + "2ccb1721e1ed7add382cf8838f7cd82106232c7b0bb7fbbc5f36291936fc8f52", + "cbde7da6d0dbf518da6923c7d82749def1260eb6632f03a7e3eaa62cb3a4eecf", + "9a9df0550f1629c3cc3c539ec83ca7e815ab8c9319ceb91f42b6b9dfc3384ede", + "f48737960c6bfd1b4631cd39048b55cad33bd0fa925a8ae094456466e16b7791", + "2514869fcc6ff50736da7f509dcc55cfe8372f82beb46529d757c0ad98fca10e", + "b21124fa2ab9868f3e7375cebd5cf95a48d0d62ed6fb62ef0e0f08f7337bb212", + "84835fd51e9bd4624f4b6feaf2387bf3652f0b03efd56cde086079f86bcf9cc2", + "868492cf4941bc0fd0ca8c37a30008fabf58e56bb3b41f7bd91e09b43914618d", + "00a8ba1a607be27447cfd6f60447a22ff6e603e347eaad21604bf56cfd3b1434", + "e50a1c9ab7655b55cda75ff82bc1d28ea48cc7fd292e5f7c72e0f48acee712de", + "249df64d683697b530c6013692fba966655c6d4e38ba40fd05a282f60570ee67", + "1e66d4f9bf24724b6ebeeba7f2ccce22cc039cba53b7e7fa76723f1dbb934e34", + "db6767231847109ee8ae86b459f0303e08e2fcf5b4165faf7edc33c66909735a", + "406ee377180093d25cc204105aaa3030473ab112f209ae1dd608ebe115a3f95b", + "0763bf2c13cdd4525a95f123d5a42ddcc9b3aad27c90dfac1f19fe2cc643f636", + "3cb4c58f6597a2e937c8bcc870bbef98fe4a2ef16089c94a0afcd0ecbbca140b", + "1a448036689115f5ad50e0587d114cd77901f23a07070b4d18ae816256d8811e", + "4bc718cc25242feeced0c3f07c5459d30e5ecd91011237dd869a2b1d32c390b9", + "127d4282b45be11fe780f20db6c1737192912449c0ba6b44ac499d9fcf546a48", + "2882d864f8aadec1babadf7ea312ca3774fd0202a4702779415a7296501e5828", + "2c1a8fb6d739f4ebfd01044c2ca11b484de6933d33c6d120c5bf36475ab7b457", + "d4224f53e199e315ffba0bb317dcf7743d764642d93770e8f6eff12506df888a", + "b8d8b26045f477d58aa21e5bd835883ae1a7bf47e416032080750e80ea7b008a", + "fad3f5f3c9d29a2165efdd3e33f0aae9e58365b379f0ac61f41ff8912f7e9281", + "c05aa5280e7bb5dc66265a8862932aeb268162c25ac2be8fca44949f94ec1ee7", + "af2c85d7085cbd5ff2760f744d22e1e5be0bb8db8817deaa14c06ba9e4cdf516", + "00305a938c19feb580e7945aaf55f217d5089185176561c5896d3643551c1aa9", + "8ac01d950862d0598424e3488ede8e9a285dd068c1d401759ecfa3c97f9bdc85", + "33f3b9159541a44f2198e6e7bff59160bb52f81c35c9f80433992edb5ed0a340", + "31244c0e5b1f7c934f92aefaab2e06b0c2b88c98576777d3e5c514a6b8ba97ad", + "72ed3556b3a25c229bb436b6bf809173540368bcc434c74548373c043f4f8602", + "00947670a458adcae6c83f223cc0b2b05cc1debeed514ea4011044ce375c8eab", + "123376bfe0702b06b98915e38ad3c8d3d8d270f2c81db595922c298d980a7991", + "b9ea637bee6f3f3bee40fb76dd375b6419fcce5fbab8523785d04b1c4956570f", + "601b25e3e686fcc9c3470bdbf4fd2af9d008ea1858cef028180e3f62df0fca7b", + "6d5a1265a95ee04afd7df5c9aee4341f31de35b9e87f2fc9e02d2fd7e0d5fa2d", + "77bef4f6fde9b94b5397e63d1690c6bcd553ca53b58c2bfcdf92726590246317", + "27f110882479c6cf64c0bb38d121e389d5ff17db4b216597c75151b78b24b2be", + "3d1d9cc9b494b22ab1df806d7b1e9745d132dd6d72ba4726f55af9210f3df4b7", + "84d60c1d787bc5d15a952183acad447856c0501c2bd5809145f8d8582c97decf", + "b79fa13459e7f875488cf30ccd2f0431cbe8dffc671880c10cd72d7e2be7e8cd", + "061c36fb310210ab07b22f39178b7f451d5142a395ececa1ec783af9c52b730b", + "fa16976c19becae31248c8477ff0e746c4e28ddcea5ee36408c05be86a142471", + "82235891577986898debf0cc0bab31fa765e925d01a27c27ec694103320d82a3", + "39989431d3ac81a61b0311181d499018fb4cddfb74b1afe4a9260b5841135989", + "a846acdc0cf349a8d2b352d3dfa8784893f5038c05277d327341d3ef7de41bce", + "f3cd88ff101e9dcaa9e05998db425b590dc45dadfcd8b8a6bc0ac7e932372055", + "a159443648d0796956f92ec4efc5aa44af0cc2fb7ba97de7e6a8e0dd16638530", + "9e381ca3bd9488a656e965de17a44487f8c0da8896ebf9c0af4c0146ce034951", + "4cfafb85f2faa88a88034c4e04b7a37be20eff7e02d3e21bdb79d5aa7de833df", + "eeae101eeff05cb6a0dee3bd70b7999858b898b8c0cc7fa96a5e9235d1377381", + "786ad41ea86eda551d0bfd3e702622e118b21e619c604772befe546e55d4cedc", + "2c2f151ad2b9d7bb8f52dc4b060d194bcc63848754d0d2983def1eb992f194dc", + "530b5035473e95e2bf117e3ba2d88d6de1e648f8672baa0c9dcb83e80ef47580", + "b34801289f8a80277b7d5fdb602ed3df305d9e915138761eb20cdd42b338509f", + "eff8301ccf9032fb1398c62896f8502a564889242c9cce7cfddf1b9b87dc9189", + "1babe594d5c84b3e67237b6abcf14b9a091368c587958eccedd4ab0367601a6f", + "b2e8b56a474d41acb1a53646d825bec845cb12c7b96dff588d6d1051136dae11", + "7b400ad7fa14af9182259b51e810e16029c947f2509fb129ebe111fdd977af3a", + "842d63a701a80a9799357ac704f2288ab62af3a62d476fd740c0f8f5c35a46b6", + "dd146653c3aabc3747df891c3a6685d24e9cfe4d61e52f5c7fc7df6f079d59ee", + "2564c5ffb78c4c132cf8c6dc06591661f2d7ee12004af6ec078af8aca04b42aa", + "8c01eb1dcc1e13892a782e9826288711f28ca7c716bb9756e49b41c8d9946694", + "f68743f290c5002b746d912702c1084cee82bdd3c323200dc0710381d98a41c1", + "292a6383fd16ec5afa4cffbf7a6fcc8c6553d4cbd2574886f623b00bf12b1e4b", + "4980e074baa3623551a56263d46933f5fca5465615467ea914fcf6e389e5eb6b", + "4cf461bc4b7f8e1104cb225bae1f3be5dc70ea47a3879257d6652b42ee302a6c", + "79f626c481d62c454a9a8e5d008141befb190c0f4e78c0f9796cdfd8b490c2d0", + "8b4ab8a45964bdb5ab237c0e014c840e224ab1ef50cf2afe79f8810613de8c9d", + "6daef9ecc84a1d29833c0edbfd38663b0e5b76f6ae1d5a22cb081984be8a1ba1", + "7cf7620117fda4ba2e7e1392a39c2c0467e3b90ef96ab021f90708e46df97e94", + "44f6020ce6f1831ef60cb293f456a5ed770aa92b1837fb571ecbf00963e77249", + "b53bc8f19e29660d7c2c9622fc080eb3839eee4b601df47eb3ac20aff8ef8343", + "114f8884785cb1ea54087bf3d19418129fbac44f8c496677778714743e678c72", + "640e38abc942b35d60027a7ca1671ef96da9dbda8d3aac61917189e0f1613a1f", + "23bc1dfe86d5be148155bcb7aea39b8cb68a887a07c7159a8832d7fa8af8d449", + "8ad3f2e44ac576c4a9c364fe497b840bae3a5673a29e69f0806208499332a001", + "ba11731e009801147b11770a0822e2f1e6cab3923726ed79efb3ed5675289c05", + "409e700d67c2541afa301f2c09a4a98de150ff30662f25dcce0bec82da44a1bb", + "74aebd9823146c25c615aff0cac7a2a7b43a3678fd4acdba428bc874c2de2351", + "1ab29e97945c3ead550846ae8af77e6f02d77d24fa8d6af4a866c45568055be2", + "729944aa7565a697548f162e0ec9f39a634fec17615a4b1b65c7b68d06da8ec7", + "dbff2d93d089c5fc366cb89c702f99022ccc53ceb39418e2a9921b1f9d895c95", + "170f0177e2fd6e2ad5e96bf1c7d5fe8443f824ca80c51cb19fe985020bac4dc6", + "44321c4c2e53ab3faa16443e0dd56d0bb18ad444224c09a686285528c04333fd", + "2af9827f953129f8eec520d1409be4d9747fe2213e877ccb06a82e9bfae761bb", + "65831a1f6d27ecf91d452140be9219d010695af0fcb2ef3f19764a4f6b3e07f9", + "103f4a32f26764273b7ab1abed25b768e7994a6bcfe4c029d2eb5a30c4e506ed", + "545d4fd5dcc11f7c29a6ba552562cf4d23890761c76ff86d6940dc707368a0ad", + "4ff9f091da95e9e8892bbe017a8fe29a05955c5c62992a78e23d3bfa4b3c7d15", + "8e82f5c6062204966bfff0f299b26b5921358e22e0880a5130004280c418827f", + "1a6df2e84c39918881ec151d620cc74007c0d122020f6c501dcaff8822f46891", + "01ff674c4dc5d40f0a53318e4a210f418dd2e504077f80e640360db4d2470be0", + "fb4b597769166e58a3b28a698f18ad0bd4c1e24d6c0e3cf58cb800b131f4fea5", + "dbef3658df7ce530752ca69e5248cbf2832c1c458f0b85918f2a4f3666d7b301", + "9b651ab03463dbfb3e7dff410e758430fe535d409805506018bb3f9ba98d726f", + "0795034bae75b616aa3d94df004f4b80717c593aae42e0eeea2bda508fd091c6", + "52d8998c0c3ea0a42e6271a43660f24f17e6201dcbe28a4e1374dd20758327b9", + "a04159ad9cdb943b39160af9eba203f3aeff9bbfd933843bf6cc32f40bbc1f54", + "cf250507d4d9f066221a2c5ce190da42e2c8e9afce5a539f09305af5074b759f", + "5fca2bf5cbda4284eb2a9aa26b55f54d43e28d0efb97e429482a11b66cbc7f8f", + "a740d86574c321a0d3ffec5124431c9a2e52882f85520f296bdb230778e74201", + "5543633ba06069b459f10a9174dff488575830c86bd32c1da6724847791ea055", + "8cb684f24f0a95ad5f4376aa8f0d9bdda6e413bf04a3c8821a547fdf46dd1938", + "b167590ad1040def2161bb854d08fc62b8e4ff4a2d60376c1692c87cb2008e7b", + "a00f55c70f539a98a23b51888c892f2b5a9b86328ec99cd56a11f0e5d8d4d49c", + "73dc98bc6b3862a513320e0e4cd61f5021b30170a2d054abbb62e87036f4ea08", + "ac0f7cc6fbc94a64b50565a929ed6aebd4c6c36000dd40b4f826052b60e98103", + "e24d3edf80ad414a6e5ffca904a259c6a28f1058c0488a524908d1ca8a2faa26", + "38d020a513200b0a6d7bad5fe1e87e39efc7f80b940698ba23a78bbe2cde0fda", + "ffb2bdb644990f4751b4795b94784b5e3c00a5d40afa2b110822eb7dd0cf2d63", + "40a6543d5faecc7fd797d8aaf091d5e4ddf5f075e7f4798b36b6ff2669afbfca", + "07ee87b5c221a21104881b0a6255c2354abfeca80be635803a387a3ef9032f3b", + "d3fe1eefcaa0d03434e6759f8b48fc50f4223edec547c94bf072d49427e2f418", + "0ce4d7ed2eb07f798588df1df044d9e124604f0178ca8941e189854daf64f26c", + "4e5657ffdeb3b88cee74452cd24a8d78ea33013f600d0a6e97c56eef47c36819", + "8e5a2d6f8b4d0878a438d7b33ed8bb7c32294968f71ebf45ab51ba45bc3836e3", + "0f81dcd7f649bbfdd0db6fc69ae5f5a7d89b99388e7d20312705ba07d16a4ab8", + "1cc3486005fa6151d957d0cb6e2599df2680055949b81d4dab85562622ded07d", + "78c8021936527768d5cd7e1dcc30ce55ab13ceb6a1209996a829e3cff8e7239d", + "ab1747694e6e07051b6739e3df2916e09d7cd8a135753c9136a8dbba0449e83a", + "25d6f0d435679e7d84147da65006a48a5a0f893ed2e956518bbcd6b98f40d368", + "3554c7682f565a20b336c40129b5f7182239f2b0b1169f39f8421f25b9ad52cd", + "51b263e67a4421f6b858eb3cafe303af0b3339e0b0509eb068fe3092d394f530", + "2bdcbc0df01dbdab2c91c4658bb9b160fe3efbb5dfecdd85ab525dc21033d90a", + "aa0105f9628907cdeebaa743c45a458553942c3e089d64a02bdcaef293581337", + "d4636ae50f908fb11cfa1f338314260f587f26c29c86cd682e5003fccb554efa", + "31ae9547640c8ebad28019e8eb66cf608074d5c1a95aefa065db97da4c1e1044", + "a228c94293d05874a00c3aa46ee128a41d10405c28e5bed3f7e7f548243fafd2", + "a7d5f3e4630be48f88cfe45147b94b9010718e55d6eaae6462a41aac52e866c3", + "56d4a383be9efe8694a572a143649be7bf89b1eb12842438162532827f7bf101", + "79f7177cf2655951fe0fe7c98cd1cca252e7eafc0b81066aab0841f7cd7895f5", + "abc1746768383ad59c647846b13dbdf51d50f9a5f2c50fac9f676531ed6d08a6", + "a1ad8a55e14acf37cd4eb5085405b62a53948065dcd3bbf299477d03b1d8cc6f", + "c731487e9086642d4e92ecbada3dc31b72049583d446d65bda3a09b4370cafdb", + "729c914190c753c6ab1f648ed04889baba5adc43e05102587672a7a0051b279d", + "3fcbf09cd64d4070062dd2eaf9ff595d3e1fefaea6996ab134e9f394e0814f56", + "2efc8bdc74b41138f0075e2603ec227f7265d3eb6e7ca16a7e1696aa83cc3635", + "cf05b470683e4090959a5e6222aabb1026d60907f9df98256ed0dc62103eff79", + "cae8a98b1c421ba34af46610134a71153d525636c02cccad8e0362b7109d7410", + "cbf00526ae885899747bad4c395b9f2c02e62bbf39fcee82565fecb1224bc311", + "b6ddac871381353848dbb438f0b7ef14fca4eb39fbda7b4eb1747ea454b3155a", + "375b66c16668308f1e768c09bc2dbb0def9dd04d2f90c42c49453f558220a338", + "a2387da7be7ec4a0ce7ef03cb1bd701330e5e950be120854efac13d903367248", + "21f4efbd857d838626a5b6eb935c1d8dc3a0a6c8defd23928c8018c4956663c1", + "ac96b176999b37719fe3a67231b811a7a435ee7b5e807fd1430803a29cab70ff", + "9c06f32a7f016a0e702b928f7de9a82297498400654109ec3a17a1c2531a36d0", + "fa932de504e54776e5b8fe75a2aa7067cff79865b766882542608eb31de1f3af", + "d994c1c85ab3611ad12fbbd4ee7e7afb30ff9ce3c67ca619d9786553b0383668", + "2e5789ccd625cf20f42a72de7cdc246ab92a1cd85ee619570549a8effa476ff9", + "ec4145c429fecc6b5109d5a4e1434c4418925fc65f53bda6cd06620dbd2d1b69", + "0978239a7a14bb768e1ca71e1d40e37a102091ad01a75f5f92c5da900761c79d", + "3382546aefe321d644bd3626c7b767bbf940df0cebfd3917c05f69c7973136cc", + "227e8c8045928af96e794585a87d6869c3ced7dab3689c5565cec54e168641a9", + "c0a3f1b8c27d0234fc64fd8d1ff9f6d3ca157b03033e505bd443f1b7bba62f7e", + "2e848b33465df0fa0a2b75d23c4df4f02824f431e78e80c0221ff5aa61348a91", + "14656b6da232af334beffd02ffb86992637993ce4aa530acf8bbc2081c5dd2eb", + "1697567eea595c835310638672cc7ff2067a568198731128280bf4167b2e45ee", + "749a784a544085ed3fe56e3ce9643d0e13aab8f3480f2fdfe7c16e15ef08d0b3", + "0b2b27579b615e086959fb35f7741791615e25c611f8ab7851c83bdfeda7d7c9", + "e444de3a6fc90b1d2626987fe98c956c1e0db2008cdbe6d863ad410e9fe0a8a3", + "89a757ea85e9d24b3e584538cded1e5db3a90bb01066af609d3401d1bb5b205f", + "b00b5a1ccbc1ec142c5ef063daa88aaac1d3e52af12256cd892803aa348c3f30", + "0ea22dee4f62ac35e5b45a3beeff82c8c32231193bcc33ba7575916dacbdebde", + "5c428762fa847e9a97fce0f306c7f0d8c4b4abdf9defcb0df0dafec41590ebb8", + "8eacd0d7d08645ad8366807a5cc9f91a4b22fd52ab4d8c74efaeb9fba398f24d", + "648ed141936c9497702be348e260b9a4aa9ff5dadc6b654381c9e54160a07f4b", + "682cc4ae34101c80a98fe4a674ef0b16d11ce70b56b3a7fc5b191b360deeaecd", + "2b82b8fa930e14bcc38f1b9bf6a99f3987b32c7e74363d20acf6bd05057cfb5e", + "1d2c04136aa4ce34040a9756ba9f16af6a3fad26b7a9669a62c3563d66a4d4a5", + "04ab01586e7e4615656127d95cd558e318457ed1641c2229b75ed676f21ae1d5", + "a7a5c84706284a3c98121e35e89e3361c39ab8b5ce26c3e479cdb4c2f30aaea9", + "d12b3299cd334b9899ce855a088577d8d757dc32125921f2ee561daa94814b7b", + "48714a6b92c6e98e3406ee38539721a3ba60254ace52c741925222955a89a8bb", + "27597f719f1a11522733a0db69462fd7d28f0a64d6cb508af5dbfee291b52f81", + "44c2fc25551c204419d798aafacde9e75b4aed663d47c0cca8c5c3482b7660ac", + "9cfc7a76eafe717ecd56501bc16aa644bac189cab58dbb90bbb0cf3a4e3053ef", + "029223d300c7186ab0c823474f67590a86e7d08a9d1ed39588307def876ad59b", + "533054cb85955768daaedd98ccd79495e4726b2e5e7d345a3cc70beb19538a57", + "60e7f410dd3c32e1c88638ca4e758db9a2a8f7e52e2faee358fdbf5dd3fce27e", + "3ed8f47b67c5cc36b0e6745a29251d0470795cb14f0ad9b30170c0e5c8efcbe6", + "1f889c28d68e869bf711e214dbe739acc35780e8b19ef9576dede8e2e91bdb9d", + "dbf00c8bd10f5ae12f38c625c8af8089cd28ff92f34a1d949dbdc20d7a80d797", + "960ef9daa1b246bb656aed819cbe898dc14ca8e558070c9468f840487c05db92", + "fe1b43ac2709550840c682ecc23d2699e9b3fcf55851999d9f09b02370aad502", + "9f58ad99bdc049a7cc29f7bc482629302d56b32ba3b4a703223945c8a61f4254", + "af61383d241fa4cee95f8065527537a237914d9b1494ac8fd3ed476a604882af", + "503e10b0bd880beb400e91fce69c4fc9f36e3c816ae86b7b8d9729d00d08efc7", + "beb9c475c020f4af8659aa27ef0d1dd143479029a8ef2f605adae61bd693c9ce", + "73b93de4843e323d7db0726e0bc301ff0194b9656bb40bd3baae073b9a4df96b", + "3f3a78847d9964cae0df94acbd73ba24eb042f44e5a473b1dc665a4db518dcbd", + "240d37aeb82815e4a61db5729ca10d069d9554e3f209eecd881d12719a47dab6", + "87f8197177364e5b345abc75771b0418890afaa0c109e2f6da58df071868c7de", + "9d0f1a386ab8b2192f4531281d50df5d354a96afca98b9122198982e0120d384", + "0bd411fccce089fbbd2eb04da0cecc3f030d036a44b78f37352816bd2bec9c1d", + "a5587eb1bb907b021e206cf17beaaeeff2c34c22a9857c0ead14d68f62b1818c", + "ed96c8097a4fa7debe2db8b1aff9baea5ebad839b60b7ac878fc93dd704f9633", + "c244897280ea22de3494642c6a8219500b884ccc77802b953f71cf12de445e28", + "e11b64f780d2e2314fe969a74a86129b8cdaf4d223376843b94f4e64e9be7eec", + "1a7771432e6782bf9a5ef436874bb9aa44a5f46d1a5a7aa785dd1fb2311d9d58", + "215be42fb8c3511dbcbccd1634895857d48d2f26de9f9f167ffa86f141819298", + "5635725b8e48404be37a8c9db3274195cd7e36a651c65d09ee8419c571d1d233", + "3bbe9d510574073e795b4f843f272a1900f31510f9ad36a522525845c90075ee", + "1b19a47569f1803ecc66d1d5163c4c6e7ab9f7e99abe3e8bcf55f8cdbb00ef7a", + "c155578db721fb204f9759119f8ff004d78ba068869c938d9ce0a57e650e085c", + "017d7f31830417c7a0ef240d7837ef9cf047ed0a8414eb085da37fe2cddbdf64", + "78ea7551cf28cb954259a28ec1340ee66d715f02c5eb7fec48145298581464f1", + "b89ee6474841186f5afab538f9ca17b4171c303d5dcc89247b79ce38389bc0da", + "f15ceaf2a19e8eb599bac5959aba3518688d4791321e618b5ff4af1700fc969f", + "6c0be8eac8a473b665f89bc3a310c033c093f5b062527a227921b072aa3123be", + "60950a25415add062c9bd5427cf5e0eb75d6fd7363af9d44656831d04cdcf7fe", + "c8ee6f1b5cafc14571642946e8d8e56191c3a9a020e5322ec30a698258c24a17", + "fd5a24fb185292c845fb8759d924a72f24c18758fe9ac5e0df99d1365e0657a4", + "85db138de6940bcd58f37836e431c6d0dda1722d21f7fe2e519d08ec8d990d7b", + "13a18fb4b02a808771e8a5eb2a64829bf27c7e6fb82b2adf8d44a920fa6a5900", + "cf37460c8ad19a604cd4d1475e8258ddbf72e3163d07bad676a0db39248f298d", + "16aaf7854629a4612442c7c7b67836990ce60a78b95bb0221646cb9d27454e2a", + "f3ff2a33d32daff5a2606ad41d8b9fa469fed175ab2d12cba2432d2cec320bcd", + "3f84dcab7582376089e959b42b47ed237ad52f5e633093bf7fcbff6164b74cbd", + "1776d3f2ca165cc20e0736e7ba69ce320b1e19575245e0e78bf628bf8a3b4c90", + "4b815273385785fe9e57dbe6bbab75423b6302f8daef245c12bc4b8572696893", + "f9daf148d2ece99869e00ed44ffc1a740792569a36d6dc21a94388453c31a08b", + "0a7f0cc9bc54b816a21785f84414bdb99f34a49a3e33e5fa1d105ba026540627", + "05ec349ece9f47e92ec0bada62dc00a7896abe818b213914b95d3cc301afa0cf", + "3f652f63459aba4684390a90687e117bfd79e09b42542733acb734adbb0a01cf", + "d7be18e61d11c4e5757f2ad5fb6b8eb70ab1f621f7785d048baec87e3c81ddb7", + "cb4ef5ac7ff2c34808e9398ff7e313cfb5b73e8b3cf9c0c3ab8059983159213c", + "c1f66f86c3dc26b2212cd7536bd40bde14015a19108d997e24d0f5b1532c5201", + "2e8766d4dd56646f8fca1cc12fe66785bd90f42d27074c71e3380732f5616558", + "feb95b15d26976b6b31bd488430da464d87102d5b35cec8bb578cffc91bc7933", + "bef956f6a785e9937e4e3bad0b1468c428534fc06658738d5e02c1a604c77003", + "fb471f8b762add49e6d91eb46cc018a6990ca7bf1fb15d26aec90f6ba968f6cd", + "d4f35e4a4e006546af1bf09f5e49b5dda60ae5dd526b85ba92d871f4f09df77b", + "795fda42bece1d711437fe504f78e940f309c89830bdb5e9dd46afe3fbb1b150", + "abaf0bc879b70ace0a82af4b9d4f32085ddc7c5d920a673c47e83199959b4de6", + "ae69a784b18524bf95e7b2349cd1c36709eb810b0168e7e140a7a411443a02dd", + "7858fa7acf40bbc2168ebf05e3389ae646d1fd4567b17470506596baa5e00ea9", + "d9f1d5f5ba2a228915342e41510bcdaac533e10025531eb9a1960b93ae667332", + "5a0def567978daf9e17209cf27f261256f11d0f8cc946c888ba3bd15e56c786a", + "589c40f1e0e442e1e227c1d45ddbd799c41cc38d025ae871bbbad98e0b65d2c4", + "d25e897ed507bc4dcbbf349cc2de3d2240ad6bede49c89fb07c9b5dcc138363c", + "b480a7829783c502490abec998b2d0253aeeee68e54bc165a5f1e98cb6a9f4c5", + "ae8c6d400c2d517eb69379c486e1ad35497e692292bbe495f0d01b4d4e48e265", + "27fe2f66e35f873b8094d7e2ced46e55811f5a936f9019e5e2db41b7ae5e819b", + "1d767698a7e6a1d6adfb55c1584880350f2c4e80f40e51776bd33a6a2e47ba8a", + "70114481a06394db7de45673ff702a2ca4047d2715d780884f3f841a493790b3", + "404487dd890566585291dbf1a22cbbb7fd70e4870b36f39ee7c710320aa0c270", + "b908e1a132f73bc705594db6e08b82fb277a87456142f6e6145fa4701d7de09c", + "7f0734a0c77fbb88b5a5a4d49f995bad567f0a676cc9326c3267211d06ffffe7", + "204e168e47177bf41e3066b1ffa5f0779d98fdc7a0ff8ac5399f1c3abd6998cd", + "fae75f8b1c0302a15813cdacc724fb9c9d06d6ac7c6a358b1f1c33a9df3da5c3", + "c18e90a57bd273f5d27751fe81ea9302f5604f24c2a2a9d306e2b0b26ed65cdc", + "dc084d1ec5c777f8cb78fa955903adb7bcc68b0d18cd2de66f9c32acfe9e9948", + "4fbad69a041e89833b72a21907459d2a9985bef82d05f70a8740f4d1abbac471", + "f0d752ca1604efdc9102728c70fc2198ed466b21acdafe6a234f7124cdc3c531", + "96a291d08e64c21d74c94982a2d1a146ca25ef24b4c2ffcdb3a8bcd77288072b", + "d84f7a140461dc63b72d66dc97af315127f8260a6176159f7120ad15e0ffbb42", + "e323b02b16317a133f695b892900cbed463c146310f13847cf52f5e8a1556403", + "56f9986c62059e091f741f5febd25cd26400acfd51302e09e853c49815345372", + "4a91c13e08448237509eefdc09ed41470f72309aeb0f3577bfbdaf2f45b359ca", + "3f1214f03f2bb53d779d05f2a3a647d0bc2550b5540ef159c2917cbab8abe006", + "c00d7a898094e86d4659ecd1ebead956c2d74969a9c7ea05b2ef5daf456cbfde", + "c9f7b7bd863e12133688f089abc3846578a8c50f8fe95d33ffae1472cd61d396", + "9c4cb359614595f16ba7a76a3a5f7a78205c32a23653a935400f147206eb4a2e", + "f81f1b1b6ec3af3a09c79607ff09a025162ddfc14cf85ddaf4ed678599031bcb", + "07518a7b6771f27678761c04c584da6475af520f2358b0886ac0f792e5b07e79", + "3d8edf9ff36d26b12f22af02685c72a8f9106295c7cd06cfe74fd59967501ef9", + "957afb2e2abda010d44ee4eda67a2b70b61c56594a31964f860b26a28cb8454f", + "f136ce60520308496e1802b6de253f4911afe61e910358b2ead474a9cf48ee6c", + "7c90e7fd57edfe1fd3504153c5fc31342a7b354e93038b45140b67fb6fd18e65", + "629c3a127f091ebee42b4d487d029bd4c3fa2ec72dbecc715e6c8c9d2671b736", + "41e8646e02f9bb0537b627ae5f269154b03d916fc490d38abbcfc3ebf6d9dda4", + "72d66600ed0cc1faa6d91c2a0da81bd90d4538cee893aae4bb0cc14eeb4dc280", + "81889ef1c6d2c81067e366ab7185eb2199be8789f114ddd6a3b693ef7821bbec", + "8adb60319e326ca0afa70e7f8ce7209bef89e1ec820a5d70f13d606add534ab6", + "ddcebfd51afd77e78f06d5a5dccbfb4d6dc61977c47ac9b1a2f6402940d9a156", + "b4cc18127051cfd93b1a5eb0358153c03f1d44b841a41cf3c2a7a23a14dec4f4", + "a3d44b5e13a0c0e6eb22da6e8811d4995d495d4d780e840ee85c83f2b1656082", + "eb69269adfc48cad8200b8e31807e9454ec615b8d33b67e7191cead52549007a", + "cc6bed8de65dc9b905e2aa95ad5fd57bb82ee21c49968cf30a6cb4b002be5f6d", + "0ee85d0f8f0f5bfb77f957de73fcb2da0ff2691b2971ea681432a67ae5c7c378", + "d8a2af3e7a30804574e74ed97275804fcc22c19607ea30251f7fcecfc86c8cd4", + "be4f45e912a7cc5a174d6cd1c058412042308381f9882d2a74c43cef9a93e51c", + "b2137011b2206e9737392b735787c1861d5df47690b5682cbd432fcac4c05198", + "f4fcc63e7a123783b643adbf0b97580b789def70d8ce5af5955b35b263f4a4fe", + "1cdf85f46a7dd1937cd43c941f1b555d582f36d3091097231f86602c73c84c7d", + "76cd77e06f946eab3f59cdccd3a3e20395f04d5e61ebc5ae6cfebbf72d96214a", + "f080f232fbf74a104a25ff2df6958e0008662596f81cfb84cd72c2a940c9b5fd", + "70a13b34f284353ecae70355403e6297fbab263f207707860ec22dc8a2da4c94", + "2d1bceb4417eabef500af978c975f18ab1b01b12946ff411832af267652f5d49", + "74847cb2b631547a13df246f10f21053e1d412b4bcc83d2a28285033c75a2839", + "0df6e0d42f19ca95d31d9b762fb58b9ee4ef0a2037cab32e49509c575c6ed19b", + "e9b6acc84e087163ed1ea23f7b7aa0fa4ea37f15473a47af6618d7f30e8f56a7", + "3578384eae5e7a10731d6de0972072f1f568a764ee9a2c2c6152eee141fdd3d7", + "69653aa6632c7fb7c69a8129861e9f3226b726a8804471165642481b598dd099", + "a296c05fca5e44afc5c2cd583cbde4a1791bdb7a8461c6676b97ea63c11fe393", + "077900cf366702fe8add4f0265cc52273b343a14218ea59ab26cd628a38240fa", + "a63d694c0e6728166e578d90061d24146d56ccda4ef281c41ce6076ad51727bc", + "1623e53bff3af6346b27b38cfc0d98f6f2ec80830789f88bbccfbecb4ce1162c", + "ce5c3ddd1852bb75590b6d8db16b4e6264a8993a3aad89f1731753565d5e54fb", + "3dc87d48cddba8c9aea71e8307e9d803925c9e4c4f55d7730d3e651dbfc7ba45", + "1cd04f320cca7fe33c2f43dd9e0098cda82edf20162a67320e68ebaf3fd74fe5", + "8d63274c4a44170e5008d2d1b790d5f468269abfa91a54c329152433a30665b9", + "89ff3946f2111436afd2b41932c475a0d09223e23b6b9e9a9ccf164d3cbe0b11", + "db63caf3eda1222a308deb317a0bc8f1deb95a6b3b0108f71d2d16691244927b", + "3d1ba03634ffaeb5d906032273191fe2595bd655a70dd8c22680fa4929012c6f", + "0d355563eac60a85c52b4b2a54c21ee7610819a9a3b30e69a9a37b5655b9b5db", + "373a44ab3936104a3d9d26a246d2365127c1726c34d2f1d7e87d7d7f6058f248", + "16022145fe055769433d4c3cd9639ce0f8f668e157bf9711e2c7bd7907178df4", + "6b34e7d77649a72887fa46cb34618d813a32fbb77e4ebdd6691527eacf5fd84c", + "df915fc7ca49225b34f2d419554d2594c8b2ffd834b2bb92f49397fcff381eac", + "6002c0ef73c781b20d7d4d9e01322f8efa8aa78e5e9792bce29a9169c7032f57", + "3a0debbc0ee42304a9edb10db770d0b8180c403971f42dad8832cc16e395c11c", + "6d76b23d46aae2e574ec2947c490f2ead9acf9592e417069e72050721224fba4", + "593602ee2803bcdd743fce93962f672cb877edc7d009be65c02a562b8604a74c", + "64759e44d4e426d1c147b405330e2fe1ed1afe168b8db60a87c935cf07ea10f6", + "d8f1eea3b2c56c9bdeeea5cc57d5fb38cda7c2dcef9276ed5602d27b9788c194", + "0da1328dfa5efb29034215279a87bf026e70aa7ceb2793e8c0ba9354580d552b", + "8bf631c56ecb7ab31c0d30bf31dd28a62877402cd6825d0ecc9c71e26f4be643", + "ae29941b70abb79f690de45e32c8b55f8114b30cae4376e991b963d4e03d970d", + "0b87ec546259a15a8c8752634c3ef219459c382d383f155f219c04d9d5bd7f01", + "87605721bbed02e5f279a29dbf5fb857a5e1d41ffbc50689ec4e1ad1ed3f2b76", + "64d33c169662adb9ae01cc1a902b8725ea84162226d860b05b22c1cf8b716b88", + "dd3e22b9aaf98f165f20c449618ebf776727386daf5695e68847462a1573f1fa", + "2eed687fa82a0df68fb2a1ccd9c7e0679f5ada28b831b105718dbd0580c0aeb6", + "3748437a03b2e086825a1b26b9a363495cb04ee3b5c6125f16fa7153d388b38d", + "d6f69cb9ff2388175b1e549df23171ea6cf0f181c52902e90740dec0fef941b5", + "81e173033c052c2a8b6c2ce928c7d9e7f7b956e9744156c8b40cf815f093de69", + "627027e95f078f86d70df8b97d509bd556c6397693025367ae4b43edc9b6fdf2", + "9c8bee1ae58091342dac7901e5846df4acbbc409656457b03e1702eeabbfc8f5", + "ee387d8e66f5bf75dfdcdeb97d28b4dbbcb1302bf20d6bf8465b7b41832550ad", + "681f0145ff4dd8a84e42b02ca5bdf4bbe8adfdff11e3ae57673740a4f98f0c67", + "9f7d29a27a80e64603c98125d6f77e23afac3ae85ab0fbbe91e74e498fa69c89", + "f23c72f39810dfa196ddac4a2b30fc97f3e05cfd67d91ef2e6ab063ee29483a9", + "cb02c55cbe1a20d200bd53e718c78f9f2307d0476082bb425d4476a70ef47172", + "2c56d7a46e2e989c71d754a9ed2ed22285adb9bc7ea0ce85e7ad0146798836e0", + "161f8ede4832a65d9d082699e93299189916a9feae06703aabba7aa7100c8078", + "c14f24845530dbca3372c7d7453441113682d76ca7c5ee93a86f8705dfa6754b", + "98e2302a8c27aff16705eac3c9b4861e0d670571ca493dd0077280785d1d32a3", + "1af7252bf32de08559d47dc9273c82a7e6cf89c2ad0d755084116bdc30e2dcb9", + "b662f05da0258a7218659df10926cb716fc6d092654ebdf9d8a0efcab72f311e", + "ad8807bdc9c0db6fff8ba8963af51f14ed809ff5cff5944d27b1923bc5cec68e", + "4502bbe76bba11effb1f91a822732bb4d1c6cbd20a12722cf7cf2276bcb224ef", + "bd100da2cd60b60a2923299e505c6c0fd23cfdeebefbcec8119567fee2b8d5ac", + "2725060e6d944dceab24729b82d3a64275ebd82cb42ab330af43f53cefa27893", + "9cad96ee381a6d63d841569e475eeff6dee67836e2d05113a8f18aee590673b2", + "c22c8e48d846ad43fc8319c4d3ed8c4af31ea7c3e079595cbb557b78e50f0a60", + "24a9bcf359c2345fdc35812a10f9f08229d11f17c5951120cda1264adb2e6d71", + "54658722a2c31e13616e4a38a817927c01d2df37830dab7e76ec16e7ec93e298", + "fdf9d5820ce67e076d78db09766a5827c6d14678d1336c8f936608d80ae83d72", + "f3c55939c47563aecd9a0be2fd16c8d839331c811403ea22d47a8dedb636f389", + "9a62ce2c9a3238370ab983857b27a6ebba407540c6a55b8d3e62fb012b9177d5", + "32ea29fa721d7727b0ad77f534bba0ccd4f08b16b1c809c60da2e83c37cd052f", + "0c8eab2d814623966122a83e079ae8629897d405fc471fe70eb8ecf63d53a6a4", + "b5f81d6be6c5ed881db8e195b850c3ebfa2cb455d0a1d14df79740eebe5d549e", + "ff39acb8667a42faa265d97a90fa7d5802c6a581dfa6bc868fc6e953b4fe3b10", + "cd1a9a932859370e971587fa26255782a1aee9a1efeafd17badd1aca3c822363", + "780034cf738e520971387345b73f42cd13770dc8cf68948ec9e8e1f6a6327f61", + "0d97a70b467fbf4af625a6b539035b19e86d011782bb7a9ce538bb89d81438f5", + "ca8dcf9e3454d6c2a346a01372c798cf2cf8c49c38f9581d705c42e4f1e7c4b4", + "0c21834cdce637a85f931e8d2d961573d1dba0ab7bee93189f74c6331995c914", + "c8aa251601172c9479b4ab2c1382a2d6608b547cf6b7a4a04a75a91fde4c08b3", + "14d36f3db5b2cfac7a99df066016eba59a21ef651e991d631c04acb11d1d2b51", + "89006b74bc61637b0808bf786df16039d0f5a26d1cde1348de0f33718d47e75c", + "098621c303cb1b9dda387a0ee66ecd267172a70defc1b403887ea74c53d5b911", + "f9428c1dc527138be5c9ddbfeed43077d06f2826b75edf9218e7b90df8b5ec29", + "245270fc3442e19475818ca0d5cd32a4728a25b5c4f2231d571eafa281e61e18", + "753e79f5b9edd2e49c30c17d3d5050a2c0265027abdf8e661281d22e6cacb701", + "7ac9b5239a33b2689c63739de793dfbbd0955c044fe062bab28f7b9e72af4755", + "ba6c405725d69b2c74661f3e4259242b054d13f197db5700d1d7728110e56955", + "a7ff3309d156a8d4af88c92e5959c98617e0c1b95a10caa544fd761f93715b80", + "4185a1fd84ac173ac31493ea80b4a6d38d37325e8d82d07ee31bbd7f5c661e17", + "78826fde40d9fed3355c37bcaaf0656a4236d58404a892d1da402955698e2904", + "9f591f6bb853601a315de1edebc0927bde202e6cdacacace8dd32bc824a8a094", + "3ae950afe6ce59737c943cd90dc8f100de10de1ee952b2d295514c4f710aa7d2", + "a75089fa658b1206f03622608239f2de628b4a4693548c864d6096e2cef76490", + "efa7ea258d706b04547b8d4a791b1113fc77a4e5547c8a308a89bcbbf8161e56", + "f0c8cd991b168fa30685d9adb508e9aeb4f2b63dc08a153b814b0cf1f989183b", + "6f1946a94c3138276073dcfe0d0d48d3cff0452993b34965a8fefe90161f0fcd", + "2bc40489956125fa47c9cff896091977b8dd0b7a0e0150f614592b11a2e4d1ff", + "b2f96fc311567ddbef1bf0fc487f973145f717f283883606ee03b4384da74003", + "afb571e0e140ddda917b23d993474493d61a8414b0bd191092b6780bed309756", + "687a64c739ccc3941a0eb74554bcd0135e0063922933ff892c7b90b88e21c7b8", + "5ddc34627e55a68c4fd82c789c10d7fecf3e380b458ecd18eb8c5ae041c0b288", + "ee7584bbd58e279c2667ffc07c1c4977654fc948a63e33633a563c757f0dd9fd", + "3b7fb2e668fbef730803f10eea0e6cd9a2819eee7c1c1418431d1dc683def7f2", + "b4e18431bfedbd98c924a64497c60ca9e4a82f22084fa1ad24c09f79b2ea6a94", + "a5073a937e8b5cbbabb8915cfd2f2fe7307e4e334d5f434077995815475f0df2", + "455b0de46808bf01d625c22f0cea27120780c8c69c0c11cf837e98823faf58ff", + "a2b26681847ad348da10f7b578cae28087a3f5aabc5f84de188ae7ba9dc754f4", + "f03694a80d8dcf0b98d4c4ae69a797218cff7688f80ea97e627e817ad36f3795", + "006670166e382b2bf369907cb2b4834d58ccdc55a4bbd6494e8f9c129f5140e6", + "ba02ab95f8cdf7e40f03a629e47a0f7d5b77096549f17a158c73e2b96c6cf0ed", + "e1180c4ae2e094ff0ef2dd8107e5f2b02bebfad6501b18965b75ce8689efea2a", + "a8114db88256707afdb0f589c4e375a7b165380a0f8c2795c0c0dc0d02f5684c", + "a8ff66f47c414f56d8129e33a45a0bd9b9d1a640a3a3454d3496ba3a800b2d1f", + "f36ca0f95e603f806380dce52d91036d8e3a5545e1dd7620a19196397b91359b", + "1c207b576a1e9e765209ac6756c7948fa3f252a4687696d0f8e831f14ca52b92", + "cc5eb95f7759dfb6cc55027d49a5fe9c52f3006568cd97a0c544638be7a887ee", + "4e15d5885837d4e5e79bff21930dcacba97d2bac39e506896e005fd55aad7e67", + "5e2e62808fbe25793a6917dfbfd06029ea30eee0892116fe6ea4696fcf62ef53", + "334d575dcfb7f6b606866af045d0a378f53447ef51d5b3d1afd83d78c0aee1eb", + "591c6dc98541b9fa044ab89f78856374c3f42ca5f8021bfa0e119d8a04a28646", + "c502d6853c8eb8a2657e069659c1f3b06878498d1b1a15f771fffaf886a1ae52", + "2d4b16c2b6cee606af3c02ae8d6a86560e2393306fed2ac0b72456a8e35e24b6", + "b2ee8ad11dbd4ddbca245b403ada57354b5df94a9de54bdf574bf0eb41645c89", + "a895e7d8adaec8c44b603648f36774980707d30ede4f5dbaf2bd3d2e3d4e90b4", + "1e76fe8a5aa1b516db5d04f3496e69170d3a6910aea79ad7554e2527b81b6b5a", + "1576d0f08a566bc422049efcdd942ba455e61a1bb2d6e1ad470e098899128f75", + "ffc42cbcc82b0b23c5616301e90aa6ed76f485cd63506ba52097a42b5c17420e", + "27fa1edc6938b7d3658c5c9d894136857fc0be8fa743924a86ee4baf583599bf", + "4c8a502fb162a9580502f4f2618be83041782f245c6023e1d6c3ecd7bbf2cbd2", + "5b72391f63f61ba48fbf8fd308ca6afb7d72b9545d1812538f2e4d1414c6fb1d", + "1968aab09560c49509dc7cdac8c5c919629a3da0a39d7628b172d5e7bde99c20", + "97656482e3f4c9dbd01cf1fd91f0deb0b319c15a89b5deec6a57fac647a81753", + "d1392d6f39252c12b76aca6762fd95086785433913e5960b3c14a708401c2897", + "c11244439c754d91aa30a2fcddeef5a0be7e300b94a5c98967785f112cdbcfff", + "21c529765b6a1048c8c946578be14e63750eeae28521fd845bf7a3ae669f203b", + "c2f22169832b5de185aaa5642f125738f164001083438328d2541c15a7f986af", + "293583e0b2b86f041204e4ddc9546ca74431407f88ce36665337a5602dbe3bbd", + "1c270bcff184b5efcdec7f630e707c3e7c90a519302233fdc08aa744d4cc0f9f", + "88a9d5a81d358a14cd92c14d669903032c93371915725d764fb01c7c1673d4e0", + "c733a54fa8d1ef07070c52ce77898eb93f148b13cdd1b6fa69884dae4b62144c", + "acc7546cee77ad0a641bd33253869e0459325db8e6e3a9c7e3812916939e5345", + "edb6a177be83d61f8e67db1b3837db0316d0205cc7303c242c697fb3c2a1af25", + "8e1e002d811734ae8033333794c2bdbe826f1c69f4dd6197e595e75837a3ca64", + "316f26c9d7b2dc523838fd4d8a102e7b817a44d00a4f31dcd30bd6529ca3070b", + "0ae34d23335c3138e2ad4a282b3bdb89542bdc29a4df69d202ac41f140e15aa9", + "6c6d48776be2e5a30e91af97bd6b0bc5a5eb76a5fd713adc399f8664918cee9f", + "dfd7c371eef529314fa853918ed4fac86e473d5bfcd9dbe261e57201d70c082f", + "0046ae85e6230a07ef27a9c7c75dc6f6ccb7742433cf8be17343ea8666512259", + "dd9fe83a560f0897bd19f23474fe744c8eade026cdb1f048e84f3459e0f19303", + "3e6a71ec2a652defe81127e60e22728c5c2b6b7ebd3f8946e347d662ff3a868b", + "a4229eb078f80905f012a474545e0e2786f98bf7fb2073dcead7ec73c65fbe9b", + "7dcad9638f5473ae40af1a5d9334ca763a09c66812806f67e75bb44e457f87c3", + "374ff61877eb866ee324a87077eb71d0a269e9845847e55a5e10fe5f3c77aade", + "ef6f393a0d140250e071d3b82f5086c13bb429266cf944bae8cdaa28834cef8b", + "9651ad41b55c6e8d6abb345d4ea06e4399cf2902d2c1cf13ebbfba0b7140c8bc", + "9d0540e58a6445fd868ea65c40185a7da7858524f419797084a602427ace7477", + "5d812312bfa56dced2d010ddbd98b79ec946a4c13fc545088c80d037e4df3c3b", + "c75d28a33c292a50e293d4e88f25421bc03c83fdbafaba4f526b8c6a7e5c2e2e", + "53cc98e7b41fa8cf94ae5ddb9807fcf8c73c209d910c892f266621fa05782ece", + "f6b589decfffc6c33957cd061404dea6482435abcabbad3485266cf4a8e7fc05", + "e4e03d0c467c2420923815fdef774f530d07c0c415e9975bed75db2ce4227dbf", + "7178a3cf86b9b27023e576a0f112ddbd3e240289bf8c4256ec384962207d749d", + "58c29327189acf5670bf364f2c9023439eab2dff643a4ff36f3d35b43263111e", + "d9b365bae902827f415288ab30e5e75d93038ffc1466356d6f5280863f18dad2", + "ffd371a7a95b47d197bc947e0e1c3cd50a96657ca14116fad6fb7ec96aec3bf1", + "10ab513a9aa33ce2ea4cf293dd0c435b5aad5a1adff65604dd3f07cfa1f340ee", + "40863aa43e50ba80bdafdba66ae053896370421d48f59c2aa962ec5e2914bd96", + "0363498ed651cf53bd469fd0dd8ffdcee6c6b4641a67bf52fd177ee7e9e81324", + "7804a9ccc78fcc0e6b6583e2fe28a32d3b409a08dc48061233f8faf88dbcfa03", + "68ae59987180df7089fe2566c5d2a61d554b267332a24dc44ffba97c1ab1b596", + "f2c29ca8dd0b0429df3fe25fe3e36e6fdef1170dbed8baa30cde80f13cd093f5", + "06d4400c68d16ce0b1b70f8fef726fdf633af85eac4e285694db95c0f8e503cd", + "8db9481d31cd20f8ebf15b2ac559ee46796f0da83b8a94139ab998e2be19f035", + "4c4a93fcff164e070a9eac32c8b7a582a00c6d32f8dd146cc603520d91fb11dd", + "3d70cb493fee1f8575dc7da9c7627d31ebf42c9842275f772ce6f0d42e06db0a", + "9f28537ea916ceebf76f1a42544c43e13e9075929228f32190d9ba4f5bb5c9b0", + "673af69eacb58312c01c9b4e93167bf8e5bec2ccb4450ab833baee81cdbbd95b", + "0d0b61e12e81bed69380c9b8eb23a2384a48bb13a4b7808a21ebdb00960e7e4c", + "284b4b1d0f126cc76b743eed1d255b181a4ea45839ef0c1dd2749ce5ae83270c", + "70cbb1998efc52d6b6aecca67551e42bb44eb6dd6e400241d2e2bab34ef9c1f5", + "a47582681ac364f031f9d19c42e0b68edc0749c5b3da9581009b5187b8b503f4", + "808c8d746109d74eae54b97f45ab7b9ed24f32835cf8a1ec9d7d46c3fae5178b", + "ab0e3e550b11a95ebe15edb506a534d70e4bdfbbc9172b251668cb8eaffa5c7b", + "8d24d23ec2012ff57024b1241dd401eea2ec104645e5dd4088b32546fd8ff904", + "523fc99da5bd881da326c94b303a852c0796792c1f3abfc1fdc7590c9a1ad80a", + "bef023158f2dfff9af92f2aa64e7da3087afe08b2cd5bed4d6a0933e46efd411", + "bdb056bd0ae30d8b926688b56a6e06029905ecb5b25922fe4f6f2004d4adfa11", + "94a65dc8805bbee393a49a95ce8c95574ab280d6cfcc4a1490cec8352754c6a4", + "50410fb63b9759e697bbb5c62d39820ce5d1f76ae9697e064578a15e0621b792", + "90bc57b7055d84741c13f3a7707770de07ac2118015e3f0d08ac9ca3a4a6e177", + "4b703ad6aaf1cbbd6f4fcaeb9745fa82f6fb055611d7164c572892b0fb786114", + "0ab836aa1a9ab4467708652e8dbec2885119358e37b32f51b46d45f33c88046c", + "1a118c7d922133ccd430b9f9857b62f8f2ce4df7d648a197f6edd3fefc07cecd", + "40e846cc774ddf6392d673f69ce95245690f25786770051ba5ad49461310b0df", + "5aadcafbcf0b1b4723d39c5d4048a8d9f3889a3f68aab66cac0ea4f0ef7d549d", + "acdd2f2a548cd325fc06b49c257bfbc9b09c016ab0859cb947aac64c4570f6e9", + "0b3ab537044f2e6142ff425079d25fcd87e0dd8187f03bf0dfc479142d8b9abd", + "497c6521a301865863f7dfcd03316ae0db690e98546116c14df05d98e8cdc893", + "ccdf5949457b2394df193f21d8cacd4971942ea3a1467e2ab13fc0cf38a2aca6", + "b38079f35e81d62e2ec89a5511537abd020e0d49f47cfeacb1fdf0000e3898c3", + "b5477b70b3e6f62fc281e349303e8e160a5cbf6af135bf3c991955b3cb963039", + "64e8bcf3fdb18248ead8b6c5a23b156d76cbc6ab5789815a2b45c365ab20d353", + "86c981eecb0af6e8ada8e68a9ba9a6cb93d0ed527a366121ab0dcf9f44d2a38d", + "93ff8bbd4dced9f09d343b2227099d63a28c121389dda386c8b58acac7e020aa", + "b9b7cc4a4e8b9f5a1ec1116d378e3f90ab3e8e4605d4cdc95b3e4b7df2815a1c", + "f2f37eaae0ce72d792c65a906b6c7334c2377c3c31e454063305e7e63e5329e0", + "cdfcf71c42e3d8d056cab0fbf3625c1b13704870709a4e0850006447f128efc0", + "83566f6ad1536d4aa2543258bdee98989807ffa133d36af3a26672708ead8410", + "68b89c27a8254d5fa39096943bd9bb0074e950fac7e686209c0113df088ae054", + "0e8c8d00c7e71968371f4597da934aeaedc4142586c6454790fff7a4e61af30e", + "0bc7e2ff2af89d45812349f03973406f27a4475c44e71589c4121a9cae9cf37e", + "53738bebafb5a06ce8140b83ef922a31a05521c07c42cb09fbcf6619bf959697", + "809f6e691647916082cde741c82df020254de10148b4b712744f1db345bc2694", + "82238f89893476756a48eaa42cffb4fcf07c8cf4cdbe12a1c43145d2b67aac07", + "ee995fdc7633e803741fffcad712a48aeafef16e5a91aba899e4295bcfa3b577", + "b65b0489ecfa62b9725b7a4c4150caf0f01350de9cfd613c90c60d0fa4875ff2", + "7988a77872e593e831989c6ca5e5e6861b63e1c2a42030d684b51fb88ab73004", + "914ea37de5dc3b2f9035cdb7fb80e6e692a0a5934756beb6d8e2ea8eee7a3895", + "d9c28dc8bc385283650fb2aeb1efe10caf848c39de37a78dbe6f47b8f3bd8b51", + "49a2581f6ebc753d70dc30d3b093882153fef2d0825252354e5beb1b35eb90aa", + "7791d37475e1e15bb9b817b180811d784963d2398c5e736a851af2ef8e622e77", + "3957f179aa7676eadd2c312cd6096f84b7a0424bc99f05c8997bac9fed057ae5", + "9d954ab5224ffd2e0301ff9b33ef834d019596a6c3f56fb634ccc9779b9c694f", + "e6235eda52b721bcc3b997bc8da554f1998b5c7ff43cb86c2d7f1915150c4ded", + "c0684e197ace1cb57ab553203b16fbccce5c993b876b90499e96d94ce308b956", + "70cb7be7ca2d01bb41c09c06fefc04e8d6dbfcdbd9b8df4bd38caa3c7fbb6001", + "96235ac39c397550e28b63f63a4bebf549215c5e72cc78488efeef131b0cdcf8", + "f3bc2994745fcbbc792d905ce1858978814bcec9a5a0a3eea4913bb87939378e", + "caf6345a1bc6013c428933fcf151ee366a11ee41070c3de2a3e5036a65145194", + "ce92dd8170fc721b8bb69c261cd868b82a2d1ccc556bef3999128a3c20bd7c5a", + "45e73df58de1b26bdc1b842b5a951da84381708cd15b40eab87ad44641777c6f", + "1a57c8e01d94f7455719c85c662f76580b9f4f530aabbecee9c5abe00fd7def4", + "c92b4679c9376df3e41d79131f42961fddcdf99afd2c476168daf9657f769373", + "2a2893a114df2630e4b32c47bd60612b4f3d9b04891e944af55fd2767d260314", + "98dfd86f3bb1b56d0e5b3d0a9b3227642b09ee0a2ae78f19eb504edd1bd91ba1", + "2bca879ad4553c1406e38ab072cda33740cf1681f18e7cf548ce040528fa5d05", + "5c3039135e50309be3292559fc0f0d6a5de7ab2803c83f206c8cfcc06683b54e", + "7b46b23f8ca2c45baf6124165030a1ee5fc27f1ae3d1226defb2574177cf728f", + "b37b504ccbcc5ae9f8b787fcc8da194fdda3a25b94fbf217444c13fa4a0863d5", + "36d5d669f4afaf603a54902269087a0f65f89962f87ba03be56b552bf04ce81f", + "f57f94affbe464d876235351bd20660ad5896e5f5bd38978d64346564a089754", + "4637eacec37dc577f7d0c780ce46a57de6da9b5173f4d0626963af480b95213b", + "e22295a0995837f730cf99e9465b0e5c41c1f4a99456f6b27d26d66b8ef2c4bf", + "33d01fca1bb71c53d131b757afe6d97342006694ae752c17747a461717f42f8d", + "ed239b46bcf7f8a66b92c15135488e1249f668625c6fd0a08a0f49c733806de0", + "324c9a8fbc527d9e9357595b1b3509847847f340cd557b9305e78fa2ba963be4", + "2fc5f4089b8b6375a41bb70257be3281cdd32778db05663c55f96cbeb976ef1c", + "691fc9f8e0e0fd9c0a0f42f17f33ea83c3462e35d777dd54734108bf273b5a4f", + "e21e3a565655f1f58ebdb421c94110e19a55030777dbbe455e350868020a574a", + "2c3e5c5791cb68bb2177332f5b79c8f7d1cf4a577a903acf225a0d11e45aa5c5", + "5707100bd5a978ef7fe1def6ba895ad388466a4db13679a899c93bcb802d3a7f", + "fb537a7e26384ea9867ad9ac845067553c1461f5c75a5bd50df1afe40b063b1c", + "afea4fc11996d2552a83a4b7bec586c9dbf3346cf2b3ee7a862b8f6ccf163522", + "67a83eb66b438519cd92e6962566f3d1277395d2edfcecdb06565e33b9b4200e", + "a763a731e9fd04d5ef02aef4e7cdc1726a34c7a048cd78c45ddf9f05c52773be", + "c992b26e5d9f56c3f1615fe30fd5d4a70d639e192583e5792909baad8695e90f", + "5f63a2abf7fde874a260f469f56f7392c715f43581fa0e0ad99522dde7c047da", + "2d92ebb998b264b0c7fe1d9dc94c8f3ac171f31a38867eefd8687216342ad4ac", + "36758fd2cddfa3f6c1658a649508727ede3db00c36b5a619260212b50b3e5da7", + "19237f8184a40216542e1f326527f7e512ada4ff9923380f9a7a7e73a0c4aba1", + "3f2d551638a07d4c6b03265f131adae2639d1600a9f31a337d5561bc22c41af6", + "212a3841885d5890d53c698bfc2ee6114c13ed4636483f65bf9f43e2a23c2dc7", + "30c6c4fede61dd964588586338cacee4e0ef8977b556b4fc104fcb6120d45d3e", + "413981cdcd8428b9e76a34010c95f810ed5f39ee66edfcda92ab0ad94f030871", + "c70a24bad26cd871d82aa4563199ce82ef8d00cebc48a1cd698ee1933ac8b6a1", + "d228077b8f08942f4f5c445f5431f6a388d8dec122471f8c24a56ac158c90742", + "6d25f50ee65fc7f46a86e1742f30afc9b9536b65762f75d9bfd1f9ca30df1d75", + "2a7ac6f4b98849eb4d6962c424e91537967350ccb60c5cad40400d46fb0dc3d6", + "002721d6c430ad953c9125dac7b4235a3f5edf80309b23fe9e197d0ef12ab0e2", + "5464573c76aa26e98ba45180d7783bf83a1ec7939c8fd8a210d635225ea1ef42", + "18395b65b1a46358ec085bafcc873706fb482866f54e02e042522ca09ff0452b", + "f4ff0049dc8e5a4f06c6274a1bcdbf8b5f8b841f921a264ea5bff329378fd620", + "19bdc09f26645d82ea05cd1fa0ffaa68885c66530b3f0eb05caa55b88aee4efa", + "6e749013aca97a5ef3b156da4dc621bfefa7e0186e5e4780c31332f9c62ac7f7", + "83800ecc6922e2a6ec0c34c1736a12f2dfcd94eb9f27c6948a5b28d961bc4ae9", + "86b6fe2935f0206083c952c07ea38c25388955eb8a806c10b69d3e52d6ec26e5", + "c7db870f8b09b9498d39b22d828b60a806f7a1de694c43d6bd29f3d76a11677d", + "b0a5809735c83af8e2a3eeb93da682bce95a699392c0c4abc8d39188563e381f", + "63af21f2f1f82d3120e4d8d98e7fda2d9f39d102c57d9c4111d07079a9ffcd47", + "ad654b3ed5a11b9a2a52fbb8dda47e91a72e4b7bac3a4ad75c074786f14c046f", + "6d9c4a44bf22648c07881e2e92a81ffe002c0177d9c7892416ce01ed1f693872", + "16f279dbb7b3f6c3d4f190b36fe2283efd5fe4669a8f70d209eeed96bf55907c", + "f79a83867614dc4d2e47353f53cb5a833582446329dbe4d5e8df941b0926dcb5", + "0cebcc9ea3729487c1b20adb8eafc6fb1398710ff6179a7fdafb1985e5f4a8e8", + "5a53e473fda88a9fd818defdc2c8e68bcea0c5aeded0c1910d6004b0e77ace89", + "f97965894c4a296fca42905242a27228e5a9a0150d31a55e65d637dcb9e60ac1", + "ec72e9e2fe2f4ab9db10b7075012c9e7cd2a4ec02b4c3b76df17f6eb27348ca2", + "a4e92900f9f5490b66e21fb66c0ae094d106eed5366a85418a86a8a3f8f87a28", + "225f9b50f64a97b9eadb625662b1a3e1b400eb86cee50fec8c62260027b4d390", + "7cd4971b0e5453d210c097904480403bd53c689c19d52e4c6a37925d14e4272d", + "3edaf61be8b6cfa06e1d42d7abd2df32033bf63cd14df8bb3ce337e62a822f16", + "89fd8d0e59d8d757b7b8a2c3a2b826e799c028df166b6897a42f08f2284d3bdd", + "0aa2fcdd4e83a4395cb5f514e1ef61ad4bb517d7c60f81bea030d5191c3c79b7", + "80a275ddf6caa4bef4039860d4c5b0c4bd1009953c19ec2c63294ad2c5c56d1d", + "e85d450859d702a0473363070a6e56d5021ebd1111232f2af28a60963acdac16", + "54d48012bd8af661baae940fb6f0d10348137cbb13c650626f42051ede55ec8f", + "bdb3a9bed24420afc6eb80930694b4afe0992318e49428156acdba5762c69e16", + "cd277985e4b4c30a1999e5bf7bf7d1cc903640a04678a93efaac1f9bb5482dea", + "d1319c6862feb76be11c089c753f62873519ec29a0c83b186efe57f50c4e342b", + "1662d8e6db309700071a801a3d066ce97f942abce241c8085f3c023258927eb9", + "e179e018705eb4a49ffa4e5271ddaa4fe1273029b521aea7a69f8f3b764ae017", + "0e7347cdb154f6d96fc8f9eb8991d4149fbb21aa3270c21fa5df61ca140a4fe1", + "34f182c2f44fab5ac9403543493be5006814b2379e0e6b7603d9e0fe76390367", + "5d710a970ba007bffbecdf12d1c920fbda1f57023719d506825e0895a2affc49", + "b99e0f318386f90df96c1d1cd4dc5bbe4726410f327ecc108108ad898e450eb5", + "7eb9ae1f66a6bdc4482175d031bd6af270fe4ff8ed1138200d6eb49e13c0b8e8", + "c508f259179f67bb6e05f2b9c5c5ea751f3a013eb925c6bfcc2a2624b962e059", + "f3d0dab6f1d30ce74c97b89f551ce0820bebad2bc1cbf4a449b11d5408cbc140", + "c116f6b0a0852a30c8a913f9e0f1680ff1be06165e19eac4d00c4e426ca9b6b3", + "25df122cffe53ab88ecb94abae69e0a1d4323eadb7537c5e4894c0e082d58529", + "a30d97485d4162e910b12effc549374221217c216b0a9487e5a07ff1fca835b8", + "5dffc63818983f6a7db2ab65d621819be9f9edac58df38571887cba22aaaf1cf", + "f042fb4eb01dde4b285f2df328f191d20621b66d7630d7691e7c69d9dcc04aaa", + "d4190e2bf10aa9800a1c2395c0c4a38737c21f8bc42dffb23addf762ff309aa6", + "4745eb0e771c9f2144ffd4abf39e63cf6d0ebe122df5f32ae399cc15fe3f7011", + "806c6fabb4059c51865f723ae619e0c55718f973680ce7eb84c49b935f59ff02", + "3352add492d3a7fa9ee046040a3f7795ec375af875c87edfdb0bc876ba14fb3b", + "27dacac03594cae3db57550410bd0f8d166863992895548f6ab782a8332462c6", + "1708396a0792c2a33b8174dd950c4a59ff06e969db873eaf2a7d82453e233b99", + "5680174e172ddbe6943cede98f8d854dabb66ba65e3349889cd6262cdf462a80", + "4960f0a4c46286d62e5bae021da9236ed0258f6c4c779e1797a9e66a47803f0a", + "06af18499315d7dd1c8d16533f0e13465d606fea2946115f23735f3fe2f6d882", + "73b0ee02de1c3c8a9b2bb8a9c48705c94df76c9c2d68a222c8493848c75327ad", + "42044a69f91d56cf8b993166dc1c724ec7d311eff01c8255987cf563d58fa5a2", + "187382209cf9b45c1df5655801a651463854a6495a7aa303887d65ad0e5b915f", + "3e44b609a8f97ea0b412abf88d7748edfa62b7ceaa174bae008e508b1b133e7e", + "d7b1c0b1bb02a6411eeb888ae26bef44c20364113bcfc1a4a3410396cb52b950", + "5534bafcac199f54a48fa713c6f7ce0568abb11b341e38b4a968bddbffffa507", + "84044786d9f286e241174796073fa47ce25c154616ba27bcca69fee1482942e1", + "45eb4756d3895fffeb913bfe149746f56e184f4c336dad29f6f81fc2a3c0168f", + "b583374fc47629a3f17476f4c9c751e851b7c4881eda50e4bead35afb77808b5", + "4a99c32e81aa36e824bc4be78495458020b1faa47d558e7263f2c0477a16e6d8", + "888b7d3a32f4566f21407c251b753b3890604001db91f88d45773bd8a53b2541", + "53eb65e5b425e7acd56ee6234ebf3d40d8fc45a3dab9968ef4273cee307c38a9", + "4bec2659f865dfae69862e128e710680f6114866b3478de257aa5ac596a66555", + "543cbc31d271c10cabc9417a4d4b6dce2dac73c20ff29bfffbbe55584e5329a1", + "4970e6469f1ace57971103d1cb2324b595ff12e34504ef8249971c76296bf578", + "010810a21205384741812fe98489cd7369d9f6fd9e6818b11dea07c78a9073b1", + "6114e32fefd54c1c2352ddf9327727bb6a34a9d8b3b7d01ce59c6e0d32700c7f", + "ecc7248b7093678e7e0ae58333dcef0eaa18e3af261b2d1722d29acacce2b8f5", + "0dc74b6f19b2bbf2f0e7f511440d4dbbc67952050bd1d962516b683ff6f9ec9b", + "fb8447ff1d5200c3acc768992705d2108c37d61b0451c7474304803e76cc09e2", + "f7f222b44c49d2d927fc2e345d64e2f0ed5f9a602758fc5f5ccc73d3b087f8bc", + "295fcc7e7fcb601b3e79c1fe403f920b622bde0f4975419c3641c4d432de8f50", + "e016f042fea1d93fcd4b2326b3ef6051e293b4abd066b3fa5c430ee7773e443c", + "7c8347faa1ad96169f5650a1d138cb10bfb60dcf861d5e2dfce6a986997d2dab", + "060928467611f28b99eaf284bc6c09d6760904ab5a6f9e072a44915a646e3ec8", + "ba582c64b17221c46d27aaf6ce1e2818562639b321666c9513c1e85727a623e6", + "e73793b5e2f33f60887c6ab428339d40d21fcf75530640977215a18db34fd7b4", + "0ea32380009a639b705fda94d93ff032d88c3126503c0d153bd0f104b039bf60", + "13bc02f7f971910299b8c61d1a16782726f22f63b31b2c0c34faece873f4418e", + "a99b3ff6413262f46cf9a44c3fd5d0424109a6373e2580fe97938c3dc04e60df", + "72b149e7ff672ec6b23352493cbcac166062282911e46d2b7ae5228d5814b720", + "58d67077a2b7c836dbc19457eef1b1e1f734370a800388bdfb1012840fb7a240", + "32beefd48538abe6eed6ae137199e03e05229d124628205cce7deb53f16f8f16", + "3e466fb18e2a437292f00acf9f6e662fdc6e0b7cb9c0606e613f81dea3393e39", + "9e537b504916eb6e9838f13eccf0c753869351778201c0813ee35cc19205409c", + "404c0560dbcd46d1aa9cd76246654bd01f62ecafd40ad2b364c426f849941c5e", + "89e5cbab667b81f08eeee6203be0af9cbe9dc0655af3272b0cb3fa722116c200", + "2fac933a1b980ba75339703bd02f055199d057355a8dbe94eda724b79c2ac534", + "c0200b5442f41779316e0e250b7bd011e12551b2babc96c9228f3187fbd17eb3", + "f3269d2e80bfa7e80397a8d32815f8fb8fc0f8dc01dd1c75cd57028a23e5d462", + "d6be8612904259aa6ff43ff30a2bbeba9d58e02f479f56480a8affc2cc9bee64", + "a67a26fdd90d624fff7e501a2a946681134e32e3afe3094a0c3776229e7db123", + "43234eef33f6bad45358374d23ded2731a35c12f472cce58d05e6d9ee56c9535", + "f0217ee0507b13f56f8417d0c92e3c76026da461c22dbb7fdb653442dcf65ef5", + "d9572135cdc5ba7afbf9871de3ffb68a1ca933e71b80e8766e678bde59ad2b52", + "9d32d8b859188827a48fbbc7660900fa51bdb1bbf3dca2ecac1392a1cbbf4757", + "daedaf79fde7f4e49a736f5b81f8b6f505884a073c5aad83997b93bb9685ee52", + "fe7b3ac50841778d1283fd836996b1865f45392d53b95f3bef90ff657a49c498", + "b89b62e4130fe4d40232744fb0182d477047acd40d32bcd15fb6181ddc890a6a", + "0ae8786dcd5bffb824b81fa038b10c9127b811664e0d9655d537d95377d6d93c", + "8608661859b838a7e8ec16e15db1114aa9db6b141877851e09439c591b46d830", + "f5351c6f978f600be1a71c95abb638bdda6a7f6587e3f66d53bda682c7aa631f", + "b6bcd591af74caa9970d39e0747bf20b56590878cc5b76da411525fd34b71cab", + "bff91200c32ad862bfee68ac1fac4b85a5e6dbba4fd00a7d09e217cd226ce777", + "40e5ebfabd309ba228262431317a2ffa268074cdf83556682d5f5e8c77660c5f", + "c379dedb960b327f684e794ec212b11fd1eb8f20a6847676554be85aa4527dd2", + "e405b5be582345aec0595f1a8a4aac6f938b539426fc7910d1271f3e2d1ba81d", + "e27a84e97deb5450a38bffa685d0461dd90e2fc9c42aa1261da9d3b9fc8fa0b0", + "c8b7e2506c4a067bacda36ed1bba81327b18a08df3fd373be25690754b9d8439", + "5b8d011d0493a5217b74d086f4b8db0bb38cb357c76b1d59441af36806ead1f5", + "4ad67654188a9c920bf0bfe25d22c433035d876e28f79a7c09c5649ae7b970eb", + "2a0abcd054b303b2077ba72f2681a7832712b526b145b62c601741efecb360db", + "bff88f3f33650251f8b9240b1bd94621460baf5d7b224030ff9619a6c708c56a", + "d8846d0b025e2eb7eb528eabb478902ad5990d507837cff5c90b1113addf4c75", + "e57728788dee9191e55c5588236680bec3f0d2cb8791da18064a6cb341b5b7a5", + "f291bfd6644103b66aaa5208ecfb536a28d6209462f98493847ba6454896a369", + "8b0a1b5d290d1069f771d50369e662c5f3c777c2ec3efee722d1a0ea58c6d9f8", + "cdc255d80343d5967d7418ad4edb8dc99ac7138c1621bb8beb0869dafce44cb3", + "cf363923e12340b2e95d7397df7775e3d6d98539001d8262048c67539be6a7c4", + "aa8e17f4657e2aa01025d8c99112d7002fdb1abb086cecb23d428081c87de6dc", + "59faa9c953bfd7f7d5bbfd363c25ed4089fbcce464b2958c9aedcd5729d4883b", + "fd0e1bdf4b5130bfaff244ebd4654e4e0df288abf7673df010bb909286f93ce2", + "f354946364f1449f4cb93847742ad2c2ec82163ccd7fb4e0d5687fccf1a2a018", + "8be151375abca57a5df2d52428068eff6f82dad14d5bfd08a699f8e747ddcf99", + "14a1cb09f11e6401ca5b119808a3176f893db83a0c23fdf9c393ac78139ba429", + "1043a8492b8a8b6a8b9355669a29fc3f2e1e01df59741458bfd3014420df60c8", + "338caae1b0d36e70fb8bbfb3e230a25fc9eab8a1c5b727e1313d61174c878411", + "8e1509bd3f536a9e263d5bbd5cb5d892aa0a860c104f0ac7af74493b9d164bd6", + "de5f3b69cd11a453b41b1595ee919dc2175ef867541c66571e6e71abba2661e4", + "653934f935d080105495891b1889aaf0403223077af687feab1570d03cc08d29", + "0cfc8f472eb4c7e30b17ecf91ba335ac1b479a01154e442d33fd0ef3c0e5dc4d", + "fbc10b32be63288c096a64a70c75dfc76f162707be9f3b09675eeb08d98a4f52", + "d76bf859e647ec2d77a7e5b5f726e8eeef59c6ab7fc1bb01c981b5cde26f3eb3", + "64fe376e3defceb8149411cc4cdffdf3dac15aedb2ae45263a0b94877c8b9ac4", + "3faacaf41e61750af0ac6df1b5a79927dba509d293a7fa2fa9e76bd3a574fd3c", + "a7a80a4cd46db9010cfe1fa8859d658d7b4534832e7b44fddee51c53d6be6408", + "c0268812c0e6cad694d5632f819b5623383fbc3e551122cad33b82536f733791", + "a5a2a39a433131759be0f1f6339c4730de3d401c54c71813066def40e6cfb046", + "61f4ea199859377dd681fd26610068f87e99081866cbf9aeaaaa1163e68fd950", + "65e2e5e77b37be178505f7f40de4e64fd4e86acb9c6236ffec62392e8184692f", + "bc82892fc9c6dc5d7bbf16ba4b2bea2cb9666815bf3f680f72be395384a299e7", + "db5139acccf8dc1294eeb56fae06397f17a040e093d307a9e7b0d62ad6b42ae2", + "43e63ea9672a65432e1b1c21baac7741c9a5f1d6c4e60ae0b4618587415c5a9f", + "75e8ad0712bf9f279d89b252e7fc17ee0add1587df3f355568999fda4dfb0a2a", + "83491196e6dfdec6dc2d09e12380cf55fca8acd4ed8af12012e41f3ac2270978", + "b31d6592a4bedfc5e5d93565a4824f2917326566070ea981e45bdaadaf3022ba", + "ad3b86deac6998141aaaf372f299bc1c39e8bbee4807cb2b6cab7cff32749815", + "e2ea5f557fea88d002756c080dff0350e2ae9c15dbbd423a2be45d660107813b", + "7750f2575cf347fc1b24fdd5b149eba76a438109f7ae02a515e2451179b2c98e", + "c34cafe9369b18ed88c2936cd7cfaa2dad9ff105a9c3a53e49da5e7ad4ae4b4d", + "c7542b4b36dab508ea0de2e1957d451160fb3616c4c81ab967d2aaa97cc0d8c8", + "9da5cd367d3dc450b2c76dfa1ea0ad1e52db56eb7426fa031f2d08d919eb39d6", + "340a3d854262aaf7a8a6bf005c21ae7ef62d58d052c1d108e7e09e708ac3f0d1", + "dc2baad94c21cf7e37a4208a6dddd74bcc84aec852eb308a39ce9f81f6fb883c", + "d21cb6a6f34d9a66c167c4939619a8823be213ceac2bf401721495628d2ea3ff", + "55f3144838496089eb2af67adccd4b27340155cf2b1cc885427f4c7d4e7a8666", + "86ac6e18b9931bd435b38f96d97aed092e1fa8be47089f00bc44b6f7897e64ff", + "b4044a883dfb2eb9f4baa42d259f87d5ab39821470f977548a435cc65897a162", + "d75f0fca387134162a37b5037c5006c3e25c991fd5c0d3eecaed3e75df90b30a", + "4ef8fd941ecc624d1f13ec8c0004744cf907bf91f0a25ea0986294b81d5e45ea", + "015e5322f9f2153091b70b0341912ea37a0da3ed20cfe9b8356c1ce882c00900", + "65f0ffa646fdd7a98f1dde849611f19b2f443703a11ff5a61568b0ba7f85b0ef", + "d9515617ba3fddf4595e395e0e033e9eae484810a478be85bb0eb3851000459b", + "e3858e031fe47a5edc3ec572173de5ea77e9ed292968af2c6ff2fbd98e5d7996", + "4fcaaf279411aa84d92f548b1dfc05458af2afe9ff577221e4b1d82a2e37368b", + "269b84393f62de52b612e2f9922a67d06e523e27d4d2fe6d3aa68caed7a2a190", + "4d179db7af1ec1b6094dd55ce21d26a54e395f23cc3428c0de6ef171d6b015ea", + "1a508533186b12799e418ae46978c8bb8bc88fbd8a478fbea38b86987d197ce1", + "ca4e5990f8a59ddaa61ea319c9ecba88cf321558b8735fc262aaa172185801b1", + "0d48ac9f64fd82bf2e95ffb1eb9f1f73a8dc778417672fbdff374e5093a35f23", + "c2b88c4f59cea3fd60369fc386ac89d22f6998a69b24f4c8ab7fee63247851a9", + "75fa241c04b786f0f2670e0b1e45134aa76445efb26280cf6dfd926f0e77446a", + "b6b80a7ed9aa4d50ec56812cd5a04fa290591976b0adce14e6657815764d1645", + "c29d8b54a265d59ba2e8eb1bed27450e411ee34ccfda853d3a83c1da2fb75b3c", + "180681a90fddc13a6f216b87128055a0947736248d967a5170149a50ec8020f8", + "f65b08dc2c498a0f01adf70e45c5ba21205e259186f928305961718fd086e08b", + "43e8f556219e8e6a7508a2c5ae19d6de0cda7b9f6e893bc19bb68e361c37c786", + "367709af13a857bd2b97701b27c88a7c430a0e791a76c72896b575e017d8d9d6", + "c9207c31bf4517e18513602801a0780242fdb91c1ef85924c2ad4ded7bbf1ada", + "9e48f7bd1d77dc7269d121584eb914303aacf25a336d42914300659e0f67871a", + "8a2b8b728756feef55e9c9bacc138eacf609589afb4422ba2ec2bf9aaaa6b0fb", + "5440c68bcfe6a804c5865c3bbf033033bcabe29e20dbd9f36f5968e2eb0bef02", + "432af6a45d6f2a44d5d6de8c82c4b43ebfc8b289b08866d0c4b4dbfe7337b31f", + "4fb77ac98b549ebda2ba41cf0ee99b55c23386dde4cb505ccc497c4337b243be", + "3f1af916ee3982bb79c31921da5c90a315c9719ef93d87be323bdfb13c6e2770", + "4268889b9776502d948382bc1c9e065793104a2ed0e8aa75b2153b62b4ccc097", + "e14719c99770cf507552d3d64eb822c8c7a6c4d12c769d6cecb6e6e05db85e47", + "70adecdd90fccabd0f39e9d41dc19c6ba9afd11ddb7f09c6ca9c3e568b9e7194", + "c9a94d170bbcb06d9170badf89f2f3533daf8e15b633dbe7ea1d02c62fd2d847", + "538c3cff924df23105e51cb100c844f356d0d05d06e3aacb7c30f98c91292f72", + "bee2c32fb72004ceb24d207bd890ba8fb4126848d2a7bb02ba580c2669f7d27d", + "98db2db86a3da56f16411bf6c2e27f2755afd38673bccc15af8991fca283cd8b", + "fd8f89cfc412008ec0752fabaec3ac72d6c89ad81286980e8138edf6ff4cd992", + "009704c84f0954d45036969fd15c7293048156ff97a76d9c72a08248a4dcd725", + "593b8d1e74c299da61950db52fd3b08f44d6cf972ec05a5c5914da9f230cacc5", + "e8a6d9cff900567aef290b62a57d8d5528b7468adbe2ee0bcef8ac47e2dc1d7c", + "9fbba3e66b12c7c926b88afb5b4a9eb03d1afaac94f673d45639ce9d4dd51416", + "b7f9c83320d1b04ecb9b4f714da462b7bb4e4ab7cf3625a5f0bb497f5405a038", + "4da28a828ab79336ff2e766d3f32e3a5ad11af8203c4428d97ce5c0054aaeca6", + "d1d9ee683589c16d6d043f383ec2023953c482f9c32704fe1a461ba17da05ea2", + "0706b6349613201f5bbb9c6125c48306ff53d4688f64dd994405abad1872d450", + "b22580d4aeda8ed6d73c59bf74efd26c3edbdeb0865c0c4b6ca61db39fc6fcdd", + "978e4abd23ead67962bb223e372c8873f283f2d6bc3b0b0ef6a21fdedbf48420", + "d37e209428acd60797e1e77636bdda5204c56af262d487469b6eb069dc5ba0d9", + "44622d438823091e0823695e5c08b633b3f0c46af0286989798e27530a20273a", + "8902b51c808d5143cf6e368faa11e4e86ff0d373cef041553662da373376ff59", + "8250faa8be69d0ccbc2df66b1046d1ccd6750503855943542b6610a9a6e9cfc2", + "e75d7f1982478da0379f975f964410f4e5645f954ace65f0ba0eeb1ba7d7b190", + "5836da561d17e30faac4342fc555878d144b1108f27112af7f425aaf90f42efb", + "b0d06739420bc6d96ccbae8336bb9b6a28adcac61f1f5712915ef42456222332", + "c8839deeb39bf4e9743d617cf5efc29e9a59f39d681128baa4e900203acab6bc", + "9fca90ccfc4d102cb60b2a6d992c9086a71b1f742dd805cb40b945e598bd255f", + "b08651ed30172874bdb63711d32ef549b43861bae4cee530da74b7874d0deffb", + "f59d3938a18249d47eae4be568d3098688df1c5eee393a913203af87f92ab871", + "68b30e55a656422b4f7757e887a7356781f17ec91acd25d2f062bb9012741423", + "ba79b09ddea8bbdbe7723a906c367b4a51cd8aa47fbe2836c169ec28b4e51af4", + "51b2803fc4d01eb5e9f011c74c5e6b294327f44052e9e56fd0b75475b3f2167b", + "2a40ad807c8ec22752fd4886570008b62762d208a5ea24140813808b64792c48", + "f2abe05e84099e996c1f9a2be789b77259322c5b28a1a24ec418038e77a7c252", + "e39996b22b171c30c0d9d7f3f9b89c8cdde606af69dbf6936ab50d4a8540a43c", + "6782c67e3849c9ce43967ee303d29b12545af84fea12c5cc4cf1f973214eb2d1", + "3477e62c947ab134c3242522425c234318e22998b4f6d7b08422427e36d01004", + "c2020358b71b1f043bff47e117027bc8112d4dd0d65131063e667da9e0974357", + "5e3c95247d735f46b45940288d0e8c0016ee553c4939aa4192ba7e52cbe63b63", + "f3ef5179680400a2a83fa5b01d823bc3906d68ffd29ef18923262472afb8c685", + "5a2f1f9be05ce5402fc0728614f98e6c5d8a30a7008fd7114d2e32f128b99468", + "80489be62cf13a2efe0f72ae08422eb8b9a033bb24783bb075e5a030b12f4d12", + "01aa376514be84131de256c9fd1f3cfa05edd4408a332e699c2f0512e8653203", + "6a59eb72f291d9209aa9ee2580a977707b28e3714d04859352826b7003759f1a", + "1ed1699d9c8af25571a1280b6f5251cf690d0005bbc222829ed1b5c8368fb7d0", + "2b8966e5eafc875e7df9b828674d7adfca2225a5c5458efe5d3490932c0011be", + "d0f30db6d5b92d763aa39c910bfd007d8ce08f58cabdfdb67e38b12478851a81", + "2b5370d6abbb3e0c0cf3d9187e5173d7784622b770dfba6c33b8d672e22e12c9", + "7a874cc8981afc43943b02be3428c2150ca2d94dc2735904be475872fd48bf4b", + "ab8bc740762787a90716c3f1fee072e57031a1525d52ce20214a984e793307b1", + "40983e94cc324c141ba33a7cc5682f3f187d832dd665a2503c4101ce3710b2d1", + "ea73153725b2ef43b2e10a05b7426aa8e241284b1702214e8dd9cd04b38df68d", + "4ab724b8efa9a325df30695daea6894d5da08aaef739f20e716619f4e595885e", + "cceb8bbf3b3bbef90da4b219fe547ad6a3edd9dcd2ae48066f2511a8769e286b", + "b8c442596d32a409e8fa7b4401d52cc8cd2e82ac216d374933dd9013535b443b", + "0a7d2aaff8434564372ad339e2ed316650f490f9932afbc9cfb5f9726edd227d", + "117e018ab57319440ca476a5799a3eb87eb7738a793fde1b95c34d32cbeb4522", + "7c84152e37659e4be0e0806e11fa7ab154e5dde6bba50409c8c475f480016120", + "4408ece10877a6ac3f4320941617b72c27fe122bf0a0b94932bec6f7f6c5953f", + "5b425b6d0c9bdb4fe93fdcb1314b2ecf3cafb77cadea5a3fbc1a377c04c77aa3", + "2352dff4400aeaefaaaa61cf37512d2076e3f5bea92d2ad51aa99f630ef25a67", + "08c9db753634d99a25264104c5b816ebc15b95e0d2f84855d32e52aa90aa5aa7", + "91d5571d7b2e0566426cef2498a5690d32e30268c0a3fc17d81e4d3acdaf65f4", + "71b408acc816438a0acf055260a433b5b0fca4a71e627e54ed7dc96079846c44", + "4c925a3c7a5c74d7ddc2095fd3c303fe9af708cc54ec1992f89719a45dd2e98e", + "60f56a735c8f2f31efd7580c90234e9fc622efcae366849c63a796bf80006a12", + "43961c4f8130c54f32b45f9ee39fe88faab957a734501e7b49e7d7ac8f81ba91", + "5a503cee0a815b1fcc4ee6000b8d88e7fa176f7fff7ebf9abe82379a9b87037c", + "b19e6f323d3ce96402d8ced832944a02728d9df0674a9a7bd8e18dc8896de94c", + "a743c955a7b559a34cd8907b9eb7a40d86f8f15a58e90b91c9fd87414a91b3ef", + "b40cb00eaa805c0d27e86cc289e34dd354d820453382954ed2ac24b27d38c2d4", + "e6016c78523ce714a360f77ad95b39bb8a724277430e6d2ea5a2130334011727", + "e8b448a80c197809e559b30fbff6a7a99d1bae7409ec00c766a129ab70e47f5f", + "4b42f9bb88185504e200703c9fa8dfbaf850978a60827cfe7688c434c502de0f", + "d48a1aaa1f36222f4acbaaa8218e249d5774acf8abea087c9e2ee9e21dfb2ab1", + "cd11b4f441f834863a99988fbecdf8b19be665edfd19ad4db62e9d290a98bd02", + "2d6152c003c48f8f6a1c2693dbb5cc5528e7e164a16beb798c772dc85bf2ee21", + "0f9af43eaaeadb7118498e562e45be1ee1e9b7f3787cbec3aeadf671a2c3138b", + "e87a2dab8e4945b8ad5fb7866efc8dc76a990638822542050506d1d0ce78fcb9", + "190a2cb2ea8d06d989d23ff075bcf61b236841a27975f89bdc085cff823e55b5", + "9b09d9ad2f3a069a4c9b33e77a833fdd6605a2095e8062f8b79a5fe197dda466", + "06dd4f3da3273bd8fe869a976006745235347ab53612247d3e0428fe152787a6", + "8dd9a7dcd863f7a8fe98ed2f452615a74560ab7ab964ff161834a6a6d5b21b4e", + "f68bdbccd9955c40848cd3acd5133f991becbcfbcf167e5d2018633229d1c0ee", + "d1990ee38345d528f0115a99aad45cf41d3a579ee1be601bee3820f181819f4a", + "b09339818aa6b2bd5bf955109969d4ed43830a672c479cf34bca1661e31eacf1", + "c5dc68711d6a27092ee6db8a779fb1972d769a4d571b759e6bca257493b1031e", + "6f7e4d57051949b2abee3b329b299d5a6f555d06cdd83811fdc7d47531ff7179", + "45862ace8f36c0a3a6d4326817f0f80f3041622e85e055e2eb89dbd20d0df324", + "b57841327c49f6c475534d0cd18ff578958790ec3c819fa5b97d8971252138e6", + "e83615f0779683ae32d0f019b20cbf2da935403f509bbb9d2856dc6201b613bb", + "be524d2863d41baa171561328c9435163eca92d6e085ebbc29a4e1e3cc6753ca", + "9dd3c020ae3e8be0f6fcc97e6fb0e8c76ce9694dd5ca8a2e8a122862f552a421", + "a7e5ca5d0be1d24c69f4992a3c1a8f6acee7929afb2538045cf737a5da62898b", + "0cdda68b16cf1e43b41e2d2eedd6547972eeb5491930f9057a3c07743219c1db", + "5c83bb522e9a09d6c81af19b7802ea30ad22484b2ef0c609ade92089e7cc7248", + "078a6ea5a4b2be65c7c423f7ad5e4ad909248729b08264b8f880ac5d90f009f3", + "47a73b39d4c6c631d148b9cc31dd43311d20d91592a9ed38d87579491cd9d855", + "7c625f9d16a27bd30064682fc896a15c4b504819233610fc4a88cccd271059a4", + "5f4c8d2cc5c67a0c393493474e74f770f8c09751cf4d712f0c906d9d3903fcc0", + "60a628f0347932c8679436a3990fe3dcf4d02fb9c24fe4fa9754717c176591e0", + "2fb12765c5e1eb877c55f574c7dc61a4123ebf7c7e72cec7d7e83420fa578853", + "2ef3b7fb03472c1f5a850b40e9b35d9a35d87f05292c1d0d43e863d7e7ec6b4d", + "16c54fff31b4da393130f6eb18111392a96e85421fcd1e928e17ae6359172221", + "56ba9c544d0ca5de806803a36a1a19f53ba914f286d92bce307f91eca1bf5652", + "e8df51a49ab47130db5332e1dd3f1ddafcc2ce80150ae3c37eb100f5627b6bf7", + "3df4f7f14f87f1ea6df31b9b8269cbdcac2d8590a89a2753d7bb11fb8ec576ce", + "ed2560a20088fa40572837ba321394fb3665cc60c15b19a2fd07333101636953", + "a5ecff5434cd0aaa835073066abb2b9f9e493dc4cb7b26983f93c94016c09297", + "af3ef059dd49490d483b8b9a99b245695483aaedbbab25bd1a54852a26b28802", + "39d2d8d4da53e0c373a1e39b4847821317f10d9fff8b7ac598b59b7e79e2be6f", + "b1b6815684cc275278a6a3b0a7de9d0c7d4216f8f1b8c8f1d075be64f70b79f4", + "fb2abc840ce8866537feb12531f420dc89ca9de12b4e192e1ef7cbe765cc4816", + "c7b6c8d3d4ec9457b4caf29f6a6198f893c51263d73d0b12e8bcf5630fea1e7f", + "aa5ae0d35bbba924165abccb826604c80f6ad55781c49e6016bd8207196f66bf", + "650d0eedab56b6492743e7c5692651e8aad37d1a9939963141a67482a686e9c3", + "22d0b3451a44adb8ef87a116097e3ac46b5f8345ebf0e5dcec960d2d55a72d93", + "e3110bee29e6162f431aa68c170d622f819bc4e289f6c3e1d695cc444c8dc171", + "92fc6ae91caffeefc1e16e816a0ef299e17c63a968d41bb91b7a6f587d34f8f2", + "76d986dbb1668f0069867df25251328e1aa15e6f31233eacc856e61b5d328e6c", + "560b821428968d6304aa517c6b0df4469059a097171e7180c0bdc1de4c8c2dfb", + "199db247cd1e61526d67823aa89c7d3e9a849a1c48983902f4e732d37491ac95", + "f3000de6fbdf5997bcd5067bddbebd4c1057346a69164446e4a2fd0fe575461d", + "a36d62775f7abac51834f25572e05a0238de20394f0b8a5cc1f33b47984c4a02", + "b6df530ded76cd3cac5eb5f6516b7b5aad5ee43beb0876c8f1eddc38f4e173af", + "a939753b3d570927cc8d90e512239cfb6e52d81b5d56ea80eb4d0c46572d9ffb", + "915cf3bab38606e7266a5f7c2179ad469b07b134765900ff6ca2a5ab83fbe80c", + "c084076c1d75861928f6a28f566527ad2913a2bf829489f57dfc8947c368c50c", + "b71e574bfd980be24d08395d32430a000953a021556ffc4d7693190c00e74e77", + "4cb8e212261c1ec6fbc52df8cac337d6f5d189537645f7d5a7f911db882db716", + "533abcd8780f59049a8462ab7ef505f5ccf1d342150c30dbab5949fb084cdf21", + "db533c3913044c03977baf092f0156071c2628208d14d81872162436b87123fa", + "ca8ac0dbf15bef994221f78c0248b9a6408ead1166e623c6db7652a16c016c06", + "0990b3a317a95d36b6b6ff1fc6736fea01529a39c0fe06e49b7fd564080b6df4", + "787469298f138d1c490251895a85d6080e52aa1df375643d195dbe7e87847949", + "68a371372ba0f489c8ab8672e257f265a418e50c6c969bbec4b24105850c371b", + "f8ba30581f3cf31050db47b7a43a5551d13b9ce7001c896bb79409f4f1d0a31f", + "23a5c21151808ad600e97139099b79c3e1ce23ac1252db87b2d4cb88359cde4a", + "9886c12c93ec0ea5d14478fa4a9a38c4a1025f8b9d595deb371d6aff79f93b04", + "4e4f81cf1628b6f43db1db218773db5f7a405311aa7ee5ae2072148a867a1437", + "a019556336364cf6a38501be26e917b0aa2cc2c37c137fc269659aa44a3e5ee8", + "7c6b6a1324fc40c2373c6215ad699f723b826f02397eb6f6326c1a5ed558a115", + "bd21001934e50a47ade3d713cec8dec036d54da0a976ed3b690ac080a7bc9ac4", + "4db0292758deb6ab35aac12fd6387276b2f32cdc859a15642f01931c845fb0ef", + "5431a87a0cd32b11db5ced545effb22f220ffc69a60ac552844db86bc7998e84", + "c6f343782af6f6ad784f9f85c1123fba0b82ac07119a61ac39ed2fbd92fc0a90", + "c412d2dea34ebbda964b17cc12c1da70e7dc428c92eaf494e17100e9ac65ffa4", + "e9b947aba6755de52a67725be3849e3383e187099aeb1bef5315d22609368b23", + "0b2d1949bead9e9995b717a821f6e5dc3136857b178638350a16c1a32149c6ff", + "dffb51c4bc98c9f0c07ce18a88877d48947706cf57f56cc6a29ce82cf2f74c07", + "97dbf9a48f3c92fd4674b20ed6f635bbd92af003de523feb5ecc4758169e5857", + "cf701be33d1b3abd3357a74ab2f7f03fa78f7ebe0362994256020fb1fad75251", + "fb26930bfdbbdd6f69be65c3c01536e09a30cedb747b69719035e31636e47937", + "5725b8ebf2345fe7e660579ae94b84e251259487da4428a5f1f0cfec19db0639", + "61b92f8043e5a035b2144fad7b1df3ca7130c56cabbe0dd2757dd737719719fc", + "35a2f85adbd0b6a818d84a16d8ccbc0947bda389b6cf43ce6ee9aa9b4aea19f2", + "69dbaa6d8a1d44259bf095b55311dd39ce028ab20de30ca49b1a173d6171109e", + "3b68203efda022be691480952c5132129d514b2cdceb0d3a8f137595be7f1c25", + "388d3dd82eecf369794186b42c964871575c03fa9772d57d31280d678f87a636", + "6b493f049d19b3d1e268babf0211d48abd018c912787a913ff71457d54ac3606", + "53261340605f5e63ecea85eabc5716f31be1d16d1c91c04f3a090773b288e62f", + "7bc9e1a6b36320bf97e60da91091c0ea848653a56cfda13f129e42719623c500", + "7347ad263d90e20635eca6fe1bc89df06a4ec273ad50e1ef78a27fb9fcffca13", + "fd286c0a1d9e4a0e0aaf97b7f2da4b219e385d7a9afc4982698574d72ae49daf", + "0f42f9f37bf77fc47e1c75e0206654e0059703f2b7dd261f91719e49e586e7ad", + "5c1eb8efa7c26ebb6a3e2cb19148783df0b7d5dac0e80b3c793ecb556166a88a", + "0fd0ace6df8a7ba03aa6d52f78c3e58a4720a3852ad936bba80e08bf7c61a287", + "020d473e1c9504366a2204d566b1ca6c968320e30382129f95a40c739cba7f19", + "ff7051698eaa84a3ffc25d5dbcf923933a1a59730db33e8161698160a090a45d", + "df0b1d015cf77e47512cda3d49be662782e52715fb747cd0f73934109a066ff1", + "930edae2fb341f18849c9b8aace01d5ed2cfaa013f59647cb542daf33ee60db6", + "acd5d0212d8d1bed17253050038a481d968cbb8007c5c69f35d328cc887723c1", + "17f76ba6af39c2d64b95554ba27b12818e60284d166a9da60a3110091c1dc3ec", + "e03914821b7b6185e239eae45598d504558175443c4f34b700c2b329beaf14cc", + "cbde6cf03818faf19d93a082a509c3601a6b043a66a222d389cd44d4a91d38cc", + "c42abe41dfa793e60e2ab797e626bd8a5904e03633b61c513c9f80c2be9ef0d0", + "b47f9040a5561c94bb5c11ac8042f34d80e144a3f5a8b7c0e47d7aa3c10812ec", + "07f44825dbef5d2c5fb089411e2eb2d4f7e7eeef6697852084acc7b1f48aa77e", + "ab143316522be7e11b267b89677251a5a1d48ce7c90ee27a6c66e1857d71e576", + "3e03c42ebff10fddf113e731d91c96cba59d8d29acb8f6e63e0fd171657414d2", + "12a073e1548bd7551d477f3ffaa790d42105596f7c161a623650f1a053c6f053", + "4882fb1b192fe817b836fff3baf788d89442f75d3ae7a529297febc3ea896779", + "a3b5c64eb6d70663ce13bad27c28cfe46ecbf721aa12d667f3903e155f37471f", + "19283ceafa4e91731909817f264f997836d529c523881913fef99c04c23f636a", + "2c9ebde51c783478616c534f8cf30c1d9bcba627e82873678054f8c05f72cd20", + "6addbacf1779166778a472eafa5388de1e51765c9264db7f44297808f30cc940", + "33c39d4cf80eddc5b93cbf11f5cf6de02023709b71c3388dfffd485becf93de5", + "73d7856eb00d8f0eef1cc6ebab299384aba68e5e7c90e4526c4a33a3f7b9725b", + "f45968304cd3eaaf430b7744dac3bd2c287c51ab1418e02973fb2f6bfd3d7847", + "1e18a797ff5a58a2a3251358d7ea2e84895a939dd63fb5a3c77753f324b9c86e", + "3ff531919376e2ad060436f9d27129b7ef17fb8ff29cfce1ef3726833aed96dd", + "92a1626588c06d5a3d74a59f23e506460f99323c09d35bf5d49bd73450803f50", + "be288f4cfe34bc80521860940d76e5a42c85f68ca190a4ad71dcdc18c4198103", + "9339b6786bf2c8c1dd4830c384e03dedac22e92bc1aa9cdc3449c674bef72ca6", + "1b22a0e578c641613e3602764129e027e101e2d423d9fde2a6718a7dbd726eb7", + "c6be938d80860b8ccc96ba087fdb2a00d980808a61dc79483710ba87f1529dde", + "4bbb241fdc1feccd155ac9feb2ab91cbe714030cab20dfbf34318ae72ecd4a6c", + "706c4c5215f54df3c0c256e9d6c54de897716618824d1fc630d00d3af059352c", + "93e883284fa263b442a033252e84dd637a6943f49f9db224855327139dc8417c", + "689dc122cf1c3b550ac11e0041b45844b794a45c31a2896a4238242422b23819", + "1aeed96414641bbf27a32c1a94b5bc224947f87bb915a53dd2362a5baaac9ba0", + "0c0f53761b2c3f168b01cb9cc05894cb86ee53a02ff0c5fd5f160a8d0d15ae76", + "da8b72cfb9146b496da6719d87da1c52f295086935e1f15da60a86b24bf29b97", + "2389058fa5e0174f217260522f49d07b76c7758b0d3f7296809104cf78561b40", + "0a59439df43ceb691a13038b87b7ed5ed2d05f7acddeb2cc502d6df537b71311", + "ad9989dc2e5e4d1a473e2f30f9581ad5dd161f68765adb6d2e70c8effb23f3ff", + "466309e21fe5c8555a231e5214596e196ad44683a8e8376e453daf42b8085d9c", + "a6ceea7c560556cdba4d7b433400688ffeffcbf27bc7d05ea507dd2d4ce948c2", + "b77e4ed4bee931d43571d6f22f0ae253ae2dc63871c5e4845c6676c7dd3f0eb7", + "7ebef185856fed824ebc3f38b79f82746b2983f8106c2ca6a8b64e410040de10", + "e9a0458703175d02b8fce2b8ca87d2a6062270e97499130aa81aea621b42567d", + "ae17cd0fa9460c9caf2c158e1ac809d0baa510933aca2413c316840ca42d69dd", + "0d485a3ddf329e8b8dd47b569c7ba6faf17d4dbe280521a520ece56136ecc218", + "20241e5f5bea83a9efe54520cb4e18b2bbda3eb83b0875f44c62994cc2f4a481", + "ecd706d3f483600f37f3b2b268cf38033db1290e0359a089e7dd44d35300eb39", + "326a9ece9955f2f872f0f6e401ff4281f1992d9f22f247ecaf25a44f5661c732", + "36f0d608c831ea7365b0ab0f2d78d44f9b6cf0a3edda625d02a0e8db1250c644", + "fb476cab0554ccbd17ef87ab086fb70310f1b468e61050e4c47a9c003f71a88f", + "eae0d52bc60c959f73cbf3ff7a84f1005b935e3d71b64dd076f23851b61e19df", + "c8320f5862f6c97350570d41ac318ab729aac137043d0ca73f467eca670fd2cd", + "ad83f844f80d1bdc54559399d55f09160e3041f63bd85f9482ef0e0159e576eb", + "faca90225e0b75e9ecdb380a4f45937016f843f188f48a956b24601dbe2e7eeb", + "da4e04a00f8b7a2724c60b78a8df4caf6d3f65a8f5b8a86561ceb9933eb87a72", + "c17975d1893a8b62915a085e714f9c6f85d9442f7916ddec6a822090c8dc655b", + "7b7f3cb7bf35478e8dab8f33acc8746aa6ed70189e0daa8e893783deb55fc50d", + "d0311b9d76278b80d7f55b8c4bacaa8dca293cccea20a784ea9fe254abe93b5a", + "91d99863528e63d01fd353457a910ad683d11614458708b31fae2454fa224b5b", + "b0ae1e07f65c9dd764db153aa46cf42f68d780e4b28e642a4559ec90e8e3cc39", + "43fea848ab8ea34e591ac9c9f98783b3f83a812a9bef6339e81bd380d4d0b0d9", + "80695fc224bc3d9f16e1be9078e62d91b1769c4147dc44421531a213b4a69460", + "ebb6cfe7e39f666efb4e256af94b5b21aac14902ef3c23e6788ffb32d16da24c", + "ef5b6beeb6cf57ad290d3be9fe6790221fe4685d99ced85082f66cea6b2e2d96", + "a4556671f393544c118880d5e2b6e0b6a9d571325e35972c8bfa5ae70a9aec8a", + "0df55165d1eb0fdd4b62a25222e060b21a4e5be7b8388e29380b86b2940efdad", + "19b60ec413d47f58a62173098d1591609281e701dd079656a09512e8684badaf", + "52e4284b527671369be86b4b20545217254f85faaa8abe39953b59fa1557442d", + "8eb1a4325c5783a09e0febbc365313f3c4d35a2c1618ee6532572ab2ef1124e3", + "48a251c051cc0c8b051b806600f00342ab81e3aa111e066e74adc044502379f9", + "97fefae91a85838a8b93a4a8cc0edadc2e50b43c1ac0e36a259a40fdf8a4e213", + "aa1342bfc905f3f255c44aa09df477228c41977c060fb5f936fc6b4838c5be7a", + "b9e6594d64492899962352ee5c918c3ade3efdf92e16bd4320e0da27ab3ce2ae", + "1a5db6b7d6da5386f76ef77cd6ddc99ff67de917819afa746dc3c5378a95f30c", + "bf64bc337a886d5d799d839f03eee210acab95f613a17db30165d3e249d07e87", + "37b794fdf56c4ab8d2b4d00506ae67f02060625d55adf6c208cd2f53b6787cb8", + "7df41b919abd69e7acee85a552ed81023aad7a4f1773b35f104af8d253e9020e", + "71be6114d2cd31bea00baa69981edc59bfaa7891ed5849178f3711f37e5a9223", + "7e8908c6f9e14300d44c2254d022578f689d8c0cab56ad73ab97c748a71e7371", + "06a70fc0c9e0e0a3a67dfec26eb5367836dc932f20f65770cd27511f76a4ef7f", + "5f902ed069cac194a12c903fd9231b3b9b73ae499e47206179a5db78053b7d48", + "c4587b59006d177ecb76f64a083c8bafc926b243a6e9ee6ef12bf0f3fae367ff", + "eeff57364b4cee2e43ed7811728c6e0dc155c94bd8630567981c00e7983c202f", + "d9ed39b27fc2cd3e1c3bdda317c856557795363bac22e5475f58614be9da5f0a", + "5fe8295650a3ca6dceb6adda98a9b2a52c8ff5aa2b879dbe74dc479c14df783e", + "bb74a5396ca5059aee5f6973f760285858dc7ee84f34631100b641d8f48b89b3", + "bc357b63fb90baaff808482eb645d5523323bf52d98c3fe493ac8eaaa4773677", + "2d052d15ac1ce4eae8e7988c586779ca83b7f20134899d1a72bc05bc94c0b35c", + "ec20f72f981ef2d1397d3f05c0cdb32110e4c7bb9b876a9aa8296f5daeee6cc4", + "039cb433d2315ab0011d4f1b3d040ec7e1f1006fc3278b96e9a8e0491d1a9d60", + "2dcdb4800462a1c11c0e832d04a1c49d9f62103f2da332286c48fe94b456b92e", + "4a8b6e2279a7392cefb1e7da03b42371a678f84c856de77cf13ff9c0891d9b00", + "57bcdcfc0ded16c30a50204f409dfc81df4ad4b1fe673d99920d9f79a397d8b7", + "0261b6d4b9c1c5a585ab93951761c05ae9cd62c56359dd821486d2b1fbbb3177", + "2a71c8ea5108d9a459c6ccb1a614cc2546ddbd85efa1eb39ab3ce0a504cdb916", + "251698409786d137dbce1f4490f83fecedaf384809eaea793c3e688bbf61811f", + "2ace8689de6c19ba00afddca95b6c4d32b9929390c628b9339a77ed21dd9ec97", + "10e81239fd71cd6fc6a7b858986c7c4e1d064811ffa0cedff96ec34e3fa9f454", + "1981916f528ccbc3547bd0daed092031c4045e1d1426398f88298f7d29e61f8b", + "271cf2cafb0c86c1058d1247771cf90cd398916ee1eb4988b19765673a2c03e6", + "9d086afd05abd5ed8e554891229752b2bd98bb124d83e5c517c727a6e41750fa", + "5f2c7ea24040a74f3badd941fc602bb8ba2c2f3dc8a6f12bf3565579587b5ac9", + "0cd14ff3e26eb0253503592d3c2231f067c0d34c9a08478b3ccb6f9708e63bb3", + "7d8c41eb7c106d34679fda959f40538e268441214561488988384d52bc4959bb", + "ad844d292ee314dcfe7b9673493aa46fc09437a3215984514f7d6e25516d82e1", + "1a2a86d0c46010632d2cc42225c28e81d33831a762904c07fc165696e0b21f56", + "dd5549881dea36dc21d79af4fe8fd523ea7198354b09f11d45cdbc16f2f1a593", + "f163f190fa2603da18b5c4d6a8ce38135027e1e27765786dda3d4aea2a9cb80b", + "6e09fafb8b6e87d6189f46e244bfac72ac097f881fd3e20548bc9b2b77a06494", + "050e25827579544b11a0659b33779e36091a9386e10529de15661476f621b4be", + "2e106b4ee4214443a32a625a8050bfbe23c994d5d11f7c06604d67449dc4b65c", + "f46f7dc3ed0045f94584f0638fed6b4a1428e757d0286e64a7d09b90bd379f44", + "4a2c43aa7fa22812393b64ef4735c4ce9dad96b2324d06204d17cd489d7ce412", + "98862e2f2fbafaf00755c99a60aa0476cb449041ee71556e1e7c6442e149e51b", + "4c118375e7aac9cd504042c81c94f58f123924bcc2dbdd07efdf3ac283bd06ec", + "bebddec9428e7802423acb00e58d998f5daa5a26bfdae57697deb463e9e86feb", + "53b03c24b8c7b3c56e56ee1e708df3eeb032bf383d7cb1b2d3fd0bb11cb984b2", + "784badbf46476520b5c6635401f74e99ef7c7a003a393aa1fd57867f185dd721", + "2f3caaeb46d182b613605bcb8bedd3831bfd1ea4e7a041c4f76e0d4718aa9eef", + "1b3aa9204c47e2d2ba2657e13382926d2ead35d868353551eaf9a66a70c669ec", + "c9f2a7d878c3f80d7627ae2b832007dababd27fd2fc13709901faebcb14b2932", + "5a39dc81cb0b6518f00f5c66ac659dbd72f881a78e0c0b13dc88ade430abb90f", + "2f0ee5fedfa55f94832fb2744a80af32bdbf7708ba65a3e0d08b446358da1499", + "1d07c7dda8607fe8e06cefe30123fb4a38302ca4e091f5ee9df4013778e13fb3", + "3fabeb2e7e8cb221b62d4e11ded6d5eaa1db1380b7639c943f60a1c5c30cad09", + "51a1fd08466d6b37cbe7a6bab184c21ca50c9feb5938283d58d89fdc1035fc9f", + "0708c2dc87f7ffaecd46d3c7dac910700bbdd5f9331f262819ec9fc90545d3c8", + "2d8f59f27fbf1306ba8453c032a2d8bc6a18ad2d8f3fec4ec260af16f6213dde", + "3a7455c90f216f2692b108f4c907cde0f64872ed13e6f75b44e5f857695ad824", + "5f1fe505ad82e5de86f16977be619103b511b0ca09393daed964ec61962cb942", + "3decaf9907eda934c124f603d3252929ccb9b99aeacf745018f2cd612ba3dcdb", + "4d232fa8d81cb6abd1fb6806b45c069b7fa7011248f55d1873daa335312dddab", + "55000d6b9fd4132be58fdfcc87b55b28dbb2bc259c33d50e03a9d470e503553a", + "225d9a9f991b12fab890ccf06ee654afad4878a3a8c0bb3ddfdd54af3ba74ee3", + "40df7f285dccf966031f1e8815bec7473a748f37c9405ad98b0ccd40c87f986d", + "c4156b9659801913909aa5955548725ed2a251e9d3b3a587786094dc299a8f61", + "5dc3a95d6f96b946237a9977b4c1e05b24799729f03c191b084c6118106fd712", + "118bf0b7868f02b1154198107b4a86873ef471153967ec7bb11341caf70c4355", + "1ad3a5310730b0b698512c8c7d3e7a8552f75b5c7eec4546860bfdc622f75e7d", + "fb74a1c13b1eb19423dd805a4d85ee771ecf52e3956b51b0409cc5bbeec34569", + "7405d158d51c83cfb8dabb3201f692e3bc089f020cae6b62a2af2c88dd8f03ed", + "08f20e5a8dea1f3217c990bc112fe510ca5aacdefabd5a2a6958339abb88ad7a", + "dec3f7bbee141e55a68d1d164e891d9bf832cab148061a1551475fe33c1bc868", + "0a311678a6266de62ceb20154e6ba5ba5560507d58d8c5798474967b4924651c", + "b4f48fd17a7cd9e0b26e90a8adfa2f9a150d47e9a203cb89df114585645c87b6", + "26a5c76a25d02b0feb4b22e549ac88447abb8fe7c86196a73292f13d8eea61aa", + "2f41b58911b04c3418bcf76dfcc500d03a55fbf1749ee83a4e981fce4e32eb4a", + "411a18b6785d5cabf9b666a9a70cdfc5a1e29228f92f80b9f8d613face0195f1", + "b17d0a2cb46dcc365e065267bbc2cc36e92e20bc0ce24d144089d69282e6fa19", + "30ede26b25bcc2e6cc42f248386c61499ae477d4fb392ee289238c40c88ab5ab", + "f1bbaafdc5f0e00a5dad95c9ddf659747c74d093e1cba8a0e72f1db1d5cd904e", + "09e411ef140daba69cfdc2c3c32fd84cd572a842f53388124b51aa2eb1e40fa5", + "2c408b685afb2574e9fe2a212e92c638090e851026ac80c9a639daf234fdb984", + "a342b54ed7092264595569a37a5e5140a48d5117f62833de3dc1923516d5b917", + "b90af846ef17cbdfdc06399f2d6790194fd2d781babbcb2c3ba005031a278650", + "16c65a8054f7c7c6beedb172498972f2c3dcf0e8da240cc8f120dcee7439c95f", + "8979da949593c80f9164828107f04602d70ca494947d6ca7722180ca85c8c0d2", + "eb4ef64a1eb138e6ec7414b3d841d24e61d6aca94a870d3568f89bfeec28ce5b", + "8db828055ca1154a1c1b60b992299b2a3cc9f9ff217e15a329b642c8d5515344", + "5813ff09a79cd74b6fb3307c277caf7e80f3506afb677eeec65555b7fdb8bccb", + "e15883831714acb3d5c08a3898ddc6280114f1b41c450f6e93a27bb36e7c6766", + "95ee3e0acc204a6bd5f5b484e907056695cf5ace25449fabe87f25d61b1262f1", + "8eb0d5da9c247c962eba9081b9b58413b8f7aaec03df2813c77ce44573a3ab1f", + "afb841db4f6c64fcfda654cf058f0a883166b4066fba6be6df5dad564da30515", + "68fce267894ffd76f551a169c9508e53810bb12e773015aa4c547a56a5806fee", + "6e614d1afb783e6b45301b2fa03788a1fe27f2808cd2b513eabeb1226e6241dd", + "6bb0cdb7d8686d79abb5453c3f5045c1acc2e268b3f2b32444e60796a18d4c12", + "9b99c863068f24297df3431025ec6564b66f2b9f64c1678b528a1dbc4bf71b16", + "204b6f2f3ec5bd0f1907fdb460e4ed6fb0d25de5cc12f777a1ce10aacbed40c4", + "615fba5e47ab0d959fbcef39996936da656e2615b954f03b61fbfaa8e1a4f500", + "529895a7de063cf6e2497d30d849a8df3c4f7339e22a486044cbb3e7496fec22", + "b7fd2e4c0b845850f044f32c5d5123af9d5eaaa693d4350c4ca8758bfb857b19", + "059e015cff4b33f019921f9242533cb45cd65e507ed882205367b746b27f4442", + "1b79153a3a25bc82c7f52a577c140d85854d76b3268e332b5871a4de4eaaa47e", + "b9730c36bf7402a5d06c8226dc11ebec9c0a4bd6a23762a6e9e78d1b996a4630", + "d22427bf91dccfacba55357d4a8038f797eeff2c4dab56220cb00d9ae897b783", + "da19f42f6dc60fcc3d075d6f38671949ce2dad31b37496a638d341757822e92d", + "78132589eb98ac63e53a329f7cee32584dd447bb9fbbbc01e27245ca29c23a23", + "e467ea63a727b9c36e666d435664875c822f94f047f60ac0d89f2733bd4d1f01", + "f4f89149b1a5779dfdfa3a9b7dc5b88791deb32c20db6d611fa013b9a2eade6b", + "7b40c196aef97aa032f8a1ba0b88aed12baf4331acacae6513e8f4fbb102dc98", + "dc87f4206941dfd0431dbae606358b03741e52c9c9830017847042f99caa1a89", + "293c650a8f2b442da199ba6dc31cbc7616c09bdfcb685d216fe5f6964ebdcc19", + "cf3736739c87082a71653540be254cad9dd2b80c788910ff51833053de0f6833", + "b7904a9d282ee147c41a87ab1de0c68f6fe6e8681b728a50b07dbf82ab61990f", + "ff43760bacfbd8a86b77ee50524df729bfc322d221bf39d73fbdcc5172be25cf", + "94b19511a901d6c9863b4e698bd3203bb45f459a706f17d5fd0217b7d4d2e880", + "5fb42d90123e7d17bdf84717dba17f8d6e2e4cb9856ebcb5dbf6892fd227d4f8", + "d403da452541afc98ea45671522db8605d28a1373baa96279149d70fec56e8db", + "c515c1f3132d7124546c2149d2f96bf8899954ed28db737d81149cbd9f84ba87", + "7559f506a7dfdd3ac7f13dbc616629b56b88f8e43e6039205182b17136e45e6d", + "6b3f153c83caaa5f7bff608f4fbc514feee1ad49652691393b26e60a3860ac49", + "b25dc960443d800d107491ed43a224db6df66c12b02b7dbd3415ad4cf38c1f25", + "a70e10b5d457c0c10ae765d7696e96d1721bbd0557386cd00db27f7dd61c8039", + "c12df355cfaa94c9154a29848e53dfb2d1bcc7b68cb1506d067f5feebeca2134", + "c9a697b7434305777c8068af8b5bfd654d74bc5ad82220f84fe16e1cc9cb2173", + "261dab494a2e8adcbdfa76b171a1dfeae0a70cfee528d781883537c35ad37546", + "df2aababde59eec1b7b499858984c1204557f599659f05f6671d1d66d2273d68", + "36bbadcddfe80758e7b91dc8bbd2d2cfca89aa1c50ad41a706c99bd192c1042f", + "bbbd1e0d041cf1b340f9b0873d876ead1b76c60f593e8ba82e2d700309ea5b4c", + "07708bd4113b5e2773ac0d65323a793e5dcc366b1260eb55f6ed80f6fb3eece5", + "b7fd39d21af1c3ff30225230a86cb382680bab72cd83c081495df252bbd31dca", + "a01f7348d23cfc9c72d4c6c8d41d00f25331a9f6b28f704e314f46b9a5fe8836", + "e14b6869725e9f7c8b4d0fbd88d1cbf9a969e800bddeb3b426ab13a9e1c6873f", + "ad16ff69baf5690783aba8de17d09a92af4fe62a985e33c0969ed734d09bede2", + "d3edfb7f2ee7cd654793fce245666efe24655095b9098152093b3e5a12cc41f7", + "50cdf981025f02190aef3c09b5aded26472fe5b155c689acd1600b6f163b0aef", + "d944eee476fcd32ecf4d85d5796e17ec2adca8f9f6ce1c1f66687e2d99a29f2c", + "6eae57da9514dd54a3f5920e1891529982d88c17e7ad6f6d8d24c731e1507992", + "ba0b8bdba07211878e29c71e21bb5aba5186e50fd45b4303f846897b8c409ae5", + "16828f8c425552366cedc41a7ca7f0df1c5e5a9dc543bfd9b0df65021d087a48", + "338222d8ae91833de9008c65cf519b1d021c6072d09cfc08f78d002300b6dbf3", + "c0a24805f980cbf60892cd89af249fb06c079c694e3cd84f0cba7f21f9e6fc9b", + "fbdde53f5541ec1feec1a381409d5ec9c6baffe97ca0c7e06f24e841d2b1161a", + "1752a73a37c14d5080efc1ec13bb1dccb9bcc0281a4d95cc2ef4b853ecfa6c31", + "fd2e0c2b50b472b90d7f4bd119085224219ee533a99922ebd8cb0f2cbee9cd7e", + "3a1d28fe96af222b8bcbbd2e937ad0d1f4128c7de8c7ae4ddf7015b07a1f6bc8", + "7919fe4c1265bc194272f2d3e0a0a0dd4853b9f318baf6287484549b01404b68", + "10f6bf39d6329af3916a571ea4ae1de70cc23a9818a866be45c267284d3abfd5", + "cb0c691d7266460c4af283861919c3ee427c09831e70161c0b7485b3f420c051", + "69d7916a1618b94a9794dbfffbdafe155f921b57b40c6a55e8ca17405d1183b0", + "0c59dc8c3caf16b480847e086cc53f4bcc55c0ad0b2d6954f943eedab457c86b", + "8accbc6c5c4abd38dd2574cf0a84286709bb377aeae9d50668b2145190ff94eb", + "66213a6bb4469587379b204233f5c4cb9213c9443c6ac8a8f5748f339223bcf8", + "1512294a5bacf0e03dd013bd22c0469b02c6a56ae6ab03d089d1b59f9bf47ba9", + "a5ed327b1392f514a7ca393657194e0cc1f5f454aefdba63e867a3905c9f3ba6", + "af21c0215b6b801617c9b590a5bbca4874f5a23e7fff46e2fc380b4e501b1b42", + "48d97a310cffb3c3f576b5615ca7d30491461980e9e6ddba01e9ab8b69c6fb87", + "c23b5db245f1cc4791a6a7f57dcbd3a72750431e1a67f5f783668c8a2825a0ea", + "ea2c2b9190a3a2e7bca55d33bdd1566863284587cbcfcdad74a617a571d006da", + "f00c701d6e9eb10e8ef79276cdc154e8ba8c4422f1d08ff330ef3f55d054d337", + "43a9d6c9ab48dc22ca3c9cf8250db069e7513537e53cd632424035f96371b52a", + "9668811e88849fe80a78b79fe33a05252ec3274a8c4c924609b8c85281ccd46b", + "f2f93449bf57bbd79c852a412dc55922802044e40a10d5b70b940a585ee720ee", + "d20bda52ea5a7e785ab94177f1d90f5688d21b3968326e9909c8d26cfe160b6c", + "1eee37e54171be9e11fdae188b2edfb0bc024b5f94de55dda4b175596d449bde", + "0af11c745a8c4891e888eed30309edc37748d712e154a078d4bdd5b13cf220b1", + "15982ded9a62a5482a3acc1ad4c33159fe6c42f09aa14365a292d1f8e4434861", + "429e21c28b9f0e415e2211ff953a01b408b9399dd2b67d0cbf212653aa9e3de5", + "9b21c3118ea6bd778beb58728a3e319c2bdc18fe9bb2218024e4d7d537758988", + "730d819c74a968d95a749664b659fece7f28f85ebb9e860542969b1f83f7135c", + "07734cbfaf9de00aa8bfdda5f205dd887fd7df72535ab729d873a5ea23080c8c", + "472f6c62614c64e30fa642fe8370b2108fc6c624bfc3652ddcabdc943b93d12e", + "41161aea39995afac088fa514c8df9905a21984920ab7ed9f5ef52f603932f4a", + "ff0ac19b15cf71021ba255bb88b9b48b8d813446364defcbd3648719c0ab4a4c", + "d9836a3268981bfe94ad1f77f2b3ced20b7079f09025391357de906d03c40e83", + "7e55b991f5f34ac4389f5574bb493fe43493e65668918329489fcd9e473eadad", + "d32bc70e6246bb9345bb5efb2ffb68f6173a0b675a8cd4c7ca74b354adaa2909", + "b9d8bbf671e794da22b3d2318c9ba690448e3a41f406b903f638b5025f7564d8", + "b28a5816f46942f4270bc625ec876659d37c5e40aaa913a6223e49e7576235d2", + "43781bb2014fa51a94bb3498c99623b76eb963f48238f9b1df9b6f31322c0970", + "8dc87e8f4df3ae436fae06c170909b87542352d349384b9c7027e950b402f65a", + "339cbb45be7f44b72f31f96503860c5a25c5f2abde498a93561fb59b433f293f", + "f2e6d476a7d47acc13d2eabd4d309398684ca739f8bce57eb55b1e79f3d1e519", + "56af0198847aa027beeb25867b66c211c78fddbf74d32c1d833d87c778120fac", + "9ac6f60c776459541ec6bc8801fb2fd02e36585debfcf3212fb0b1614ae2f6cd", + "e1792f3d1d3d161adf761720889612dd3cc96eedbbe2a432a03f7539b0cf405b", + "0618cceaafb6f8e91a6c592127ea2b5b0cb1194c3210c3d643cbd25e047a2c5c", + "c7deb398d09de0de32c114870e0843d7700252917eb69fb0546830437308e970", + "e0ac7eb0b844399d04bdf65c93459a19b00c9a23bfbee05b3deca40a05b9d389", + "23a16f8f604e45d067ae5a3b94755fd327505b4a0959844aab30ff5808f79b46", + "488b6589b506e1a4501b2485091e717411867900783807af78f87dff7a46cf0c", + "56c965124e66ae2c716bf67050bdb320f38385280e7a7c6cd51bd7f7ce43d32b", + "19edef6e3e69990ec222877913340628049da39409aab79c93af2b6948a93d58", + "128eab2b73e6a6e9c535e15b22559826c3e9d678859d92653a5b0611acd12d52", + "b60504a4e6fb685fefeb7053d02d89bff2c41c86795c4061a89d0c3685da8e84", + "6038bf6c7728e0927950e6865113a34a6646cf0ec0a2d15e1f1015b87ed6b091", + "1bc443a013d98a82943adba3b89cbdbbb38d8aabbc0d4df57d218717d50f50c1", + "9765ae2f7bd51d2631470a84f4acb6d9669f3e211ba7b3d1480b80a0cb53dea5", + "cdbb6c307084183baa8b44cd6a93f014687b2e8f6aaae063fec6eb2c61d3a331", + "d386012578fa5b91e761a714209d38301129c8ca4d1d393549a4801a9b1e0094", + "05387de7db27a628101147c5e01cf481a68866c5f4be570f35a50e5a96dd8976", + "ad2775261f5a12d7fcaea9666b48dac6ffec096d643df0bdb53dab400abc9fa9", + "c95adaffc299856ba5bdceb376860a43c0a55383ccda166614af9333986f18b5", + "2db34fa42ab7e9970883aa5744fd4ae926463a3acbd35ecabd5854f369bf3f5e", + "068881adabd87a5d568f27064212ab1bb362583ab9fcae18fdb03dbc0f3185aa", + "9ef7cd94ebb5f2b74db7933a06459ad972f43f21260ce84baf26904eae9090a6", + "99afd610f6087a27df4c16d1faa3e80af5dba4194e7aad1347dc282f493951d8", + "d036a9a86f34c9eda2f4d54a8f9df4c470c0f97695c4e5dc9de60e85af56a1e2", + "7f0137290a2f6b51638f1a439091c26c66ddd7afc9a8525c86d0490952957f5e", + "a755f2fa40c875c9592313dfad65fbe58d17940dd1fd5ffc38f9520f91b8fa63", + "6690697131f9aeea3f3a74f83c6e34475cf3c1d6c63e1555174ad875a908ece5", + "26f83c084041837eafe3647ce38404a2e24b2c3f5a870fbc57ee2f75fe863156", + "aa90255d432e68be319e26ab5b1423400d2013117121636cb27ec494a887ce49", + "72a7f09acd5ab6283df40bd92802a67c66c5308f661e98f3c12b2dd3c26817ce", + "de85e7574117c4c773bec442121ac01cdcdecd533946624f5956258644eed440", + "51e4500dcd4477c425cf9276a556041162c6d9978d56f394d32a10c963f3049e", + "7a37ec2c99e082dac260c89e7fb72c23a1f958c31e50624217391cb31b715ef4", + "1611fa2786e97a786d6f0a9eed8bf4b9a30a2158c6b9722eba150b4d53398e1e", + "0ce07e0ba171681afaa50f91d3ba9e1c1801f6c9d49d7d695d2bee3ca563dab2", + "4a9cfeba7938b7c64cd17a04e6191e9ee3b07eb39720f76794518b264817eaed", + "ba2e9a35c933057f7775202da766bd3686c4268f32d9287d8cd3e0a371002d76", + "41e92e37ed2717cac094610c3da3c64079d81b0f18adfbfbcdc46860082d05ba", + "1dd6a4446b8615ea23597f2f1fb4f01e8bb999c5cd098099694dd2d9fba09e71", + "a32552daccda15299cf311e0bb4c7ef3378f303997e5b2f3892cf3265c9aafd0", + "4a17194413577d14d26f1c1fce778081c7cedee904af0ed3a25abbde34774885", + "2d5b0ed95502bae6f4bd6738def6edf4e7e351f879b8c034646fa7421d0bf675", + "0c32676576aa6862d224cbc313ee6576fdcfb71c8041e83a789380c58413354e", + "f1c53b8e6989c4f0d55a5a08239393a9b8a5517244d8f04577300048f9e21e87", + "4c6e479e828fbd26116908338cf72758557d523d190b7cbe3c33cdb00f91f281", + "fcbab6fae7d8bacee71d82f2c1ab679c48a85ea750247f93478baafe4d8682f9", + "f79fced898dfdd08f18178825490803c4203a029faf29cbf08d8f1602f032430", + "d2f0731c4ff11f4cb6234e6f22717d0a2ae395409329cac53785269a7c26ec3a", + "535b3030171da4c461763c488d7da9e89c8cfd7c1c2dadbd360bb99edeb9b0aa", + "9f5e861a8376f92be1917f3157180c119f5b2bea66ef40bdfa77ec8f6136df7b", + "9a7f91c1a05612ddc81567d6637a6279b614dc9fba3cd0322777f437f6cf11ee", + "3e1474061b17403e96e750b3ad725b570d4ca96f98f583e0cd5dd26b84877cbe", + "c046e99c6ceb99a1ee02f92bf5099e2c44a5adebd5290dee742cc5ae9da7076f", + "0736d3d6b42c0b68cea5a9c774183db0d7ac138e6303036e46da245dda405f44", + "2677862a927266eedf5f926a1844495ed7f31f671dac9f58996ae17c637ca140", + "df622da8693db1d4245400e8bdf8a28190c1d29c27d30046f9fd1178a7554c67", + "b0adea932e628a977282825083c96a26a276dc13e623380a45a590fccd543be1", + "1c54ee5a29b8ed8013b00cdcbdd654272965ce4be0e38d7afb413e65cc8525e3", + "62b605565cc85abfa0861daebbdb0b13d58732e59bc1163d9ee5b411fc6f7af3", + "7b7226d49d687fd471e0eae0f54d9529f330d58b40a255aa871ccd36c8b048c4", + "9ea175109678095977395a2337781a4b7a0bad3984d030b6bb0223772487d27e", + "949862062b71287fb7eba6eb72efed9a06402d4372e8d4c32de15961298b9455", + "4200d4862ab89d7e11f465dbb15e722eea2ead1b6eadcf55eb76cdee2dcaaeb2", + "daeb4b3ae41eab84db5c30d4032541872bd9910f36c2f3708467b640f3d56217", + "8340ac58b1ea529e8f4f0bcf297dc402d89f412252deb830d9e6ea626ec2221b", + "b1a09ab896bccb3a2b2dd487d97392e5a3de1f2a8937cb30091a5be8320e43d6", + "772812821e2409ab4b668507b3cf42fe3bef220d4970081db69cfbb992c54a0b", + "af59ff44fe6084dcb0f148dd58429a66985c8f445d58bf7d734ee5d6cf80002c", + "308def2d531cbe674b75dcc421bc3a5ef9d1ab5c8df737243643b2692a7280a6", + "e207877fa88deb5ffae9b19ebc61d0ee6453d6c7bf911aa01263030cfa64f8a2", + "eea9b387577ce6c76eae8f5d2d4fac9de61dbfe4b03382024a752c7f90a8e366", + "ebd0fa6d638bbf5ef8948054b2c2424d490b16dcf19ba35801c25cb388a4004f", + "0f41ea73a5debb480585af132f3910f3c7b98a577fd595882121631431af470a", + "4dd11303be6ed6afbdc8a9017827c6fbf2fa8ea263117a9f08f09284559bd3a2", + "77dce608339c3e4933ebda49d99d52c3eba9e585f81bb6b94de1eeb6c4f19aff", + "e6bca493413498ac5b2ecb083cd17687e02f2fbfad5da93d4c31a02c9eb72c81", + "4b7eaef4ba7578f43422618c729ce5b1d362a11c437f4d9693da8eee9bbe6b01", + "0fffe2e60e034ee0092da0c22f560bcdce5553fc95eaf8478c3aed2d9713dcf1", + "d7dc0f79e6077711fbd11be2c49a57d984dfe43e2357de902f694f561cce6691", + "51cfd0c3aa1b96da6be92c65ef7dc25ac5f10236b515403cb7f8492791f3bbc3", + "309d74e89ffd04cc8f050dfc4be50085f197f6c23ac5f84c22f79a2729ca62b1", + "a139b028838d489dce5fa08da18ba5ea10969860dd813eb64c755f9f8a0c856e", + "2987cd385ac6ee4a84b0c2c472fea3828306b7addd11cdd28aa416ad9950a9f2", + "97e9199855dfed3d4fc6a8746ed9dee32586d73066e024e8a96bb124a187b3c6", + "d5aca4fca7529a7917cd26c7710c20eefad94ab118b592db8fa22a4c258e99ff", + "08a67a8a0f262f70e4039e10091a296fcc196e9520d5ac37f836ab109205181c", + "e71586f3b63c70b6d65672a7d84781aa67ce0d70f41e4240f56066ab67d1baeb", + "f2bfba19ad8573d2c7d48527ab2cc275f91fe08d82829908013f53dacb9478e1", + "8af272ab08b9367911a97394a580c59d6e6ffe763aadc619edef7c3b827bc927", + "8980fd4836c9ff27cfd5a999fa5a535ca557e32adce854752addef70c4144aa3", + "5d976c7296257e3a0b8411bfba3f372f8d35bd7e73ed92bc26af88e322177178", + "a01565d4915e7fb6689db3f1fb7fbddfc9e8647381c115e1c0a7b1490e8f755b", + "a8fcedb1c3ed358fc5305bdc8b618bc0f354781472eccbb8960c8ca3a0f3233c", + "094ac7a83a427bfab8eff10ecbce65c26670bacd70c941f623ff0298d354edc3", + "d7b2b22315b3ee15e124dcf4560d3bea585842f4163daf0b879a8b0c55390637", + "e6e8072a24ab7bffa9ce6e9a359c33b210e8b66172e1d35a5c8ed28871c40029", + "39c1bcabe1c30d0798d8bbc275d83a503299b08fa2935552892ce03bad2c0bc9", + "2ce057410a13bb152f276afa305eb94e0e31c5dd82a2eb828d0a01710a276756", + "86b8f99ed5240df548dcd85fd87fb2dbc78e21e117524eacebe7ed88d7340a8f", + "0ace53d368852d27daea0ec59bef8f1d5088ceda03719add6a02bc70e3aac785", + "48c7e89b19636c77e186a093b1f2946ddd692f964ba6f2d2ed136b8c2f490566", + "6d9e0dcbe46da467509a5a8a2aa709a31d47795f9f3bdb3a01812ced98dddd5c", + "1526564dfcf5b6ae528444ee7a260771bbf30aa846da37d07dafb1d743c9f68f", + "2987aef1d875722ea6f1684883eefb0ad566fd0017b7ef6094a1752e2ba68550", + "22bc33eea0b2fdfd447021a90c90d10d24652add142991676418a72e8151682c", + "0d72148b5f75fe9f6e36c5f042244bb5e35a27b62777db7ca1e1072eee9a8e48", + "ef71137c514cfa65fa5fda9b517e3c267a9c7a7a7fcd4b9177dad57d0fc2add5", + "11efa0f3f84cd47c1cb0c2d4634f317202ce00e52fa18468910a68cb317326fa", + "750711ec221178fb1ee7549333d5dfa060283226953f9096782c24cf78771345", + "9d621cbeb49423a46c56581dd621246766d6c1d2e885f38cd36ca18f88c6966e", + "e459b29d49a394e84000903ccad0edde2b83096f73e93d144cf8a4fd041d37bc", + "902e39fc44a6a9a712c9d42e0b98a08f793f9eb468ab13c120c87bf6309b68f6", + "5fd3dd562fb5d0c97a66fc5d6f2d047b7c12e9518b95d6d4b96d3cc649e893c4", + "ae5a35203af76d78f8d4c506b18fb4e1c67e5798706d7a1b98912e9fbf6669ed", + "8806e940bc2274296217b392b69987b4772bac5aa5a96980a848c1ec769bb8d5", + "bad155b7d45404ede0f07382e8d74d344dbbfe462c8b3658dc6ead106d418e9a", + "fcf3012d4e101fd7affd972b1768ea8200fddce6e0fb7e403e4fd9ba3f01bac0", + "8ce68b64c7b5675e0663e81e61c4b4379cc5f8845dbdf191031b591a0e10914e", + "ec2253aabaa2731500b94ca5b60c8226e405ea23831e87882fa127cacbed1e70", + "8333c77cd6b63b5e538dde44a5f1c37822c5e1259d7c6674ebc98d17431720af", + "abecc9a68c658a5c31008c6c594d581d36fee313f8ec2aaac469f2fdf4525569", + "e4b493bfaeeb85f35f83e84a318b3a1df7df1497ba1b03b498099e747956ef52", + "8dda479f521d6dfee106e861380824d6d06c770d1472a189bcf251a1bf0a0f55", + "e38a654a52e216ff4a200af142e4a8b9c38c448876b70b84dfc9de5211ca7024", + "12b0bf0288ad6eaad0392192143f5765bddc84d7c99c3ce7e33711083251e878", + "98055e28a5af0dbe2cdc674820e18c92a38ac89cd2b6d3e2f87a589e1f054eab", + "8e05d03fef5e1e2f69f9d4fac674d82e351e34106ad29073ae9490e9ffc15b25", + "bcbb675310f133824ce336a3c43f36f8f6bc1dc9be8efbfe3e7270fa0e7eb5af", + "20af3852b4aef5ea3fabd3847cd03c064cd822b30b5c1652b91b414d08704fb8", + "cc18a3362023966193c072630da0afe74cc08d9eb767866d85c557e6a6f26387", + "950e603915e7fcedf0dc158daff822db034c0a240f7cdc5063bbb428d1408414", + "12658fe8bebcd857c22f584c8e31d6172a87f089e1d883130b7dcb4c1600518b", + "661d8c02f86e16355681f3a682aa0efea86a985ad29e2b64b343f5315a9268b2", + "617a333e2cd2482f83715845421a1e25506724d4737f7bf8dbe9f3faa5a94aa9", + "1cf208a9abaff984c259300f9345c92683eadd83d11a6a4024ee47b3b4d1d045", + "db15ccc93275a34f2728c92076086f0f4c7b2068fab2b009adf7f0d48bdb888a", + "befb08a1da6b7c3136dca3d29c7c0c853c2d91644e55e4303cfc8be64f699538", + "ca3755a1d62e3b4a74fc458daa9a7faa3316fe7f6fb40a62282e9128882d4675", + "4fe123bbaa5d874195f63e85aea06ab08355e0e57f2204308439917ffb080a92", + "cae10b8938dd5806cfe5ea1944b39296a0a73b5957a82f308d9141558b21c6eb", + "124dfeb77b60b21d782aeb2e4998ef15ca8755a3809fc3420b1f4a7125a1f4f5", + "406e044140ce58e8cdf36d57a41892fe4b15a16871f8318afb4ba0ce43365b5a", + "59f62af4c197b3ab5b58998677a39f42ae62738fb962f775529889cd7db10925", + "7eaa2867fa01e79b9e64e17f41df246f099a3f1ec05f9feef690274f55b00182", + "3c0125bf287ec0efa250f7dfdc4f784a4b11475e7bdc5ecccbf7c6dc591d0284", + "06b9701560e87fcad9f9f9eb761b9b78bed1eb979bcbc252b9ebaaa465319a2b", + "64bf409c31578efb2717df97ff8125c218e5b2740cce771186288de418110e2c", + "91adc1e2ecabaa94988334b107b23a78af49d6cd6a8a7fa45dd0a3de7e7a5bb5", + "99ae7e7cbf973d9eee35901a95212ced0cacccdea07eac0d88b5cd29e980b956", + "563e70183d98c899d0f0fca96634f77ec89ad4bacb3f52ed2f736555149779e0", + "7f01f980bfd6c3c60f3628d61afd9bc5c92f50998b3ebb0813db0487d2c23f56", + "aaa5cfc4028fa4a0b2f1303372ab35e5c4e4a6d5816bd6b819926e8949a4bd73", + "52968b2efd496a5de2dcf26a6502e8c6c74dd1249a40723a17e84bd331dae9b6", + "9137b4da3041a7e4a3b88deffe35f371c0d8f746867c9959eca986d4a24034eb", + "1d81a4be4ab49d3baa09a94794061d770bd224d3701fe00b76680297ca819bee", + "628cc2eb26c7278894ed8ec317b4c60afe8ad5fda359f151300e1501d6b27091", + "cc9378c99c54db8ea7cfa4261e74349b30e53fea0232dfc65e595419b7311c78", + "3f3a959ed7bd378b9ea2822b166760ececc99ff65c340e9a99b550b4e233bc38", + "234aeab4fff850bdd013574828def84a96ebf53a59187fae764d691c1e16fc9b", + "b065e4e7d29530028fe92e1f48fd4944cfbcd4a4bb1aeedab43866b694f0fba5", + "5df39fea0e235a747476259f34f8b66d739f9d7bf47f50c19891222fb66ed6f3", + "67fef34becf3a89191220819e985761682583e9e2fac7fcce2181843e567b13c", + "587293d74305af4ad1bcc2d88f6ac16c47ba265e3ac5ffa5f0f6235ea8cac952", + "36a9a03e9fbc0237bd52ecf7fa23626d8ac83e91d54cc0471cc9277929582879", + "78a673152ae87540f2b3a38ba47ec5bd5cf047da62cd6be3682a723bc87ee1a9", + "8b48afe535024d84514d81fdea58934ca0f23e031565d7f74d05ec7a7d946801", + "d81ee7e407ad1311d68f0b53994e6fb68131c596735329f76cb4d18c4ddbfb77", + "16be4607f8bee1e191857372b0263ed401bfa173f2752736fbe50abb7e58da98", + "d05083dd5e6d5e0a2c320a1752c724d3a13428b9dbd2833fc67c86628f6eb1c3", + "0b2f6370271a17242d09d0292a1e0344d2806cc89e8e6e125c72ed47317171c9", + "aa1332229c78544ada666a1e7cc54fb5fa31c586370de5388bfadd86176b2250", + "d605e3d0ca62ce111f73d2ea48c5fbb56c5d47c2bc28eb68c398515703119072", + "9e0549f039e6c530dc1432adcaa40a3ee2d69329fe1a2245c6c0461c16931eaa", + "cb2f3789ae09d724f9eddb3f104e755ed2ddc3f5bac87aeb91f76fa53784e881", + "6e3b3835f989decf5441e8c524aa95b6b312d5732d614fe24d80f9dd39bad1c5", + "318440cfef57053d806d6f9c03c1331947018cf2f479b71d9f5ff4d1bf940ae9", + "51050b2569885e0a61fc2487c8428e6fdb8861727de7e8d97f7901e6f583acc7", + "02e37462d082a033c382c04ec6d90c629bd96cd0379eac6399a718d9988bf3b9", + "3515eaa553cf9f35d85fdeb367eb3eb930a79dcdefcdaf2e6eb00c550deea9f5", + "1321c47c060f371589bf9a1dc6557b9b49434a4ad6ad15f6749a2bf71e26ce14", + "627cf526b8c336e3a3d8c3e82e5503fea6338f14a438e8de6bb3dc5674a10da6", + "7cd3b2289dc1190b6ea21b9d011ec5574fa251d222af63a52bebefecbd89d7c1", + "9bb3ad4efd34c6839bf4b641d1f745d8ce60c829f22b773de0fedd566c3ca60d", + "679c477e999ae78bde65ce99acb6c2bacbcea8214ba4b7fd477ae91b1d21792d", + "bd3ec90f50cdbfa74372cfcee0f017fe7342935eb93d9367ca277dff7b02eaff", + "6a73b3ac5c74db3e940df13ea5e049432e2488b3997767dfd700e9aeb34f9cfa", + "a642e0ef2b4c17cbacb868ec0ca7d43ae9cefee5e0dc29e59e561b44aff05615", + "1a77398f0d8673a9fccd3342338f6e5fc1e96800beebe04f4aba48afdd41f6a3", + "d2bf9e00bf6050ad68e7a3ea1261b3c9f9b6394d53da2f9f1712199ff5fa96ac", + "d17c0276250825f6c8ab121c999c61cc892874488a4bc95a104dc0382e511aaa", + "7be2cb478a857b81957d7169a01c6692a254e7b029b42995b6c262e6d30f0038", + "2af54e9f62193195b0b927462e63a661c5abe69fff22037e047257dd41089646", + "120849c155437bbec39e71267d3320eae464e949c7ed1e812c91f3c5ead6ba1d", + "4d77309ed2522ec89b7e8e0f0189cfe9124babae465007ab5c905a9541d63a4b", + "4eebf5a0cca448832d9a5224a39250ecac4a18c650baafe2d8804241c274dfc1", + "5d29b8c14ac83e531b87534ef17531cfec84f49c764e6410c8d5682fd80204a7", + "625d9a10f136b103e85f0e7863df74db8049f77a75e52361748873fbdb2a3f73", + "eb21a9d33e58daa0ead2dad3a71d4e5eaa7b0321c6cc2d4e86464c70cf366634", + "6b9066c0d9049b0634edf92b34140d02a49e7a6d76483d14c32125f3ea41d8f4", + "b68f5ebb20554bddbcbd92371c4e536210a0674e61577d552b6b405939acddfc", + "2f8fc98373422a6dce98f183f25488520794b45b947c18ab0e1b924dee353b61", + "64643340086927f78905c52784692557869bcf723b2ad19b69fd0349601aa7e5", + "39c72e17874ba3a3850231ad77319c564e5fc1f23414c80d0531f8e618895ac6", + "9e2afd30de5bfa30e237d9f2f773766a82e20d1a7efa10d34e37048b172d2e91", + "c0888a46377fe1194a84313d3e9f64e90cb94648cbadc93ca1c077960b2946bf", + "c7a54836aa914f27df8424e40d4c42183c7dce32798f7964fade258768beaa4d", + "485865c169e3e60c2b0cd97fe7caf04bb5592a9a08c5abf53de92f5fa745845a", + "77038185b0024d9e2e0881b8d57e4dea0b59fcbd2bc2a776c8d7b29b5356a240", + "19be080d3f5d828b2826c221f35a07200540c47c5bd5c374175f8d1b55dcc8e9", + "32d5a77433130085014cfa02eb0b0ff36be96763b8f100502ee09a93fa4a62bd", + "9572c95caca053688f8cde1436a7d5652f1bb5bb4da92dd200df11d2a27da378", + "b3db618f9e7680be0c485377b1f9e874798b5b9cb2800a098053b025c47e70f6", + "67a77a0cdf101d5fca5d17bf2b49f3c00632bb4a53c4ef06617762f36c8c8e07", + "1fff3805be7779014868c91462838db4e8f88e7759179f144008534a1724fcda", + "efd453c249b898711874f643c4b66efd58e91ad5b3aee1aa6ad7a477b7248a94", + "bf94e5f1c7b5c8483285a8dea4345e7519581d486fcb27a8da43a2b48be22346", + "7ccb34279395a288bc3ce100245c6de77fd9fc35956f9ece35e6475bbd6f6ef8", + "810ce536aa750b60cfc6acff3ff65b36515f60c1abe5816aa5e38549b0cf21ad", + "be1c2775730ae524f44e05fc989ea33b24d262db5cb630cd13d56a2324f8fe37", + "7b8bf85e45285fa4b800e544da002c0c43c2b87644dfb62c42090631c5abd1d5", + "96e286ddeb65c5e54b4a8325cd695b53f4aff1fda635ce72c7b14ce1e926c7d4", + "12e0310edca52b08cb8a18dd2e382da62908b476f14b1a8e0aa993a945e84399", + "e8ceee44ab4241cb19d5da582abc8b9c7ec4d71bbfa456bbda2d3dfb120c1e82", + "e3951d2637df7a24c3fb48360da87247d998c9f092d7ebe349bf5b493cf89034", + "2657c928cb139ac30cb495a790e8a125d452a541267e71ca1635cb69d830c349", + "4d0fab3685c9e14d8382253670d0453430010fdb95da9a0cf5427760ff8f4bf5", + "85a65d69081dc88743067a1ae0f5abef0adf79ba23500954217f990bdd17246d", + "b3d6e83386b11a0d736d43f1eb3235388abdfaf2c90e94ba02d4c50a3c830a66", + "f1f207e702a02318635f35a70141ff96d50b1058f57c01f84fd5973112eec5bd", + "10e412a4f7c37f47b63d39cbde4721f65670bc2085ec8db8a6ebd2db0f19531c", + "a7679d3b0d86f7d19b865f9c25aa91c1bbcfcc89cac3415138393f68958699f2", + "9be5b69545813b368cb6fd8e8e2f0843a3299c268f3c78691d62f14cc08e1fd5", + "c7ef2594ead75dd56e8effb151e72c72efd2e3151c22f113a9952c2d2a094ef8", + "43406fa30813bf114ad791f19f91783ee623dbf5882e9361dbb9dd42dcc124a9", + "f45c2618d639e25d60a6ff4e3d3c54aca3afda8887feeda48b3931d5fb3a890d", + "021d36f9b63ae9509a139f5919a2c7c6daaa8b775b67d175d9fdc2caf1545247", + "221662e3384acfda6b419aca64194a7e2b515c470eb515641a43ad280f5d8c64", + "93a2280c52123349f7143d8f90b8862985eab8856ce01c1e65847f74e82f3585", + "97595d2f3e15492623c1180c3448d91e27e8caa91a2286ffdde6e0e01d73487c", + "554e51f4e422d00765369712a73dbe5e18c4f59aa55546b9aff3f1b94c8f75ee", + "373c9b4a6fa3fc747881123d342ad6fd4bfe280c1550517f329e7b5fd9c90824", + "5466a29fb42efa17c090eedb46bb213988ef7656634eff61d399c4540bf185d4", + "68882db32387229d3c096e4f075c9075d1128759c3fdf5e5d8bcdfe24072d559", + "f703f98c5b75380e73f861b3ceb685094aee6341309d5161b761fcca78acda0e", + "59f93b2d63f4643f566199a92d955bd78caaf7ec7e3078fd5264f5b478ac09b2", + "639731cfd7ab582be092da8ec8da53718c206b8f0fa5c75953fcff3cb116a172", + "2bfe2a097a074fdff140fef54c104953996a032dc162edf32dc357e09abb3fcc", + "466dd64bfa5468a071705d8b3f86206a481673ced10e0a4f172d7486cd29e8ce", + "a40e511b719b6275dedb1b46f2c5f8b0cd725312ab239132c5b9b29547f6f764", + "6583f450fd9001e5355551acb58a3c95ac663f8f58a82cc3f9f31135fe492442", + "2a818d069c34b05be52769437dba3051f931aae1b1453e950c591b3920c3b3ee", + "0b26d32e1e31400f4c3a5e2750fee52239b2dc3728f927ec455ba49fcc16bbbd", + "7d90705556fd11248be8e53c04bafeb395b123c3b42d3e0e07f02ae5bba74681", + "79ad2329c40c96cd2e4d694d51d2610b8a28971d6b253f5a54c22301afdb293d", + "147eed7b0ff213c3ba36f679ba864cb2b050de968fdedcdcb48e6916bc264574", + "d027dfcaaf6e04f676c619244515b823e7adff3dd01da6730342421168a61c45", + "e2199dbc29264ddf0edc446706d684cc19ab7c81fd19f2c25a6aedfaf38bff61", + "74219656debd84f572508135fb03c4ddb80a992c629a1483a4b32535156ee1b7", + "cc6b74e9616a1facb82a483e19039348ccbc6a387a3c6991f50133ca844bb499", + "3084b541105fe892ddfad7efe4d428a8cfb5890c6970d4c4d05bc1b1e7ea38ef", + "a0b5c69bf883147418969ffca6cd45e9f710223b8da9bd93d77ce8aec76d4bea", + "e08c7e844d6fa6cdcabb04975fb6c66ceda4cadef3b1e4fad7367207d674e973", + "6033e2342b470e18a442cc73eebfc5e77303cd4ecb3507930f777cd2fb740a15", + "3c20d132ea8e829fd8fb7b18cf5f9a5a71dd88bf35bc81f7cf37b4ab4cc7b961", + "0ee98db4b01bea147bbcb1bd492a66c85abe9e20985738bb5a035d587badff8d", + "bd78400ee797aba19c82c3bf060a13d720be4cab6358a39075e1ad721cfd0b99", + "91c0f7efc5e43eefa3e587d1e2685bbbf68a7b064c06da9b21f3e38be2f8915e", + "f0c2ff1abe61e4c0c3092afba482e7eef6f0744c267b5334e060485d01974b18", + "dd4d830bd248066832624a2a5ceca133b9f5b9fa66c1d3a41eb4cc29169f955d", + "04175c02b43883fa4b3c4b990334e2c3e23e85ddc36edcf04bee9f86e8a116af", + "2eafcbc1db5005dbf74c90a56554ba71201e21d21ef4a77094f4b1c6c6d10497", + "f6c826a659eef7eca7685ce1e574066a007906163041aaba2ac90a12f0315660", + "2dc4d5de794e832b499f493c99fa948e10079f74552f23fc5d3700722e8d0f8d", + "c02e4905c3876a3fa382c1a7f90cf91496d66da1b2495cb3f5a345993360280a", + "5a375be2718ac37a38ee1c84b84476695a384e2a2ff43068c8d7989b4fd3cea5", + "b70b2e6d667be606bd1c511f7e2cf631f533948cbb5af26db8650d4101253f63", + "35a49d20ab277ac8c092985c9faad58320d1af7427124c4828888f4bd7491b36", + "2a67df5cdd3e8c2a1241dfd24554af46feee6b138b4809663f86e9012ec47b27", + "b50c7e29386970d39258f395e05f3caeb8648d9a40a0c14a603ba1e729c1782e", + "0db1d79fc1686e0388b8439fe7331862314b77ced506dfdaccc2a21faf74f44f", + "6b3393eacda7dc18a2ce451f2541681f5dd4e9af21a2624956542b60268a9405", + "8e17ed5e690277e1156294c6904f6899de1497d15d8862a98cfe349c5296ec13", + "e44b9eb141e8ff07acd5bc5a2fdd6d334192e27b27b5f4c26a225ad28cd9ab9a", + "02b6fe45acb7af83932640c81519a7cf4d33af40e8c66510855286f1b869df7f", + "c8aff4d8670456d4e64051e65f018226f7abcb40dc77b7a36a934f4afeb42888", + "aa4a4dabb9038bb1bc8dafa93397b77b81e263fcfff59eb86c07920e6c8c218f", + "3b8203b9ed708044720313b67f9b98cf9e96544e95ada0200bc4ea0e54a3c633", + "524aced0165da7aa4aed8d02720276c3895a37fcfd8db9cffbc9d353ccc960c4", + "553fcf8e9e53d37da153a4f8bfd16a2c5379da0472076719b05a26d10100801b", + "fbb7b3e31f1d0d1606dc34bf1c624db7c62133e173203dad5cf4c3792351fbbb", + "e62eddd4a815b35bf6916c615a52f185395baceaa52cee8a836de3a4db0c5381", + "9ea285ecbb3cc9189511d2148e3d732bd98582324d2a597ccb942e8b0158548c", + "6c9ecf00a9f605bd50375d0793e8ebad382060daebbbad0ec1427d5501b95186", + "00a5392f9bf5114694596652d3f47debb758a1ef4a408490688a8b3b6f4d076d", + "c9d5bf16c017b12a95d62a92b270c9c100fee78fc85c91f830c56be00235ce7f", + "68ea43f029fa915aafc14bfa5eb8ab1ab1e6e20c467c0985dfeeb72af1859659", + "172c4fada590aedd1eb45a16ca2ac669cc0371f12aceecf1467e0dd6e2af387c", + "0e8c60669d5b505ff9f6fb941f3247bee6458fa1d6f1829c1444135c55b5b58a", + "83e174c96ba972dd05b14f9e7d8dc0923ad96cdf6b2926c81baece8c7c3ccbc0", + "55699232a0c96bd45cf5bbb81ae2e033d22b534b2fcec3b70ab661d22bc25b0f", + "9d4ba8d5cb7404ae253587e6a583c595214407007c049988a08b51e8c05a67ef", + "1d63978b718b86d8d84bd1f6e7cd053fec76dbc922c597abeeee64066d02b57a", + "6d03e10f0787dd16296266414cb6bacb961387fbec686348f586a4d053718f02", + "e4ae9203d1b61a37a98b21769f110e46de7ab6772d15b4af93a9f39cb2d096a8", + "a5de66a7fa9666e03e6dd9e114d3cbed11e14454758fb20f42586685509f315e", + "f0accba58611444a9eb7c3d41218740ffed85556762d5245f4d1bbd6d958d488", + "95de4a732eaee2996786c28c3ae80a7bbfd34ad276694cdd64194e7d45d6b1b8", + "59b03bd978a4a5ed212fd094e90048aca406d16c196410eb8be8122f2856d1d0", + "2b6c3018cfd7cc55746864434c9b2c610a4971e60d9128a338451d46c78bfc86", + "2428da7b24364be8aa93c6a255543d7da27f4cbe57dde5f14a8512fa5ffb981d", + "bce26d655a0efb6f287be49da07393f30feee6bb6c2306bbad88e04c8984e7d0", + "0fe2ad6dbadcd9c423b5eebde12b78365ff1c4fbc85e933206523afc2a2f8a48", + "67482decfd58bdc49de9ec5a8147945130a928b98202457647c66865ca336c71", + "7d22130b398f4085766f4909a68b4f7fa2c4b26db1e107f3a2ef29417ee5cfae", + "98778612b5124e3d7d455f725f4f2462ef780f0323e0c36cb9bc81811c00a045", + "51e7511c0f2ef6bfc7e17c7906fd604e42e862f3f4475d50d86bfc1401099d0d", + "f120fe5e0ef2b223977b4519069cac7e4e77c587c5b011663d06aa08f80fd477", + "9d00ace943765ba7f2a3a414f534d8e751af83fed0f8e872c36b311a15ae21bf", + "93cc266fd13aaa341f03d20115f066cb126497bc0d6d5de719c9f80604a2e57c", + "ddddd5d3636ab27be531f09a9a93e5d8b39fb2c03a79d4b7908f2b40b52bd1f2", + "626c9f4c4e3bad8d65523b0f4b820f4e59a720007fc35aa925bc99d9deae3e00", + "af9d9060c20e16bb1f94b3da86b8f4c074c472f2d402ebcadb879d9d5853dc78", + "309db5e72eed85d70fb56caffd8bfc1dffb79fb9b599916468a68982c975dca9", + "0738d677af3228b95cf444baba3469dd67aaeba54a93c91ae62845d2ab12bb3b", + "720666a5e21383c6546a87b8cc93be7044c87c015ee2995f87b55bb291e9924b", + "21d91ecc9946962e60ae1119f45687a670eb725a392425e3dca05d108808a314", + "253c1ba525d54f4075c8aa01ee5383d0ea6a41b62d243fb06d4f90987e557578", + "73429a7a5981f25624a1ea521c95c1aa4e744bca69c6b63f495e3c72571d403f", + "e92100e5825a11d06c55a544f83e71a1cb86516805549d0d16c085a690645682", + "3969b8d71699943cd01e410fb2b072638ffdc59d10eb3316ee12a98783c09181", + "3dd6b9d720af724e3bba14e7fc41213a0c6b7fd5c4a6d6081cdde8b05941023e", + "a8c6b92481b9b52f50fc996f3fc023f720b832f79f306aebe9e1db5d5d680f33", + "84bde27cda62db77fac212cc9a399467cf466e0d5af32984b04f389c28e6b0b6", + "d416c534ceaa07ce856d3fe9fb6d8cae21f28aa45eadd4fee27b791e69c8f045", + "0b370fe3369b09d22ddd43f870a428c4b65974430a18261c59781ae77d941480", + "906ad75351144066d6d0d914c5145664825d2004d778aeb41aec56644f81ba25", + "3c57221b786a6de68d8d8db9db8ed3928a20eff9e688ad1121d73085a0fe9d75", + "e1c262992728e3ede684e77f4a06efde3e2eebf8f12a0406dcede1aafe299648", + "c60819a050b2d4e6ac4d72ea6683fde04dbaf6b66bd74ff988fbc7d2b29b9bf2", + "e8468896b3f9734cd0f7c721ee0896ad1548902340242b7fdd16e39b361c4ac5", + "6a8f24b12c66adc9e771cc6b52392688bb8535c1ba026ca7f79d26ce2c7ed9a2", + "f5d607b7d774d7c5d81d6c8d58b4a7ed576403a4dfce8f2e63e1b7ce9c7f8a02", + "011d439d089fcb3c7d44c2d5a6420cf60cf1b8716db31286f7c9ce390a0f2fa6", + "d6fe9c609aefadabb0efff44e5375ea4eacc5237c21b58a2e199cc2899dbfbb0", + "3a0b54bd93d3305354b07b6bb7807bace50476c4e120a0b327de1cdb93617079", + "41e55404a5998f71571a200c032127a6241b2e7b232e82147fb6769d8256bd29", + "5756ac43c1df14927d3af8ef161c403ee1f92b1ed296d0adc72e2cb45d98d7de", + "f82cf2b7895ba7802d0478d2b20b069d3c8500637fe98d4194da02c884cf9d17", + "3a10b6cc7cc3bb244fbd0d49910e588abe7dbaf759d4cf44783a7139af064824", + "5a9b9b31eaedbb651f42d207a14b502b4b73885330c37f8e615ad89e3e61213b", + "e61688578b28b999d906798525736d64a5aac1b5544b7507badb20b34b556d8c", + "45b27a6ef06340c3ed81433083ac81ce99abcc951f5e334919f3ff56cd1c75d6", + "fceb7a9bd727f1e8438f617d02674039902f195dcbbfb01b43d5f72edea02831", + "d19c5e3b8aeb8f8b4c08ee8b61f34277bfe579398a76e28e58b972cd69089b61", + "a32c7ab77af334e2d5c4eb3caf95d3aaad689f28a2dc3a7557f2528be402407e", + "b6c17c21cecca5ac92a1fdc1032369e4c6d9cf2fd4f1d5b4406e1d7e7606b2ac", + "be1b81276adee2c0f4df4cdc6d1b82df87dceaf516b7e4d994e023a937479f9f", + "439251ab04c231c48b891702a2061921fa88cfef44741ee173853b6bf8490430", + "26b7068c7272d9feaab03fef3083e50d49885e375f77792d59ff0cd502c03df1", + "85dce2eba1a9ae5a41d06854f4db88dd7fe022727be35ad6b18e6087c8b91f1b", + "e4f7178d36429096577518bf47e2d296c021ae7349aca78e2c0a4c606acca5a8", + "e8d88f547187b278275c799b25a94d814f3174bac46077df9820a338b4d9c267", + "ac8f48385b06e1c43da7fb74e522d347603b7b973ac3c3d06412ad72f5762fd6", + "22f39a936e8e401a404457cbbef921c3568ce016a5c75de9256011b988c252c6", + "77fa7038e7ded01577a2ae592cdc9663311b5e1e42f5f87ad8a66ae0e9a54c28", + "87f6e5ae2adc65a736c9b1746ec1f18340bbee45481d7c1f5509ad71d5f3c9c2", + "180e9ee8a321a1e91c490cd90afd74d556ae2a9bae223ec6c094587f359870b0", + "f6c87366cdd0be058ebf9295d8b6b42729be58a0f7f057fe3243f9b2949ada26", + "d45c27211e8a4b77a9e76c12db25e6e9ba9d03647f3691d158edbd8b7c38ab69", + "8ebac15b55c4a2c5d3f4866858fe3ab9324bd785036dc0ce8ababa396419eb35", + "6a80d86e69694ebeec5f6f5a5d37d12c2f813147c1116d5d26538d25f8882729", + "2cf7e4c33c2a63ef54c444020bac3648738bee48779db4a3a0f9e72ca5b4e380", + "9d101afcd85b7331d4008230f29b761b9f798e8814d123140e8b648b230261f9", + "c6ee0ade266785f9dadcf6892bf4b98d7bbc292aaf2bfe5b7dd484785760375b", + "94d239b78781096e3e8e7e6a7d9ad70d0804adeb6a6675fc5da4f05d1152c19d", + "55ca49e0e313f14ae0f214ac0a51843158ee07aa5f381cb9d596b8612dbd3f36", + "e3e3672131fe9a71f54a41b0e8143c63bc0aeb823288527bda069a1616a41529", + "ad1b386b668b79c7cada251a6c2bd19c53e51045e4a4562b4b9212852d334556", + "bd08a3ba2d8603332b31b2394a556edf6ef949ec8d49e5d54a13318213a0da65", + "b34731e855ce7fc834aa0846c72ce7f455f40564b11181b340ec07a003787e88", + "ae292c8d03de63af642539e6559960770d80dc4e9590c46f54ab8a74b68f145c", + "10a28c04e90016bc744986e50d2a240ff553cea32c8ccbfc218445d6f9e32271", + "182df763c8b4dc2c1698567546dac90ed5de00c033322c9f731a64f08cf7ae61", + "eaad2e6f6dbc000fe3b8f60fc7ce7011c22c4d062ae2c33a3621098190027381", + "1e7d630f59f8c968c54a803f8c70e15d85a1214be4e0f8e82cab9b2056df4ef6", + "af485e0990350c9814bf8a8b3db91f44d4d459f513f9e0635ff54a444a45055e", + "c5339ed11420ef44af6d7eec085a7495ac1f59264119184d3183a937d7012295", + "ee4c98e7b5e1b5fc19c8e5e75b9be39690ebf15bc9b1e5fbb3ecb75a2ec89d3a", + "c88b24689438c8f097cec81f2178fd162d4c53ba51f1b835c9b7d1f33f3bfed4", + "b3f12ee06f773536bc4882441d7b40153fd02ff4abd97a5686bc8ec294de5303", + "74b69c91d7b24cbc734d5bbfccf3442b10aa94f835dd49e84aeb9fb93d48f984", + "7249af9eedd2ff198a93c9fd96d3847d05fd24e4a2dee3213f8b0157028a7a1e", + "4c663e23616d31135cf9b4c33a89556d9973b2df0fe8b5a851163ce3e770df33", + "4afede394dff0a2b6f0b1b3dedf82614ca5133814c9466e7856a53cf33a83359", + "b7f3d596f87080e6fe4b0c5e17364dc87ba269ca561689d20085c9890270145a", + "a91c666326b9198e9954c4461da9e173c21042edbc8e63e6527f69aa06144ad5", + "c11375b5ba44d9be64cf8ccc8eaba4689ce8d0a1d75f4b932d14b4e26b962ba8", + "c737cddd051af963d4109063a7c98bfbbe500abdf2761545fb2e3f592f1c3183", + "9b09f03742db7491a7a96f20d2735d669383b59570c919c5f8f4d804ccf552af", + "112ba278ae78125b0ee2a9ae9d4c857d7b09d3636d8afd9e175a3557917409f2", + "3ec2366bdb151bc04838ad4459790d217d7e70104dbb6a13de48c8e97873c874", + "e592d021807bc768bc2eb1b6b7832d7326565c39b82658ce9c340ee50a443626", + "446de54c9e312644e2fd20f49b8d9926edeb2c995e627414da25b664f14e4fb2", + "c0e6c970fb25f5ec11f0c76316e7eff5bb180acd21bc777616ad3212edf5cb3c", + "32c3a1e954262361dfea8dcd6d4e74c436d53e6518a2bfd080302187e454ef3d", + "af6c5f72dbd65e75a91a40ba9c6abad575f12b7fd8b66c41d88948fa8e026f3d", + "2f858194e1da082ac8ba94373768de9fa1842a1adba13b2bf6c67474d0a233c8", + "74b19f6456e34582f963dba6d7faebb6289050227f55d5bead6aed9f6394eb41", + "e249aa1cc573b722bfb4e6613dfc70e2a5d6cb7df94ba1002e7f549f57aedcef", + "0ad80c6bbea362b8bdb966cbc50135bfa321bb8cbb0179f76f445dde3d156c8b", + "898f699a5c52bdada6a62d24ac05ec8cb043f5679b554ae5a9b4d666a440fb76", + "305cf536a22fcbdee3353cdf59ddbd42e24ea1a8b91967d5038ca3f9094868c5", + "2847f3395f902b011e2eb0ce5e995cd97af861c925e04021a5bd893447025060", + "91f09953dda135db571d5e159e07a495b0ca9d2eb6ba584b6bff019ae5693624", + "648126d6266a94fd567040da2a9becb5e6be4055f9f4176d252ea8b29915b8a1", + "46451db7b8576c094fa561a81e6d1300220b42b886e3e3b5faeb95c78090e6e0", + "97817465e9219742eaf8121d0d053224793083991313a7b376093f5cdf7329b2", + "4e5c52de7f2d110910f12126b36f9ee712f4a70ec48a86ef10b21767b876396f", + "6510078a1522e2f137600f9183802c1581f882bd9fa0f8f1391484f84cf0fb43", + "2b687ea5a740865b89abc5657546a55b7d5f7fe4e4796354b4b4266f0c0ec10c", + "879d6960613b3138e2a92c821026f5858a99338db1f769923ccb7f32ec4afcd6", + "6c37cd022e745b6666c0388b81296be90dce442972de52400fa3adb8e734d2ee", + "e58f22310f59fdddb90479cc36c763c1fcf60d6e09501d51c495dcb95c033d05", + "70ac02944106daddc97b53db0a0f9bdc1cdf7e789b698b4234a88d8acb945233", + "e797d50c461ed06e255f8106694f1e3c3dff45ca342b631c9d93fd6868ee7862", + "d645d1ce9d365a5ea79ccaa9b24ec597a029e07e667bdd5461a7cec83344f83a", + "2fdc7e98eaa0910fd04914e2ac4c1216e2a4d37621e23c69daa7539b6d403ad6", + "3ea00f497980193dac3261c3e12d7593b1668465227775c53b01e0029a3dbaee", + "eff07e9c0aa7f889f74bd0daabfa20017aeac105fb09d11647059f37eb1027eb", + "69d3cce33ba2f60e895201dd8d0a92db55e4db99b04de54976d3f11ca280dfe4", + "64aef37268e77bcbbda83cb568ba365aa650c4363e22f08ebd9ea8dd4c5ec3ba", + "837f82d3bed0341335bd5ab9d633bb5b1117a0b71664315643f710321451b08d", + "52b1677f38980e1ba9eee33faf8a60f50d76a9020fc18aadad44ef12d102c630", + "c5727a2a22170ed9a40b4e003e7039bb6f51ef719faa103159869f715cd79d64", + "2bd8376008ef3339ad49383a12754f63710ab01657cf717aa945f8ebeb351d26", + "617d5e3734a176c6aaa970e78f412532263398023f922604d69d73a34d7c9600", + "b508b6f323862d1393522ce67c9f267f8f7a08c4f17ec2d7e9230df6a7dcc83d", + "003b1c8857c2366b415d5b1eb23ab8c0705065bd87fe08e7c25114098bc2d020", + "9c18e71750ab5a52520fa5c6f4b48047d34af42b9feedfcfa96a9b9cf6c2231d", + "c3d69220994caf490d6d8f4c7f7689da6e533bdbf775309af7b13b25359c15db", + "64945a074fe5d593413f740fbb0fb036efc1585073a5c769e6b7bba5083bff67", + "da64240fb5aefe994b80dcbf86c6775e1d8b9827c90ea914dc89ed96994c1b06", + "f3dcceebebff82171d500aa5bfa42d923af47fd9d935ff3b24e4246c2c240bb0", + "5e9c33d78cdd3d5f71a74981aee540c199f9928c132f047ff975f9c4528929ed", + "e83e08ca018747334d5bef54eff1527d03fc478a9a2ce3bc1d08eb74b1e05c5a", + "2b4404ceb208a4cbcca4eca597cc25e50c156d1d4320b937795c201bf62e8e27", + "6cf6e54742a3e3a2469296b51863123d6939a3e4f18973bed3350f361a0a7fa1", + "173648ce3c3faebf66b1b400e2ba1d09a6f7a0e90c4f6eb259cf141cf2702868", + "1602f84cd08034833d4d6b6f0a63017840b0517fcd716dc5bb82071f81cacf22", + "5c9a5a89db7ce103784cd0a185ef589967930fa6e92aa90fb8c02031694a9404", + "a99a83be39be7d0d247f3805d7c78f47bf16a3babf9d63cb68e28c0f630e15c2", + "9a92a01899a649676ac792b2a2be8db3ab24f1f464a1af6b3c03b7f67e572510", + "e0448af4bfdabe13a044362ab133a5560a77e8e6605df7d9b36f22a9ccdd806f", + "8f675662f98a9961e9a67114a98f442359b8797f729c94c5a041ebdb937e0b57", + "6dd8437315fb9be06fcbb22ebc82103648da5b142622857175dd75a0f78c7a8f", + "6c4a96d440dbfc0f26f1bdf1e1c0ac273b573c36f14e55342fd98435e806b676", + "700725449099dc98933ea1c8f1b9fdaf21ccb7f7439451cbdcf763e7b633e52d", + "22c986a29a24c02703a8be6fc3a17f00975fe6f323366da8284a4c1070b18160", + "037036f85de49e6136d1f54c2b1f754d61c262bb88c806b72a156e272d60c535", + "8813017f3878a97b15c272982b95b3534a4475dbc3a6f40aaee3eb9dd593eb2a", + "403a7a97d3c47c57e89982086de458cc1c62d01449945e9adeb160bdb487e7c8", + "e1ae2a3ca0a102d9d7b80462c5c75ce1fd49a341a3ae60a497a98c9e84dc4d2c", + "7d3cf5fdda0516c200a2a5d622c256d481f9764d6a72799995e01b31c97f4f26", + "a080f0e1bf33dc8190c844c3b13c56c058ea08af44e1c7363d55e8fd8a7a3afc", + "a611f43917fd4c23e5bb65ba8dcbde6db510142346cad2b9ab596fa75635d9e3", + "ba068f56418356e2c0ee98119cc3030fa7a9b8c27d5530c93f134e899afedc02", + "81a7d27fa4e8d911410a733248f102e5a9eff4d8f5617114e97c1267cbf3d348", + "ea711ec0f66cec18c17b3bfa39967b00480a0df0575412c0e8a7aa8869ab93ac", + "dc2efe412c0c7cf5d7e838641bdbe2d59877ca74357536da58aaa0fd1b711ac0", + "93e321c3371d69fade1451f2f537579c54934aba5271a018540f70d4ac91a78e", + "8f1368dfc99618f3e1b2302ebee88319371106bef78471b0eb876f2f06a3a818", + "5fffc415587f815bf9645acdd481294fc4d83ac5b9a37275e8725921eaeed7f7", + "3f29e067de0e036efd2684b9c54e0e6b2787fb9d83504c1201c4d0a017a0785f", + "07f4a000178a78a4a8faa370d5aea0aa5628ff669522fbf6ee0d016ce016f8dc", + "773ac8e936b1618243407a82d1ec9a6feaa5ebd9cbd82619d19b5bd7f0297536", + "af56203cef3a9126c3442d964fe09bfeba50d9243fd9bb87ec38a961c59b5960", + "fbbdfc94f38b514470de4290bba2ebdff7aac5523d97a0abbb20859ebc82065a", + "471ab6bea6f7cae837a9fc601f70c7c8e53e84a544e43008d945875d61df04ad", + "e20fa6fa46cd4d80d54d393c86e49b282ea1526c5f339e579bad5b39304498b8", + "cac629d37126684710d0bb74fb52d529f2d16c73734f2005de6de9a504ad84ff", + "a953032ffba659a809d5f733daa9db681082aa65dd08c0b05664dbefd87707ba", + "5bab2244233e9f9f2d7191dfbe1ac968652ea1bc0fac48a5557b01c40d260f4a", + "9d09dc8281294423fd516835ab30f15416cfd011e74dff4bf5e05f87416f0cf6", + "893776d2cccda1f86b0e405d13a75a11a96528a45e0872e4ffc0b87513594100", + "758a1e6461aeb3ffb55c231caacce8f663d83eef4d12f8cf94d69b09252b6e06", + "0dee497dd3a7bbc26c1b2b2f807e55bc62765b3557cd50960e8b913a2e3acdba", + "35a08cc32bdce97e91ba274bde6d6f19c74ae3fd3566cf1c40002a0775872505", + "689104a13715e4e881a4640fd5e4bcd46687c67f53c91db4b8cae8d21cf90722", + "e48d19e8a8a211358e7e7ffe964e981d3a1d11ae76a2b44a44faa856566abc5b", + "28d7c2498774be7540d3b85cd67e873db198267ebd59cefb1164969d56544940", + "8440cbb16acf418689ac4fe6d2b65ddd949e96458542539d30bfe2125e3a88ca", + "6f837c7e4a9f4c4c1b17fb202a5bc7479add8cc0d26564766328f77455627961", + "fb786f9dc125141f04857fd1393abe9172ad9c3aade782bd4d8dc23018c662bb", + "be101e3278740014287a572b2b27fe3e20712bf589db356e800fad1e633d34b6", + "02770ff50283c20a9a9de291de391fe7ebb4744da607a7780b10a20c49a753c2", + "84801d626b5998746e0865aec4fcd1378bb376edcc763a80ee96373b01cbf72c", + "1da1dd3aa77a4e2d8aa0857ccd857d4952e58ab49c26a1f0cb60275030f6607e", + "04fefbda5ec9f182c2a22ad6bbbc42ffa322338e3562976b8d32b3406c2e38c9", + "e8ccdcb248078a482359f4f7973d3b2069794b9777c4e2ac21558fbffee59c8c", + "51507a05fbdfcc522a1f29099cc06458591bcd5809d2b210901e275dc1daed7d", + "05e6b311d3a3241022b0152924627706edf857161004dc785983c5c8e4059b19", + "986ccd7ef43c8085b738a0e2eb84b98aa920fd19f372c34c37569a5c7354d6df", + "37c4370297131fcef6101bb43e37ada63ee4c63eadd53c683cdffda2c22b6a5f", + "c8a736669a5a1c69c6088735306b2aa6da1fa685584446b27c53612344a28d39", + "0c829d4a74a40e4338a406a268de64a9ac1b58ad2f0655cf1412cfe10a81f128", + "ea8aeeeb0fcfa66319b5d1560c1c7e805cb445b678f60735a5450233a08f5146", + "0c03e2902e4374dc2ae6e9d46b27028af86c461ec65b9708829f6e341c5dfa86", + "427623b7f21f13216b47d64b3cce69d8695a12a8b76ceda909038e8ed1ad7ad6", + "309bd0bf85e4b9bb130a17df032a6d057d2f4774abca3a8af6fdbf25de9182d9", + "7f3da6402da41b66b0e5e957a40a787c53a3a31228f06d45f6ddfc275c06e815", + "9453352b0b6541e28b5c36d1ef833b7f49dad472ac7bc01712f04a1d0bd8cb3d", + "35b6a024cb364ee9e7ac55b3ee07e137b7dc7756c6e7131d02d603613f876578", + "6ce8b20bf01933590374ff85f4a2fcab43cde2bbf9bf7b56c7e4c21acce02af1", + "f02acb1e0f786b78b042e5d5e4d25fe711ecc05b2cbdc37077a4a1b66d925808", + "b5697acfcc9d3d4b58bf01ee959bb519b165d6e2b3ff799dcd8581f2e4ecc4b4", + "e5dcd86553008584de8d4e67edb1049198922900801bf51da55efcb7de45c37b", + "e0c3e5997bd114db9c09728c21b8b35748eee68f8eb305b5cc099be3c265d327", + "6c1e0ef8496d1c39fcde030239772ddf1182807dde25d54fea68085434a08b02", + "4fbd6bcb31a607f792cca7f514d8259a05f684092be8ac91b242bc90169a7042", + "9dbd18a509716c53037d8713d0353094d55b48253cb0d38cd7bc08959d7ff014", + "a444e2b82333cbd5dfe250e393e245d5db8b78f91bbb6a572761a0a43ed56b38", + "b163cba8c2d0441ac59033f7742733cb77812bd74de1147ae8173faae4f9269b", + "0ac9c6fc35728d1317490b51b36277e825a49ba95be2a6783ef7af99e49228f7", + "abe44cdbc6aea6876b8f009d6279479dca2c4f17c650c51bd7de410bbf23cd24", + "4b9370d4ae2fe08db33f468ffceca5d9fe6b0273ef2887a4ea8522293870e0a1", + "98b3b29996faf548b808efa1fd4ca779882bb75cc2ab3304f049b429a3638a97", + "dfb57e8a34f4911773133529e0e222f6a5cf691b25a65c288f8d6baa68b5f406", + "d1e35d54b3df21193102e9dcbb194a294323522ae35c68597b4b08f53cbf36ec", + "53e8bb244f3c2c90fcc8ceb1662e03aab3e1a781fa1697f7383e0bdc59831195", + "255874e4ced532485777b878f2d779c62853bd8d4d05ddadfa6163465703dc8c", + "6b61c176da93e56226cb6fb7a4f8acf1df87d02be81f823dc851680ae64f80b6", + "f035af0a109e467fd4ecd7c45bed043dd1ba0d02164c2b2aa0ab1989d8eaeff7", + "81b239e1213841934e989034959c9ca0448ad64ac3ab919751c2289600a20723", + "251b4f7823c8449e2e154cbab4aa72fb2cc55495da5fdc2d31075aa5647ba4cd", + "0769a11a881bfd2f02a087803235d344ad2f005010e40ecd7af5db96245dd719", + "f4963f675eefacf4cdd14ff7b759d5752aecee4cd704744e58b9bbbe078e0faa", + "28916a97e55790f8e0a95c1203c1f8f1eac99b866aac5800445ef2d888b4be87", + "575a46ecc4c7757656b9371e9b8dc058f99f9d9ad20a3e6de2d4e6cbc193711c", + "935994c1df9323d2bdcbab8762904e48edf6298904f3ae7e234599366bfd86ef", + "dec5bac62e0ef64db4936f818e325e12aed29ee0933277259185da1298dd0a28", + "3ba063cbea967fc0b7bb9a25c5a12ee801eaa92f5570d1d1a977a229834fd0a0", + "3327cc41ebebc9e82f06f836d5868f9c5023b05498bd73d836f04179d24fc7b9", + "8d70f449bda2223d0d647d7543bc3a2c29fedffaef789dc5bd73d25a6f0afd38", + "9902826a10b226265f357562bb20774358dbc636e790257a57af7f791b600acf", + "c5821a347232865496580de7f3043822ca56eea4f8266c51e3be4a16016cdeed", + "2817d6dee3e6706b92989f2dcef7cd0fef5d2263ed3d1c5dab8ba99550b1e5d5", + "b918f2ce206701dfe60eee1d40fad119540e928b8da813fe29d77a2e26c2738b", + "7f836479ecd2c4aec31c96935e2ce932135249c6077184b837f724fbdd425408", + "1381c39fb5a68c134b948aacf72b5127a7b0d5a721ed2091da25a3856b9a9af8", + "80e6ab5756de5eba8551f62d8943e5f650f4ae8864ea6bdffc3257ef1072679a", + "ae7dd5fcf4688844f5ef72471cc0273e0e2f8f43ba4fc3dd41208f70b880e5d4", + "e0f1d8c1a745ede1198fefbd0555bc118f7ccbd9ee783d8ed7a148f54fb1903f", + "33c8a75fad49fd3b3bb1e031fb283858f79b05a8b461eb1f8f0e9d41c53474bc", + "3864e97cd6f47c2eaf3fdcbb105e014accb4eb902be8a43dfca77dfe0d92761e", + "e524e7c40053d3d1da35ac8a584ad7034cd30de0e9513bf7731f8e5f51f7178e", + "12e121ad8a9be231659d08746b1a8b2b9247304dddc69cec074711970986cc6d", + "149a0d197a6c2dba3044e43f5fac90f6a797e0d4c8d91e241725e963083bb472", + "6a062ba02e79d2b2c001e1689daa1b3ade7c917ef26b30987798123818b202bd", + "99e47c124d128f741f30e7c5bc7575f11a005befaa26ab60b560fe4a65717aa0", + "147961e3d28573540582cb4a6e266f3a677d9e5b24da19c29fa611d3f8fba8a9", + "59261c94c9000fe26b3b050c4cf7ab06343d4ffcbe3934da65f12d8a7b0c6862", + "7428bc174ef7fd68911d46b35da30bbf8a292bb6b9ea99489b0c28bb55876e54", + "6fbff71f7187e67e5cc11f85ecc7d063284c6829ffc2b647d9439cf6d80c71b3", + "d49db17aa1e54596333d1962db0050b54a96abc6184f28b46778ac3eb9bc2290", + "082436ca15376750587883056c2b5fceafd232f6fd91d839dcf7f138583565d0", + "2d6c7614ddf121cedd9db989f73da61c3e5d5d7a876139d2d441d8dc28bef0ab", + "10b5eb9db4b22b9047b2c9a2b40abefc9786d8f0b33514bcef81c462f241ca60" + ], + "key_material": { + "scan_priv_key": "000000000000000000000000000000000000000000000000000000000000dead", + "spend_priv_key": "000000000000000000000000000000000000000000000000000000000000beef" + }, + "labels": [ + 0 + ] + }, + "expected": { + "addresses": [ + "sp1qqtnvg7hxjck9ag24naytgd7pjwsmevwh95ydwht58w3uh7uw0ta7kq6xcdal4dazgg2txp472hdz4svms98328lqdflmpxppmc6ykamcrv9s7w9g", + "sp1qqtnvg7hxjck9ag24naytgd7pjwsmevwh95ydwht58w3uh7uw0ta7kqmk4a3pm24z0yepw6aw27pku56md0hq5ace0l3p3jstkqaajwrsjqc5dde5" + ], + "n_outputs": 2323, + "tweak": "022c333a8938ca0c7433c5e39d440b469f58374d0363b5bffc66aca1cc10d7b609", + "shared_secret": "03abe0979dbbe4e1bb2cd2e06524a411c0b829c1d2282052da817b46f1d6e598e4", + "input_pub_key_sum": "02bca87f72e604e8850064552bedf380ca4584227057efe12a6cc238470658aaa3" + } + } + ] + } +] diff --git a/lwk_wollet/Cargo.toml b/lwk_wollet/Cargo.toml index 03f68f84c..7aa9ca093 100644 --- a/lwk_wollet/Cargo.toml +++ b/lwk_wollet/Cargo.toml @@ -27,6 +27,7 @@ electrum-client = { version = "0.25.0", optional = true, default-features = fals "proxy", ] } bip39 = "2.0.0" +bech32 = "0.11" elements-miniscript.workspace = true thiserror = { workspace = true } once_cell = "1.18.0" @@ -146,6 +147,10 @@ registry = ["reqwest"] name = "e2e" path = "tests/e2e.rs" +[[test]] +name = "silent_payments" +path = "tests/silent_payments.rs" + [[example]] name = "list_transactions" diff --git a/lwk_wollet/src/error.rs b/lwk_wollet/src/error.rs index 3a1583572..98b46bacd 100644 --- a/lwk_wollet/src/error.rs +++ b/lwk_wollet/src/error.rs @@ -68,6 +68,9 @@ pub enum Error { #[error(transparent)] Secp256k1(#[from] crate::secp256k1::Error), + #[error(transparent)] + SilentPayment(#[from] crate::silent_payments::SilentPaymentError), + #[error(transparent)] HexToBytesError(#[from] crate::hashes::hex::HexToBytesError), diff --git a/lwk_wollet/src/lib.rs b/lwk_wollet/src/lib.rs index 6be8c34b8..1233a0c7f 100644 --- a/lwk_wollet/src/lib.rs +++ b/lwk_wollet/src/lib.rs @@ -112,6 +112,7 @@ mod pos; mod pset_create; #[cfg(feature = "registry")] pub mod registry; +pub mod silent_payments; mod tx_builder; mod update; mod util; @@ -135,6 +136,10 @@ pub use crate::model::{ pub use crate::pegin::fed_peg_script; #[cfg(feature = "registry")] pub use crate::registry::RegistryAssetData; +pub use crate::silent_payments::{ + SilentPaymentAddress, SilentPaymentKeys, SilentPaymentScanner, SilentPaymentTxOut, + SilentPaymentWollet, +}; pub use crate::tx_details::{TxDetails, TxOpt, TxOutDetails, TxsOpt}; pub use crate::wollet::DirectoryIdHash; diff --git a/lwk_wollet/src/silent_payments/address.rs b/lwk_wollet/src/silent_payments/address.rs new file mode 100644 index 000000000..242cbb98a --- /dev/null +++ b/lwk_wollet/src/silent_payments/address.rs @@ -0,0 +1,273 @@ +use std::fmt::Display; +use std::str::FromStr; + +use bech32::primitives::decode::CheckedHrpstring; +use bech32::{Bech32m, ByteIterExt, Fe32, Fe32IterExt, Hrp}; +use elements::secp256k1_zkp::PublicKey; +use lwk_common::Network; + +use super::SilentPaymentError; + +/// Human readable part of a silent payment address on Liquid +const HRP_LIQUID: &str = "lqsp"; + +/// Human readable part of a silent payment address on Liquid testnet +const HRP_TESTNET: &str = "tlqsp"; + +/// Human readable part of a silent payment address on Elements regtest +const HRP_REGTEST: &str = "elsp"; + +/// The only address version currently defined +const VERSION_0: Fe32 = Fe32::Q; + +/// Version reserved by BIP352 to signal a backward incompatible change +const VERSION_RESERVED: Fe32 = Fe32::L; + +/// Length of the address payload: two compressed public keys +const PAYLOAD_LEN: usize = 66; + +/// The network a [`SilentPaymentAddress`] belongs to. +/// +/// Unlike [`Network`] it only distinguishes what is actually encoded in the address, so that +/// parsing and re-encoding an address is lossless. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum SilentPaymentNetwork { + /// Liquid mainnet, addresses start with "lqsp1" + Liquid, + + /// Liquid testnet, addresses start with "tlqsp1" + LiquidTestnet, + + /// Elements regtest, addresses start with "elsp1" + ElementsRegtest, +} + +impl SilentPaymentNetwork { + fn hrp(&self) -> Hrp { + let s = match self { + SilentPaymentNetwork::Liquid => HRP_LIQUID, + SilentPaymentNetwork::LiquidTestnet => HRP_TESTNET, + SilentPaymentNetwork::ElementsRegtest => HRP_REGTEST, + }; + Hrp::parse_unchecked(s) + } + + fn from_hrp(hrp: &Hrp) -> Option { + match hrp.to_lowercase().as_str() { + HRP_LIQUID => Some(SilentPaymentNetwork::Liquid), + HRP_TESTNET => Some(SilentPaymentNetwork::LiquidTestnet), + HRP_REGTEST => Some(SilentPaymentNetwork::ElementsRegtest), + _ => None, + } + } +} + +impl From for SilentPaymentNetwork { + fn from(network: Network) -> Self { + match network { + Network::Liquid => SilentPaymentNetwork::Liquid, + Network::TestnetLiquid => SilentPaymentNetwork::LiquidTestnet, + Network::CustomElements(_) => SilentPaymentNetwork::ElementsRegtest, + } + } +} + +/// A silent payment address, a static address that can be published without losing privacy. +/// +/// It encodes the scan public key and the spend public key of the receiver, senders derive a +/// fresh confidential taproot output from those keys and from their own transaction inputs, so +/// that two payments to the same address are unlinkable on chain. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SilentPaymentAddress { + version: Fe32, + scan_public_key: PublicKey, + spend_public_key: PublicKey, + network: SilentPaymentNetwork, +} + +impl SilentPaymentAddress { + /// Create a version 0 silent payment address + pub fn new( + scan_public_key: PublicKey, + spend_public_key: PublicKey, + network: SilentPaymentNetwork, + ) -> Self { + Self { + version: VERSION_0, + scan_public_key, + spend_public_key, + network, + } + } + + /// The key the receiver uses to detect payments, its secret counterpart is needed to scan + /// the blockchain + pub fn scan_public_key(&self) -> PublicKey { + self.scan_public_key + } + + /// The key the outputs are derived from, its secret counterpart is needed to spend the + /// received funds + pub fn spend_public_key(&self) -> PublicKey { + self.spend_public_key + } + + /// The network this address belongs to + pub fn network(&self) -> SilentPaymentNetwork { + self.network + } + + /// Whether this address can be paid on the given network + pub fn is_for_network(&self, network: Network) -> bool { + self.network == network.into() + } + + /// Encode with an arbitrary human readable part, the BIP352 encoding is shared with + /// Bitcoin so this allows to check our codec against the BIP352 test vectors + pub(super) fn encode_with_hrp(&self, hrp: Hrp) -> String { + let mut payload = [0u8; PAYLOAD_LEN]; + payload[..33].copy_from_slice(&self.scan_public_key.serialize()); + payload[33..].copy_from_slice(&self.spend_public_key.serialize()); + + payload + .iter() + .copied() + .bytes_to_fes() + .with_checksum::(&hrp) + .with_witness_version(self.version) + .chars() + .collect() + } + + /// Decode without checking the human readable part, see [`Self::encode_with_hrp`] + pub(super) fn decode_with_hrp( + s: &str, + ) -> Result<(Hrp, Fe32, PublicKey, PublicKey), SilentPaymentError> { + let mut parsed = CheckedHrpstring::new::(s) + .map_err(|e| SilentPaymentError::AddressEncoding(e.to_string()))?; + let version = parsed + .remove_witness_version() + .ok_or_else(|| SilentPaymentError::AddressEncoding("missing version".into()))?; + if version == VERSION_RESERVED { + return Err(SilentPaymentError::AddressVersion(version.to_char())); + } + let payload: Vec = parsed.byte_iter().collect(); + // a future version may append data after the keys, which stay at the beginning + let expected_len = if version == VERSION_0 { + payload.len() == PAYLOAD_LEN + } else { + payload.len() >= PAYLOAD_LEN + }; + if !expected_len { + return Err(SilentPaymentError::AddressLength(payload.len())); + } + let scan = PublicKey::from_slice(&payload[..33])?; + let spend = PublicKey::from_slice(&payload[33..PAYLOAD_LEN])?; + Ok((parsed.hrp(), version, scan, spend)) + } +} + +impl Display for SilentPaymentAddress { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.encode_with_hrp(self.network.hrp())) + } +} + +impl FromStr for SilentPaymentAddress { + type Err = crate::Error; + + fn from_str(s: &str) -> Result { + let (hrp, version, scan_public_key, spend_public_key) = Self::decode_with_hrp(s)?; + let network = SilentPaymentNetwork::from_hrp(&hrp) + .ok_or_else(|| SilentPaymentError::AddressHrp(hrp.to_lowercase()))?; + Ok(Self { + version, + scan_public_key, + spend_public_key, + network, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::silent_payments::test_vectors::test_vectors; + + fn address() -> SilentPaymentAddress { + let scan = "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4"; + let spend = "025cc9856d6f8375350e123978daac200c260cb5b5ae83106cab90484dcd8fcf36"; + SilentPaymentAddress::new( + scan.parse().unwrap(), + spend.parse().unwrap(), + SilentPaymentNetwork::Liquid, + ) + } + + #[test] + fn address_roundtrip() { + let address = address(); + let encoded = address.to_string(); + assert!(encoded.starts_with("lqsp1q"), "{encoded}"); + assert_eq!(encoded.parse::().unwrap(), address); + assert_eq!( + address.scan_public_key().to_string(), + "0220bcfac5b99e04ad1a06ddfb016ee13582609d60b6291e98d01a9bc9a16c96d4" + ); + + for (network, prefix) in [ + (SilentPaymentNetwork::LiquidTestnet, "tlqsp1q"), + (SilentPaymentNetwork::ElementsRegtest, "elsp1q"), + ] { + let a = SilentPaymentAddress::new( + address.scan_public_key(), + address.spend_public_key(), + network, + ); + assert!(a.to_string().starts_with(prefix), "{a}"); + assert_eq!(a.to_string().parse::().unwrap(), a); + } + } + + #[test] + fn address_network() { + let address = address(); + assert!(address.is_for_network(Network::Liquid)); + assert!(!address.is_for_network(Network::TestnetLiquid)); + assert!(!address.is_for_network(Network::default_regtest())); + } + + #[test] + fn address_invalid() { + // a bitcoin silent payment address must not be accepted on liquid + let bitcoin = "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv"; + assert!(bitcoin.parse::().is_err()); + + let mut wrong_checksum = address().to_string(); + wrong_checksum.pop(); + wrong_checksum.push('q'); + assert!(wrong_checksum.parse::().is_err()); + + let truncated = &address().to_string()[..50]; + assert!(truncated.parse::().is_err()); + } + + /// The encoding is the same as BIP352, only the human readable part differs, so the + /// addresses in the BIP352 test vectors must round trip through our codec. + #[test] + fn bip352_address_encoding() { + use crate::silent_payments::test_vectors::{encode_bip352_address, parse_bip352_address}; + + let mut checked = 0; + for vector in test_vectors() { + for receiving in &vector.receiving { + for address in &receiving.expected.addresses { + let parsed = parse_bip352_address(address); + assert_eq!(&encode_bip352_address(&parsed), address); + checked += 1; + } + } + } + assert_eq!(checked, 44); + } +} diff --git a/lwk_wollet/src/silent_payments/hashes.rs b/lwk_wollet/src/silent_payments/hashes.rs new file mode 100644 index 000000000..ca35079ad --- /dev/null +++ b/lwk_wollet/src/silent_payments/hashes.rs @@ -0,0 +1,115 @@ +use elements::hashes::{sha256t_hash_newtype, Hash, HashEngine}; + +sha256t_hash_newtype! { + /// The tag of the [`InputsHash`] + pub struct InputsTag = hash_str("BIP0352/Inputs"); + + /// Commits to the transaction inputs, it makes the shared secret unique per transaction + #[hash_newtype(forward)] + pub struct InputsHash(_); +} + +sha256t_hash_newtype! { + /// The tag of the [`LabelHash`] + pub struct LabelTag = hash_str("BIP0352/Label"); + + /// Tweaks the spend key of an address so that a receiver can tell apart the payments + /// made to different addresses derived from the same keys + #[hash_newtype(forward)] + pub struct LabelHash(_); +} + +sha256t_hash_newtype! { + /// The tag of the [`SharedSecretHash`] + pub struct SharedSecretTag = hash_str("BIP0352/SharedSecret"); + + /// Tweaks the spend key of an address, once per output paid to the same address in a + /// given transaction + #[hash_newtype(forward)] + pub struct SharedSecretHash(_); +} + +sha256t_hash_newtype! { + /// The tag of the [`BlindingHash`] + pub struct BlindingTag = hash_str("Silent-Payment-Blinding-Key/1.0"); + + /// Derives the blinding key of a silent payment output, this is the Liquid specific part + /// of the protocol: the sender blinds the output to a key that the receiver can compute + /// back from the shared secret + #[hash_newtype(forward)] + pub struct BlindingHash(_); +} + +impl InputsHash { + /// `input_hash = hash_BIP0352/Inputs(outpoint_L || A)` + pub fn compute(smallest_outpoint: &[u8; 36], sum_input_keys: &[u8; 33]) -> Self { + let mut engine = Self::engine(); + engine.input(smallest_outpoint); + engine.input(sum_input_keys); + Self::from_engine(engine) + } +} + +impl LabelHash { + /// `label_tweak = hash_BIP0352/Label(ser256(b_scan) || ser32(m))` + pub fn compute(scan_key: &[u8; 32], m: u32) -> Self { + let mut engine = Self::engine(); + engine.input(scan_key); + engine.input(&m.to_be_bytes()); + Self::from_engine(engine) + } +} + +impl SharedSecretHash { + /// `t_k = hash_BIP0352/SharedSecret(ser_P(ecdh_shared_secret) || ser32(k))` + pub fn compute(shared_secret: &[u8; 33], k: u32) -> Self { + let mut engine = Self::engine(); + engine.input(shared_secret); + engine.input(&k.to_be_bytes()); + Self::from_engine(engine) + } +} + +impl BlindingHash { + /// `blinding_key = hash_Silent-Payment-Blinding-Key/1.0(ser_P(ecdh_shared_secret) || ser32(k))` + pub fn compute(shared_secret: &[u8; 33], k: u32) -> Self { + let mut engine = Self::engine(); + engine.input(shared_secret); + engine.input(&k.to_be_bytes()); + Self::from_engine(engine) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use elements::hashes::{sha256, sha256t::Tag, HashEngine}; + + fn midstate(tag: &str) -> [u8; 32] { + let tag_hash = sha256::Hash::hash(tag.as_bytes()); + let mut engine = sha256::Hash::engine(); + engine.input(&tag_hash[..]); + engine.input(&tag_hash[..]); + engine.midstate().to_byte_array() + } + + #[test] + fn tagged_hashes_midstates() { + assert_eq!( + InputsTag::engine().midstate().to_byte_array(), + midstate("BIP0352/Inputs") + ); + assert_eq!( + LabelTag::engine().midstate().to_byte_array(), + midstate("BIP0352/Label") + ); + assert_eq!( + SharedSecretTag::engine().midstate().to_byte_array(), + midstate("BIP0352/SharedSecret") + ); + assert_eq!( + BlindingTag::engine().midstate().to_byte_array(), + midstate("Silent-Payment-Blinding-Key/1.0") + ); + } +} diff --git a/lwk_wollet/src/silent_payments/inputs.rs b/lwk_wollet/src/silent_payments/inputs.rs new file mode 100644 index 000000000..64ac87b1e --- /dev/null +++ b/lwk_wollet/src/silent_payments/inputs.rs @@ -0,0 +1,376 @@ +use elements::hashes::{hash160, Hash}; +use elements::{OutPoint, Script, Transaction}; + +use crate::secp256k1::{Parity, PublicKey, Scalar, XOnlyPublicKey}; +use crate::EC; + +use super::hashes::InputsHash; +use super::SilentPaymentError; + +/// The BIP341 NUMS point, used as taproot internal key when there is no key path spend. +/// +/// Inputs revealing it in the control block are not considered for the shared secret, because +/// the sender does not know the corresponding secret key. +const NUMS_H: [u8; 32] = [ + 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54, 0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, + 0x07, 0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf, 0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0, +]; + +/// Size of a serialized outpoint: 32 bytes of txid plus 4 bytes of vout +const OUTPOINT_LEN: usize = 36; + +/// A transaction input as needed to compute the silent payment shared secret. +/// +/// The spent output script is needed because for taproot inputs the public key is not in the +/// witness, and because for the other input types it tells how the key must be extracted. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SilentPaymentInput { + outpoint: OutPoint, + prevout_script_pubkey: Script, + script_sig: Script, + witness: Vec>, + known_public_key: Option, + eligible: bool, +} + +impl SilentPaymentInput { + /// Create an input from its spending data and the script of the output it spends, as a + /// receiver scanning a transaction sees it + pub fn new( + outpoint: OutPoint, + prevout_script_pubkey: Script, + script_sig: Script, + witness: Vec>, + ) -> Self { + Self { + outpoint, + prevout_script_pubkey, + script_sig, + witness, + known_public_key: None, + eligible: true, + } + } + + /// Create an input of a transaction that has not been signed yet, as a sender building it + /// has it: the public key is known in advance instead of being extracted from the witness. + /// + /// Fails if `public_key` does not match `prevout_script_pubkey`, which would make the + /// receiver compute a different shared secret and miss the payment. + pub fn spending( + outpoint: OutPoint, + prevout_script_pubkey: Script, + public_key: PublicKey, + ) -> Result { + let public_key = matching_public_key(&prevout_script_pubkey, public_key) + .ok_or(SilentPaymentError::IneligibleInput(outpoint))?; + Ok(Self { + outpoint, + prevout_script_pubkey, + script_sig: Script::new(), + witness: vec![], + known_public_key: Some(public_key), + eligible: true, + }) + } + + /// Create an input that does not contribute its public key to the shared secret, only its + /// outpoint: an input spending an output that is not one of the eligible types, or a + /// peg-in, whose spending data lives on the Bitcoin chain and whose script is not known + /// here, in which case `prevout_script_pubkey` is empty. + /// + /// Marking an eligible input as not eligible makes the payment undetectable by the + /// receiver, which computes the shared secret from the signed transaction. + pub fn other(outpoint: OutPoint, prevout_script_pubkey: Script) -> Self { + Self { + outpoint, + prevout_script_pubkey, + script_sig: Script::new(), + witness: vec![], + known_public_key: None, + eligible: false, + } + } + + /// The outpoint spent by this input + pub fn outpoint(&self) -> OutPoint { + self.outpoint + } + + /// The outpoint as it is serialized in the transaction, which is what BIP352 hashes + pub(super) fn serialized_outpoint(&self) -> [u8; OUTPOINT_LEN] { + let mut bytes = [0u8; OUTPOINT_LEN]; + bytes[..32].copy_from_slice(&self.outpoint.txid.to_byte_array()); + bytes[32..].copy_from_slice(&self.outpoint.vout.to_le_bytes()); + bytes + } + + /// The public key this input contributes to the sum, if the input is eligible. + /// + /// Eligible inputs are P2TR (unless spent via a script path revealing the NUMS point), + /// P2WPKH, P2SH-P2WPKH and P2PKH, always with a compressed or x-only public key. + pub fn public_key(&self) -> Option { + if !self.eligible { + return None; + } + if self.known_public_key.is_some() { + return self.known_public_key; + } + let spk = &self.prevout_script_pubkey; + if spk.is_v1_p2tr() { + self.taproot_public_key() + } else if spk.is_v0_p2wpkh() { + self.witness_public_key() + } else if spk.is_p2sh() { + // the only eligible P2SH input is a P2SH wrapped P2WPKH, whose script sig is a + // single push of the redeem script + let script_sig = self.script_sig.as_bytes(); + let is_p2sh_p2wpkh = script_sig.len() == 23 + && script_sig[0] == 0x16 + && Script::from(script_sig[1..].to_vec()).is_v0_p2wpkh(); + is_p2sh_p2wpkh.then(|| self.witness_public_key()).flatten() + } else if spk.is_p2pkh() { + self.p2pkh_public_key() + } else { + None + } + } + + /// Whether this input spends an output this version of the protocol does not know how to + /// handle, which makes the whole transaction ineligible + pub(super) fn spends_unknown_witness_version(&self) -> bool { + witness_version(&self.prevout_script_pubkey).is_some_and(|version| version > 1) + } + + fn taproot_public_key(&self) -> Option { + let mut witness = self.witness.as_slice(); + if witness.len() > 1 && witness.last().is_some_and(|e| e.first() == Some(&0x50)) { + // the annex is not part of the spending data + witness = &witness[..witness.len() - 1]; + } + if witness.len() > 1 { + let control_block = witness.last()?; + let internal_key = control_block.get(1..33)?; + if internal_key == NUMS_H { + return None; + } + } + let x_only = + XOnlyPublicKey::from_slice(&self.prevout_script_pubkey.as_bytes()[2..]).ok()?; + Some(PublicKey::from_x_only_public_key(x_only, Parity::Even)) + } + + fn witness_public_key(&self) -> Option { + compressed_public_key(self.witness.last()?) + } + + fn p2pkh_public_key(&self) -> Option { + // the script sig can be malleated, so instead of taking the last push we look for the + // push matching the public key hash committed in the spent script + let key_hash = &self.prevout_script_pubkey.as_bytes()[3..23]; + let script_sig = self.script_sig.as_bytes(); + (33..=script_sig.len()) + .rev() + .map(|end| &script_sig[end - 33..end]) + .find(|candidate| hash160::Hash::hash(candidate).as_byte_array() == key_hash) + .and_then(compressed_public_key) + } +} + +fn compressed_public_key(bytes: &[u8]) -> Option { + // uncompressed keys are not eligible, `PublicKey::from_slice` would accept them + (bytes.len() == 33) + .then(|| PublicKey::from_slice(bytes).ok()) + .flatten() +} + +/// The version of a witness program, `None` if the script is not one +fn witness_version(script_pubkey: &Script) -> Option { + let script = script_pubkey.as_bytes(); + if !(4..=42).contains(&script.len()) || script[1] as usize != script.len() - 2 { + return None; + } + match script[0] { + 0 => Some(0), + version @ 0x51..=0x60 => Some(version - 0x50), + _ => None, + } +} + +/// The key `public_key` contributes when spending `script_pubkey`, `None` if the script is not +/// one of the eligible types or if the key is not the one committed in the script +fn matching_public_key(script_pubkey: &Script, public_key: PublicKey) -> Option { + let script = script_pubkey.as_bytes(); + let key_hash = |key: &PublicKey| hash160::Hash::hash(&key.serialize()).to_byte_array(); + if script_pubkey.is_v1_p2tr() { + let (x_only, _) = public_key.x_only_public_key(); + // whatever the parity of the key held by the sender, the input contributes the key + // with even parity + (x_only.serialize() == script[2..]) + .then(|| PublicKey::from_x_only_public_key(x_only, Parity::Even)) + } else if script_pubkey.is_v0_p2wpkh() { + (key_hash(&public_key) == script[2..22]).then_some(public_key) + } else if script_pubkey.is_p2pkh() { + (key_hash(&public_key) == script[3..23]).then_some(public_key) + } else if script_pubkey.is_p2sh() { + // the only eligible P2SH input is a P2SH wrapped P2WPKH + let mut redeem_script = vec![0x00, 0x14]; + redeem_script.extend_from_slice(&key_hash(&public_key)); + let redeem_hash = hash160::Hash::hash(&redeem_script).to_byte_array(); + (redeem_hash == script[2..22]).then_some(public_key) + } else { + None + } +} + +/// The inputs of a transaction, coupled with the scripts of the outputs they spend. +/// +/// The scripts must be in the same order as the transaction inputs. +pub fn transaction_inputs( + tx: &Transaction, + prevout_script_pubkeys: &[Script], +) -> Result, SilentPaymentError> { + if tx.input.len() != prevout_script_pubkeys.len() { + return Err(SilentPaymentError::PrevoutsMismatch { + inputs: tx.input.len(), + prevouts: prevout_script_pubkeys.len(), + }); + } + Ok(tx + .input + .iter() + .zip(prevout_script_pubkeys) + .map(|(input, script_pubkey)| { + if input.is_pegin { + SilentPaymentInput::other(input.previous_output, Script::new()) + } else { + SilentPaymentInput::new( + input.previous_output, + script_pubkey.clone(), + input.script_sig.clone(), + input.witness.script_witness.clone(), + ) + } + }) + .collect()) +} + +/// The tweak data of a transaction: `input_hash * A`, where `A` is the sum of the public keys +/// of the eligible inputs. +/// +/// This is the only per transaction data a client needs to detect payments, index servers serve +/// it so that clients do not have to download and parse every transaction. +/// +/// Returns `None` if the transaction cannot contain silent payment outputs, either because none +/// of its inputs is eligible or because the keys sum up to the point at infinity. +/// +/// `inputs` must contain **all** the inputs of the transaction, ineligible ones included, +/// because the smallest outpoint is computed over all of them. +pub fn tweak_data(inputs: &[SilentPaymentInput]) -> Result, SilentPaymentError> { + if inputs.iter().any(|i| i.spends_unknown_witness_version()) { + // a future version of the protocol may define how to derive a key from such an input, + // skipping the transaction now avoids having to scan it twice then + return Ok(None); + } + let public_keys: Vec<_> = inputs.iter().filter_map(|i| i.public_key()).collect(); + if public_keys.is_empty() { + return Ok(None); + } + let refs: Vec<_> = public_keys.iter().collect(); + let sum = match PublicKey::combine_keys(&refs) { + Ok(sum) => sum, + // the keys sum up to the point at infinity, no shared secret can be derived + Err(_) => return Ok(None), + }; + + let smallest_outpoint = inputs + .iter() + .map(|i| i.serialized_outpoint()) + .min() + .ok_or(SilentPaymentError::NoInputs)?; + + let input_hash = InputsHash::compute(&smallest_outpoint, &sum.serialize()); + let input_hash = Scalar::from_be_bytes(input_hash.to_byte_array()) + .map_err(|_| SilentPaymentError::InvalidInputHash)?; + Ok(Some(sum.mul_tweak(&EC, &input_hash)?)) +} + +/// The tweak data of a transaction, see [`tweak_data`] +pub fn tweak_data_from_tx( + tx: &Transaction, + prevout_script_pubkeys: &[Script], +) -> Result, SilentPaymentError> { + tweak_data(&transaction_inputs(tx, prevout_script_pubkeys)?) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::silent_payments::test_vectors::{test_vectors, Vin}; + + fn inputs(vin: &[Vin]) -> Vec { + vin.iter().map(Vin::input).collect() + } + + #[test] + fn bip352_tweak_data() { + let mut checked = 0; + for vector in test_vectors() { + for receiving in &vector.receiving { + let inputs = inputs(&receiving.given.vin); + let tweak = tweak_data(&inputs).unwrap(); + match &receiving.expected.tweak { + Some(expected) => { + assert_eq!( + tweak.expect("tweak is expected").to_string(), + *expected, + "{}", + vector.comment + ); + } + None => assert!(tweak.is_none(), "{}", vector.comment), + } + checked += 1; + } + } + assert!(checked > 0); + } + + /// A transaction spending an output of an unknown witness version is not scanned at all, + /// even if it has eligible inputs + #[test] + fn unknown_witness_version() { + let vector = test_vectors() + .into_iter() + .find(|v| v.comment == "Simple send: two inputs") + .unwrap(); + let mut inputs = inputs(&vector.receiving[0].given.vin); + assert!(tweak_data(&inputs).unwrap().is_some()); + + let unknown = crate::silent_payments::test_vectors::script( + "5220000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + ); + let outpoint = inputs[0].outpoint(); + inputs.push(SilentPaymentInput::other(outpoint, unknown)); + assert!(tweak_data(&inputs).unwrap().is_none()); + } + + #[test] + fn bip352_input_public_key_sum() { + for vector in test_vectors() { + for receiving in &vector.receiving { + let keys: Vec<_> = inputs(&receiving.given.vin) + .iter() + .filter_map(|i| i.public_key()) + .collect(); + let refs: Vec<_> = keys.iter().collect(); + let sum = PublicKey::combine_keys(&refs).map(|k| k.to_string()).ok(); + assert_eq!( + sum, receiving.expected.input_pub_key_sum, + "{}", + vector.comment + ); + } + } + } +} diff --git a/lwk_wollet/src/silent_payments/keys.rs b/lwk_wollet/src/silent_payments/keys.rs new file mode 100644 index 000000000..074edfcd0 --- /dev/null +++ b/lwk_wollet/src/silent_payments/keys.rs @@ -0,0 +1,160 @@ +use elements::bitcoin::bip32::{DerivationPath, Xpriv}; +use elements::bitcoin::NetworkKind; +use lwk_common::Network; + +use crate::secp256k1::{PublicKey, SecretKey}; +use crate::{Error, EC}; + +use super::scan::label_tweak; +use super::{SilentPaymentAddress, SilentPaymentError, SilentPaymentNetwork}; + +/// BIP352 purpose, as defined in BIP43 +const PURPOSE: u32 = 352; + +/// The keys of a silent payment wallet, as derived in BIP352. +/// +/// Only the scan secret key is needed to detect payments, the spend secret key is needed to +/// spend them: a watch only wallet is expected to hold [`SilentPaymentKeys::scan_secret_key`] +/// and [`SilentPaymentKeys::spend_public_key`] only. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SilentPaymentKeys { + scan: SecretKey, + spend: SecretKey, +} + +impl SilentPaymentKeys { + /// Create from the two secret keys + pub fn new(scan: SecretKey, spend: SecretKey) -> Self { + Self { scan, spend } + } + + /// Derive the keys of the given account, `m/352'/coin_type'/account'/{1,0}'/0` + pub fn from_xprv(xprv: &Xpriv, account: u32) -> Result { + let coin_type = if xprv.network == NetworkKind::Main { + 1776 + } else { + 1 + }; + let derive = |change: u32| -> Result { + let path: DerivationPath = + format!("m/{PURPOSE}h/{coin_type}h/{account}h/{change}h/0").parse()?; + Ok(xprv.derive_priv(&EC, &path)?.private_key) + }; + Ok(Self { + scan: derive(1)?, + spend: derive(0)?, + }) + } + + /// Derive the keys of the given account from a BIP39 mnemonic, see + /// [`SilentPaymentKeys::from_xprv`] + pub fn from_mnemonic(mnemonic: &str, network: Network, account: u32) -> Result { + let mnemonic: bip39::Mnemonic = mnemonic + .parse() + .map_err(|e: bip39::Error| Error::Generic(format!("invalid mnemonic: {e}")))?; + let network = if network.is_mainnet() { + NetworkKind::Main + } else { + NetworkKind::Test + }; + let xprv = Xpriv::new_master(network, &mnemonic.to_seed(""))?; + Self::from_xprv(&xprv, account) + } + + /// The key to detect payments + pub fn scan_secret_key(&self) -> SecretKey { + self.scan + } + + /// The key to spend the payments received + pub fn spend_secret_key(&self) -> SecretKey { + self.spend + } + + /// The public counterpart of [`SilentPaymentKeys::scan_secret_key`] + pub fn scan_public_key(&self) -> PublicKey { + PublicKey::from_secret_key(&EC, &self.scan) + } + + /// The public counterpart of [`SilentPaymentKeys::spend_secret_key`] + pub fn spend_public_key(&self) -> PublicKey { + PublicKey::from_secret_key(&EC, &self.spend) + } + + /// The address to give out to receive payments + pub fn address(&self, network: Network) -> SilentPaymentAddress { + SilentPaymentAddress::new( + self.scan_public_key(), + self.spend_public_key(), + network.into(), + ) + } + + /// The address labelled with `m`, see [`super::SilentPaymentScanner::labelled_address`] + pub fn labelled_address( + &self, + network: Network, + m: u32, + ) -> Result { + let tweak = label_tweak(&self.scan, m)?; + let spend = self.spend_public_key().add_exp_tweak(&EC, &tweak.into())?; + let network: SilentPaymentNetwork = network.into(); + Ok(SilentPaymentAddress::new( + self.scan_public_key(), + spend, + network, + )) + } + + /// The secret key spending an output paid to the address labelled with `m` + pub fn labelled_spend_secret_key(&self, m: u32) -> Result { + let tweak = label_tweak(&self.scan, m)?; + Ok(self.spend.add_tweak(&tweak.into())?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use lwk_common::Network; + + const MNEMONIC: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; + + #[test] + fn derivation() { + let keys = SilentPaymentKeys::from_mnemonic(MNEMONIC, Network::Liquid, 0).unwrap(); + let address = keys.address(Network::Liquid); + assert_eq!(address.scan_public_key(), keys.scan_public_key()); + assert_eq!(address.spend_public_key(), keys.spend_public_key()); + assert!(address.to_string().starts_with("lqsp1q")); + + // the account and the network change the keys + let other = SilentPaymentKeys::from_mnemonic(MNEMONIC, Network::Liquid, 1).unwrap(); + assert_ne!(keys, other); + let testnet = + SilentPaymentKeys::from_mnemonic(MNEMONIC, Network::TestnetLiquid, 0).unwrap(); + assert_ne!(keys, testnet); + assert!(testnet + .address(Network::TestnetLiquid) + .to_string() + .starts_with("tlqsp1q")); + + // scan and spend keys are different + assert_ne!(keys.scan_secret_key(), keys.spend_secret_key()); + } + + #[test] + fn labelled() { + let keys = SilentPaymentKeys::from_mnemonic(MNEMONIC, Network::Liquid, 0).unwrap(); + let address = keys.address(Network::Liquid); + let labelled = keys.labelled_address(Network::Liquid, 1).unwrap(); + assert_eq!(labelled.scan_public_key(), address.scan_public_key()); + assert_ne!(labelled.spend_public_key(), address.spend_public_key()); + + let secret_key = keys.labelled_spend_secret_key(1).unwrap(); + assert_eq!( + PublicKey::from_secret_key(&EC, &secret_key), + labelled.spend_public_key() + ); + } +} diff --git a/lwk_wollet/src/silent_payments/mod.rs b/lwk_wollet/src/silent_payments/mod.rs new file mode 100644 index 000000000..755b4a996 --- /dev/null +++ b/lwk_wollet/src/silent_payments/mod.rs @@ -0,0 +1,163 @@ +//! Silent payments on Liquid, an implementation of [BIP352] adapted to confidential +//! transactions. +//! +//! A silent payment address is a static address that can be published without giving up +//! privacy: the sender derives a fresh output from the address and from the inputs it is +//! spending, and only the receiver can recognize it. Nothing is added to the transaction, so +//! the receiver has to scan the chain looking for outputs paying to it. +//! +//! # Differences from BIP352 +//! +//! The key derivation is unchanged, what Liquid adds is blinding. Outputs are confidential, so +//! the sender must blind them to a key the receiver can compute back: that key is derived from +//! the same shared secret used for the output key, with the tag +//! `Silent-Payment-Blinding-Key/1.0`. +//! +//! Addresses use the same encoding of BIP352 with a different human readable part, so that an +//! address of the wrong chain cannot be paid by mistake: `lqsp` for Liquid, `tlqsp` for Liquid +//! testnet and `elsp` for Elements regtest. +//! +//! Peg-in inputs never contribute to the shared secret, their spending data is on the Bitcoin +//! chain. Their outpoints are still considered when looking for the smallest one. +//! +//! # Receiving +//! +//! ``` +//! # use lwk_wollet::silent_payments::{SilentPaymentKeys, SilentPaymentWollet}; +//! # use lwk_wollet::elements::{Script, Transaction}; +//! # use lwk_wollet::{Network, WolletBuilder}; +//! # fn main() -> Result<(), lwk_wollet::Error> { +//! let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; +//! let network = Network::TestnetLiquid; +//! let keys = SilentPaymentKeys::from_mnemonic(mnemonic, network, 0)?; +//! let mut wollet = SilentPaymentWollet::new(network, keys.scan_secret_key(), keys.spend_public_key()); +//! +//! // give this address out, it can be reused without linking the payments +//! println!("{}", wollet.address()); +//! # Ok(()) +//! # } +//! +//! // for every transaction of the chain, together with the scripts of the outputs it spends +//! fn scan( +//! wollet: &mut SilentPaymentWollet, +//! tx: &Transaction, +//! prevout_script_pubkeys: &[Script], +//! ) -> Result<(), lwk_wollet::Error> { +//! wollet.scan_transaction(tx, prevout_script_pubkeys)?; +//! +//! // the discovered outputs can be tracked by a regular watch only wallet +//! if !wollet.is_empty() { +//! let wollet = WolletBuilder::new(wollet.network(), wollet.wollet_descriptor()?).build()?; +//! } +//! Ok(()) +//! } +//! ``` +//! +//! [BIP352]: https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki + +use elements::OutPoint; + +mod address; +mod hashes; +mod inputs; +mod keys; +mod scan; +mod send; +mod wollet; + +#[cfg(test)] +mod test_vectors; + +pub use address::{SilentPaymentAddress, SilentPaymentNetwork}; +pub use inputs::{transaction_inputs, tweak_data, tweak_data_from_tx, SilentPaymentInput}; +pub use keys::SilentPaymentKeys; +pub use scan::{SilentPaymentScanner, SilentPaymentTxOut, CHANGE_LABEL, K_MAX}; +pub use send::{derive_outputs, SilentPaymentOutput}; +pub use wollet::SilentPaymentWollet; + +/// Errors of the silent payments module +#[derive(thiserror::Error, Debug)] +pub enum SilentPaymentError { + /// The address is not a valid bech32m string + #[error("Invalid silent payment address: {0}")] + AddressEncoding(String), + + /// The address has a version this implementation cannot handle + #[error("Unsupported silent payment address version '{0}'")] + AddressVersion(char), + + /// The address does not contain two public keys + #[error("Invalid silent payment address payload length: {0}")] + AddressLength(usize), + + /// The address is not for a Liquid network + #[error("Unknown silent payment address prefix '{0}'")] + AddressHrp(String), + + /// The scripts of the spent outputs do not match the transaction inputs + #[error("The transaction has {inputs} inputs but {prevouts} previous outputs were given")] + PrevoutsMismatch { + /// Number of inputs of the transaction + inputs: usize, + /// Number of previous outputs given + prevouts: usize, + }, + + /// A transaction always has at least one input + #[error("The transaction has no inputs")] + NoInputs, + + /// None of the inputs can contribute to the shared secret, so no output can be derived + #[error("None of the transaction inputs is eligible for silent payments")] + NoEligibleInputs, + + /// The sum of the input public keys is the point at infinity, the transaction cannot pay + /// to a silent payment address + #[error("The inputs of the transaction sum up to the point at infinity")] + InputsSumToInfinity, + + /// An input eligible for silent payments was given without its secret key + #[error("No secret key was given for input {0}")] + MissingInputSecretKey(OutPoint), + + /// The secret key given for an input does not match the public key of that input + #[error("The secret key given for input {0} does not match its public key")] + WrongInputSecretKey(OutPoint), + + /// The input cannot contribute its public key, either because the output it spends is not + /// one of the eligible types or because the key given does not match that output + #[error("The input {0} cannot contribute its public key")] + IneligibleInput(OutPoint), + + /// No recipient was given + #[error("No recipient was given")] + NoRecipients, + + /// A transaction cannot pay more than [`K_MAX`] outputs to the same scan key + #[error("More than {K_MAX} outputs pay to the same scan key")] + TooManyOutputsPerScanKey, + + /// The recipients are not all on the same network + #[error("The recipients are not all on the same network")] + MixedNetworks, + + /// Happens with negligible probability + #[error("The input hash is not a valid secret key")] + InvalidInputHash, + + /// Happens with negligible probability + #[error("The label hash is not a valid secret key")] + InvalidLabelHash, + + /// Happens with negligible probability + #[error("The shared secret hash is not a valid secret key")] + InvalidSharedSecretHash, + + /// Happens with negligible probability + #[error("The blinding key hash is not a valid secret key")] + InvalidBlindingHash, + + /// Error from the secp256k1 library + #[error(transparent)] + Secp256k1(#[from] crate::secp256k1::Error), +} diff --git a/lwk_wollet/src/silent_payments/scan.rs b/lwk_wollet/src/silent_payments/scan.rs new file mode 100644 index 000000000..a706fbe36 --- /dev/null +++ b/lwk_wollet/src/silent_payments/scan.rs @@ -0,0 +1,482 @@ +use std::collections::HashMap; + +use elements::confidential::{Asset, AssetBlindingFactor, Nonce, Value, ValueBlindingFactor}; +use elements::hashes::Hash; +use elements::{OutPoint, Script, Transaction, TxOut, TxOutSecrets}; +use serde::{Deserialize, Serialize}; + +use crate::secp256k1::{Parity, PublicKey, Scalar, SecretKey, XOnlyPublicKey}; +use crate::{ExternalUtxo, EC}; + +use super::hashes::{BlindingHash, LabelHash, SharedSecretHash}; +use super::inputs::tweak_data_from_tx; +use super::{SilentPaymentAddress, SilentPaymentError, SilentPaymentNetwork}; + +/// Maximum number of outputs a transaction can pay to the same scan key, as defined by BIP352 +pub const K_MAX: u32 = 2323; + +/// The label reserved for the change of a silent payment wallet +pub const CHANGE_LABEL: u32 = 0; + +/// The weight needed to spend a silent payment output: a taproot key path signature +const SPENDING_WEIGHT: usize = 66; + +/// Detects the outputs of a transaction paying to a silent payment address. +/// +/// It needs the scan secret key and the spend public key, in other words what a watch only +/// wallet is expected to hold: it can tell which outputs are received but it cannot spend them. +#[derive(Debug, Clone)] +pub struct SilentPaymentScanner { + scan_secret_key: SecretKey, + spend_public_key: PublicKey, + labels: HashMap<[u8; 33], Label>, +} + +#[derive(Debug, Clone, Copy)] +struct Label { + m: u32, + tweak: SecretKey, +} + +/// An output of a transaction paying to a silent payment address of the wallet +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SilentPaymentTxOut { + outpoint: OutPoint, + txout: TxOut, + tweak: SecretKey, + label: Option, + blinding_key: SecretKey, + unblinded: Option, +} + +impl SilentPaymentTxOut { + /// The outpoint of this output + pub fn outpoint(&self) -> OutPoint { + self.outpoint + } + + /// The transaction output + pub fn txout(&self) -> &TxOut { + &self.txout + } + + /// The script pubkey of this output, a taproot script + pub fn script_pubkey(&self) -> &Script { + &self.txout.script_pubkey + } + + /// The value to add to the spend secret key to obtain the secret key of this output + pub fn tweak(&self) -> SecretKey { + self.tweak + } + + /// The label of the address this output was paid to, [`CHANGE_LABEL`] is the change + pub fn label(&self) -> Option { + self.label + } + + /// The secret key needed to unblind this output, it is derived from the shared secret so + /// that the sender can blind an output that only the receiver knows how to find + pub fn blinding_key(&self) -> SecretKey { + self.blinding_key + } + + /// The unblinded asset and value of this output, `None` if unblinding failed + pub fn unblinded(&self) -> Option<&TxOutSecrets> { + self.unblinded.as_ref() + } + + /// The secret key spending this output, given the spend secret key of the wallet + pub fn spending_secret_key( + &self, + spend_secret_key: &SecretKey, + ) -> Result { + Ok(spend_secret_key.add_tweak(&Scalar::from(self.tweak))?) + } + + /// This output as an input for [`crate::TxBuilder::add_external_utxos`], `None` if it + /// could not be unblinded + pub fn to_external_utxo(&self) -> Option { + Some(ExternalUtxo { + outpoint: self.outpoint, + txout: self.txout.clone(), + tx: None, + unblinded: *self.unblinded.as_ref()?, + max_weight_to_satisfy: SPENDING_WEIGHT, + }) + } +} + +impl SilentPaymentScanner { + /// Create a scanner for the given keys + pub fn new(scan_secret_key: SecretKey, spend_public_key: PublicKey) -> Self { + Self { + scan_secret_key, + spend_public_key, + labels: HashMap::new(), + } + } + + /// Also detect the outputs paid to the address labelled with `m`, see + /// [`SilentPaymentScanner::labelled_address`] + pub fn add_label(&mut self, m: u32) -> Result<(), SilentPaymentError> { + let tweak = label_tweak(&self.scan_secret_key, m)?; + let point = PublicKey::from_secret_key(&EC, &tweak); + self.labels.insert(point.serialize(), Label { m, tweak }); + Ok(()) + } + + /// The scan public key + pub fn scan_public_key(&self) -> PublicKey { + PublicKey::from_secret_key(&EC, &self.scan_secret_key) + } + + /// The spend public key + pub fn spend_public_key(&self) -> PublicKey { + self.spend_public_key + } + + /// The address to give out to receive payments + pub fn address(&self, network: SilentPaymentNetwork) -> SilentPaymentAddress { + SilentPaymentAddress::new(self.scan_public_key(), self.spend_public_key, network) + } + + /// The address labelled with `m`, payments to it are detected only if the label has been + /// added with [`SilentPaymentScanner::add_label`]. + /// + /// Labels allow to tell apart payments made to different addresses of the same wallet + /// without publishing more than one set of keys, `m` = [`CHANGE_LABEL`] is reserved for the + /// change and must not be given out. + pub fn labelled_address( + &self, + network: SilentPaymentNetwork, + m: u32, + ) -> Result { + let tweak = label_tweak(&self.scan_secret_key, m)?; + let spend_public_key = self + .spend_public_key + .add_exp_tweak(&EC, &Scalar::from(tweak))?; + Ok(SilentPaymentAddress::new( + self.scan_public_key(), + spend_public_key, + network, + )) + } + + /// The shared secret between the sender and the receiver, `input_hash * b_scan * A` + fn shared_secret(&self, tweak_data: &PublicKey) -> Result { + Ok(tweak_data.mul_tweak(&EC, &Scalar::from(self.scan_secret_key))?) + } + + /// The scripts a transaction with the given tweak data would pay to, if it paid to this + /// wallet: the first one for the address itself and one for each label. + /// + /// A light client matches these against a block filter to know whether it is worth + /// downloading a transaction and scanning it with + /// [`SilentPaymentScanner::scan_transaction_with_tweak_data`]. + pub fn candidate_script_pubkeys( + &self, + tweak_data: &PublicKey, + ) -> Result, SilentPaymentError> { + let shared_secret = self.shared_secret(tweak_data)?; + let (_, output_key) = self.output_key(&shared_secret, 0)?; + let mut scripts = vec![taproot_script_pubkey(&output_key)]; + for label in self.labels.values() { + let labelled = output_key.add_exp_tweak(&EC, &Scalar::from(label.tweak))?; + scripts.push(taproot_script_pubkey(&labelled)); + } + Ok(scripts) + } + + /// Detect the outputs of `tx` paying to this wallet. + /// + /// `prevout_script_pubkeys` are the scripts of the outputs spent by `tx`, in the same order + /// as its inputs. They are needed because taproot inputs do not reveal their public key in + /// the witness. + pub fn scan_transaction( + &self, + tx: &Transaction, + prevout_script_pubkeys: &[Script], + ) -> Result, SilentPaymentError> { + match tweak_data_from_tx(tx, prevout_script_pubkeys)? { + Some(tweak_data) => self.scan_transaction_with_tweak_data(tx, &tweak_data), + None => Ok(vec![]), + } + } + + /// Detect the outputs of `tx` paying to this wallet, using the tweak data obtained from an + /// index server instead of the outputs spent by `tx`, see + /// [`crate::silent_payments::tweak_data`] + pub fn scan_transaction_with_tweak_data( + &self, + tx: &Transaction, + tweak_data: &PublicKey, + ) -> Result, SilentPaymentError> { + let shared_secret = self.shared_secret(tweak_data)?; + let txid = tx.txid(); + + let mut candidates: Vec<(u32, XOnlyPublicKey)> = tx + .output + .iter() + .enumerate() + .filter_map(|(vout, txout)| { + let script_pubkey = &txout.script_pubkey; + if !script_pubkey.is_v1_p2tr() { + return None; + } + let key = XOnlyPublicKey::from_slice(&script_pubkey.as_bytes()[2..]).ok()?; + Some((vout as u32, key)) + }) + .collect(); + + let mut found = vec![]; + for k in 0..K_MAX { + if candidates.is_empty() { + break; + } + let (tweak, output_key) = self.output_key(&shared_secret, k)?; + let Some((position, label)) = self.match_output(&candidates, &output_key)? else { + break; + }; + let (vout, _) = candidates.remove(position); + let tweak = match label { + Some(label) => tweak.add_tweak(&Scalar::from(label.tweak))?, + None => tweak, + }; + let blinding_key = blinding_key(&shared_secret, k)?; + let txout = tx.output[vout as usize].clone(); + found.push(SilentPaymentTxOut { + outpoint: OutPoint::new(txid, vout), + unblinded: unblind(&txout, &blinding_key), + txout, + tweak, + label: label.map(|l| l.m), + blinding_key, + }); + } + // outputs are found in the order they were created by the sender, return them in the + // order they appear in the transaction + found.sort_by_key(|txout| txout.outpoint.vout); + Ok(found) + } + + /// `t_k` and `P_k = B_spend + t_k * G` + fn output_key( + &self, + shared_secret: &PublicKey, + k: u32, + ) -> Result<(SecretKey, PublicKey), SilentPaymentError> { + let tweak = output_tweak(shared_secret, k)?; + let output_key = self + .spend_public_key + .add_exp_tweak(&EC, &Scalar::from(tweak))?; + Ok((tweak, output_key)) + } + + /// The position of the output paying to `output_key`, and the label it was paid to + fn match_output( + &self, + candidates: &[(u32, XOnlyPublicKey)], + output_key: &PublicKey, + ) -> Result)>, SilentPaymentError> { + let (expected, _) = output_key.x_only_public_key(); + if let Some(position) = candidates.iter().position(|(_, key)| *key == expected) { + return Ok(Some((position, None))); + } + if self.labels.is_empty() { + return Ok(None); + } + let negated_output_key = output_key.negate(&EC); + for (position, (_, key)) in candidates.iter().enumerate() { + // the label is the difference between the output and the key we derived, both + // parities of the output must be tried since the transaction only commits to the + // x coordinate + let key = PublicKey::from_x_only_public_key(*key, Parity::Even); + for candidate in [key, key.negate(&EC)] { + let Ok(label) = candidate.combine(&negated_output_key) else { + continue; + }; + if let Some(label) = self.labels.get(&label.serialize()) { + return Ok(Some((position, Some(*label)))); + } + } + } + Ok(None) + } +} + +/// `t_k = hash_BIP0352/SharedSecret(ser_P(ecdh_shared_secret) || ser32(k))` +pub(super) fn output_tweak( + shared_secret: &PublicKey, + k: u32, +) -> Result { + let hash = SharedSecretHash::compute(&shared_secret.serialize(), k); + SecretKey::from_slice(hash.as_byte_array()) + .map_err(|_| SilentPaymentError::InvalidSharedSecretHash) +} + +/// `label_tweak = hash_BIP0352/Label(ser256(b_scan) || ser32(m))` +pub(super) fn label_tweak( + scan_secret_key: &SecretKey, + m: u32, +) -> Result { + let hash = LabelHash::compute(&scan_secret_key.secret_bytes(), m); + SecretKey::from_slice(hash.as_byte_array()).map_err(|_| SilentPaymentError::InvalidLabelHash) +} + +/// The secret key the output is blinded to, the Liquid specific part of the derivation +pub(super) fn blinding_key( + shared_secret: &PublicKey, + k: u32, +) -> Result { + let hash = BlindingHash::compute(&shared_secret.serialize(), k); + SecretKey::from_slice(hash.as_byte_array()).map_err(|_| SilentPaymentError::InvalidBlindingHash) +} + +/// The taproot script paying to `output_key`, silent payment outputs commit to the key as it +/// is, without applying the BIP341 tweak +pub(super) fn taproot_script_pubkey(output_key: &PublicKey) -> Script { + let (x_only, _) = output_key.x_only_public_key(); + let mut script = Vec::with_capacity(34); + script.push(0x51); + script.push(0x20); + script.extend_from_slice(&x_only.serialize()); + Script::from(script) +} + +fn unblind(txout: &TxOut, blinding_key: &SecretKey) -> Option { + match (txout.asset, txout.value, txout.nonce) { + (Asset::Confidential(_), Value::Confidential(_), Nonce::Confidential(_)) => { + txout.unblind(&EC, *blinding_key).ok() + } + (Asset::Explicit(asset), Value::Explicit(value), _) => Some(TxOutSecrets::new( + asset, + AssetBlindingFactor::zero(), + value, + ValueBlindingFactor::zero(), + )), + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::silent_payments::test_vectors::{ + encode_bip352_address, taproot_script, test_vectors, transaction, Vin, + }; + use elements::hex::ToHex; + + #[test] + fn bip352_receiving() { + let mut checked = 0; + for vector in test_vectors() { + for receiving in &vector.receiving { + let scanner = receiving.scanner(); + let inputs: Vec<_> = receiving.given.vin.iter().map(Vin::input).collect(); + let scripts: Vec<_> = receiving + .given + .outputs + .iter() + .map(|o| taproot_script(o)) + .collect(); + let tx = transaction(&inputs, &scripts); + + let found = match super::super::tweak_data(&inputs).unwrap() { + Some(tweak_data) => scanner + .scan_transaction_with_tweak_data(&tx, &tweak_data) + .unwrap(), + None => vec![], + }; + + assert_eq!( + found.len(), + receiving.expected_outputs(), + "{}", + vector.comment + ); + let spend_secret_key = receiving.given.key_material.spend_priv_key.parse().unwrap(); + for (found, expected) in found.iter().zip(&receiving.expected.outputs) { + assert_eq!( + found.tweak().secret_bytes().to_hex(), + expected.priv_key_tweak, + "{}", + vector.comment + ); + assert_eq!( + found.script_pubkey().as_bytes()[2..].to_hex(), + expected.pub_key, + "{}", + vector.comment + ); + // the wallet can spend what it detects + let secret_key = found.spending_secret_key(&spend_secret_key).unwrap(); + let (x_only, _) = secret_key.x_only_public_key(&EC); + assert_eq!(x_only.serialize().to_hex(), expected.pub_key); + } + checked += 1; + } + } + assert!(checked > 0); + } + + #[test] + fn bip352_addresses() { + let mut checked = 0; + for vector in test_vectors() { + for receiving in &vector.receiving { + let scanner = receiving.scanner(); + let network = SilentPaymentNetwork::Liquid; + let mut addresses = vec![scanner.address(network)]; + for label in &receiving.given.labels { + addresses.push(scanner.labelled_address(network, *label).unwrap()); + } + let addresses: Vec<_> = addresses.iter().map(encode_bip352_address).collect(); + assert_eq!( + addresses, receiving.expected.addresses, + "{}", + vector.comment + ); + checked += addresses.len(); + } + } + assert_eq!(checked, 44); + } + + #[test] + fn bip352_candidate_script_pubkeys() { + for vector in test_vectors() { + for receiving in &vector.receiving { + let scanner = receiving.scanner(); + let inputs: Vec<_> = receiving.given.vin.iter().map(Vin::input).collect(); + let Some(tweak_data) = super::super::tweak_data(&inputs).unwrap() else { + continue; + }; + let candidates = scanner.candidate_script_pubkeys(&tweak_data).unwrap(); + assert_eq!(candidates.len(), receiving.given.labels.len() + 1); + + // a transaction paying to this wallet always pays to one of the candidates, + // which is what makes filter matching work + let scripts: Vec<_> = receiving + .given + .outputs + .iter() + .map(|o| taproot_script(o)) + .collect(); + let tx = transaction(&inputs, &scripts); + let found = scanner + .scan_transaction_with_tweak_data(&tx, &tweak_data) + .unwrap(); + if !found.is_empty() { + assert!( + found + .iter() + .any(|txout| candidates.contains(txout.script_pubkey())), + "{}", + vector.comment + ); + } + } + } + } +} diff --git a/lwk_wollet/src/silent_payments/send.rs b/lwk_wollet/src/silent_payments/send.rs new file mode 100644 index 000000000..a81bac014 --- /dev/null +++ b/lwk_wollet/src/silent_payments/send.rs @@ -0,0 +1,255 @@ +use std::collections::BTreeMap; + +use elements::hashes::Hash; +use elements::{Address, AddressParams, Script}; + +use crate::secp256k1::{PublicKey, Scalar, SecretKey}; +use crate::EC; + +use super::hashes::InputsHash; +use super::scan::{blinding_key, output_tweak, taproot_script_pubkey}; +use super::{SilentPaymentAddress, SilentPaymentError, SilentPaymentInput, K_MAX}; + +/// An output paying to a silent payment address. +/// +/// On Liquid the receiver cannot be found by scanning if the output is not blinded to the key +/// derived from the shared secret, so an output is fully described by its script and its +/// blinding key. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SilentPaymentOutput { + script_pubkey: Script, + blinding_public_key: PublicKey, +} + +impl SilentPaymentOutput { + /// The taproot script this output must pay to + pub fn script_pubkey(&self) -> &Script { + &self.script_pubkey + } + + /// The key this output must be blinded to + pub fn blinding_public_key(&self) -> PublicKey { + self.blinding_public_key + } + + /// The confidential address this output must pay to + pub fn address(&self, params: &'static AddressParams) -> Address { + Address::from_script(&self.script_pubkey, Some(self.blinding_public_key), params) + .expect("a taproot script always maps to an address") + } +} + +/// Derive the outputs paying to the given silent payment addresses. +/// +/// `inputs` must contain **all** the inputs of the transaction, each one with the secret key +/// unlocking it, or `None` if the input is not eligible for silent payments (in which case it +/// only contributes its outpoint). +/// +/// The returned outputs are in the same order as `recipients`. Repeating an address is allowed +/// and produces different outputs. +pub fn derive_outputs( + inputs: &[(SilentPaymentInput, Option)], + recipients: &[SilentPaymentAddress], +) -> Result, SilentPaymentError> { + if recipients.is_empty() { + return Err(SilentPaymentError::NoRecipients); + } + let network = recipients[0].network(); + if recipients.iter().any(|r| r.network() != network) { + return Err(SilentPaymentError::MixedNetworks); + } + + if let Some((input, _)) = inputs + .iter() + .find(|(input, _)| input.spends_unknown_witness_version()) + { + // the receiver would not scan the transaction at all + return Err(SilentPaymentError::IneligibleInput(input.outpoint())); + } + let secret_key = inputs_secret_key(inputs)?; + let smallest_outpoint = inputs + .iter() + .map(|(input, _)| input.serialized_outpoint()) + .min() + .ok_or(SilentPaymentError::NoInputs)?; + let public_key = PublicKey::from_secret_key(&EC, &secret_key); + let input_hash = InputsHash::compute(&smallest_outpoint, &public_key.serialize()); + let input_hash = Scalar::from_be_bytes(input_hash.to_byte_array()) + .map_err(|_| SilentPaymentError::InvalidInputHash)?; + let secret_key = secret_key.mul_tweak(&input_hash)?; + + // outputs paid to the same scan key share the shared secret and are told apart by the + // counter `k` + let mut groups: BTreeMap<[u8; 33], Vec<(usize, PublicKey)>> = BTreeMap::new(); + for (i, recipient) in recipients.iter().enumerate() { + groups + .entry(recipient.scan_public_key().serialize()) + .or_default() + .push((i, recipient.spend_public_key())); + } + + let mut outputs = vec![None; recipients.len()]; + for (scan_public_key, group) in groups { + if group.len() > K_MAX as usize { + return Err(SilentPaymentError::TooManyOutputsPerScanKey); + } + let scan_public_key = PublicKey::from_slice(&scan_public_key)?; + let shared_secret = scan_public_key.mul_tweak(&EC, &Scalar::from(secret_key))?; + for (k, (i, spend_public_key)) in group.into_iter().enumerate() { + let k = k as u32; + let tweak = output_tweak(&shared_secret, k)?; + let output_key = spend_public_key.add_exp_tweak(&EC, &Scalar::from(tweak))?; + outputs[i] = Some(SilentPaymentOutput { + script_pubkey: taproot_script_pubkey(&output_key), + blinding_public_key: PublicKey::from_secret_key( + &EC, + &blinding_key(&shared_secret, k)?, + ), + }); + } + } + + Ok(outputs.into_iter().flatten().collect()) +} + +/// The sum of the secret keys of the eligible inputs, `a` +fn inputs_secret_key( + inputs: &[(SilentPaymentInput, Option)], +) -> Result { + // `None` is zero, which is not a valid secret key but is a valid intermediate sum + let mut sum: Option = None; + let mut eligible = false; + for (input, secret_key) in inputs { + let Some(public_key) = input.public_key() else { + continue; + }; + eligible = true; + let secret_key = secret_key + .ok_or_else(|| SilentPaymentError::MissingInputSecretKey(input.outpoint()))?; + // taproot inputs contribute the key with even parity, so the secret key of an odd key + // must be negated + let secret_key = if PublicKey::from_secret_key(&EC, &secret_key) == public_key { + secret_key + } else { + let negated = secret_key.negate(); + if PublicKey::from_secret_key(&EC, &negated) != public_key { + return Err(SilentPaymentError::WrongInputSecretKey(input.outpoint())); + } + negated + }; + sum = match sum { + None => Some(secret_key), + Some(sum) => sum.add_tweak(&Scalar::from(secret_key)).ok(), + }; + } + if !eligible { + return Err(SilentPaymentError::NoEligibleInputs); + } + sum.ok_or(SilentPaymentError::InputsSumToInfinity) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::silent_payments::test_vectors::{test_vectors, Vin}; + use elements::hex::ToHex; + + #[test] + fn bip352_sending() { + let mut checked = 0; + for vector in test_vectors() { + for sending in &vector.sending { + let inputs: Vec<_> = sending + .given + .vin + .iter() + .map(|vin| (vin.input(), vin.secret_key())) + .collect(); + let recipients: Vec<_> = sending + .given + .recipients + .iter() + .flat_map(|r| vec![r.address(); r.count]) + .collect(); + + let alternatives = &sending.expected.outputs; + let outputs = derive_outputs(&inputs, &recipients); + if alternatives.iter().all(|a| a.is_empty()) { + assert!(outputs.is_err(), "{}", vector.comment); + checked += 1; + continue; + } + let mut outputs: Vec = outputs + .unwrap_or_else(|e| panic!("{}: {e}", vector.comment)) + .iter() + .map(|o| o.script_pubkey().as_bytes()[2..].to_hex()) + .collect(); + outputs.sort(); + let matches = alternatives.iter().any(|alternative| { + let mut alternative = alternative.clone(); + alternative.sort(); + alternative == outputs + }); + assert!(matches, "{}: {outputs:?}", vector.comment); + checked += 1; + } + } + assert!(checked > 0); + } + + /// Sender and receiver must derive the same blinding key, or the receiver cannot unblind + /// what it receives + #[test] + fn blinding_key_roundtrip() { + let mut checked = 0; + for vector in test_vectors() { + for (sending, receiving) in vector.sending.iter().zip(&vector.receiving) { + let inputs: Vec<_> = sending + .given + .vin + .iter() + .map(|vin| (vin.input(), vin.secret_key())) + .collect(); + let recipients: Vec<_> = sending + .given + .recipients + .iter() + .flat_map(|r| vec![r.address(); r.count]) + .collect(); + let Ok(outputs) = derive_outputs(&inputs, &recipients) else { + continue; + }; + + let scanner = receiving.scanner(); + let scan_inputs: Vec<_> = receiving.given.vin.iter().map(Vin::input).collect(); + let Some(tweak_data) = super::super::tweak_data(&scan_inputs).unwrap() else { + continue; + }; + let tx = crate::silent_payments::test_vectors::transaction( + &scan_inputs, + &outputs + .iter() + .map(|o| o.script_pubkey().clone()) + .collect::>(), + ); + let found = scanner + .scan_transaction_with_tweak_data(&tx, &tweak_data) + .unwrap(); + for output in &found { + let expected = outputs + .iter() + .find(|o| o.script_pubkey() == output.script_pubkey()) + .expect("the scanner found an output we did not create"); + assert_eq!( + PublicKey::from_secret_key(&EC, &output.blinding_key()), + expected.blinding_public_key(), + "{}", + vector.comment + ); + checked += 1; + } + } + } + assert!(checked > 0); + } +} diff --git a/lwk_wollet/src/silent_payments/test_vectors.rs b/lwk_wollet/src/silent_payments/test_vectors.rs new file mode 100644 index 000000000..93dae077d --- /dev/null +++ b/lwk_wollet/src/silent_payments/test_vectors.rs @@ -0,0 +1,225 @@ +//! The BIP352 test vectors. +//! +//! They cover the key derivation, which is the same on Liquid, so they are used to check the +//! whole protocol but the blinding. + +use std::str::FromStr; + +use bech32::Hrp; +use elements::encode::deserialize; +use elements::hex::FromHex; +use elements::{OutPoint, Script, Sequence, Transaction, TxIn, TxInWitness, TxOut, Txid}; +use serde::Deserialize; + +use crate::secp256k1::{PublicKey, SecretKey}; +use crate::EC; + +use super::{SilentPaymentAddress, SilentPaymentInput, SilentPaymentNetwork, SilentPaymentScanner}; + +#[derive(Deserialize)] +pub struct TestVector { + pub comment: String, + pub sending: Vec, + pub receiving: Vec, +} + +#[derive(Deserialize)] +pub struct Sending { + pub given: SendingGiven, + pub expected: SendingExpected, +} + +#[derive(Deserialize)] +pub struct SendingGiven { + pub vin: Vec, + pub recipients: Vec, +} + +#[derive(Deserialize)] +pub struct SendingExpected { + /// Every element is a valid set of outputs, the order within a set is not specified + pub outputs: Vec>, +} + +#[derive(Deserialize)] +pub struct Recipient { + pub address: String, + + /// How many outputs pay to this address + #[serde(default = "one")] + pub count: usize, +} + +fn one() -> usize { + 1 +} + +#[derive(Deserialize)] +pub struct Receiving { + pub given: ReceivingGiven, + pub expected: ReceivingExpected, +} + +#[derive(Deserialize)] +pub struct ReceivingGiven { + pub vin: Vec, + pub outputs: Vec, + pub key_material: KeyMaterial, + pub labels: Vec, +} + +#[derive(Deserialize)] +pub struct KeyMaterial { + pub spend_priv_key: String, + pub scan_priv_key: String, +} + +#[derive(Deserialize)] +pub struct ReceivingExpected { + pub addresses: Vec, + + #[serde(default)] + pub outputs: Vec, + + /// Used instead of `outputs` when they are too many to be listed + #[serde(default)] + pub n_outputs: Option, + + pub tweak: Option, + + #[serde(default)] + pub input_pub_key_sum: Option, +} + +#[derive(Deserialize)] +pub struct ExpectedOutput { + pub priv_key_tweak: String, + pub pub_key: String, +} + +#[derive(Deserialize)] +pub struct Vin { + pub txid: String, + pub vout: u32, + + #[serde(rename = "scriptSig")] + pub script_sig: String, + + pub txinwitness: String, + + pub prevout: Prevout, + + #[serde(default)] + pub private_key: Option, +} + +#[derive(Deserialize)] +pub struct Prevout { + #[serde(rename = "scriptPubKey")] + pub script_pub_key: ScriptPubKey, +} + +#[derive(Deserialize)] +pub struct ScriptPubKey { + pub hex: String, +} + +impl Vin { + pub fn input(&self) -> SilentPaymentInput { + let outpoint = OutPoint::new(Txid::from_str(&self.txid).unwrap(), self.vout); + SilentPaymentInput::new( + outpoint, + script(&self.prevout.script_pub_key.hex), + script(&self.script_sig), + witness(&self.txinwitness), + ) + } + + pub fn secret_key(&self) -> Option { + self.private_key.as_ref().map(|k| k.parse().unwrap()) + } +} + +impl Recipient { + pub fn address(&self) -> SilentPaymentAddress { + parse_bip352_address(&self.address) + } +} + +impl Receiving { + pub fn scanner(&self) -> SilentPaymentScanner { + let scan = self.given.key_material.scan_priv_key.parse().unwrap(); + let spend: SecretKey = self.given.key_material.spend_priv_key.parse().unwrap(); + let mut scanner = SilentPaymentScanner::new(scan, PublicKey::from_secret_key(&EC, &spend)); + for label in &self.given.labels { + scanner.add_label(*label).unwrap(); + } + scanner + } + + pub fn expected_outputs(&self) -> usize { + self.expected + .n_outputs + .unwrap_or(self.expected.outputs.len()) + } +} + +/// The BIP352 test vectors use the bitcoin human readable part, the payload is the same +pub fn parse_bip352_address(address: &str) -> SilentPaymentAddress { + let (_, _, scan, spend) = SilentPaymentAddress::decode_with_hrp(address).unwrap(); + SilentPaymentAddress::new(scan, spend, SilentPaymentNetwork::Liquid) +} + +/// See [`parse_bip352_address`] +pub fn encode_bip352_address(address: &SilentPaymentAddress) -> String { + address.encode_with_hrp(Hrp::parse_unchecked("sp")) +} + +pub fn test_vectors() -> Vec { + serde_json::from_str(lwk_test_util::bip352_test_vectors()).unwrap() +} + +pub fn script(hex: &str) -> Script { + Script::from(Vec::::from_hex(hex).unwrap()) +} + +/// The taproot script paying to the given x-only public key +pub fn taproot_script(x_only: &str) -> Script { + script(&format!("5120{x_only}")) +} + +fn witness(hex: &str) -> Vec> { + if hex.is_empty() { + return vec![]; + } + deserialize(&Vec::::from_hex(hex).unwrap()).unwrap() +} + +/// A transaction spending the given inputs and paying to the given scripts. +/// +/// Values are explicit: the BIP352 vectors are about key derivation, blinding is checked +/// against transactions built by the wallet itself. +pub fn transaction(inputs: &[SilentPaymentInput], scripts: &[Script]) -> Transaction { + Transaction { + version: 2, + lock_time: elements::LockTime::ZERO, + input: inputs + .iter() + .map(|input| TxIn { + previous_output: input.outpoint(), + is_pegin: false, + script_sig: Script::new(), + sequence: Sequence::MAX, + asset_issuance: Default::default(), + witness: TxInWitness::empty(), + }) + .collect(), + output: scripts + .iter() + .map(|script_pubkey| TxOut { + script_pubkey: script_pubkey.clone(), + ..TxOut::default() + }) + .collect(), + } +} diff --git a/lwk_wollet/src/silent_payments/wollet.rs b/lwk_wollet/src/silent_payments/wollet.rs new file mode 100644 index 000000000..144fb590b --- /dev/null +++ b/lwk_wollet/src/silent_payments/wollet.rs @@ -0,0 +1,237 @@ +use std::collections::BTreeMap; + +use elements::{OutPoint, Script, Transaction}; +use lwk_common::Network; + +use crate::secp256k1::{PublicKey, SecretKey}; +use crate::{cache::Height, Error, ExternalUtxo, WolletDescriptor}; + +use super::{ + SilentPaymentAddress, SilentPaymentError, SilentPaymentKeys, SilentPaymentScanner, + SilentPaymentTxOut, +}; + +/// A watch only wallet detecting the payments made to a silent payment address. +/// +/// Silent payment outputs cannot be derived in advance, they are discovered scanning the +/// blockchain. Once discovered they are ordinary confidential taproot outputs, so they can be +/// tracked by a [`crate::Wollet`] built on [`SilentPaymentWollet::wollet_descriptor`], which +/// takes care of their balance, of the transaction history and of the outputs being spent. +#[derive(Debug, Clone)] +pub struct SilentPaymentWollet { + network: Network, + scanner: SilentPaymentScanner, + outputs: BTreeMap, + last_scanned_height: Option, +} + +impl SilentPaymentWollet { + /// Create a wallet detecting the payments to the address made of the given keys + pub fn new(network: Network, scan_secret_key: SecretKey, spend_public_key: PublicKey) -> Self { + Self { + network, + scanner: SilentPaymentScanner::new(scan_secret_key, spend_public_key), + outputs: BTreeMap::new(), + last_scanned_height: None, + } + } + + /// Create a wallet from the keys of a signer, note that the spend secret key is not kept + pub fn from_keys(network: Network, keys: &SilentPaymentKeys) -> Self { + Self::new(network, keys.scan_secret_key(), keys.spend_public_key()) + } + + /// Also detect the payments made to the address labelled with `m` + pub fn add_label(&mut self, m: u32) -> Result<(), SilentPaymentError> { + self.scanner.add_label(m) + } + + /// The network of this wallet + pub fn network(&self) -> Network { + self.network + } + + /// The address to give out to receive payments + pub fn address(&self) -> SilentPaymentAddress { + self.scanner.address(self.network.into()) + } + + /// The address labelled with `m`, see [`SilentPaymentScanner::labelled_address`] + pub fn labelled_address(&self, m: u32) -> Result { + self.scanner.labelled_address(self.network.into(), m) + } + + /// The scanner of this wallet, to detect payments without adding them to the wallet + pub fn scanner(&self) -> &SilentPaymentScanner { + &self.scanner + } + + /// Scan a transaction and add the outputs paying to this wallet. + /// + /// `prevout_script_pubkeys` are the scripts of the outputs spent by the transaction, in the + /// same order as its inputs. + /// + /// Returns the outputs discovered by this call, outputs already known are not returned. + pub fn scan_transaction( + &mut self, + tx: &Transaction, + prevout_script_pubkeys: &[Script], + ) -> Result, SilentPaymentError> { + let found = self.scanner.scan_transaction(tx, prevout_script_pubkeys)?; + Ok(self.insert(found)) + } + + /// Scan a transaction with the tweak data obtained from an index server, see + /// [`SilentPaymentScanner::scan_transaction_with_tweak_data`] + pub fn scan_transaction_with_tweak_data( + &mut self, + tx: &Transaction, + tweak_data: &PublicKey, + ) -> Result, SilentPaymentError> { + let found = self + .scanner + .scan_transaction_with_tweak_data(tx, tweak_data)?; + Ok(self.insert(found)) + } + + fn insert(&mut self, found: Vec) -> Vec { + found + .into_iter() + .filter(|txout| { + self.outputs + .insert(txout.outpoint(), txout.clone()) + .is_none() + }) + .collect() + } + + /// The outputs discovered so far, ordered by outpoint. + /// + /// Note that an output could have been spent already, ask a [`crate::Wollet`] built on + /// [`SilentPaymentWollet::wollet_descriptor`] for the unspent ones. + pub fn outputs(&self) -> impl Iterator { + self.outputs.values() + } + + /// Whether no output has been discovered yet + pub fn is_empty(&self) -> bool { + self.outputs.is_empty() + } + + /// The output with the given outpoint, if it belongs to this wallet + pub fn output(&self, outpoint: &OutPoint) -> Option<&SilentPaymentTxOut> { + self.outputs.get(outpoint) + } + + /// The height of the last block scanned, to resume scanning after a restart + pub fn last_scanned_height(&self) -> Option { + self.last_scanned_height + } + + /// See [`SilentPaymentWollet::last_scanned_height`] + pub fn set_last_scanned_height(&mut self, height: Height) { + self.last_scanned_height = Some(height); + } + + /// The descriptor tracking the outputs discovered so far. + /// + /// It changes every time an output is discovered, a [`crate::Wollet`] built on it must be + /// rebuilt to see the new outputs. + pub fn wollet_descriptor(&self) -> Result { + if self.outputs.is_empty() { + return Err(Error::Generic( + "no silent payment output has been discovered yet".into(), + )); + } + let descriptor: Vec = self + .outputs + .values() + .map(|txout| { + format!( + "{}:{:x}", + txout.blinding_key().display_secret(), + txout.script_pubkey() + ) + }) + .collect(); + descriptor.join(",").parse() + } + + /// The outputs discovered so far, as inputs for + /// [`crate::TxBuilder::add_external_utxos`]. + /// + /// Signing them requires the secret key returned by + /// [`SilentPaymentTxOut::spending_secret_key`], a taproot key path signature over the + /// output key without the BIP341 tweak. + pub fn external_utxos(&self) -> Vec { + self.outputs + .values() + .filter_map(|txout| txout.to_external_utxo()) + .collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::silent_payments::test_vectors::{taproot_script, test_vectors, transaction, Vin}; + use crate::silent_payments::tweak_data; + + /// A wallet built on the descriptor of the discovered outputs sees the same scripts + #[test] + fn wollet_descriptor() { + let network = Network::default_regtest(); + let mut checked = 0; + for vector in test_vectors() { + for receiving in &vector.receiving { + let scanner = receiving.scanner(); + let mut wollet = SilentPaymentWollet { + network, + scanner: scanner.clone(), + outputs: BTreeMap::new(), + last_scanned_height: None, + }; + assert!(wollet.is_empty()); + assert!(wollet.wollet_descriptor().is_err()); + + let inputs: Vec<_> = receiving.given.vin.iter().map(Vin::input).collect(); + let scripts: Vec<_> = receiving + .given + .outputs + .iter() + .map(|o| taproot_script(o)) + .collect(); + let tx = transaction(&inputs, &scripts); + let Some(tweak_data) = tweak_data(&inputs).unwrap() else { + continue; + }; + let found = wollet + .scan_transaction_with_tweak_data(&tx, &tweak_data) + .unwrap(); + if found.is_empty() { + continue; + } + // scanning twice does not duplicate the outputs + assert!(wollet + .scan_transaction_with_tweak_data(&tx, &tweak_data) + .unwrap() + .is_empty()); + assert_eq!(wollet.outputs().count(), found.len()); + + let descriptor = wollet.wollet_descriptor().unwrap(); + for (i, output) in wollet.outputs().enumerate() { + let script = descriptor + .script_pubkey(crate::Chain::External, i as u32) + .unwrap(); + assert_eq!(&script, output.script_pubkey()); + let address = descriptor + .address(i as u32, network.address_params()) + .unwrap(); + assert!(address.is_blinded()); + } + checked += 1; + } + } + assert!(checked > 0); + } +} diff --git a/lwk_wollet/tests/silent_payments.rs b/lwk_wollet/tests/silent_payments.rs new file mode 100644 index 000000000..5b13feec1 --- /dev/null +++ b/lwk_wollet/tests/silent_payments.rs @@ -0,0 +1,526 @@ +//! Silent payments on a real Liquid transaction: a sender builds and signs a confidential +//! transaction paying to a silent payment address, the receiver scans it and can unblind and +//! spend what it received. + +use elements::bitcoin::PublicKey as BitcoinPublicKey; +use elements::confidential::{Asset, AssetBlindingFactor, Value, ValueBlindingFactor}; +use elements::encode::serialize; +use elements::hashes::Hash; +use elements::secp256k1_zkp::{Message, PublicKey, SecretKey}; +use elements::sighash::SighashCache; +use elements::{ + Address, AddressParams, AssetId, EcdsaSighashType, LockTime, OutPoint, Script, Sequence, + Transaction, TxIn, TxInWitness, TxOut, TxOutSecrets, Txid, +}; +use lwk_wollet::silent_payments::{ + derive_outputs, transaction_inputs, SilentPaymentInput, SilentPaymentKeys, SilentPaymentWollet, +}; +use lwk_wollet::{Chain, Network, WolletDescriptor, EC}; +use std::str::FromStr; + +const MNEMONIC: &str = + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; +const OTHER_MNEMONIC: &str = + "legal winner thank year wave sausage worth useful legal winner thank yellow"; +const FUNDED: u64 = 100_000; +const SENT: u64 = 90_000; +const FEE: u64 = 10_000; + +/// The wallet of the sender: a single p2wpkh output it can spend +struct Sender { + secret_key: SecretKey, + outpoint: OutPoint, + script_pubkey: Script, + secrets: TxOutSecrets, +} + +impl Sender { + fn new(asset: AssetId) -> Self { + let secret_key = SecretKey::from_slice(&[11u8; 32]).unwrap(); + let public_key = BitcoinPublicKey::new(PublicKey::from_secret_key(&EC, &secret_key)); + let address = Address::p2wpkh(&public_key, None, &AddressParams::ELEMENTS); + Self { + secret_key, + outpoint: OutPoint::new(Txid::from_slice(&[7u8; 32]).unwrap(), 0), + script_pubkey: address.script_pubkey(), + // the output being spent is explicit, so its blinding factors are zero + secrets: TxOutSecrets::new( + asset, + AssetBlindingFactor::zero(), + FUNDED, + ValueBlindingFactor::zero(), + ), + } + } + + /// The input as the sender has it while building the transaction, before signing + fn input(&self) -> SilentPaymentInput { + let public_key = PublicKey::from_secret_key(&EC, &self.secret_key); + SilentPaymentInput::spending(self.outpoint, self.script_pubkey.clone(), public_key).unwrap() + } + + /// Build a transaction paying `recipient_address` and sign it, so that the public key of + /// the input ends up in the witness where the receiver looks for it + fn pay(&self, asset: AssetId, script_pubkey: Script, blinder: PublicKey) -> Transaction { + let mut rng = elements::bitcoin::secp256k1::rand::thread_rng(); + let (txout, ..) = TxOut::new_last_confidential( + &mut rng, + &EC, + SENT, + asset, + script_pubkey, + blinder, + &[self.secrets], + &[], + ) + .unwrap(); + + let mut tx = Transaction { + version: 2, + lock_time: LockTime::ZERO, + input: vec![TxIn { + previous_output: self.outpoint, + is_pegin: false, + script_sig: Script::new(), + sequence: Sequence::MAX, + asset_issuance: Default::default(), + witness: TxInWitness::empty(), + }], + output: vec![txout, TxOut::new_fee(FEE, asset)], + }; + self.sign(&mut tx); + tx + } + + fn sign(&self, tx: &mut Transaction) { + let public_key = BitcoinPublicKey::new(PublicKey::from_secret_key(&EC, &self.secret_key)); + let script_code = + Address::p2pkh(&public_key, None, &AddressParams::ELEMENTS).script_pubkey(); + let sighash = SighashCache::new(&*tx).segwitv0_sighash( + 0, + &script_code, + Value::Explicit(FUNDED), + EcdsaSighashType::All, + ); + let message = Message::from_digest_slice(sighash.as_byte_array()).unwrap(); + let mut signature = EC + .sign_ecdsa(&message, &self.secret_key) + .serialize_der() + .to_vec(); + signature.push(EcdsaSighashType::All as u8); + tx.input[0].witness.script_witness = vec![signature, public_key.to_bytes()]; + } +} + +fn setup() -> ( + Network, + AssetId, + Sender, + SilentPaymentKeys, + SilentPaymentWollet, +) { + let network = Network::default_regtest(); + let asset = *network.policy_asset(); + let keys = SilentPaymentKeys::from_mnemonic(MNEMONIC, network, 0).unwrap(); + let wollet = SilentPaymentWollet::from_keys(network, &keys); + (network, asset, Sender::new(asset), keys, wollet) +} + +/// The sender derives the output from the address and its own inputs, the receiver finds it +/// scanning the transaction without any communication with the sender +#[test] +fn receive_silent_payment() { + let (network, asset, sender, keys, mut wollet) = setup(); + + let address = wollet.address(); + assert!(address.to_string().starts_with("elsp1q")); + assert!(address.is_for_network(network)); + // the address is parsed back by the sender from its string representation + let address = address.to_string().parse().unwrap(); + + let inputs = vec![(sender.input(), Some(sender.secret_key))]; + let outputs = derive_outputs(&inputs, &[address]).unwrap(); + assert_eq!(outputs.len(), 1); + let tx = sender.pay( + asset, + outputs[0].script_pubkey().clone(), + outputs[0].blinding_public_key(), + ); + + let found = wollet + .scan_transaction(&tx, &[sender.script_pubkey.clone()]) + .unwrap(); + assert_eq!(found.len(), 1); + let output = &found[0]; + assert_eq!(output.outpoint(), OutPoint::new(tx.txid(), 0)); + assert_eq!(output.script_pubkey(), outputs[0].script_pubkey()); + assert_eq!(output.label(), None); + + // the receiver knows what it received even if the transaction is confidential + assert!(matches!(output.txout().value, Value::Confidential(_))); + assert!(matches!(output.txout().asset, Asset::Confidential(_))); + let unblinded = output.unblinded().unwrap(); + assert_eq!(unblinded.value, SENT); + assert_eq!(unblinded.asset, asset); + + // and it can spend it + let secret_key = output + .spending_secret_key(&keys.spend_secret_key()) + .unwrap(); + let (x_only, _) = secret_key.x_only_public_key(&EC); + assert_eq!(&output.script_pubkey().as_bytes()[2..], &x_only.serialize()); + + // scanning the same transaction again does not add the output twice + assert!(wollet + .scan_transaction(&tx, &[sender.script_pubkey.clone()]) + .unwrap() + .is_empty()); + assert_eq!(wollet.outputs().count(), 1); + + // the received output can be tracked by a watch only wallet + let descriptor = wollet.wollet_descriptor().unwrap(); + let script = descriptor.script_pubkey(Chain::External, 0).unwrap(); + assert_eq!(&script, output.script_pubkey()); + let address = descriptor.address(0, network.address_params()).unwrap(); + assert_eq!( + address.blinding_pubkey.unwrap(), + PublicKey::from_secret_key(&EC, &output.blinding_key()) + ); + // the descriptor round trips, it is what a wallet persists + let parsed: WolletDescriptor = descriptor.to_string().parse().unwrap(); + assert_eq!(parsed.to_string(), descriptor.to_string()); + + // and it can be used as an input of a transaction + let utxos = wollet.external_utxos(); + assert_eq!(utxos.len(), 1); + assert_eq!(utxos[0].unblinded.value, SENT); +} + +/// Another wallet does not see the payment, not even when it is the one scanning +#[test] +fn other_wallet_does_not_receive() { + let (network, asset, sender, _, wollet) = setup(); + let other = SilentPaymentKeys::from_mnemonic(OTHER_MNEMONIC, network, 0).unwrap(); + let mut other = SilentPaymentWollet::from_keys(network, &other); + + let address = wollet.address().to_string().parse().unwrap(); + let inputs = vec![(sender.input(), Some(sender.secret_key))]; + let outputs = derive_outputs(&inputs, &[address]).unwrap(); + let tx = sender.pay( + asset, + outputs[0].script_pubkey().clone(), + outputs[0].blinding_public_key(), + ); + + let found = other + .scan_transaction(&tx, &[sender.script_pubkey.clone()]) + .unwrap(); + assert!(found.is_empty()); + assert!(other.is_empty()); +} + +/// Two payments to the same address are two unrelated outputs +#[test] +fn two_payments_are_unlinkable() { + let (_, asset, sender, _, mut wollet) = setup(); + let address: lwk_wollet::SilentPaymentAddress = wollet.address().to_string().parse().unwrap(); + + let first = { + let inputs = vec![(sender.input(), Some(sender.secret_key))]; + derive_outputs(&inputs, &[address.clone()]).unwrap() + }; + + // the same sender pays again, from a different input + let mut other_sender = Sender::new(asset); + other_sender.outpoint = OutPoint::new(Txid::from_slice(&[9u8; 32]).unwrap(), 1); + let second = { + let inputs = vec![(other_sender.input(), Some(other_sender.secret_key))]; + derive_outputs(&inputs, &[address]).unwrap() + }; + + assert_ne!(first[0].script_pubkey(), second[0].script_pubkey()); + assert_ne!( + first[0].blinding_public_key(), + second[0].blinding_public_key() + ); + + for (sender, outputs) in [(&sender, &first), (&other_sender, &second)] { + let tx = sender.pay( + asset, + outputs[0].script_pubkey().clone(), + outputs[0].blinding_public_key(), + ); + let found = wollet + .scan_transaction(&tx, &[sender.script_pubkey.clone()]) + .unwrap(); + assert_eq!(found.len(), 1); + } + assert_eq!(wollet.outputs().count(), 2); +} + +/// Labels tell apart payments made to different addresses of the same wallet +#[test] +fn receive_on_labelled_address() { + let (network, asset, sender, keys, mut wollet) = setup(); + wollet.add_label(7).unwrap(); + + let address = wollet.labelled_address(7).unwrap(); + assert_ne!( + address.spend_public_key(), + wollet.address().spend_public_key() + ); + assert_eq!( + address.scan_public_key(), + wollet.address().scan_public_key() + ); + + let inputs = vec![(sender.input(), Some(sender.secret_key))]; + let outputs = derive_outputs(&inputs, &[address]).unwrap(); + let tx = sender.pay( + asset, + outputs[0].script_pubkey().clone(), + outputs[0].blinding_public_key(), + ); + + let found = wollet + .scan_transaction(&tx, &[sender.script_pubkey.clone()]) + .unwrap(); + assert_eq!(found.len(), 1); + assert_eq!(found[0].label(), Some(7)); + assert_eq!(found[0].unblinded().unwrap().value, SENT); + + // a wallet that did not add the label does not see the payment + let mut without_label = + SilentPaymentWollet::new(network, keys.scan_secret_key(), keys.spend_public_key()); + assert!(without_label + .scan_transaction(&tx, &[sender.script_pubkey.clone()]) + .unwrap() + .is_empty()); +} + +/// A light client gets the tweak data from an index server instead of the spent outputs +#[test] +fn scan_with_tweak_data_from_index_server() { + let (_, asset, sender, _, mut wollet) = setup(); + let address = wollet.address().to_string().parse().unwrap(); + let inputs = vec![(sender.input(), Some(sender.secret_key))]; + let outputs = derive_outputs(&inputs, &[address]).unwrap(); + let tx = sender.pay( + asset, + outputs[0].script_pubkey().clone(), + outputs[0].blinding_public_key(), + ); + + // what the server computes and serves for this transaction + let prevouts = [sender.script_pubkey.clone()]; + let inputs = transaction_inputs(&tx, &prevouts).unwrap(); + let tweak_data = lwk_wollet::silent_payments::tweak_data(&inputs) + .unwrap() + .unwrap(); + + // the client matches the scripts it can derive against the block filter, then scans the + // transactions that matched + let candidates = wollet + .scanner() + .candidate_script_pubkeys(&tweak_data) + .unwrap(); + assert!(tx + .output + .iter() + .any(|txout| candidates.contains(&txout.script_pubkey))); + + let found = wollet + .scan_transaction_with_tweak_data(&tx, &tweak_data) + .unwrap(); + assert_eq!(found.len(), 1); + assert_eq!(found[0].unblinded().unwrap().value, SENT); +} + +/// A transaction with no eligible input cannot pay to a silent payment address +#[test] +fn no_eligible_inputs() { + let (_, asset, sender, _, mut wollet) = setup(); + let address = wollet.address().to_string().parse().unwrap(); + + // a peg-in input spends coins on the bitcoin chain, it does not contribute its key + let pegin = SilentPaymentInput::other(sender.outpoint, Script::new()); + assert!(derive_outputs(&[(pegin, None)], &[address]).is_err()); + + let inputs = vec![(sender.input(), Some(sender.secret_key))]; + let outputs = derive_outputs(&inputs, &[wollet.address()]).unwrap(); + let mut tx = sender.pay( + asset, + outputs[0].script_pubkey().clone(), + outputs[0].blinding_public_key(), + ); + tx.input[0].is_pegin = true; + assert!(wollet + .scan_transaction(&tx, &[sender.script_pubkey.clone()]) + .unwrap() + .is_empty()); + + // the scripts of the spent outputs must match the inputs + tx.input[0].is_pegin = false; + assert!(wollet.scan_transaction(&tx, &[]).is_err()); +} + +/// The transaction is a valid Liquid transaction, not just a structure the scanner accepts +#[test] +fn transaction_is_well_formed() { + let (_, asset, sender, _, wollet) = setup(); + let address = wollet.address().to_string().parse().unwrap(); + let inputs = vec![(sender.input(), Some(sender.secret_key))]; + let outputs = derive_outputs(&inputs, &[address]).unwrap(); + let tx = sender.pay( + asset, + outputs[0].script_pubkey().clone(), + outputs[0].blinding_public_key(), + ); + + let bytes = serialize(&tx); + let decoded: Transaction = elements::encode::deserialize(&bytes).unwrap(); + assert_eq!(decoded.txid(), tx.txid()); + assert!(tx.output[0].script_pubkey.is_v1_p2tr()); + assert!(tx.output[1].is_fee()); + assert!(tx.output[0].witness.rangeproof.is_some()); + assert!(tx.output[0].witness.surjection_proof.is_some()); + + // the output pays to the confidential address the sender could have shown to the user + let address = outputs[0].address(&AddressParams::ELEMENTS); + assert!(address.is_blinded()); + assert_eq!(address.script_pubkey(), tx.output[0].script_pubkey); + assert_eq!(Address::from_str(&address.to_string()).unwrap(), address); +} + +/// The test vectors published in the ELIP, any change here is a change to the specification +#[test] +fn elip_test_vectors() { + let cases = [ + ( + Network::Liquid, + "e4284c50d48a373098d44638e3c9d6f6eca770aaa54562eb8cdc0cff8cd3b550", + "ca29645fc7167f2810c909fa52bcd177b82aa3bfbe999ef579a6607926dc7f93", + "lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcqc9sw44xujvc7ejg4w5lt4zxpvc3fk446qdcrrmsgg9cnax8ujj7spnvlwq", + "lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcqmujrcapnql6np3dpdjwswdwdcs0h3rs637agjsaeac6thmlcfpyy2eg0eh", + "lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcq3789yp3lv5nvyyvhucjttyanypmzdg7qpadykuz7xd9zenvh6dwylcw0sj", + ), + ( + Network::TestnetLiquid, + "38658693c017c46fd6b8bb94b8766c123cd5baf6026338305b6f59f82b36f9c0", + "9fd37137e760930c7208fa905e991c78c522689d237a220b2820c3ddb4c745a8", + "tlqsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq5rxzzunfck6d45va2jcqxk429agt3e4klf3vzmcgp3zqthryhhqgu2k23l", + "tlqsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq4y0sngs4cjaw6cxuy7p6rlslx6h54lmppe9kx4r2ylf63tkem26qqt6u7t", + "tlqsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzqknp3yhxtxhcvkczk4vfvdx0z8pfff3n78nelgppy4l2sss3famcgq974m5", + ), + ( + Network::default_regtest(), + "38658693c017c46fd6b8bb94b8766c123cd5baf6026338305b6f59f82b36f9c0", + "9fd37137e760930c7208fa905e991c78c522689d237a220b2820c3ddb4c745a8", + "elsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq5rxzzunfck6d45va2jcqxk429agt3e4klf3vzmcgp3zqthryhhqgd4lgkm", + "elsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq4y0sngs4cjaw6cxuy7p6rlslx6h54lmppe9kx4r2ylf63tkem26q35n7e0", + "elsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzqknp3yhxtxhcvkczk4vfvdx0z8pfff3n78nelgppy4l2sss3famcg36hhus", + ), + ]; + + for (network, scan, spend, address, change, label_1) in cases { + let keys = SilentPaymentKeys::from_mnemonic(MNEMONIC, network, 0).unwrap(); + assert_eq!(keys.scan_secret_key().display_secret().to_string(), scan); + assert_eq!(keys.spend_secret_key().display_secret().to_string(), spend); + assert_eq!(keys.address(network).to_string(), address); + assert_eq!( + keys.labelled_address(network, 0).unwrap().to_string(), + change + ); + assert_eq!( + keys.labelled_address(network, 1).unwrap().to_string(), + label_1 + ); + } + + // a payment on Liquid, from a P2WPKH input to the address above + let network = Network::Liquid; + let keys = SilentPaymentKeys::from_mnemonic(MNEMONIC, network, 0).unwrap(); + let secret_key = SecretKey::from_slice(&[11u8; 32]).unwrap(); + let public_key = PublicKey::from_secret_key(&EC, &secret_key); + let prevout = Address::p2wpkh( + &BitcoinPublicKey::new(public_key), + None, + &AddressParams::LIQUID, + ); + let outpoint = OutPoint::new( + Txid::from_str("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16").unwrap(), + 0, + ); + assert_eq!( + public_key.to_string(), + "02552c630b64b54bf50210c9e253d38bd4949c72e22873500f6285c2bede312a84" + ); + assert_eq!( + format!("{:x}", prevout.script_pubkey()), + "0014db3f00d429f2715383cc594258ec11d6de526697" + ); + + let input = + SilentPaymentInput::spending(outpoint, prevout.script_pubkey(), public_key).unwrap(); + let tweak_data = lwk_wollet::silent_payments::tweak_data(&[input.clone()]) + .unwrap() + .unwrap(); + assert_eq!( + tweak_data.to_string(), + "03398173f560782d934ddf4f5a291c47fd0866d6e26a97a7407b810e1873e34777" + ); + + let outputs = derive_outputs(&[(input, Some(secret_key))], &[keys.address(network)]).unwrap(); + assert_eq!( + format!("{:x}", outputs[0].script_pubkey()), + "512092a9d712661ac4c1ebd5e3953a5af8a60037fb69e7c559ecacbce41a050262d7" + ); + assert_eq!( + outputs[0].blinding_public_key().to_string(), + "0368c38c6542751c6c8778c31f60043d3dd5efa3dd03ccc1dff7b7c4ab6ae3afb8" + ); + assert_eq!( + outputs[0].address(&AddressParams::LIQUID).to_string(), + "lq1pqd5v8rr9gf63cmy80rp37cqy857atmarm5pueswl77muf2m2uwhm3y4f6ufxvxkyc84atcu48fd03fsqxlakne79t8k2e08yrgzsyckhf2sstv683ytv" + ); + + // and what the receiver derives when it finds the output + let sender = Sender { + secret_key, + outpoint, + script_pubkey: prevout.script_pubkey(), + secrets: TxOutSecrets::new( + *network.policy_asset(), + AssetBlindingFactor::zero(), + FUNDED, + ValueBlindingFactor::zero(), + ), + }; + let tx = sender.pay( + *network.policy_asset(), + outputs[0].script_pubkey().clone(), + outputs[0].blinding_public_key(), + ); + let mut wollet = SilentPaymentWollet::from_keys(network, &keys); + let found = wollet + .scan_transaction(&tx, &[prevout.script_pubkey()]) + .unwrap(); + assert_eq!(found.len(), 1); + assert_eq!( + found[0].tweak().display_secret().to_string(), + "a4c41218034595e818fce768dded0e52409abfe23501fb94e6787c32debad2bd" + ); + assert_eq!( + found[0].blinding_key().display_secret().to_string(), + "f01c7948615e9ead2e612cf325857a605346bdd240896263ec888d2d4b7d1e27" + ); + assert_eq!( + found[0] + .spending_secret_key(&keys.spend_secret_key()) + .unwrap() + .display_secret() + .to_string(), + "6eed7677ca5c151029c5f16330a9dfcb3e1686bb4452fa4ea04c7e1f3561110f" + ); + assert_eq!(found[0].unblinded().unwrap().value, SENT); +} From 59e410cd5f4c680b70096eed1d4450ff45e6926b Mon Sep 17 00:00:00 2001 From: soloking1412 Date: Thu, 23 Jul 2026 00:52:20 +0530 Subject: [PATCH 2/5] docs: link silent payments reference implementation --- docs/elip-silent-payments.mediawiki | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/elip-silent-payments.mediawiki b/docs/elip-silent-payments.mediawiki index f572393c1..365c6d0d3 100644 --- a/docs/elip-silent-payments.mediawiki +++ b/docs/elip-silent-payments.mediawiki @@ -519,8 +519,10 @@ Expected: ==Reference implementation== -[https://github.com/Blockstream/lwk Liquid Wallet Kit], in the silent_payments module of the lwk_wollet crate. -It implements the address encoding, the key derivation, the input eligibility rules, the sender and receiver derivations and the blinding key derivation, and it is checked against both the BIP-352 test vectors and the vectors of this document. +[https://github.com/Blockstream/lwk Liquid Wallet Kit], in the silent_payments module of the lwk_wollet crate, proposed in [https://github.com/Blockstream/lwk/pull/170 pull request 170]. + +It implements the address encoding, the key derivation, the input eligibility rules, the sender and receiver derivations and the blinding key derivation. +It is checked against the [https://github.com/bitcoin/bips/blob/master/bip-0352/send_and_receive_test_vectors.json BIP-352 test vectors], sending and receiving, against the test vectors of this document, and against transactions built, blinded and signed as a wallet would build them, which is what covers the blinding. ==Footnotes== From cf16666b7ddeb6f616d6d3fdbb8eed2fc142a4b4 Mon Sep 17 00:00:00 2001 From: soloking1412 Date: Mon, 27 Jul 2026 17:55:09 +0530 Subject: [PATCH 3/5] wollet: align silent payments blinding tag with ELIP draft --- docs/elip-silent-payments.mediawiki | 8 ++++---- lwk_wollet/src/silent_payments/hashes.rs | 6 +++--- lwk_wollet/src/silent_payments/mod.rs | 2 +- lwk_wollet/tests/silent_payments.rs | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/elip-silent-payments.mediawiki b/docs/elip-silent-payments.mediawiki index 365c6d0d3..f90acfba7 100644 --- a/docs/elip-silent-payments.mediawiki +++ b/docs/elip-silent-payments.mediawiki @@ -251,7 +251,7 @@ This is the part that Elements adds. The output MUST be blinded to the public key Ck = ck·G, where
-  c_k = hash_Silent-Payment-Blinding-Key/1.0(ser_P(ecdh_shared_secret) || ser_32(k))
+  c_k = hash_LiquidSilentPayments/Blind(ser_P(ecdh_shared_secret) || ser_32(k))
 
with the same k used to derive Pk. @@ -512,9 +512,9 @@ Expected: * tweak data, input_hash · A: 03398173f560782d934ddf4f5a291c47fd0866d6e26a97a7407b810e1873e34777 * output tweak, t0: a4c41218034595e818fce768dded0e52409abfe23501fb94e6787c32debad2bd * output scriptPubKey: 512092a9d712661ac4c1ebd5e3953a5af8a60037fb69e7c559ecacbce41a050262d7 -* blinding secret key, c0: f01c7948615e9ead2e612cf325857a605346bdd240896263ec888d2d4b7d1e27 -* blinding public key, C0: 0368c38c6542751c6c8778c31f60043d3dd5efa3dd03ccc1dff7b7c4ab6ae3afb8 -* confidential address of the output: lq1pqd5v8rr9gf63cmy80rp37cqy857atmarm5pueswl77muf2m2uwhm3y4f6ufxvxkyc84atcu48fd03fsqxlakne79t8k2e08yrgzsyckhf2sstv683ytv +* blinding secret key, c0: 807c22b1cb486e8de84f7f2c7a97c72559ef0f5bcd0033eb071c17a63762146e +* blinding public key, C0: 02bd12b91875000fdbff57302ac8f22ce20010061ef626899c569c6059098d3af3 +* confidential address of the output: lq1pq2739wgcw5qqlkll2ucz4j8j9n3qqyqxrmmzdzvu26wxqkgf35a08y4f6ufxvxkyc84atcu48fd03fsqxlakne79t8k2e08yrgzsyckhf736l4spplpl * secret key spending the output, bspend + t0: 6eed7677ca5c151029c5f16330a9dfcb3e1686bb4452fa4ea04c7e1f3561110f ==Reference implementation== diff --git a/lwk_wollet/src/silent_payments/hashes.rs b/lwk_wollet/src/silent_payments/hashes.rs index ca35079ad..f015574ed 100644 --- a/lwk_wollet/src/silent_payments/hashes.rs +++ b/lwk_wollet/src/silent_payments/hashes.rs @@ -31,7 +31,7 @@ sha256t_hash_newtype! { sha256t_hash_newtype! { /// The tag of the [`BlindingHash`] - pub struct BlindingTag = hash_str("Silent-Payment-Blinding-Key/1.0"); + pub struct BlindingTag = hash_str("LiquidSilentPayments/Blind"); /// Derives the blinding key of a silent payment output, this is the Liquid specific part /// of the protocol: the sender blinds the output to a key that the receiver can compute @@ -71,7 +71,7 @@ impl SharedSecretHash { } impl BlindingHash { - /// `blinding_key = hash_Silent-Payment-Blinding-Key/1.0(ser_P(ecdh_shared_secret) || ser32(k))` + /// `blinding_key = hash_LiquidSilentPayments/Blind(ser_P(ecdh_shared_secret) || ser32(k))` pub fn compute(shared_secret: &[u8; 33], k: u32) -> Self { let mut engine = Self::engine(); engine.input(shared_secret); @@ -109,7 +109,7 @@ mod tests { ); assert_eq!( BlindingTag::engine().midstate().to_byte_array(), - midstate("Silent-Payment-Blinding-Key/1.0") + midstate("LiquidSilentPayments/Blind") ); } } diff --git a/lwk_wollet/src/silent_payments/mod.rs b/lwk_wollet/src/silent_payments/mod.rs index 755b4a996..00fc38d1b 100644 --- a/lwk_wollet/src/silent_payments/mod.rs +++ b/lwk_wollet/src/silent_payments/mod.rs @@ -11,7 +11,7 @@ //! The key derivation is unchanged, what Liquid adds is blinding. Outputs are confidential, so //! the sender must blind them to a key the receiver can compute back: that key is derived from //! the same shared secret used for the output key, with the tag -//! `Silent-Payment-Blinding-Key/1.0`. +//! `LiquidSilentPayments/Blind`. //! //! Addresses use the same encoding of BIP352 with a different human readable part, so that an //! address of the wrong chain cannot be paid by mistake: `lqsp` for Liquid, `tlqsp` for Liquid diff --git a/lwk_wollet/tests/silent_payments.rs b/lwk_wollet/tests/silent_payments.rs index 5b13feec1..108b337d9 100644 --- a/lwk_wollet/tests/silent_payments.rs +++ b/lwk_wollet/tests/silent_payments.rs @@ -477,11 +477,11 @@ fn elip_test_vectors() { ); assert_eq!( outputs[0].blinding_public_key().to_string(), - "0368c38c6542751c6c8778c31f60043d3dd5efa3dd03ccc1dff7b7c4ab6ae3afb8" + "02bd12b91875000fdbff57302ac8f22ce20010061ef626899c569c6059098d3af3" ); assert_eq!( outputs[0].address(&AddressParams::LIQUID).to_string(), - "lq1pqd5v8rr9gf63cmy80rp37cqy857atmarm5pueswl77muf2m2uwhm3y4f6ufxvxkyc84atcu48fd03fsqxlakne79t8k2e08yrgzsyckhf2sstv683ytv" + "lq1pq2739wgcw5qqlkll2ucz4j8j9n3qqyqxrmmzdzvu26wxqkgf35a08y4f6ufxvxkyc84atcu48fd03fsqxlakne79t8k2e08yrgzsyckhf736l4spplpl" ); // and what the receiver derives when it finds the output @@ -512,7 +512,7 @@ fn elip_test_vectors() { ); assert_eq!( found[0].blinding_key().display_secret().to_string(), - "f01c7948615e9ead2e612cf325857a605346bdd240896263ec888d2d4b7d1e27" + "807c22b1cb486e8de84f7f2c7a97c72559ef0f5bcd0033eb071c17a63762146e" ); assert_eq!( found[0] From ae4c0fa30b96c2fee182c3b8ab0c5b9dfe68ab7f Mon Sep 17 00:00:00 2001 From: soloking1412 Date: Mon, 27 Jul 2026 18:26:57 +0530 Subject: [PATCH 4/5] wollet: add silent payments tweak server client --- lwk_wollet/Cargo.toml | 2 + lwk_wollet/src/silent_payments/client.rs | 263 +++++++++++++++++++++++ lwk_wollet/src/silent_payments/mod.rs | 6 + lwk_wollet/src/silent_payments/wollet.rs | 49 ++++- lwk_wollet/tests/silent_payments.rs | 111 +++++++++- 5 files changed, 427 insertions(+), 4 deletions(-) create mode 100644 lwk_wollet/src/silent_payments/client.rs diff --git a/lwk_wollet/Cargo.toml b/lwk_wollet/Cargo.toml index 7aa9ca093..59edb8783 100644 --- a/lwk_wollet/Cargo.toml +++ b/lwk_wollet/Cargo.toml @@ -117,6 +117,7 @@ default = [ "amp0", "prices", "registry", + "tweak_server", ] serial = ["lwk_jade/serial"] # this is a dev-dep feature esplora = ["reqwest", "age"] @@ -141,6 +142,7 @@ amp0 = [ "rmp-serde", ] prices = ["reqwest"] +tweak_server = ["reqwest"] registry = ["reqwest"] [[test]] diff --git a/lwk_wollet/src/silent_payments/client.rs b/lwk_wollet/src/silent_payments/client.rs new file mode 100644 index 000000000..2abfbfed2 --- /dev/null +++ b/lwk_wollet/src/silent_payments/client.rs @@ -0,0 +1,263 @@ +//! Client for a silent payments index server following the "Tweak Server" model of the +//! [BIP352 index server specification]. +//! +//! The wallet asks the server for the tweak data of a block, derives locally the scripts it +//! would be paid to, and downloads a block only when a compact block filter says one of those +//! scripts is in it. No key is ever shared with the server, and the server does not learn +//! which transactions the wallet is interested in. +//! +//! [BIP352 index server specification]: https://github.com/silent-payments/BIP0352-index-server-specification + +use elements::bitcoin::bip158::BlockFilter; +use elements::hashes::Hash; +use elements::hex::FromHex; +use elements::{BlockHash, Script}; +use serde::Deserialize; + +use crate::cache::Height; +use crate::secp256k1::PublicKey; +use crate::Error; + +/// Information about a tweak server instance, as returned by `GET /getinfo` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ServerInfo { + /// The version of the server + pub version: String, + + /// The network the server is indexing + pub network: String, + + /// The height of the last block the server has indexed + pub block_height: Height, +} + +/// The tweak data of a block, as returned by `GET /tweaks/:blockheight` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BlockTweaks { + /// The height of the block + pub height: Height, + + /// The hash of the block, needed to match against its filter + pub hash: BlockHash, + + /// One tweak per silent payment eligible transaction of the block + pub tweaks: Vec, +} + +#[derive(Deserialize)] +struct ServerInfoResponse { + version: String, + network: String, + block_height: Height, +} + +#[derive(Deserialize)] +struct TweaksResponse { + height: Height, + hash: String, + tweaks: Vec, +} + +#[derive(Deserialize)] +struct FilterResponse { + filter: String, +} + +impl TryFrom for BlockTweaks { + type Error = Error; + + fn try_from(response: TweaksResponse) -> Result { + let tweaks = response + .tweaks + .iter() + .map(|tweak| { + let bytes = Vec::::from_hex(tweak)?; + Ok(PublicKey::from_slice(&bytes)?) + }) + .collect::, Error>>()?; + Ok(BlockTweaks { + height: response.height, + hash: response.hash.parse()?, + tweaks, + }) + } +} + +/// A client for a silent payments tweak server, see the [module documentation](self) +#[derive(Debug, Clone)] +pub struct TweakServerClient { + client: reqwest::Client, + base_url: String, +} + +impl TweakServerClient { + /// Create a client for the server at `base_url` + pub fn new(base_url: &str) -> Self { + Self { + client: reqwest::Client::new(), + base_url: base_url.trim_end_matches('/').to_string(), + } + } + + async fn get(&self, path: &str) -> Result { + let url = format!("{}{path}", self.base_url); + let response = self.client.get(&url).send().await?; + let status = response.status(); + if !status.is_success() { + return Err(Error::EsploraHttpError { + url, + status: status.as_u16(), + body: response.text().await.ok(), + }); + } + Ok(response.json().await?) + } + + /// Basic information about the server, `GET /getinfo` + pub async fn get_info(&self) -> Result { + let response: ServerInfoResponse = self.get("/getinfo").await?; + Ok(ServerInfo { + version: response.version, + network: response.network, + block_height: response.block_height, + }) + } + + /// The tweak data of the block at `height`, `GET /tweaks/:blockheight` + pub async fn tweaks(&self, height: Height) -> Result { + let response: TweaksResponse = self.get(&format!("/tweaks/{height}")).await?; + response.try_into() + } + + /// The BIP158 basic filter of the block at `height`, `GET /filters/:blockheight` + /// + /// A wallet may obtain the same filter from any other source, for instance from an + /// Elements node run with `-blockfilterindex=basic`. + pub async fn filter(&self, height: Height) -> Result { + let response: FilterResponse = self.get(&format!("/filters/{height}")).await?; + Ok(BlockFilter::new(&Vec::::from_hex(&response.filter)?)) + } +} + +/// Whether any of `scripts` is in the block filter. +/// +/// A false positive only costs a block download, a false negative would lose a payment, so a +/// filter that cannot be parsed is treated as a match. +pub fn filter_matches(filter: &BlockFilter, block_hash: &BlockHash, scripts: &[Script]) -> bool { + if scripts.is_empty() { + return false; + } + // BIP158 keys the filter on the block hash bytes, which are the same on Elements + let block_hash = elements::bitcoin::BlockHash::from_byte_array(block_hash.to_byte_array()); + filter + .match_any(&block_hash, scripts.iter().map(|s| s.as_bytes())) + .unwrap_or(true) +} + +/// Blocking version of [`TweakServerClient`] +#[cfg(not(target_arch = "wasm32"))] +#[derive(Debug)] +pub struct BlockingTweakServerClient { + rt: tokio::runtime::Runtime, + client: TweakServerClient, +} + +#[cfg(not(target_arch = "wasm32"))] +impl BlockingTweakServerClient { + /// Create a client for the server at `base_url` + pub fn new(base_url: &str) -> Result { + Ok(Self { + rt: tokio::runtime::Runtime::new()?, + client: TweakServerClient::new(base_url), + }) + } + + /// See [`TweakServerClient::get_info`] + pub fn get_info(&self) -> Result { + self.rt.block_on(self.client.get_info()) + } + + /// See [`TweakServerClient::tweaks`] + pub fn tweaks(&self, height: Height) -> Result { + self.rt.block_on(self.client.tweaks(height)) + } + + /// See [`TweakServerClient::filter`] + pub fn filter(&self, height: Height) -> Result { + self.rt.block_on(self.client.filter(height)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const TWEAK: &str = "03398173f560782d934ddf4f5a291c47fd0866d6e26a97a7407b810e1873e34777"; + const HASH: &str = "1aea43f7205bd02c03b081f3c6de4604756bc1a37ae2ca6e34b1936137756870"; + + fn client(status: &'static str, body: &'static str) -> TweakServerClient { + let url = lwk_test_util::serve_http_response(status, "application/json", body, false); + TweakServerClient::new(&url) + } + + #[tokio::test] + async fn get_info() { + let body = + r#"{"version":"1.0.0","network":"liquidv1","block_height":3984398,"dust_limit":0}"#; + let info = client("200 OK", body).get_info().await.unwrap(); + assert_eq!(info.version, "1.0.0"); + assert_eq!(info.network, "liquidv1"); + assert_eq!(info.block_height, 3_984_398); + } + + #[tokio::test] + async fn tweaks() { + let body = r#"{ + "height": 100, + "hash": "1aea43f7205bd02c03b081f3c6de4604756bc1a37ae2ca6e34b1936137756870", + "dust_limit": 0, + "filter_spent": 0, + "tweaks": ["03398173f560782d934ddf4f5a291c47fd0866d6e26a97a7407b810e1873e34777"] + }"#; + let tweaks = client("200 OK", body).tweaks(100).await.unwrap(); + assert_eq!(tweaks.height, 100); + assert_eq!(tweaks.hash.to_string(), HASH); + assert_eq!(tweaks.tweaks.len(), 1); + assert_eq!(tweaks.tweaks[0].to_string(), TWEAK); + } + + /// Most blocks contain no silent payment, this must not be an error + #[tokio::test] + async fn block_without_tweaks() { + let body = r#"{ + "height": 100, + "hash": "1aea43f7205bd02c03b081f3c6de4604756bc1a37ae2ca6e34b1936137756870", + "tweaks": [] + }"#; + let tweaks = client("200 OK", body).tweaks(100).await.unwrap(); + assert!(tweaks.tweaks.is_empty()); + } + + /// The error body of the index server is JSON, it must not be parsed as a success + #[tokio::test] + async fn http_error() { + let body = r#"{"error":{"code":400,"message":"Invalid block height"}}"#; + let err = client("400 Bad Request", body).tweaks(1).await.unwrap_err(); + assert!( + matches!(err, Error::EsploraHttpError { status: 400, .. }), + "{err:?}" + ); + } + + /// A tweak that is not a public key would make the wallet miss every payment in the block, + /// so it must be rejected rather than skipped + #[tokio::test] + async fn malformed_tweak() { + let body = r#"{ + "height": 1, + "hash": "1aea43f7205bd02c03b081f3c6de4604756bc1a37ae2ca6e34b1936137756870", + "tweaks": ["00"] + }"#; + assert!(client("200 OK", body).tweaks(1).await.is_err()); + } +} diff --git a/lwk_wollet/src/silent_payments/mod.rs b/lwk_wollet/src/silent_payments/mod.rs index 00fc38d1b..60e93fd71 100644 --- a/lwk_wollet/src/silent_payments/mod.rs +++ b/lwk_wollet/src/silent_payments/mod.rs @@ -58,6 +58,8 @@ use elements::OutPoint; mod address; +#[cfg(feature = "tweak_server")] +mod client; mod hashes; mod inputs; mod keys; @@ -69,6 +71,10 @@ mod wollet; mod test_vectors; pub use address::{SilentPaymentAddress, SilentPaymentNetwork}; +#[cfg(all(feature = "tweak_server", not(target_arch = "wasm32")))] +pub use client::BlockingTweakServerClient; +#[cfg(feature = "tweak_server")] +pub use client::{filter_matches, BlockTweaks, ServerInfo, TweakServerClient}; pub use inputs::{transaction_inputs, tweak_data, tweak_data_from_tx, SilentPaymentInput}; pub use keys::SilentPaymentKeys; pub use scan::{SilentPaymentScanner, SilentPaymentTxOut, CHANGE_LABEL, K_MAX}; diff --git a/lwk_wollet/src/silent_payments/wollet.rs b/lwk_wollet/src/silent_payments/wollet.rs index 144fb590b..0fdc7cf05 100644 --- a/lwk_wollet/src/silent_payments/wollet.rs +++ b/lwk_wollet/src/silent_payments/wollet.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use elements::{OutPoint, Script, Transaction}; use lwk_common::Network; @@ -94,6 +94,53 @@ impl SilentPaymentWollet { Ok(self.insert(found)) } + /// The scripts a block with the given tweak data could pay this wallet to, one set per + /// tweak, to be matched against the block filter before downloading the block. + /// + /// See [`SilentPaymentScanner::candidate_script_pubkeys`]. + pub fn candidate_script_pubkeys( + &self, + tweaks: &[PublicKey], + ) -> Result, SilentPaymentError> { + let mut scripts = Vec::with_capacity(tweaks.len()); + for tweak in tweaks { + scripts.extend(self.scanner.candidate_script_pubkeys(tweak)?); + } + Ok(scripts) + } + + /// Scan a block for payments to this wallet, given the tweak data of its transactions. + /// + /// A tweak is matched to the transaction containing one of the scripts it can derive, so + /// `tweaks` does not have to be in any particular order, and transactions that no tweak + /// matches are skipped without deriving anything further. + /// + /// Returns the outputs discovered by this call, outputs already known are not returned. + pub fn scan_block( + &mut self, + tweaks: &[PublicKey], + txs: &[Transaction], + ) -> Result, SilentPaymentError> { + let mut candidates = HashMap::new(); + for tweak in tweaks { + for script in self.scanner.candidate_script_pubkeys(tweak)? { + candidates.insert(script, *tweak); + } + } + + let mut found = vec![]; + for tx in txs { + let tweak = tx + .output + .iter() + .find_map(|txout| candidates.get(&txout.script_pubkey)); + if let Some(tweak) = tweak { + found.extend(self.scanner.scan_transaction_with_tweak_data(tx, tweak)?); + } + } + Ok(self.insert(found)) + } + fn insert(&mut self, found: Vec) -> Vec { found .into_iter() diff --git a/lwk_wollet/tests/silent_payments.rs b/lwk_wollet/tests/silent_payments.rs index 108b337d9..53c31af1a 100644 --- a/lwk_wollet/tests/silent_payments.rs +++ b/lwk_wollet/tests/silent_payments.rs @@ -2,6 +2,7 @@ //! transaction paying to a silent payment address, the receiver scans it and can unblind and //! spend what it received. +use elements::bitcoin::bip158::{BlockFilter, GcsFilterWriter}; use elements::bitcoin::PublicKey as BitcoinPublicKey; use elements::confidential::{Asset, AssetBlindingFactor, Value, ValueBlindingFactor}; use elements::encode::serialize; @@ -9,11 +10,12 @@ use elements::hashes::Hash; use elements::secp256k1_zkp::{Message, PublicKey, SecretKey}; use elements::sighash::SighashCache; use elements::{ - Address, AddressParams, AssetId, EcdsaSighashType, LockTime, OutPoint, Script, Sequence, - Transaction, TxIn, TxInWitness, TxOut, TxOutSecrets, Txid, + Address, AddressParams, AssetId, BlockHash, EcdsaSighashType, LockTime, OutPoint, Script, + Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutSecrets, Txid, }; use lwk_wollet::silent_payments::{ - derive_outputs, transaction_inputs, SilentPaymentInput, SilentPaymentKeys, SilentPaymentWollet, + derive_outputs, filter_matches, transaction_inputs, tweak_data, SilentPaymentInput, + SilentPaymentKeys, SilentPaymentWollet, }; use lwk_wollet::{Chain, Network, WolletDescriptor, EC}; use std::str::FromStr; @@ -26,6 +28,10 @@ const FUNDED: u64 = 100_000; const SENT: u64 = 90_000; const FEE: u64 = 10_000; +/// The Golomb-Rice parameters of a BIP158 basic filter +const FILTER_M: u64 = 784_931; +const FILTER_P: u8 = 19; + /// The wallet of the sender: a single p2wpkh output it can spend struct Sender { secret_key: SecretKey, @@ -524,3 +530,102 @@ fn elip_test_vectors() { ); assert_eq!(found[0].unblinded().unwrap().value, SENT); } + +/// The BIP158 basic filter over the given scripts, as an Elements node builds it +fn block_filter(block_hash: &BlockHash, scripts: &[&Script]) -> BlockFilter { + let key = block_hash.to_byte_array(); + let k0 = u64::from_le_bytes(key[..8].try_into().unwrap()); + let k1 = u64::from_le_bytes(key[8..16].try_into().unwrap()); + + let mut content = Vec::new(); + let mut writer = GcsFilterWriter::new(&mut content, k0, k1, FILTER_M, FILTER_P); + for script in scripts { + writer.add_element(script.as_bytes()); + } + writer.finish().unwrap(); + BlockFilter::new(&content) +} + +/// The full "Tweak Server" receive flow: the wallet asks for the tweaks of a block, derives +/// the scripts it could be paid to, matches them against the block filter, and scans only on +/// a match. +#[test] +fn tweak_server_scan_flow() { + let network = Network::default_regtest(); + let (_, asset, sender, _, mut wollet) = setup(); + let inputs = vec![(sender.input(), Some(sender.secret_key))]; + + let outputs = + derive_outputs(&inputs, &[wollet.address().to_string().parse().unwrap()]).unwrap(); + let tx = sender.pay( + asset, + outputs[0].script_pubkey().clone(), + outputs[0].blinding_public_key(), + ); + + // a payment to somebody else, in the same block + let other_keys = SilentPaymentKeys::from_mnemonic(OTHER_MNEMONIC, network, 0).unwrap(); + let other_outputs = derive_outputs( + &inputs, + &[other_keys.address(network).to_string().parse().unwrap()], + ) + .unwrap(); + let other_tx = sender.pay( + asset, + other_outputs[0].script_pubkey().clone(), + other_outputs[0].blinding_public_key(), + ); + + let block = [tx.clone(), other_tx.clone()]; + let prevouts = [sender.script_pubkey.clone()]; + + // the tweak data the server serves for this block + let tweaks: Vec<_> = block + .iter() + .map(|tx| { + let inputs = transaction_inputs(tx, &prevouts).unwrap(); + tweak_data(&inputs).unwrap().unwrap() + }) + .collect(); + + // the client derives one candidate script per tweak and tests them against the filter + let candidates = wollet.candidate_script_pubkeys(&tweaks).unwrap(); + assert_eq!(candidates.len(), tweaks.len()); + + let block_hash = BlockHash::from_slice(&[3u8; 32]).unwrap(); + let scripts: Vec<_> = block + .iter() + .flat_map(|tx| tx.output.iter()) + .map(|txout| &txout.script_pubkey) + .filter(|script| !script.is_empty()) + .collect(); + assert!(filter_matches( + &block_filter(&block_hash, &scripts), + &block_hash, + &candidates + )); + + // a block without them must not match, otherwise every block gets downloaded + let unrelated = Script::from(vec![0x6a]); + assert!(!filter_matches( + &block_filter(&block_hash, &[&unrelated]), + &block_hash, + &candidates + )); + + // on a match the block is scanned, and only the payment to this wallet is found + let found = wollet.scan_block(&tweaks, &block).unwrap(); + assert_eq!(found.len(), 1); + assert_eq!(found[0].script_pubkey(), outputs[0].script_pubkey()); + assert_eq!(found[0].unblinded().unwrap().value, SENT); + + // rescanning the same block does not add the output twice + assert!(wollet.scan_block(&tweaks, &block).unwrap().is_empty()); + assert_eq!(wollet.outputs().count(), 1); + + // the other wallet finds its own payment in the same block, and only that one + let mut other = SilentPaymentWollet::from_keys(network, &other_keys); + let found = other.scan_block(&tweaks, &block).unwrap(); + assert_eq!(found.len(), 1); + assert_eq!(found[0].script_pubkey(), other_outputs[0].script_pubkey()); +} From 7ead806aa2b92421cccdd68d76576262772a24da Mon Sep 17 00:00:00 2001 From: soloking1412 Date: Mon, 27 Jul 2026 18:29:15 +0530 Subject: [PATCH 5/5] docs: drop silent payments ELIP draft --- docs/elip-silent-payments.mediawiki | 533 ---------------------------- 1 file changed, 533 deletions(-) delete mode 100644 docs/elip-silent-payments.mediawiki diff --git a/docs/elip-silent-payments.mediawiki b/docs/elip-silent-payments.mediawiki deleted file mode 100644 index f90acfba7..000000000 --- a/docs/elip-silent-payments.mediawiki +++ /dev/null @@ -1,533 +0,0 @@ -
-  ELIP: ?
-  Layer: Wallet
-  Title: Silent Payments for Elements
-  Author: Maheswaran Velmurugan 
-  Comments-Summary: No comments yet.
-  Comments-URI: https://github.com/ElementsProject/elips/wiki/Comments:ELIP-????
-  Status: Draft
-  Type: Standards Track
-  Created: 2026-07-23
-  License: BSD-3-Clause
-
- -==Introduction== - -===Abstract=== - -This document describes how [https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki BIP-352] silent payments can be used on Elements based chains, where transaction outputs are confidential. - -A silent payment address is a static address that can be published without giving up privacy. -The sender derives a fresh taproot output from the address and from the inputs it is spending, and only the receiver can recognize it. -Nothing is added to the transaction, so the receiver has to scan the chain. - -Key derivation is unchanged from BIP-352. -What Elements adds is blinding: an output is confidential, so the sender must blind it to a key that the receiver is able to compute back. -This document defines that key as a tagged hash of the same ECDH shared secret used to derive the output key, so that no data other than the transaction itself is needed to receive a payment. - -It also defines the address encoding, which is the BIP-352 encoding with an Elements specific human readable part, the rules that decide which inputs contribute to the shared secret on a chain that has peg-ins and issuances, and the client and server side of the "Tweak Server" model of the [https://github.com/silent-payments/BIP0352-index-server-specification BIP-352 index server specification], which is how a wallet is expected to scan. - -===Copyright=== - -This document is licensed under the 3-clause BSD license. - -===Motivation=== - -Receiving on Elements based chains today means either reusing an address or handing out a fresh one for every payment. - -Address reuse is bad for privacy: confidential transactions hide the amount and the asset, but not the scriptPubKey, so anybody can see that two payments went to the same script, and anybody who has been given that address can watch it. -This is not a corner case: it is what happens whenever an address is published, embedded in a donation page, or printed on an invoice that is paid more than once. - -Handing out a fresh address per payment avoids the problem but requires an interaction with the receiver at payment time, which is not always possible, and it puts the burden of gap limits and address recovery on the wallet. - -Silent payments remove the interaction: one static address, one output per payment, no on chain link between payments. -The cost is that the receiver has to scan, which is why this document also specifies how a wallet scans without downloading every block. - -Elements cannot use BIP-352 as is, because a BIP-352 output is a plain taproot output while an Elements output is normally confidential. -A confidential output is blinded to a public key chosen by the sender: unless the receiver can derive the corresponding secret key it cannot unblind the output, and an output it cannot unblind is an output it cannot spend -Knowing the secret key of an output is not enough to spend it on Elements: to build a valid transaction the wallet needs the asset and value blinding factors of the outputs it is spending, which are recovered by rewinding the range proof with the blinding secret key. -. -This document fills exactly that gap, and resolves the ambiguities that a chain with peg-ins, issuances and explicit fee outputs introduces in the BIP-352 rules. - -==Design== - -===Overview=== - -Three things are added to BIP-352, everything else is unchanged: - -# A '''blinding key derivation'''. The sender blinds the output to blinding_keyk·G, where blinding_keyk is a tagged hash of the ECDH shared secret and the output counter. The receiver derives the same key while scanning and unblinds the output. -# An '''address encoding''' with an Elements specific human readable part, so that a Bitcoin silent payment address cannot be paid on Liquid and vice versa. -# '''Input eligibility rules''' for the inputs that only exist on Elements: peg-ins never contribute their public key, and the outpoint of an input is serialized with the peg-in and issuance flags cleared. - -For receiving, this document specifies the "Tweak Server" model of the BIP-352 index server specification: the wallet asks a server for the per transaction tweak data of each block, derives locally the scripts it would be paid to, and only downloads a block when a compact block filter says that one of those scripts is in it. -The wallet never shares its keys with the server. - -===Drawbacks=== - -Deriving the blinding key from the shared secret means that whoever knows the scan secret key can unblind every payment received by that wallet, not just detect it. -On Bitcoin, sharing the scan key with a scanning service reveals which outputs belong to the wallet; on Elements it would also reveal every amount and asset. -This is the reason why this document specifies the Tweak Server model, in which the scan key never leaves the wallet, and why the "Remote Scanner" model of the index server specification is '''not''' recommended here. - -The sender can unblind the outputs it creates. This is inherent to confidential transactions: the sender picks the ephemeral key used to blind the output, so it already knows the amount and the asset of what it sends, whatever the blinding key derivation is. - -Scanning cost is proportional to the number of blocks since the wallet birthday, not to the number of payments received. A wallet restored from a backup that only contains the seed must scan from the taproot activation height. - -Labels have to be backed up, or payments received on a labelled address are not detected on recovery. This is the same drawback as BIP-352. - -==Specification== - -===Notation=== - -All the arithmetic is on the secp256k1 curve, with generator G and group order n. - -{| class="wikitable" -! Notation !! Meaning -|- -| serP(P) || the 33 byte compressed serialization of the point P -|- -| ser32(i) || the 4 byte big endian serialization of the integer i -|- -| ser256(x) || the 32 byte big endian serialization of the scalar x -|- -| hashtag(x) || the [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#design BIP-340 tagged hash] of x with tag tag -|} - -Inside a formula, || denotes concatenation. - -An operation that yields 0 or a value not smaller than n, or that yields the point at infinity, MUST be treated as a failure wherever it appears below. - -===Keys=== - -A silent payment wallet is defined by two key pairs: - -* the '''scan key''' bscan, Bscan = bscan·G, used to detect the payments; -* the '''spend key''' bspend, Bspend = bspend·G, used to spend them. - -A watch only wallet holds bscan and Bspend: it detects and unblinds the payments but cannot spend them. - -When the keys are derived from a BIP-32 seed, the derivation of BIP-352 is used, with the Elements coin type: - -
-  scan_key:  m / 352' / coin_type' / account' / 1' / 0
-  spend_key: m / 352' / coin_type' / account' / 0' / 0
-
- -where coin_type is 1776 on Liquid and 1 on any test network, as in [https://github.com/satoshilabs/slips/blob/master/slip-0044.md SLIP-44]. - -===Address encoding=== - -A silent payment address is encoded as in BIP-352: bech32m -Bech32m and not bech32, for every version, unlike segwit addresses. This is what BIP-352 does. -, with a version character followed by the payload, and a maximum length of 1023 characters. - -The payload of a version 0 address is exactly 66 bytes: - -
-  ser_P(B_scan) || ser_P(B_m)
-
- -where Bm is the spend public key, tweaked by a label if the address is labelled (see [[#Labels|Labels]]). - -The human readable part identifies the chain: - -{| class="wikitable" -! Chain !! HRP !! Example prefix -|- -| Liquid || lqsp || lqsp1q -|- -| Liquid testnet || tlqsp || tlqsp1q -|- -| Elements regtest || elsp || elsp1q -|} - -A wallet MUST reject an address whose human readable part is not the one of the chain it is operating on. - -A wallet MUST reject version 31 (l), which BIP-352 reserves to signal a backward incompatible change. -For a version between 1 and 30 a wallet MUST accept a payload of 66 bytes or more and use the first 66 bytes, ignoring the rest; for version 0 the payload MUST be exactly 66 bytes. - -===Transaction eligibility=== - -A transaction can pay to a silent payment address, and MUST be scanned by a receiver, if and only if all of the following hold: - -* it has at least one output that is a taproot output; -* it has at least one eligible input, as defined below, and the public keys of its eligible inputs do not sum to the point at infinity; -* none of its inputs spends an output whose script is a witness program of version greater than 1 -As in BIP-352: skipping the transactions that spend a script this version of the protocol does not understand leaves a clean upgrade path, since a wallet will never have to scan the same transaction once with the rules of this document and once with the rules of a future one. -. - -A sender MUST NOT select an input that spends a witness program of version greater than 1, and MUST use a sighash flag that commits to all the inputs: if the inputs change after the outputs have been derived, the receiver computes a different shared secret and the payment is lost. - -===Input eligibility=== - -An input is '''eligible''' if it contributes its public key to the shared secret. The rules of BIP-352 apply, with the peg-in rule added. - -An input is eligible if and only if the output it spends has one of the following scripts, and the corresponding public key is compressed or x-only: - -{| class="wikitable" -! Spent script !! Public key -|- -| P2TR || the output key, read from the spent scriptPubKey, lifted with even Y -|- -| P2WPKH || the last element of the witness -|- -| P2SH-P2WPKH || the last element of the witness, if the scriptSig is a single push of a P2WPKH redeem script -|- -| P2PKH || the 33 byte push in the scriptSig whose HASH160 is the one committed in the spent script -The scriptSig of a P2PKH input can be malleated by anybody, so the public key cannot simply be taken from the last push: it is found by scanning the scriptSig backwards for a push that hashes to the committed value. - -|} - -An input is '''not''' eligible if: - -* it is a '''peg-in''' input. The output it spends is on the Bitcoin chain, so a wallet scanning Elements cannot read the script that would tell how to extract the key. Its outpoint is still taken into account when looking for the smallest one. -* it spends a P2TR output through a script path whose control block reveals the BIP-341 NUMS point H = lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0) as internal key, in which case nobody knows the corresponding secret key. The annex, if present, is removed before looking at the control block. -* its public key is uncompressed. -* it spends anything else, in particular a P2WSH or a bare P2SH output. - -An input carrying an asset issuance or reissuance is eligible under the rules above: an issuance does not change how the input is spent. - -===Outpoint serialization=== - -BIP-352 commits to the smallest outpoint of the transaction. On Elements the vout field of an input carries, in its two most significant bits, the peg-in and issuance flags -See CTxIn in Elements: the flags are set when the transaction is serialized and are stripped when it is parsed, so a wallet that hashes the parsed outpoint and a wallet that hashes the raw bytes would compute different values. -. - -An outpoint MUST be serialized as: - -
-  txid (32 bytes, as in the transaction) || vout (4 bytes, little endian)
-
- -where vout is the index '''with the peg-in flag (230) and the issuance flag (231) cleared''', that is the value obtained after parsing the transaction. -The vout of a coinbase input, 0xffffffff, is not masked, as it carries no flags. - -outpointL is the lexicographically smallest of the serializations of '''all''' the outpoints of the transaction, including the ones of inputs that are not eligible. - -===Shared secret=== - -Let A = A1 + ... + Ai be the sum of the public keys of the eligible inputs of the transaction, and - -
-  input_hash = hash_BIP0352/Inputs(outpoint_L || ser_P(A))
-
- -The shared secret between the sender, which knows a = a1 + ... + ai, and the receiver, which knows bscan, is - -
-  ecdh_shared_secret = input_hash · a · B_scan       (sender)
-                     = input_hash · b_scan · A       (receiver)
-
- -A transaction with no eligible input, or whose eligible input keys sum to the point at infinity, cannot pay to a silent payment address. -An intermediate sum equal to zero is not a failure, only the final one is. - -The receiver does not need A and input_hash separately: input_hash · A, called the '''tweak data''' of the transaction, is enough, which is what makes the light client model of [[#Receiving|Receiving]] possible. - -===Output derivation=== - -For every output paid to the same Bscan in a transaction, with a counter k starting at 0: - -
-  t_k = hash_BIP0352/SharedSecret(ser_P(ecdh_shared_secret) || ser_32(k))
-  P_k = B_m + t_k·G
-
- -The scriptPubKey of the output is Pk encoded as a taproot output: - -
-  OP_1 OP_PUSHBYTES_32 
-
- -Pk is the output key itself: the BIP-341 tweak is '''not''' applied -This is what BIP-352 does, and it is why a received output cannot be described by an eltr() descriptor, see [[#Representation of a received output|Representation of a received output]]. -. - -A transaction MUST NOT contain more than Kmax = 2323 outputs paying to the same Bscan. - -===Blinding key derivation=== - -This is the part that Elements adds. - -The output MUST be blinded to the public key Ck = ck·G, where - -
-  c_k = hash_LiquidSilentPayments/Blind(ser_P(ecdh_shared_secret) || ser_32(k))
-
- -with the same k used to derive Pk. - -The sender blinds the output as usual, using Ck as the blinding public key of the receiver, which is the same as paying to the confidential address made of Ck and of the scriptPubKey above. -The receiver, once it has detected the output, derives ck and unblinds the output by rewinding its range proof, recovering the asset, the value and the blinding factors. - -A sender MUST blind the output. -A receiver MUST also detect an output that is not blinded -An explicit output is valid on Elements, and it is spendable by the receiver without any unblinding. Detecting it costs nothing, since detection only looks at the scriptPubKey, and not detecting it would make the funds unrecoverable. -, and SHOULD warn that the amount and the asset of that payment are public. - -===Labels=== - -Labels are unchanged from BIP-352. For an integer m ≥ 0: - -
-  label_tweak_m = hash_BIP0352/Label(ser_256(b_scan) || ser_32(m))
-  B_m           = B_spend + label_tweak_m·G
-
- -B0 is reserved for the change of the wallet and MUST NOT be published. - -A wallet detects a payment to a labelled address only if it knows the label, so the set of labels in use is part of the wallet backup. - -===Sending=== - -To pay a set of recipients: - -# Choose the inputs of the transaction. Every eligible input contributes its secret key ai; for a P2TR input whose public key has odd Y, ai is negated. -# Compute outpointL over all the inputs, then a, A, input_hash and, for each distinct Bscan among the recipients, ecdh_shared_secret. -# Group the recipients by Bscan. Within a group, derive one output per recipient entry with k = 0, 1, .... Paying the same address twice is allowed and produces two different outputs. -# Blind each output to Ck and finish the transaction as usual. - -The set of inputs MUST be final before the outputs are derived, because the output scripts commit to it. -A wallet that selects inputs to cover an amount therefore has to select the inputs first and derive the outputs after, and it MUST re-derive the outputs if the input set changes -This is the same constraint as in BIP-352 and the reason why [https://github.com/bitcoin/bips/blob/master/bip-0375.mediawiki BIP-375] adds silent payment fields to PSBT. Nothing prevents an equivalent PSET extension, which this document leaves to a future proposal: a wallet that builds and signs the whole transaction, which is the common case, does not need it. -. - -The fee output of the transaction, which has an empty scriptPubKey, is never a silent payment output. - -===Receiving=== - -A receiver detects the payments of a transaction from its tweak data and its outputs, without any other information about the sender. - -Given the tweak data of a transaction, the receiver computes ecdh_shared_secret = bscan · tweak_data and then, for k = 0, 1, ... up to Kmax: - -# Compute tk and Pk = Bspend + tk·G. -# If a taproot output of the transaction, not already matched, has x-only(Pk) as output key, it is a payment to the address of this wallet. -# Otherwise, for each taproot output not already matched, with output key O, compute label = O' − Pk for O' equal to lift_x(O) and to −lift_x(O). If label is label_tweakm·G for a known label m, it is a payment to the address labelled m, and the tweak of the output is tk + label_tweakm. -# If no output matched, stop. Otherwise remove the matched output, derive its blinding key ck, unblind it, increment k and repeat. - -The decision to continue MUST be based on whether an output matched cryptographically, not on whether the wallet considers it spendable or worth its dust threshold. - -The secret key spending an output found with tweak t is - -
-  d = (b_spend + t) mod n
-
- -and the output is spent as a taproot key path spend with d, without applying the BIP-341 tweak. - -===Scanning with a Tweak Server=== - -A wallet is expected to obtain the tweak data from an index server implementing the "Tweak Server" stack of the [https://github.com/silent-payments/BIP0352-index-server-specification BIP-352 index server specification]. -The wallet does not share its keys with the server, and the server does not learn which transactions the wallet is interested in. - -====Server==== - -For every transaction that satisfies [[#Transaction eligibility|Transaction eligibility]], the server computes and stores its tweak data serP(input_hash · A), 33 bytes. -A server needs the outputs spent by a transaction to do so, which is why a wallet cannot compute this from the block alone. - -The server MUST index from the height at which taproot activated, which on Liquid is block 1,663,200 -Block 1aea43f7205bd02c03b081f3c6de4604756bc1a37ae2ca6e34b1936137756870, mined on 2022-01-16. No transaction before it can have a taproot output, so no transaction before it can pay to a silent payment address. -. - -The following endpoints are defined, as a profile of the index server specification: - -GET /getinfo - -
-{
-  "version": "1.0.0",
-  "network": "liquidv1" | "liquidtestnet" | "elementsregtest",
-  "block_height": 3984398,
-  "dust_limit": 0,
-  "filter_spent": "optional"
-}
-
- -dust_limit MUST be 0. -Amounts on Elements are confidential, so a server cannot tell a dust output from any other one, and the dust filtering of the index server specification does not apply. - -GET /tweaks/:blockheight, with the optional query parameter filterSpent - -
-{
-  "height": 3984398,
-  "hash": "…",
-  "dust_limit": 0,
-  "filter_spent": 0,
-  "tweaks": ["03398173f560782d934ddf4f5a291c47fd0866d6e26a97a7407b810e1873e34777", …]
-}
-
- -One tweak per silent payment eligible transaction of the block. -When filterSpent is set, the server omits the transactions all of whose taproot outputs have already been spent, which a wallet that is catching up after a long time can use to skip outputs it would only receive to see them spent -Spending is visible on Elements even though amounts are not, so this optimization applies unchanged. -. - -GET /filters/:blockheight - -
-{
-  "height": 3984398,
-  "hash": "…",
-  "filter_type": 0,
-  "filter": "…"
-}
-
- -The BIP-158 basic filter of the block, as computed by Elements -Elements computes the basic filter as Bitcoin does: it contains the scriptPubKey of every output that is neither empty nor an OP_RETURN, and the scriptPubKey of every output spent by the block. Fee outputs have an empty scriptPubKey and are therefore not in the filter, and confidential outputs are, since a scriptPubKey is never confidential. -, keyed as in BIP-158 on the hash of the block. -A wallet MAY obtain the same filter from any other source, for instance from an Elements node run with -blockfilterindex=basic. - -A server MAY implement GET /compute-index/:blockheight as defined in the index server specification, which returns the txid of each eligible transaction along with its tweak. It is not needed by the algorithm below. - -====Client==== - -For each block from the wallet birthday to the tip: - -# GET /tweaks/:height. -# For each tweak, derive the '''candidate scripts''': the taproot script of P0, plus one for each known label m, of P0 + label_tweakm·G. -# Test all the candidate scripts of the block against the block filter. If none matches, the block contains no payment for this wallet: continue from the next height. -# Otherwise download the block. For each tweak whose candidate script is the scriptPubKey of an output of a transaction of the block, run the algorithm of [[#Receiving|Receiving]] on that transaction with that tweak, and record the outputs found. -# Record the height as scanned. - -A wallet MUST re-scan the blocks that a reorganization disconnected, and MUST drop the outputs it had found in them. - -A payment is detected only once it is in a block: the tweak of a transaction in the mempool is not served by a tweak server, and asking for it would tell the server which transactions the wallet is interested in. - -The candidate scripts only cover k = 0, but a transaction paying to this wallet always contains the k = 0 output, so a payment is never missed; the transaction is then scanned for all the values of k. -The tweaks do not have to be matched to a transaction by position: a tweak is matched to the transaction that contains one of its candidate scripts. - -A wallet SHOULD verify the tweaks it receives, by recomputing them from the block it has downloaded whenever it has the spent outputs it needs, and SHOULD compare the filter against the block it downloaded. -A server that omits a tweak makes the wallet miss a payment; it cannot make the wallet accept an output that is not its own, because every output is verified against the keys of the wallet before being recorded. - -Since the tweak data of a block is 33 bytes per eligible transaction, and Liquid produces one block per minute, a wallet that scans every block downloads at most 33 · t · 1440 bytes per day, where t is the number of eligible transactions per block: with the 6.5 transactions per block measured on Liquid around height 3,984,390, and in the worst case in which all of them are eligible, that is about 310 kB per day, plus one filter per block. - -===Representation of a received output=== - -A received output is an ordinary confidential taproot output, described by: - -* its scriptPubKey, -* its blinding secret key ck, needed to unblind it, -* its tweak t, needed to spend it, together with bspend. - -A wallet MUST persist the outputs it discovers, or it has to scan again from the birthday. - -An [https://github.com/ElementsProject/ELIPs/blob/main/elip-0150.mediawiki ELIP-150] CT descriptor cannot describe such an output today: the descriptor ct(ck,eltr(Pk)) would apply the BIP-341 tweak to Pk and produce a different script. -A rawtr() equivalent for Elements, that is a key spend only taproot descriptor that uses its key as the output key, would make ct(ck,elrawtr(Pk)) the natural representation of a received output; adding it is left to a separate proposal. - -==Rationale== - -'''Why derive the blinding key from the shared secret?''' -The alternative is to publish a third key in the address and blind to it, or to a tweak of it. -It would not hide anything more from the sender, which picks the ephemeral key used to blind the output and therefore knows the amount it is sending in any case, and it would make the address longer and incompatible with the BIP-352 encoding. -It would, however, separate the ability to detect a payment from the ability to unblind it, which is only useful in a model where the scan key is shared with a third party. This document recommends against that model, so the simpler derivation was chosen. - -'''Why a separate tag instead of reusing tk?''' -Using tk as the blinding key would tie two unrelated secrets together: anybody learning the blinding key of an output, for instance because the receiver revealed it to prove a payment -Revealing the blinding key of an output is the usual way to prove to a third party what was received, since it lets them unblind that single output. -, would also learn the tweak, and with the spend public key they could compute the output key. A separate tagged hash keeps the two independent. - -'''Why a different human readable part?''' -A silent payment address does not commit to a chain in any other way. -Paying a Bitcoin address on Liquid, or the reverse, would create an output that the receiver cannot detect and that the sender cannot recover. -The prefixes are the Elements address prefixes, lq, tlq and el, with sp appended. - -'''Why are peg-ins not eligible?''' -The output a peg-in spends lives on the Bitcoin chain: the scanning wallet would need Bitcoin data to know which of the rules of [[#Input eligibility|Input eligibility]] applies, which defeats the purpose of a light client. -Their outpoints are still part of outpointL, because they are part of the transaction and both sides can read them from it. - -'''Why clear the flags in the outpoint?''' -The alternative, hashing the outpoint as it appears in the serialized transaction, would make the value depend on whether the input carries an issuance, which is unrelated to the payment, and would be easy to get wrong: a wallet naturally works with parsed transactions, where the flags have already been stripped. -The same choice makes the derivation identical to BIP-352 for the transactions that have no peg-in and no issuance. - -'''Why the Tweak Server model?''' -As explained in [[#Drawbacks|Drawbacks]], on Elements the scan key unblinds as well as detects, so the models of the index server specification in which the scan key is shared with a service would give that service every amount and every asset of the wallet. The Tweak Server model does not share any key. - -'''Why only taproot outputs?''' -As in BIP-352. A silent payment output is a fresh key that has never been published, so there is nothing to gain from a script, and restricting to one output type keeps the scanning work of the receiver proportional to the taproot outputs of a block. - -'''Interaction with discounted fees.''' -A silent payment output is an ordinary confidential taproot output, so [https://github.com/ElementsProject/ELIPs/blob/main/elip-0200.mediawiki ELIP-200] discounted fees apply to it unchanged. - -==Backwards compatibility== - -This document does not change consensus rules and does not require any change to Elements: it describes how existing outputs are derived and found. -A silent payment transaction is an ordinary transaction, and it is indistinguishable from any other transaction with taproot outputs. - -A wallet that does not implement this document cannot pay to a silent payment address: the address is a new format, and parsing it fails, so funds cannot be sent to a script that nobody can spend. - -A wallet that implements this document can receive from any sender that does, and can spend the outputs it receives with any signer able to produce a taproot key path signature for a given secret key. - -Scanning requires the tweak data of every transaction, which no Elements node exposes today. -Until an index server is available, a wallet can compute the tweak data itself from the full blocks and from the outputs those blocks spend, which is only practical for a node or for a wallet with a short scanning range. - -==Test vectors== - -The BIP-352 [https://github.com/bitcoin/bips/blob/master/bip-0352/send_and_receive_test_vectors.json send and receive test vectors] apply unchanged to everything that is not blinding: the address encoding, the input eligibility, the tweak data, the output derivation and the labels. -An implementation SHOULD run them, encoding the addresses with the Bitcoin human readable part for the purpose of the comparison. - -The vectors below cover what is specific to this document. - -===Keys and addresses=== - -All of them are derived from the BIP-39 mnemonic - -
-abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
-
- -with an empty passphrase, account 0. - -* Liquid, m/352'/1776'/0'/{1,0}'/0 -** scan secret key: e4284c50d48a373098d44638e3c9d6f6eca770aaa54562eb8cdc0cff8cd3b550 -** spend secret key: ca29645fc7167f2810c909fa52bcd177b82aa3bfbe999ef579a6607926dc7f93 -** address: lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcqc9sw44xujvc7ejg4w5lt4zxpvc3fk446qdcrrmsgg9cnax8ujj7spnvlwq -** change address, label 0: lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcqmujrcapnql6np3dpdjwswdwdcs0h3rs637agjsaeac6thmlcfpyy2eg0eh -** address labelled 1: lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcq3789yp3lv5nvyyvhucjttyanypmzdg7qpadykuz7xd9zenvh6dwylcw0sj -* Liquid testnet, m/352'/1'/0'/{1,0}'/0 -** scan secret key: 38658693c017c46fd6b8bb94b8766c123cd5baf6026338305b6f59f82b36f9c0 -** spend secret key: 9fd37137e760930c7208fa905e991c78c522689d237a220b2820c3ddb4c745a8 -** address: tlqsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq5rxzzunfck6d45va2jcqxk429agt3e4klf3vzmcgp3zqthryhhqgu2k23l -** change address, label 0: tlqsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq4y0sngs4cjaw6cxuy7p6rlslx6h54lmppe9kx4r2ylf63tkem26qqt6u7t -** address labelled 1: tlqsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzqknp3yhxtxhcvkczk4vfvdx0z8pfff3n78nelgppy4l2sss3famcgq974m5 -* Elements regtest, m/352'/1'/0'/{1,0}'/0, same keys as testnet -** address: elsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq5rxzzunfck6d45va2jcqxk429agt3e4klf3vzmcgp3zqthryhhqgd4lgkm -** change address, label 0: elsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzq4y0sngs4cjaw6cxuy7p6rlslx6h54lmppe9kx4r2ylf63tkem26q35n7e0 -** address labelled 1: elsp1qqdpels3srq45dlezqvk20t3dlueftry6p5thc7msjm0s6jm3g84jzqknp3yhxtxhcvkczk4vfvdx0z8pfff3n78nelgppy4l2sss3famcg36hhus - -===A payment=== - -A transaction on Liquid spending one P2WPKH input and paying to the Liquid address above. - -Given: - -* input outpoint: f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16:0 -* input secret key: 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b -* input public key: 02552c630b64b54bf50210c9e253d38bd4949c72e22873500f6285c2bede312a84 -* spent scriptPubKey: 0014db3f00d429f2715383cc594258ec11d6de526697 -* recipient: lqsp1qqgvh6dnt5eyvw54a0dvt4er7tkq0hdefm39sta8adph4vufq650tcqc9sw44xujvc7ejg4w5lt4zxpvc3fk446qdcrrmsgg9cnax8ujj7spnvlwq - -Expected: - -* tweak data, input_hash · A: 03398173f560782d934ddf4f5a291c47fd0866d6e26a97a7407b810e1873e34777 -* output tweak, t0: a4c41218034595e818fce768dded0e52409abfe23501fb94e6787c32debad2bd -* output scriptPubKey: 512092a9d712661ac4c1ebd5e3953a5af8a60037fb69e7c559ecacbce41a050262d7 -* blinding secret key, c0: 807c22b1cb486e8de84f7f2c7a97c72559ef0f5bcd0033eb071c17a63762146e -* blinding public key, C0: 02bd12b91875000fdbff57302ac8f22ce20010061ef626899c569c6059098d3af3 -* confidential address of the output: lq1pq2739wgcw5qqlkll2ucz4j8j9n3qqyqxrmmzdzvu26wxqkgf35a08y4f6ufxvxkyc84atcu48fd03fsqxlakne79t8k2e08yrgzsyckhf736l4spplpl -* secret key spending the output, bspend + t0: 6eed7677ca5c151029c5f16330a9dfcb3e1686bb4452fa4ea04c7e1f3561110f - -==Reference implementation== - -[https://github.com/Blockstream/lwk Liquid Wallet Kit], in the silent_payments module of the lwk_wollet crate, proposed in [https://github.com/Blockstream/lwk/pull/170 pull request 170]. - -It implements the address encoding, the key derivation, the input eligibility rules, the sender and receiver derivations and the blinding key derivation. -It is checked against the [https://github.com/bitcoin/bips/blob/master/bip-0352/send_and_receive_test_vectors.json BIP-352 test vectors], sending and receiving, against the test vectors of this document, and against transactions built, blinded and signed as a wallet would build them, which is what covers the blinding. - -==Footnotes== - - - -==Acknowledgments== - -Silent payments were designed by Ruben Somsen and Josie Baker in BIP-352; this document only adapts their work to confidential transactions.