Skip to content

Commit 70a7143

Browse files
authored
Merge pull request bitcoin#1622 from theStack/bip352-simplify_input-hash_flow
BIP-352: generate `input_hash` after summing up keys (simplification)
2 parents 85cda4e + fe0f835 commit 70a7143

File tree

2 files changed

+7
-9
lines changed

2 files changed

+7
-9
lines changed

bip-0352.mediawiki

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,8 @@ In our simplified example we have been referring to Alice's transactions as havi
9898

9999
Alice performs the tweak with the sum of her input private keys in the following manner:
100100

101-
* Let ''A = A<sub>1</sub> + A<sub>2</sub> + ... + A<sub>n</sub>''
102-
* Let ''input_hash = hash(outpoint<sub>L</sub> || A)'', where ''outpoint<sub>L</sub>'' is the smallest outpoint lexicographically<ref name="why_smallest_outpoint">'''Why use the lexicographically smallest outpoint for the hash?''' Recall that the purpose of including the input hash is so that the sender and receiver can both come up with a deterministic nonce that ensures that a unique address is generated each time, even when reusing the same scriptPubKey as an input. Choosing the smallest outpoint lexicographically satisifes this requirement, while also ensuring that the generated output is not dependent on the final ordering of inputs in the transaction. Using a single outpoint also works well with memory constrained devices (such as hardware signing devices) as it does not require the device to have the entire transaction in memory in order to generate the silent payment output.</ref>
103101
* Let ''a = a<sub>1</sub> + a<sub>2</sub> + ... + a<sub>n</sub>''
102+
* Let ''input_hash = hash(outpoint<sub>L</sub> || (a·G))'', where ''outpoint<sub>L</sub>'' is the smallest outpoint lexicographically<ref name="why_smallest_outpoint">'''Why use the lexicographically smallest outpoint for the hash?''' Recall that the purpose of including the input hash is so that the sender and receiver can both come up with a deterministic nonce that ensures that a unique address is generated each time, even when reusing the same scriptPubKey as an input. Choosing the smallest outpoint lexicographically satisifes this requirement, while also ensuring that the generated output is not dependent on the final ordering of inputs in the transaction. Using a single outpoint also works well with memory constrained devices (such as hardware signing devices) as it does not require the device to have the entire transaction in memory in order to generate the silent payment output.</ref>
104103
* Let ''P<sub>0</sub> = B + hash(input_hash·a·B || 0)·G''
105104
106105
''' Spend and Scan Key '''
@@ -284,7 +283,6 @@ The receiver obtains the public key from the ''scriptSig''. The receiver MUST pa
284283

285284
The sender and receiver MUST calculate an input hash for the transaction in the following manner:
286285

287-
* Let ''A = A<sub>1</sub> + A<sub>2</sub> + ... + A<sub>n</sub>'', where each ''A<sub>i</sub>'' is the public key of an input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list<ref name="why_include_A"></ref>
288286
* Let ''input_hash = hash<sub>BIP0352/Inputs</sub>(outpoint<sub>L</sub> || A)'', where ''outpoint<sub>L</sub>'' is the smallest outpoint lexicographically by txid and vout used in the transaction<ref name="why_smallest_outpoint"></ref>
289287
290288
=== Sender ===
@@ -301,10 +299,10 @@ The sending wallet performs coin selection as usual with the following restricti
301299

302300
After the inputs have been selected, the sender can create one or more outputs for one or more silent payment addresses in the following manner:
303301

304-
* Generate the ''input_hash'' with the smallest outpoint lexicographically, using the method described above
305302
* Collect the private keys for each input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
306303
* For each private key ''a<sub>i</sub>'' corresponding to a [https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki BIP341] taproot output, check that the private key produces a point with an even Y coordinate and negate the private key if not<ref name="why_negate_taproot_private_keys">'''Why do taproot private keys need to be checked?''' Recall from [https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki BIP340] that each X-only public key has two corresponding private keys, ''d'' and ''n - d''. To maintain parity between sender and receiver, it is necessary to use the private key corresponding to the even Y coordinate when performing the ECDH step since the receiver will assume the even Y coordinate when summing the taproot X-only public keys.</ref>
307304
* Let ''a = a<sub>1</sub> + a<sub>2</sub> + ... + a<sub>n</sub>'', where each ''a<sub>i</sub>'' has been negated if necessary
305+
* Generate the ''input_hash'' with the smallest outpoint lexicographically and ''A = a·G'', using the method described above
308306
* Group receiver silent payment addresses by ''B<sub>scan</sub>'' (e.g. each group consists of one ''B<sub>scan</sub>'' and one or more ''B<sub>m</sub>'')
309307
* For each group:
310308
** Let ''ecdh_shared_secret = input_hash·a·B<sub>scan</sub>''
@@ -335,8 +333,8 @@ A scan and spend key pair using BIP32 derivation are defined (taking inspiration
335333

336334
If each of the checks in ''[[#scanning-silent-payment-eligible-transactions|Scanning silent payment eligible transactions]]'' passes, the receiving wallet must:
337335

338-
* Generate the ''input_hash'' with the smallest outpoint lexicographically, using the method described above
339336
* Let ''A = A<sub>1</sub> + A<sub>2</sub> + ... + A<sub>n</sub>'', where each ''A<sub>i</sub>'' is the public key of an input from the ''[[#inputs-for-shared-secret-derivation|Inputs For Shared Secret Derivation]]'' list
337+
* Generate the ''input_hash'' with the smallest outpoint lexicographically and ''A'', using the method described above
340338
* Let ''ecdh_shared_secret = input_hash·b<sub>scan</sub>·A''
341339
* Check for outputs:
342340
** Let ''outputs_to_check'' be the taproot output keys from all taproot outputs in the transaction (spent and unspent).

bip-0352/reference.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def decode_silent_payment_address(address: str, hrp: str = "tsp") -> Tuple[ECPub
117117
return B_scan, B_spend
118118

119119

120-
def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, recipients: List[str], hrp="tsp") -> List[str]:
120+
def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], outpoints: List[COutPoint], recipients: List[str], hrp="tsp") -> List[str]:
121121
G = ECKey().set(1).get_pubkey()
122122
negated_keys = []
123123
for key, is_xonly in input_priv_keys:
@@ -127,6 +127,7 @@ def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes,
127127
negated_keys.append(k)
128128

129129
a_sum = sum(negated_keys)
130+
input_hash = get_input_hash(outpoints, a_sum * G)
130131
silent_payment_groups: Dict[ECPubKey, List[ECPubKey]] = {}
131132
for recipient in recipients:
132133
B_scan, B_m = decode_silent_payment_address(recipient, hrp=hrp)
@@ -236,9 +237,8 @@ def scanning(b_scan: ECKey, B_spend: ECPubKey, A_sum: ECPubKey, input_hash: byte
236237

237238
sending_outputs = []
238239
if (len(input_pub_keys) > 0):
239-
A_sum = reduce(lambda x, y: x + y, input_pub_keys)
240-
input_hash = get_input_hash([vin.outpoint for vin in vins], A_sum)
241-
sending_outputs = create_outputs(input_priv_keys, input_hash, given["recipients"], hrp="sp")
240+
outpoints = [vin.outpoint for vin in vins]
241+
sending_outputs = create_outputs(input_priv_keys, outpoints, given["recipients"], hrp="sp")
242242

243243
# Note: order doesn't matter for creating/finding the outputs. However, different orderings of the recipient addresses
244244
# will produce different generated outputs if sending to multiple silent payment addresses belonging to the

0 commit comments

Comments
 (0)