From d092ef7f1950cbdc1d16463a20982c93dcb66092 Mon Sep 17 00:00:00 2001 From: 42pupusas Date: Mon, 1 Jun 2026 19:58:19 -0600 Subject: [PATCH 1/6] Add Silent Payments for the Liquid Network draft A Standards Track draft specifying BIP-352 Silent Payments adapted to the Liquid Network's Confidential Transactions. Each normative rule is tagged as BIP-352-derived, a Liquid adaptation, or an open design choice (stated preferentially). Includes worked test vectors and abstract data structures. --- elip-silent-payments-liquid.mediawiki | 515 ++++++++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 elip-silent-payments-liquid.mediawiki diff --git a/elip-silent-payments-liquid.mediawiki b/elip-silent-payments-liquid.mediawiki new file mode 100644 index 0000000..a9907b3 --- /dev/null +++ b/elip-silent-payments-liquid.mediawiki @@ -0,0 +1,515 @@ +
+  ELIP: ?
+  Layer: Applications
+  Title: Silent Payments for the Liquid Network
+  Author: 42pupusas
+  Comments-Summary: No comments yet.
+  Comments-URI: https://github.com/ElementsProject/elips/wiki/Comments:ELIP-????
+  Status: Draft
+  Type: Standards Track
+  Created: 2026-06-01
+  License: BSD-3-Clause
+
+ +==Introduction== + +===Abstract=== + +This document specifies Silent Payments for the Liquid Network. Silent Payments, +defined for Bitcoin in [https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki BIP-352], +allow a receiver to publish a single static address from which a sender derives a +fresh, unlinkable on-chain output for every payment, without any interaction and +without leaving a reusable public identifier on the chain. + +Applying this construction to Liquid requires reconciling it with Confidential +Transactions (CT), in which every output additionally commits to a blinded asset +and amount and carries an ECDH nonce that allows the intended receiver to recover +those values. This document reuses the BIP-352 key-derivation core unchanged, adapts +the output representation to Liquid's deployed output types, and introduces a +deterministic derivation of the per-output blinding key from the silent-payment +shared secret so that a confidential output can be both discovered and unblinded by +the receiver without any additional round trip. It further specifies a light-client +receive flow following the "tweak server" model of the +[https://github.com/silent-payments/BIP0352-index-server-specification BIP-352 index server specification]. + +===Copyright=== + +This document is licensed under the 3-clause BSD license. + +===Motivation=== + +Address reuse harms the privacy of all participants on a transparent or +semi-transparent ledger. Confidential Transactions hide the asset and amount of a +Liquid output, but the output script — and therefore an address that is reused — is +public. A receiver who wishes to accept many payments to a published address +(donations, a point-of-sale identifier, a recurring counterparty) must today either +reuse an address, linking those payments, or run an interactive protocol to hand out +fresh addresses. + +Silent Payments remove this trade-off: the receiver publishes one static address, +and each sender independently derives a distinct output that only the receiver can +recognize. On Liquid this is particularly valuable, because it composes naturally +with CT — the payment graph gains the unlinkability of Silent Payments while the +amounts retain the confidentiality of CT. + +This document defines how to construct and recognize such outputs on Liquid so that +independent implementations interoperate. + +==Conventions and Provenance of Each Rule== + +Throughout this document, every normative rule is tagged to make its origin explicit: + +* '''[BIP-352]''' — the rule is taken unchanged from BIP-352. Implementations SHOULD reuse existing, reviewed BIP-352 logic for these parts. +* '''[Liquid]''' — the rule is an adaptation made necessary by a structural difference between Liquid and Bitcoin (most importantly, Confidential Transactions and Liquid's deployed output types). These are the substantive technical contributions of this document. +* '''[Choice]''' — the rule reflects a design decision for which alternatives exist. Where this document states a value or behavior under a [Choice] tag, that value is the '''preferred''' option of this draft; the rationale and the alternatives considered are recorded in the [[#Rationale|Rationale]] section. These are the points on which reviewer input is most actively sought. + +Notation follows BIP-352: + +* G is the secp256k1 generator; n the curve order. +* Lowercase letters denote scalars (private keys); uppercase letters the corresponding points, e.g. A = a·G. +* serP(P) is the 33-byte compressed encoding of a point P; ser32(i) the 4-byte big-endian encoding of an integer i. +* · is scalar–point multiplication and + is point addition or scalar addition mod n as appropriate. +* hash_tag(m) is the BIP-340 tagged hash SHA256(SHA256(tag) || SHA256(tag) || m) with ASCII tag tag. + +==Design== + +===Overview=== + +A receiver holds two key pairs: a '''scan''' key pair (b_scan, B_scan) and +a '''spend''' key pair (b_spend, B_spend). The static silent-payment +address encodes the two public keys. + +To pay the address, a sender: + +# aggregates the private keys of its eligible transaction inputs into a single scalar a and forms A = a·G '''[BIP-352]'''; +# computes a transaction-bound input_hash and an ECDH shared secret S with the receiver's scan key '''[BIP-352]'''; +# derives, for output index k, a spend public key P_k that only the receiver can later re-derive '''[BIP-352]'''; +# places P_k in a Liquid output of the deployed type '''[Liquid]''', and blinds that output's asset and amount to a blinding key that is itself derived from S '''[Liquid]'''. + +To receive, the receiver (or a light client acting on its behalf) obtains, for each +candidate transaction, the value input_hash·A, completes the shared +secret with its scan key, re-derives the candidate spend keys and their output +scripts, matches them against the transaction's outputs, and — on a match — derives +the same blinding key to unblind the asset and amount and the spend key to later +spend the output. + +The remainder of this section defines each step. + +===Receiver keys and address=== + +The receiver's scan and spend key pairs are independent secp256k1 key pairs. '''[BIP-352]''' +Their derivation from a seed is left to the wallet; a deterministic scheme analogous +to BIP-352's (a dedicated purpose under BIP-32) is RECOMMENDED but out of scope for +interoperability, since only the public keys are transmitted. '''[Choice]''' + +The address is the Bech32m '''[BIP-352]''' encoding of a version symbol followed by +the payload serP(B_scan) || serP(B_spend) (66 bytes), with a +network-specific human-readable part. '''[Liquid] [Choice]''' This draft uses: + +{| class="wikitable" +! Network !! HRP +|- +| Liquid (mainnet) || lq +|- +| Liquid testnet / regtest || tlq +|} + +and version symbol q (the Bech32 character for value 0), denoting +version 0. As in BIP-352, the 90-character Bech32 length limit does '''not''' apply +to silent-payment addresses. '''[BIP-352]''' + +A distinct, network-specific HRP (rather than Bitcoin's sp/tsp) +ensures a Liquid silent-payment address can never be confused with a Bitcoin one, +nor a mainnet address with a testnet one. + +===Input aggregation and the input hash=== + +A sender forms the aggregated input private key by summing the private keys of all +'''eligible''' inputs (defined below): '''[BIP-352]''' + +
+a = a_1 + a_2 + ... + a_u   (mod n)
+A = a·G
+
+ +If a = 0 the sender MUST abort: no payment is defined. '''[BIP-352]''' + +Let outpoint_L be the lexicographically smallest input outpoint, encoded +as in a transaction (32-byte txid in internal byte order followed by the 4-byte +little-endian index — identical to the Elements consensus encoding of an outpoint). +The input hash is: '''[BIP-352]''' + +
+input_hash = int(hashBIP0352/Inputs( outpoint_L || serP(A) )) mod n
+
+ +====Eligible inputs '''[Liquid] [Choice]'''==== + +An input is eligible to contribute to a (sender) and A +(scanner) if and only if it is spent by a single public key whose value is +unambiguously recoverable from the input, and it is not an issuance, reissuance, or +peg-in input. Concretely this draft includes: + +* P2WPKH (elwpkh) — the dominant Liquid output type — taking the public key from the witness; +* P2SH-wrapped P2WPKH — likewise; +* P2PKH — taking the public key from the scriptSig. + +and excludes issuance/reissuance inputs, peg-in inputs, the fee output (which is +never an input), and any input whose signing public key is not uniquely determined. + +This mirrors BIP-352's eligible-input set with two Liquid-specific notes. First, +Liquid blinds the asset and amount of an input's spent output, but the '''signing +public key''' used here is the one revealed in the witness/scriptSig and is +independent of blinding; aggregation therefore proceeds exactly as on Bitcoin. +Second, BIP-352's requirement to negate Taproot input keys to an even Y-coordinate +before summing does '''not''' apply here, because the eligible types above are not +Taproot key-path spends; keys are summed directly. Should Liquid Taproot key-path +spends become eligible in a future version, the BIP-352 even-Y rule would apply to +them unchanged. + +===Shared secret and output spend key=== + +Both parties compute the same ECDH shared secret: '''[BIP-352]''' + +
+sender:    S = input_hash · a · B_scan
+receiver:  S = input_hash · b_scan · A
+
+ +For each output index k = 0, 1, 2, ... paid to the same recipient in the +same transaction: '''[BIP-352]''' + +
+t_k = int(hashBIP0352/SharedSecret( serP(S) || ser32(k) )) mod n
+P_k = B_spend + t_k·G
+
+ +The receiver, upon recognizing an output at index k, can spend it with +the private key b_spend + t_k (mod n). '''[BIP-352]''' + +===Output representation '''[Liquid] [Choice]'''=== + +In BIP-352 the output is a P2TR output whose key is P_k, visible in the +clear. Liquid does not deploy Taproot in general wallet usage, and the overwhelmingly +common Liquid output type is elwpkh (version-0 witness public-key hash). +This draft therefore defines a silent-payment output as a '''confidential +version-0 P2WPKH output''' whose witness program is HASH160(serP(P_k)): + +
+scriptPubKey = OP_0 
+
+ +with the asset and amount blinded as in any CT output (see the next section). + +A consequence is that the value an index server publishes per transaction is a +'''compressed''' point (or equivalently the candidate scripts derived from it), +rather than the x-only key used in the Bitcoin index-server model. + +Emulating Taproot via Liquid's tapscript support was considered and is deferred: +it adds deployment surface for negligible benefit given current Liquid usage. A +future version MAY define a Taproot output type, at which point the BIP-352 x-only +conventions would apply to it directly. + +===Output blinding key '''[Liquid]'''=== + +This is the central adaptation required by Confidential Transactions and has no +counterpart in BIP-352. + +On Liquid, an output is blinded to a '''blinding key''': the sender places an ECDH +nonce in the output, and the holder of the corresponding blinding private key can +recover the asset, amount, and their blinding factors. For ordinary addresses the +blinding key is derived from the output script (e.g. SLIP-77 or +[https://github.com/ElementsProject/ELIPs/blob/main/elip-0151.mediawiki ELIP-151]). +A silent-payment output's script, however, is not known to the receiver in advance — +it is discovered by scanning — so a script-derived blinding key cannot be used. + +This document specifies that the blinding key of a silent-payment output is derived +deterministically from the silent-payment shared secret, in a '''dedicated hash +domain''' disjoint from the spend-key derivation: + +
+bk_k = hashLiquidSilentPayments/Blind( serP(S) || ser32(k) )   (a 32-byte scalar)
+BK_k = bk_k·G
+
+ +The sender blinds the output to BK_k (i.e. uses BK_k as the +receiver blinding public key when constructing the output's CT nonce, range proof, +and commitments, exactly as for any confidential output). The receiver, having +recomputed S, derives bk_k and unblinds the output. No +out-of-band exchange and no additional interaction are required: the same shared +secret that yields the spend key also yields the blinding key. + +Because bk_k and t_k are outputs of a random oracle (a +tagged hash) evaluated on '''disjoint domains''' over the same secret S, +they are independent: knowledge of one does not assist in recovering the other or +S. The domain tag LiquidSilentPayments/Blind MUST differ +from the BIP-352 spend domain BIP0352/SharedSecret. + +Two alternatives were rejected. Publishing a single fixed blinding key in the +address would link all of a receiver's outputs through a common blinding key, +negating the unlinkability Silent Payments provides. Exchanging a per-output blinding +key out of band would reintroduce the interaction Silent Payments is designed to +eliminate. + +===Labels '''[BIP-352]'''=== + +Labels are supported exactly as in BIP-352. For an integer label m: + +
+label_tweak_m = int(hashBIP0352/Label( ser256(b_scan) || ser32(m) )) mod n
+B_spend,m     = B_spend + label_tweak_m·G
+
+ +A labeled address encodes B_spend,m in place of B_spend; the +scan key is unchanged. The output spend key becomes B_spend,m + t_k·G and +is spendable with b_spend + t_k + label_tweak_m (mod n). Label +m = 0 is reserved to mark change. The output blinding key is unaffected +by labels: it depends only on S and k. + +Implementations MAY support only the change label (m = 0) in a first +version. '''[Choice]''' + +==Light-client receive: the tweak server model== + +The receive bounty for this work requires following the tweak-server model of the +[https://github.com/silent-payments/BIP0352-index-server-specification BIP-352 index server specification]. +That model has a server compute, per silent-payment-eligible transaction, the +'''partial tweak''' '''[BIP-352]''': + +
+T = input_hash · A
+
+ +a single public key that requires no scan key to compute. A light client fetches the +partial tweaks for a block height, completes the shared secret locally with its scan +key, and matches: + +
+S = b_scan · T   (= input_hash · b_scan · A)
+
+ +For each k, the client derives P_k and the candidate output +script and checks it against the transaction's outputs, advancing k +until a configurable number of consecutive misses (a gap limit), since multiple +outputs may be paid to the same address. '''[BIP-352]''' On a match the client +derives bk_k to unblind and b_spend + t_k to spend. + +===Matching on Liquid omits compact filters '''[Liquid] [Choice]'''=== + +In the Bitcoin index-server model the client cannot recompute an output's +scriptPubKey from chain data without first guessing the tweak, so it uses +[https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki BIP-158] compact +block filters to test candidate scripts and then downloads only matching blocks. + +On Liquid this intermediate filtering step is unnecessary: Confidential Transactions +blind the asset, amount, and nonce of an output but '''not its scriptPubKey''', so +output scripts are available in the clear from ordinary block or transaction data. +A client that has derived a candidate script can therefore match it directly against +the transaction outputs it already retrieves. Accordingly this draft does not require +BIP-158 filters; an implementation MAY still use a filter or other index as an +optimization, but it is not part of the protocol. This is the one substantive +deviation from the Bitcoin index-server model, and it is forced by — and made safe +by — Liquid's public output scripts. + +===Server interface '''[Choice]'''=== + +The protocol requires only that, for each block height, a client can obtain the list +of partial tweaks T for that block's silent-payment-eligible +transactions. An abstract client interface is therefore minimal: + +
+tweaks(block_height) -> [ serP(T_1), serP(T_2), ... ]
+
+ +A concrete wire format is intentionally left to a companion specification or to +existing Liquid indexing infrastructure; for example a REST endpoint returning an +array of compressed points per height, or an extension of an existing +descriptor-oriented indexing service. The cryptographic protocol in this document is +independent of that choice. + +==Spending a received output '''[Liquid]'''== + +A received silent-payment output is controlled by the scalar +d = b_spend + t_k (+ label_tweak_m), which is generally '''not''' a +BIP-32-derivable key. Signing such an input therefore requires a signer that accepts +a base key and an additive tweak (or equivalently the tweaked key directly), rather +than only keys located at a derivation path. + +This is a software-signer capability: deriving d and producing an +ordinary signature for the corresponding P2WPKH input is straightforward and uses no +new cryptography. It is the only signer-side change Silent Payments require on +Liquid, and it is sufficient for a wallet to send to, receive from, and spend +silent-payment outputs. + +Hardware-signer support is a separate matter. Common hardware-signer protocols +identify the signing key by a registered descriptor or derivation path and expose no +channel for an additive per-input tweak; signing a silent-payment output on such +devices therefore requires firmware-level support for key tweaks, which is out of +scope for this version and is noted as future work. Hardware support for Silent +Payments is nascent on Bitcoin as well. + +==Abstract data structures== + +The following abstract structures summarize the values exchanged or derived; field +encodings are as defined above. They are illustrative, not an API. + +A silent-payment address: + +
+SilentPaymentAddress {
+    scan_pubkey:  serP(B_scan)     // 33 bytes
+    spend_pubkey: serP(B_spend)    // 33 bytes; B_spend,m for a labeled address
+}
+// wire: Bech32m( hrp, version=0, scan_pubkey || spend_pubkey )
+
+ +Aggregated input data computed by a sender: + +
+AggregatedInputs {
+    a:          scalar             // sender only
+    A:          point              // = a·G
+    input_hash: scalar             // = H_Inputs(outpoint_L || serP(A))
+}
+
+ +A derived silent-payment output (for index k): + +
+SilentPaymentOutput {
+    spend_pubkey:  P_k = B_spend + t_k·G
+    blinding_pubkey: BK_k = bk_k·G
+    // scriptPubKey = OP_0 , asset/amount blinded to BK_k
+}
+
+ +The light-client view per eligible transaction: + +
+PartialTweak = serP( input_hash · A )      // published by the index/tweak server
+
+ +==Test Vectors== + +The following worked example fixes all inputs and lists every intermediate and final +value, so that an independent implementation can reproduce the construction +byte-for-byte. All byte strings are hex. + +Receiver keys (32-byte scalars): + +
+b_scan  = 1111111111111111111111111111111111111111111111111111111111111111
+b_spend = 2222222222222222222222222222222222222222222222222222222222222222
+
+ +Two eligible inputs, each a P2WPKH spend, with private keys and outpoints: + +
+input 0: priv = 3131...31 (0x31 x32), outpoint txid = 1010...10 (0x10 x32), vout = 0
+input 1: priv = 3232...32 (0x32 x32), outpoint txid = 2020...20 (0x20 x32), vout = 1
+
+ +Aggregated input values (outpoint_L is input 0, the lexicographically smaller): + +
+A          = 031195a8046dcbb8e17034bca630065e7a0982e4e36f6f7e5a8d4554e4846fcd99
+input_hash = d392922c00280a7e8d282182f5026f2fddbc74c1e1de18b4822128b2b77ec641
+
+ +Per-output derived values: + +
+k = 0:
+  P_k (spend pubkey) = 02a29d9716417c964ca9e477343e71ffe730a4991a3eaad668eabec84e9feb7931
+  BK_k (blinding pub) = 0344e1289497e6da66fde710d2f38de053fc07355e405524401d7d609df5a1a8cc
+  bk_k (blinding priv) = 70ab8897b64bd21b427339ff4d014b883191ef6425862246c53bfc27a59aa3f0
+  spend priv (b_spend + t_k) = f03c436d2cd67ae1fecf7d88a38aa3a03c0abea43feaf6da8eb71e2e3a866bda
+  scriptPubKey = 0014aad1065758c02efbf32e797f108007741068eb25
+
+k = 1:
+  P_k (spend pubkey) = 0229d77654023af267dbe9cb7ff1956f947c816f203494381308387168fb010c92
+  BK_k (blinding pub) = 03efdeda770ccdbe8bf466fba48bfd2b2c436ab0c04658fc6d6c277de5078129fa
+  bk_k (blinding priv) = 945ba73a9804f62089c7d2ffdc079031031f0aebab372cec17ef9c110ebceb10
+  spend priv (b_spend + t_k) = 9eff3472230fc83ef5ea8f8c80401c4eecd595a048bd2482a107d3a49baa5a58
+  scriptPubKey = 001481bc88e345e0a585da4ee2157eef9e18bff416cf
+
+ +The unlabeled mainnet (HRP lq) address for these keys: + +
+lq1qqd8n2k7uklxq4aegau7vawtptkgxsja4kt99lpv6krctwpq8tpc65qjxd4lu4etruh9sngx3su9mtqp5fqzxz7re59y5nnez9p03ht3lyu7836wf
+
+ +A conforming implementation MUST reproduce A, input_hash, +and for each k the values P_k, BK_k, +bk_k, the spend private key, and the scriptPubKey, and MUST produce the +address above. Because the asset and amount blinding factors are randomized per +output, the full blinded output is not byte-reproducible; the recovery property is +instead stated as: an output blinded to BK_k by the construction above +unblinds correctly under bk_k, and fails to unblind under any other key. + +==Rationale== + +'''Why reuse the BIP-352 key derivation unchanged.''' The spend-key path +(a, A, input_hash, S, +t_k, P_k, labels) is the reviewed cryptographic core of +Silent Payments. Reusing it verbatim maximizes interoperability with existing +implementations and tooling and confines the novel surface to the parts Liquid +actually forces to differ. + +'''Why a confidential P2WPKH output rather than P2TR ([Choice]).''' Liquid wallets +overwhelmingly use elwpkh; using the deployed type avoids a Taproot +dependency and matches existing relay and wallet behavior. The cost is divergence +from BIP-352's x-only convention and a compressed-point value in the index-server +data. A future Taproot output type can be added without disturbing the rest of this +specification. + +'''Why derive the blinding key from the shared secret ([Liquid]).''' This is the +mechanism that lets a confidential output be both discovered and unblinded +non-interactively. A dedicated hash domain keeps the blinding key independent of the +spend key. The security of the scheme reduces to the random-oracle treatment of the +tagged hash and the secrecy of S (which only the sender and the holder +of b_scan can compute); a formal security argument and adversarial +analysis are expected to accompany review. The construction has been validated by a +reference implementation against the test vectors above, including adversarial cases +(an observer holding all public data but not b_scan cannot unblind; +incorrect keys never unblind; spend and blinding values are independent across many +randomized cases). + +'''Why omit BIP-158 on Liquid ([Choice]).''' The index-server filter step exists to +let a client test candidate scripts without downloading every block. Because Liquid +output scripts are public, a client can match candidate scripts directly, so the +filter step is redundant. This is a deliberate, narrowly-scoped deviation from the +Bitcoin index-server model, justified entirely by Liquid's public scripts; it does +not change the cryptographic protocol. + +'''Why a distinct, network-specific HRP ([Choice]).''' To make cross-chain and +cross-network address confusion impossible. + +==Backwards Compatibility== + +This document defines a new, opt-in address type and output convention. It introduces +no consensus change and does not affect existing addresses, descriptors, or +transactions. Wallets that do not implement it are unaffected; a silent-payment +output, once created, is an ordinary confidential P2WPKH output on the chain and is +spent by an ordinary signature, so existing relay and validation rules apply +unchanged. + +Discovering and spending silent-payment outputs requires wallet support (scanning and +tweak-aware signing). Hardware signers require firmware support for additive key +tweaks, which does not exist in common protocols today; until then, silent-payment +outputs are usable with software signing. + +==Reference Implementation== + +A reference implementation demonstrating address encoding, input aggregation, +sender output derivation, the shared-secret-derived blinding key, confidential output +construction and recovery, the tweak-server scan flow, labels, and tweak-aware +signing, together with the test vectors and adversarial tests referenced above, is +expected to accompany this proposal prior to a status of Final. + +==Acknowledgements== + +This specification builds directly on BIP-352 and the BIP-352 index server +specification, and on the Confidential Transactions and CT-descriptor work of the +Elements Project. From ab997b4a94945bf0bdc73ccbd040bb1b581f5eb9 Mon Sep 17 00:00:00 2001 From: 42pupusas Date: Tue, 2 Jun 2026 15:22:48 -0600 Subject: [PATCH 2/6] Condense Silent Payments for Liquid ELIP per reviewer feedback Trim BIP-352 re-explanation to focus on Liquid-specific differences: - Shorten abstract and motivation prose - Reduce BIP-352-compliant sections to brief restatements - Remove Rationale section (reasoning is stated inline in each [Liquid]/[Choice] design section) --- elip-silent-payments-liquid.mediawiki | 210 +++++++------------------- 1 file changed, 55 insertions(+), 155 deletions(-) diff --git a/elip-silent-payments-liquid.mediawiki b/elip-silent-payments-liquid.mediawiki index a9907b3..d02363f 100644 --- a/elip-silent-payments-liquid.mediawiki +++ b/elip-silent-payments-liquid.mediawiki @@ -15,21 +15,14 @@ ===Abstract=== -This document specifies Silent Payments for the Liquid Network. Silent Payments, -defined for Bitcoin in [https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki BIP-352], -allow a receiver to publish a single static address from which a sender derives a -fresh, unlinkable on-chain output for every payment, without any interaction and -without leaving a reusable public identifier on the chain. - -Applying this construction to Liquid requires reconciling it with Confidential -Transactions (CT), in which every output additionally commits to a blinded asset -and amount and carries an ECDH nonce that allows the intended receiver to recover -those values. This document reuses the BIP-352 key-derivation core unchanged, adapts -the output representation to Liquid's deployed output types, and introduces a -deterministic derivation of the per-output blinding key from the silent-payment -shared secret so that a confidential output can be both discovered and unblinded by -the receiver without any additional round trip. It further specifies a light-client -receive flow following the "tweak server" model of the +This document specifies Silent Payments for the Liquid Network, building on +[https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki BIP-352]. It assumes +familiarity with BIP-352 and describes only what Liquid requires to differ: the +BIP-352 key-derivation core is reused unchanged, while three things are adapted to +Confidential Transactions (CT) and Liquid's deployed output types — the output +representation, a per-output blinding key derived from the silent-payment shared +secret (so a confidential output can be discovered and unblinded non-interactively), +and a light-client receive flow following the "tweak server" model of the [https://github.com/silent-payments/BIP0352-index-server-specification BIP-352 index server specification]. ===Copyright=== @@ -38,22 +31,17 @@ This document is licensed under the 3-clause BSD license. ===Motivation=== -Address reuse harms the privacy of all participants on a transparent or -semi-transparent ledger. Confidential Transactions hide the asset and amount of a -Liquid output, but the output script — and therefore an address that is reused — is -public. A receiver who wishes to accept many payments to a published address -(donations, a point-of-sale identifier, a recurring counterparty) must today either -reuse an address, linking those payments, or run an interactive protocol to hand out -fresh addresses. +Confidential Transactions hide a Liquid output's asset and amount, but its script — +and so any reused address — is public. A receiver accepting many payments to one +published address must today either reuse an address, linking those payments, or run +an interactive protocol to hand out fresh ones. -Silent Payments remove this trade-off: the receiver publishes one static address, -and each sender independently derives a distinct output that only the receiver can -recognize. On Liquid this is particularly valuable, because it composes naturally -with CT — the payment graph gains the unlinkability of Silent Payments while the -amounts retain the confidentiality of CT. - -This document defines how to construct and recognize such outputs on Liquid so that -independent implementations interoperate. +Silent Payments remove this trade-off, and compose naturally with CT: the receiver +publishes one static address, each sender independently derives a distinct output +only the receiver can recognize, and the payment graph gains the unlinkability of +Silent Payments while amounts keep the confidentiality of CT. This document defines +how to construct and recognize such outputs on Liquid so that independent +implementations interoperate. ==Conventions and Provenance of Each Rule== @@ -61,7 +49,7 @@ Throughout this document, every normative rule is tagged to make its origin expl * '''[BIP-352]''' — the rule is taken unchanged from BIP-352. Implementations SHOULD reuse existing, reviewed BIP-352 logic for these parts. * '''[Liquid]''' — the rule is an adaptation made necessary by a structural difference between Liquid and Bitcoin (most importantly, Confidential Transactions and Liquid's deployed output types). These are the substantive technical contributions of this document. -* '''[Choice]''' — the rule reflects a design decision for which alternatives exist. Where this document states a value or behavior under a [Choice] tag, that value is the '''preferred''' option of this draft; the rationale and the alternatives considered are recorded in the [[#Rationale|Rationale]] section. These are the points on which reviewer input is most actively sought. +* '''[Choice]''' — the rule reflects a design decision for which alternatives exist. Where this document states a value or behavior under a [Choice] tag, that value is the '''preferred''' option of this draft. These are the points on which reviewer input is most actively sought. Notation follows BIP-352: @@ -86,21 +74,15 @@ To pay the address, a sender: # derives, for output index k, a spend public key P_k that only the receiver can later re-derive '''[BIP-352]'''; # places P_k in a Liquid output of the deployed type '''[Liquid]''', and blinds that output's asset and amount to a blinding key that is itself derived from S '''[Liquid]'''. -To receive, the receiver (or a light client acting on its behalf) obtains, for each -candidate transaction, the value input_hash·A, completes the shared -secret with its scan key, re-derives the candidate spend keys and their output -scripts, matches them against the transaction's outputs, and — on a match — derives -the same blinding key to unblind the asset and amount and the spend key to later -spend the output. - -The remainder of this section defines each step. +To receive, the receiver re-derives the candidate spend keys and output scripts from +input_hash·A and its scan key, matches them against the transaction's +outputs, and on a match derives the blinding key to unblind and the spend key to spend. ===Receiver keys and address=== -The receiver's scan and spend key pairs are independent secp256k1 key pairs. '''[BIP-352]''' -Their derivation from a seed is left to the wallet; a deterministic scheme analogous -to BIP-352's (a dedicated purpose under BIP-32) is RECOMMENDED but out of scope for -interoperability, since only the public keys are transmitted. '''[Choice]''' +The scan and spend key pairs are independent secp256k1 key pairs as in BIP-352. '''[BIP-352]''' +Seed derivation is left to the wallet (a BIP-32 scheme analogous to BIP-352's is +RECOMMENDED), since only the public keys are transmitted. '''[Choice]''' The address is the Bech32m '''[BIP-352]''' encoding of a version symbol followed by the payload serP(B_scan) || serP(B_spend) (66 bytes), with a @@ -124,24 +106,12 @@ nor a mainnet address with a testnet one. ===Input aggregation and the input hash=== -A sender forms the aggregated input private key by summing the private keys of all -'''eligible''' inputs (defined below): '''[BIP-352]''' - -
-a = a_1 + a_2 + ... + a_u   (mod n)
-A = a·G
-
- -If a = 0 the sender MUST abort: no payment is defined. '''[BIP-352]''' - -Let outpoint_L be the lexicographically smallest input outpoint, encoded -as in a transaction (32-byte txid in internal byte order followed by the 4-byte -little-endian index — identical to the Elements consensus encoding of an outpoint). -The input hash is: '''[BIP-352]''' - -
-input_hash = int(hashBIP0352/Inputs( outpoint_L || serP(A) )) mod n
-
+Input aggregation (a, A) and the input_hash are +computed exactly as in BIP-352, over the '''eligible''' inputs defined below. '''[BIP-352]''' +The only Liquid-specific note on encoding is that outpoint_L — the +lexicographically smallest input outpoint used in the input hash — is encoded as in a +transaction (32-byte txid in internal byte order followed by the 4-byte little-endian +index), which is identical to the Elements consensus encoding of an outpoint. ====Eligible inputs '''[Liquid] [Choice]'''==== @@ -157,36 +127,28 @@ peg-in input. Concretely this draft includes: and excludes issuance/reissuance inputs, peg-in inputs, the fee output (which is never an input), and any input whose signing public key is not uniquely determined. -This mirrors BIP-352's eligible-input set with two Liquid-specific notes. First, -Liquid blinds the asset and amount of an input's spent output, but the '''signing -public key''' used here is the one revealed in the witness/scriptSig and is -independent of blinding; aggregation therefore proceeds exactly as on Bitcoin. -Second, BIP-352's requirement to negate Taproot input keys to an even Y-coordinate -before summing does '''not''' apply here, because the eligible types above are not -Taproot key-path spends; keys are summed directly. Should Liquid Taproot key-path -spends become eligible in a future version, the BIP-352 even-Y rule would apply to -them unchanged. +This mirrors BIP-352's eligible-input set, with two Liquid-specific notes. First, the +signing public key used here is the one revealed in the witness/scriptSig and is +independent of CT blinding, so aggregation proceeds exactly as on Bitcoin. Second, +BIP-352's even-Y negation of Taproot input keys does '''not''' apply, since none of +the eligible types above are Taproot key-path spends; keys are summed directly. The +rule would apply unchanged should Liquid Taproot key-path spends become eligible in a +future version. -===Shared secret and output spend key=== +===Shared secret and output spend key '''[BIP-352]'''=== -Both parties compute the same ECDH shared secret: '''[BIP-352]''' - -
-sender:    S = input_hash · a · B_scan
-receiver:  S = input_hash · b_scan · A
-
- -For each output index k = 0, 1, 2, ... paid to the same recipient in the -same transaction: '''[BIP-352]''' +The ECDH shared secret S, the per-output tweak t_k, and the +output spend key P_k = B_spend + t_k·G are computed exactly as in BIP-352; +the receiver spends a recognized output at index k with the private key +b_spend + t_k (mod n). The symbols S and k are +restated here only because the Liquid-specific blinding key below is derived from them:
+S   = input_hash · a · B_scan   (sender)  =  input_hash · b_scan · A   (receiver)
 t_k = int(hashBIP0352/SharedSecret( serP(S) || ser32(k) )) mod n
 P_k = B_spend + t_k·G
 
-The receiver, upon recognizing an output at index k, can spend it with -the private key b_spend + t_k (mod n). '''[BIP-352]''' - ===Output representation '''[Liquid] [Choice]'''=== In BIP-352 the output is a P2TR output whose key is P_k, visible in the @@ -253,46 +215,22 @@ eliminate. ===Labels '''[BIP-352]'''=== -Labels are supported exactly as in BIP-352. For an integer label m: - -
-label_tweak_m = int(hashBIP0352/Label( ser256(b_scan) || ser32(m) )) mod n
-B_spend,m     = B_spend + label_tweak_m·G
-
- -A labeled address encodes B_spend,m in place of B_spend; the -scan key is unchanged. The output spend key becomes B_spend,m + t_k·G and -is spendable with b_spend + t_k + label_tweak_m (mod n). Label -m = 0 is reserved to mark change. The output blinding key is unaffected -by labels: it depends only on S and k. +Labels are supported exactly as in BIP-352 and need no Liquid adaptation. The only +Liquid-specific note is that the output blinding key (below) is unaffected by labels: +it depends only on S and k, not on the labeled spend key. Implementations MAY support only the change label (m = 0) in a first version. '''[Choice]''' ==Light-client receive: the tweak server model== -The receive bounty for this work requires following the tweak-server model of the -[https://github.com/silent-payments/BIP0352-index-server-specification BIP-352 index server specification]. -That model has a server compute, per silent-payment-eligible transaction, the -'''partial tweak''' '''[BIP-352]''': - -
-T = input_hash · A
-
- -a single public key that requires no scan key to compute. A light client fetches the -partial tweaks for a block height, completes the shared secret locally with its scan -key, and matches: - -
-S = b_scan · T   (= input_hash · b_scan · A)
-
- -For each k, the client derives P_k and the candidate output -script and checks it against the transaction's outputs, advancing k -until a configurable number of consecutive misses (a gap limit), since multiple -outputs may be paid to the same address. '''[BIP-352]''' On a match the client -derives bk_k to unblind and b_spend + t_k to spend. +The light-client receive flow follows the tweak-server model of the +[https://github.com/silent-payments/BIP0352-index-server-specification BIP-352 index server specification] +unchanged: the server publishes a per-transaction '''partial tweak''' +T = input_hash · A (no scan key needed), and the client completes +S = b_scan · T and runs the BIP-352 gap-limit match. '''[BIP-352]''' On a +match it derives bk_k to unblind and b_spend + t_k to spend. +The Liquid-specific divergences are below. ===Matching on Liquid omits compact filters '''[Liquid] [Choice]'''=== @@ -448,44 +386,6 @@ output, the full blinded output is not byte-reproducible; the recovery property instead stated as: an output blinded to BK_k by the construction above unblinds correctly under bk_k, and fails to unblind under any other key. -==Rationale== - -'''Why reuse the BIP-352 key derivation unchanged.''' The spend-key path -(a, A, input_hash, S, -t_k, P_k, labels) is the reviewed cryptographic core of -Silent Payments. Reusing it verbatim maximizes interoperability with existing -implementations and tooling and confines the novel surface to the parts Liquid -actually forces to differ. - -'''Why a confidential P2WPKH output rather than P2TR ([Choice]).''' Liquid wallets -overwhelmingly use elwpkh; using the deployed type avoids a Taproot -dependency and matches existing relay and wallet behavior. The cost is divergence -from BIP-352's x-only convention and a compressed-point value in the index-server -data. A future Taproot output type can be added without disturbing the rest of this -specification. - -'''Why derive the blinding key from the shared secret ([Liquid]).''' This is the -mechanism that lets a confidential output be both discovered and unblinded -non-interactively. A dedicated hash domain keeps the blinding key independent of the -spend key. The security of the scheme reduces to the random-oracle treatment of the -tagged hash and the secrecy of S (which only the sender and the holder -of b_scan can compute); a formal security argument and adversarial -analysis are expected to accompany review. The construction has been validated by a -reference implementation against the test vectors above, including adversarial cases -(an observer holding all public data but not b_scan cannot unblind; -incorrect keys never unblind; spend and blinding values are independent across many -randomized cases). - -'''Why omit BIP-158 on Liquid ([Choice]).''' The index-server filter step exists to -let a client test candidate scripts without downloading every block. Because Liquid -output scripts are public, a client can match candidate scripts directly, so the -filter step is redundant. This is a deliberate, narrowly-scoped deviation from the -Bitcoin index-server model, justified entirely by Liquid's public scripts; it does -not change the cryptographic protocol. - -'''Why a distinct, network-specific HRP ([Choice]).''' To make cross-chain and -cross-network address confusion impossible. - ==Backwards Compatibility== This document defines a new, opt-in address type and output convention. It introduces From ab0c3d604d5139be6833a6da8c54b51263024752 Mon Sep 17 00:00:00 2001 From: 42pupusas Date: Tue, 2 Jun 2026 15:27:44 -0600 Subject: [PATCH 3/6] Condense light-client receive section to a single paragraph The flow is essentially identical to BIP-352; fold the three subsections (filter omission, server interface) into one paragraph plus the interface example. --- elip-silent-payments-liquid.mediawiki | 38 ++++++--------------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/elip-silent-payments-liquid.mediawiki b/elip-silent-payments-liquid.mediawiki index d02363f..27d55b3 100644 --- a/elip-silent-payments-liquid.mediawiki +++ b/elip-silent-payments-liquid.mediawiki @@ -230,41 +230,19 @@ unchanged: the server publishes a per-transaction '''partial tweak''' T = input_hash · A (no scan key needed), and the client completes S = b_scan · T and runs the BIP-352 gap-limit match. '''[BIP-352]''' On a match it derives bk_k to unblind and b_spend + t_k to spend. -The Liquid-specific divergences are below. - -===Matching on Liquid omits compact filters '''[Liquid] [Choice]'''=== - -In the Bitcoin index-server model the client cannot recompute an output's -scriptPubKey from chain data without first guessing the tweak, so it uses -[https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki BIP-158] compact -block filters to test candidate scripts and then downloads only matching blocks. - -On Liquid this intermediate filtering step is unnecessary: Confidential Transactions -blind the asset, amount, and nonce of an output but '''not its scriptPubKey''', so -output scripts are available in the clear from ordinary block or transaction data. -A client that has derived a candidate script can therefore match it directly against -the transaction outputs it already retrieves. Accordingly this draft does not require -BIP-158 filters; an implementation MAY still use a filter or other index as an -optimization, but it is not part of the protocol. This is the one substantive -deviation from the Bitcoin index-server model, and it is forced by — and made safe -by — Liquid's public output scripts. - -===Server interface '''[Choice]'''=== - -The protocol requires only that, for each block height, a client can obtain the list -of partial tweaks T for that block's silent-payment-eligible -transactions. An abstract client interface is therefore minimal: +The only divergence is that the BIP-158 compact-filter step is unnecessary on Liquid +'''[Liquid] [Choice]''': Confidential Transactions blind an output's asset, amount, +and nonce but '''not its scriptPubKey''', so a client matches its derived candidate +scripts directly against the public output scripts it already retrieves. Filters MAY +still be used as an optimization but are not part of the protocol. The protocol +therefore requires only that, per block height, a client can obtain that block's +partial tweaks; the concrete wire format is left to a companion specification or +existing Liquid indexing infrastructure. '''[Choice]'''
 tweaks(block_height) -> [ serP(T_1), serP(T_2), ... ]
 
-A concrete wire format is intentionally left to a companion specification or to -existing Liquid indexing infrastructure; for example a REST endpoint returning an -array of compressed points per height, or an extension of an existing -descriptor-oriented indexing service. The cryptographic protocol in this document is -independent of that choice. - ==Spending a received output '''[Liquid]'''== A received silent-payment output is controlled by the scalar From 5f2a62cbdf317ed94fd410ec1026dd6a34bad8a3 Mon Sep 17 00:00:00 2001 From: 42pupusas Date: Tue, 2 Jun 2026 16:19:49 -0600 Subject: [PATCH 4/6] Make Taproot the output representation, matching BIP-352 exactly Switch silent-payment outputs from confidential P2WPKH to confidential Taproot (OP_1 ), with P_k used directly per BIP-352 (no script tree, no taptweak). This aligns the output, spend path, and index-server data with BIP-352's x-only conventions verbatim. - Output is now Taproot-only; eligible inputs keep BIP-352's full set (P2TR, P2WPKH, P2SH-P2WPKH, P2PKH), so an SP output is itself eligible as a later input and the even-Y rule applies unchanged. - Spending is an ordinary BIP-340 key-path spend (even-Y normalized). - Collapse the now-pure-BIP-352 sections (input aggregation, eligible inputs, shared secret, output representation) into one consolidated 'Reused from BIP-352 unchanged' section. - Update test vectors (Taproot scriptPubKeys) and reference-implementation note (verified against LWK, reproduces vectors byte-for-byte). --- elip-silent-payments-liquid.mediawiki | 117 ++++++++------------------ 1 file changed, 34 insertions(+), 83 deletions(-) diff --git a/elip-silent-payments-liquid.mediawiki b/elip-silent-payments-liquid.mediawiki index 27d55b3..8580678 100644 --- a/elip-silent-payments-liquid.mediawiki +++ b/elip-silent-payments-liquid.mediawiki @@ -72,7 +72,7 @@ To pay the address, a sender: # aggregates the private keys of its eligible transaction inputs into a single scalar a and forms A = a·G '''[BIP-352]'''; # computes a transaction-bound input_hash and an ECDH shared secret S with the receiver's scan key '''[BIP-352]'''; # derives, for output index k, a spend public key P_k that only the receiver can later re-derive '''[BIP-352]'''; -# places P_k in a Liquid output of the deployed type '''[Liquid]''', and blinds that output's asset and amount to a blinding key that is itself derived from S '''[Liquid]'''. +# places P_k in a Taproot (P2TR) output '''[BIP-352]''', and blinds that output's asset and amount to a blinding key that is itself derived from S '''[Liquid]'''. To receive, the receiver re-derives the candidate spend keys and output scripts from input_hash·A and its scan key, matches them against the transaction's @@ -104,73 +104,28 @@ A distinct, network-specific HRP (rather than Bitcoin's sp/ts ensures a Liquid silent-payment address can never be confused with a Bitcoin one, nor a mainnet address with a testnet one. -===Input aggregation and the input hash=== +===Reused from BIP-352 unchanged '''[BIP-352]'''=== -Input aggregation (a, A) and the input_hash are -computed exactly as in BIP-352, over the '''eligible''' inputs defined below. '''[BIP-352]''' -The only Liquid-specific note on encoding is that outpoint_L — the -lexicographically smallest input outpoint used in the input hash — is encoded as in a -transaction (32-byte txid in internal byte order followed by the 4-byte little-endian -index), which is identical to the Elements consensus encoding of an outpoint. - -====Eligible inputs '''[Liquid] [Choice]'''==== - -An input is eligible to contribute to a (sender) and A -(scanner) if and only if it is spent by a single public key whose value is -unambiguously recoverable from the input, and it is not an issuance, reissuance, or -peg-in input. Concretely this draft includes: - -* P2WPKH (elwpkh) — the dominant Liquid output type — taking the public key from the witness; -* P2SH-wrapped P2WPKH — likewise; -* P2PKH — taking the public key from the scriptSig. - -and excludes issuance/reissuance inputs, peg-in inputs, the fee output (which is -never an input), and any input whose signing public key is not uniquely determined. - -This mirrors BIP-352's eligible-input set, with two Liquid-specific notes. First, the -signing public key used here is the one revealed in the witness/scriptSig and is -independent of CT blinding, so aggregation proceeds exactly as on Bitcoin. Second, -BIP-352's even-Y negation of Taproot input keys does '''not''' apply, since none of -the eligible types above are Taproot key-path spends; keys are summed directly. The -rule would apply unchanged should Liquid Taproot key-path spends become eligible in a -future version. - -===Shared secret and output spend key '''[BIP-352]'''=== - -The ECDH shared secret S, the per-output tweak t_k, and the -output spend key P_k = B_spend + t_k·G are computed exactly as in BIP-352; -the receiver spends a recognized output at index k with the private key -b_spend + t_k (mod n). The symbols S and k are -restated here only because the Liquid-specific blinding key below is derived from them: +Because silent-payment outputs are Taproot, the entire derivation and output path is +BIP-352 verbatim; the symbols below are restated only because the Liquid blinding key +(next section) is built from S and k:
+input aggregation: a = Σ a_i,  A = a·G  (eligible inputs per BIP-352)
+input_hash = int(hashBIP0352/Inputs( outpoint_L || serP(A) )) mod n
 S   = input_hash · a · B_scan   (sender)  =  input_hash · b_scan · A   (receiver)
 t_k = int(hashBIP0352/SharedSecret( serP(S) || ser32(k) )) mod n
 P_k = B_spend + t_k·G
+scriptPubKey = OP_1           (P_k used directly; no script tree, no taptweak)
 
-===Output representation '''[Liquid] [Choice]'''=== - -In BIP-352 the output is a P2TR output whose key is P_k, visible in the -clear. Liquid does not deploy Taproot in general wallet usage, and the overwhelmingly -common Liquid output type is elwpkh (version-0 witness public-key hash). -This draft therefore defines a silent-payment output as a '''confidential -version-0 P2WPKH output''' whose witness program is HASH160(serP(P_k)): - -
-scriptPubKey = OP_0 
-
- -with the asset and amount blinded as in any CT output (see the next section). - -A consequence is that the value an index server publishes per transaction is a -'''compressed''' point (or equivalently the candidate scripts derived from it), -rather than the x-only key used in the Bitcoin index-server model. - -Emulating Taproot via Liquid's tapscript support was considered and is deferred: -it adds deployment surface for negligible benefit given current Liquid usage. A -future version MAY define a Taproot output type, at which point the BIP-352 x-only -conventions would apply to it directly. +The eligible-input set (P2TR key-path, P2WPKH, P2SH-P2WPKH, P2PKH), the even-Y rule for +Taproot keys, gap-limited scanning, and the x-only output key are all exactly as in +BIP-352 — so a silent-payment output is itself eligible as an input to a later one. +Two Liquid notes only: outpoint_L uses the Elements consensus outpoint +encoding (32-byte txid, internal order, then 4-byte little-endian vout) '''[Liquid]''', +and the asset and amount are blinded as in any CT output '''[Liquid]''' — see the next +section. The scriptPubKey itself is identical to a BIP-352 output. ===Output blinding key '''[Liquid]'''=== @@ -243,19 +198,14 @@ existing Liquid indexing infrastructure. '''[Choice]''' tweaks(block_height) -> [ serP(T_1), serP(T_2), ... ] -==Spending a received output '''[Liquid]'''== - -A received silent-payment output is controlled by the scalar -d = b_spend + t_k (+ label_tweak_m), which is generally '''not''' a -BIP-32-derivable key. Signing such an input therefore requires a signer that accepts -a base key and an additive tweak (or equivalently the tweaked key directly), rather -than only keys located at a derivation path. +==Spending a received output '''[BIP-352]'''== -This is a software-signer capability: deriving d and producing an -ordinary signature for the corresponding P2WPKH input is straightforward and uses no -new cryptography. It is the only signer-side change Silent Payments require on -Liquid, and it is sufficient for a wallet to send to, receive from, and spend -silent-payment outputs. +Spending is an ordinary BIP-340 Taproot key-path spend with +d = b_spend + t_k (+ label_tweak_m) (even-Y normalized), exactly as in +BIP-352. Since d is not a BIP-32-derivable key, the only signer-side +requirement is a signer that accepts a base key plus an additive tweak rather than only +keys at a derivation path — a software-signer capability, sufficient to send, receive, +and spend silent-payment outputs. Hardware-signer support is a separate matter. Common hardware-signer protocols identify the signing key by a registered descriptor or derivation path and expose no @@ -295,7 +245,7 @@ A derived silent-payment output (for index k): SilentPaymentOutput { spend_pubkey: P_k = B_spend + t_k·G blinding_pubkey: BK_k = bk_k·G - // scriptPubKey = OP_0 , asset/amount blinded to BK_k + // scriptPubKey = OP_1 , asset/amount blinded to BK_k } @@ -318,7 +268,7 @@ b_scan = 1111111111111111111111111111111111111111111111111111111111111111 b_spend = 2222222222222222222222222222222222222222222222222222222222222222 -Two eligible inputs, each a P2WPKH spend, with private keys and outpoints: +Two eligible inputs (any BIP-352-eligible type), with private keys and outpoints:
 input 0: priv = 3131...31 (0x31 x32), outpoint txid = 1010...10 (0x10 x32), vout = 0
@@ -340,14 +290,14 @@ k = 0:
   BK_k (blinding pub) = 0344e1289497e6da66fde710d2f38de053fc07355e405524401d7d609df5a1a8cc
   bk_k (blinding priv) = 70ab8897b64bd21b427339ff4d014b883191ef6425862246c53bfc27a59aa3f0
   spend priv (b_spend + t_k) = f03c436d2cd67ae1fecf7d88a38aa3a03c0abea43feaf6da8eb71e2e3a866bda
-  scriptPubKey = 0014aad1065758c02efbf32e797f108007741068eb25
+  scriptPubKey = 5120a29d9716417c964ca9e477343e71ffe730a4991a3eaad668eabec84e9feb7931
 
 k = 1:
   P_k (spend pubkey) = 0229d77654023af267dbe9cb7ff1956f947c816f203494381308387168fb010c92
   BK_k (blinding pub) = 03efdeda770ccdbe8bf466fba48bfd2b2c436ab0c04658fc6d6c277de5078129fa
   bk_k (blinding priv) = 945ba73a9804f62089c7d2ffdc079031031f0aebab372cec17ef9c110ebceb10
   spend priv (b_spend + t_k) = 9eff3472230fc83ef5ea8f8c80401c4eecd595a048bd2482a107d3a49baa5a58
-  scriptPubKey = 001481bc88e345e0a585da4ee2157eef9e18bff416cf
+  scriptPubKey = 512029d77654023af267dbe9cb7ff1956f947c816f203494381308387168fb010c92
 
The unlabeled mainnet (HRP lq) address for these keys: @@ -369,8 +319,8 @@ unblinds correctly under bk_k, and fails to unblind under any other This document defines a new, opt-in address type and output convention. It introduces no consensus change and does not affect existing addresses, descriptors, or transactions. Wallets that do not implement it are unaffected; a silent-payment -output, once created, is an ordinary confidential P2WPKH output on the chain and is -spent by an ordinary signature, so existing relay and validation rules apply +output, once created, is an ordinary confidential Taproot output on the chain and is +spent by an ordinary key-path signature, so existing relay and validation rules apply unchanged. Discovering and spending silent-payment outputs requires wallet support (scanning and @@ -380,11 +330,12 @@ outputs are usable with software signing. ==Reference Implementation== -A reference implementation demonstrating address encoding, input aggregation, -sender output derivation, the shared-secret-derived blinding key, confidential output -construction and recovery, the tweak-server scan flow, labels, and tweak-aware -signing, together with the test vectors and adversarial tests referenced above, is -expected to accompany this proposal prior to a status of Final. +A reference implementation against the Liquid Wallet Kit (LWK) covers address +encoding, input aggregation, sender output derivation, the shared-secret-derived +blinding key, confidential Taproot output construction and recovery, the tweak-server +scan flow, labels, and tweak-aware BIP-340 key-path signing (including even-Y +normalization), together with the test vectors and the adversarial property tests +referenced above. It reproduces the test vectors in this document byte-for-byte. ==Acknowledgements== From 0124bbb78954ea60bc8b5e055dcc1d49edf96fae Mon Sep 17 00:00:00 2001 From: 42pupusas Date: Tue, 2 Jun 2026 16:31:30 -0600 Subject: [PATCH 5/6] Fix SP address HRP collision with native Liquid; drop standalone Labels section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous lq/tlq HRP collided with ordinary Liquid CONFIDENTIAL addresses (blech32 lq/tlq), defeating the distinct-HRP goal. Switch to lqsp/tlqsp, which differ from every existing Liquid HRP (ex/tex unconfidential, lq/tlq confidential) and from Bitcoin's sp/tsp. Update the test-vector address accordingly (verified against LWK). Also fold the Labels section into the consolidated BIP-352 reused section (labels need no Liquid adaptation); the one substantive note — the blinding key is label-independent — moves to the blinding-key section. --- elip-silent-payments-liquid.mediawiki | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/elip-silent-payments-liquid.mediawiki b/elip-silent-payments-liquid.mediawiki index 8580678..8062ab0 100644 --- a/elip-silent-payments-liquid.mediawiki +++ b/elip-silent-payments-liquid.mediawiki @@ -91,18 +91,24 @@ network-specific human-readable part. '''[Liquid] [Choice]''' This draft uses: {| class="wikitable" ! Network !! HRP |- -| Liquid (mainnet) || lq +| Liquid (mainnet) || lqsp |- -| Liquid testnet / regtest || tlq +| Liquid testnet / regtest || tlqsp |} and version symbol q (the Bech32 character for value 0), denoting version 0. As in BIP-352, the 90-character Bech32 length limit does '''not''' apply to silent-payment addresses. '''[BIP-352]''' -A distinct, network-specific HRP (rather than Bitcoin's sp/tsp) -ensures a Liquid silent-payment address can never be confused with a Bitcoin one, -nor a mainnet address with a testnet one. +A distinct, network-specific HRP is required: it must differ both from Bitcoin's +silent-payment HRP (sp/tsp) '''and''' from the HRPs of +ordinary Liquid addresses — in particular Liquid's '''confidential''' addresses use +lq/tlq (blech32), so a silent-payment HRP of lq +would collide with them. The lqsp/tlqsp HRPs are distinct +from every existing Liquid address HRP (ex/tex for +unconfidential, lq/tlq for confidential) as well as from +Bitcoin's, so a silent-payment address can never be confused with a Bitcoin one, with +a native Liquid one, nor a mainnet address with a testnet one. ===Reused from BIP-352 unchanged '''[BIP-352]'''=== @@ -120,8 +126,9 @@ scriptPubKey = OP_1 (P_k used directly; no script tree, n The eligible-input set (P2TR key-path, P2WPKH, P2SH-P2WPKH, P2PKH), the even-Y rule for -Taproot keys, gap-limited scanning, and the x-only output key are all exactly as in -BIP-352 — so a silent-payment output is itself eligible as an input to a later one. +Taproot keys, gap-limited scanning, labels (including the change label m = 0), +and the x-only output key are all exactly as in BIP-352 — so a silent-payment output is +itself eligible as an input to a later one. Two Liquid notes only: outpoint_L uses the Elements consensus outpoint encoding (32-byte txid, internal order, then 4-byte little-endian vout) '''[Liquid]''', and the asset and amount are blinded as in any CT output '''[Liquid]''' — see the next @@ -160,7 +167,9 @@ Because bk_k and t_k are outputs of a random oracle (a tagged hash) evaluated on '''disjoint domains''' over the same secret S, they are independent: knowledge of one does not assist in recovering the other or S. The domain tag LiquidSilentPayments/Blind MUST differ -from the BIP-352 spend domain BIP0352/SharedSecret. +from the BIP-352 spend domain BIP0352/SharedSecret. The blinding key is +also unaffected by BIP-352 labels: it depends only on S and k, +not on the labeled spend key. Two alternatives were rejected. Publishing a single fixed blinding key in the address would link all of a receiver's outputs through a common blinding key, @@ -168,15 +177,6 @@ negating the unlinkability Silent Payments provides. Exchanging a per-output bli key out of band would reintroduce the interaction Silent Payments is designed to eliminate. -===Labels '''[BIP-352]'''=== - -Labels are supported exactly as in BIP-352 and need no Liquid adaptation. The only -Liquid-specific note is that the output blinding key (below) is unaffected by labels: -it depends only on S and k, not on the labeled spend key. - -Implementations MAY support only the change label (m = 0) in a first -version. '''[Choice]''' - ==Light-client receive: the tweak server model== The light-client receive flow follows the tweak-server model of the @@ -300,10 +300,10 @@ k = 1: scriptPubKey = 512029d77654023af267dbe9cb7ff1956f947c816f203494381308387168fb010c92 -The unlabeled mainnet (HRP lq) address for these keys: +The unlabeled mainnet (HRP lqsp) address for these keys:
-lq1qqd8n2k7uklxq4aegau7vawtptkgxsja4kt99lpv6krctwpq8tpc65qjxd4lu4etruh9sngx3su9mtqp5fqzxz7re59y5nnez9p03ht3lyu7836wf
+lqsp1qqd8n2k7uklxq4aegau7vawtptkgxsja4kt99lpv6krctwpq8tpc65qjxd4lu4etruh9sngx3su9mtqp5fqzxz7re59y5nnez9p03ht3lyudcfhfe
 
A conforming implementation MUST reproduce A, input_hash, From 8d634b1e65ff8478db9630340ecc40c5bed4e206 Mon Sep 17 00:00:00 2001 From: 42pupusas Date: Tue, 2 Jun 2026 19:41:34 -0600 Subject: [PATCH 6/6] Scope down Reference Implementation section Reduce the Reference Implementation paragraph to what the public reference covers: it reproduces the test vectors byte-for-byte and demonstrates non-interactive unblinding of the shared-secret-blinded output. Wallet integration (scanning, signing, transaction building) is left to implementations, rather than enumerated here. --- elip-silent-payments-liquid.mediawiki | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/elip-silent-payments-liquid.mediawiki b/elip-silent-payments-liquid.mediawiki index 8062ab0..18b9cfe 100644 --- a/elip-silent-payments-liquid.mediawiki +++ b/elip-silent-payments-liquid.mediawiki @@ -330,12 +330,11 @@ outputs are usable with software signing. ==Reference Implementation== -A reference implementation against the Liquid Wallet Kit (LWK) covers address -encoding, input aggregation, sender output derivation, the shared-secret-derived -blinding key, confidential Taproot output construction and recovery, the tweak-server -scan flow, labels, and tweak-aware BIP-340 key-path signing (including even-Y -normalization), together with the test vectors and the adversarial property tests -referenced above. It reproduces the test vectors in this document byte-for-byte. +A reference implementation, built on the cryptographic primitives of the Liquid Wallet +Kit (LWK), reproduces the test vectors in this document byte-for-byte and demonstrates +that a confidential output blinded to the shared-secret-derived key can be unblinded +non-interactively by the receiver. Wallet integration — scanning, signing, and +transaction building — is left to implementations. ==Acknowledgements==