From 80b5f505636b96110373090f173eb6c81e29b567 Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 09:19:15 +0100 Subject: [PATCH 01/14] add __lt__ method to ECPubKey Add __lt__ operator to allow for sorting lists of ECPubKeys. The comparison is done lexicographically. --- secp256k1.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/secp256k1.py b/secp256k1.py index 10083c6..32ac2cb 100644 --- a/secp256k1.py +++ b/secp256k1.py @@ -254,6 +254,10 @@ def __eq__(self, other): assert isinstance(other, ECPubKey) return self.get_bytes() == other.get_bytes() + def __lt__(self, other): + assert isinstance(other, ECPubKey) + return self.get_bytes(False) < other.get_bytes(False) + def __hash__(self): return hash(self.get_bytes()) From fb0bd41795289e0988a2bb6a4fc98cdd7bd7de39 Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 09:29:02 +0100 Subject: [PATCH 02/14] move derive_keypair func to generate script This function is only needed for generating the test vectors. --- generate-test-vector.py | 47 ++++++++++++++++++++++++++--------------- reference.py | 17 +-------------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/generate-test-vector.py b/generate-test-vector.py index 6dbcfe1..91d9556 100755 --- a/generate-test-vector.py +++ b/generate-test-vector.py @@ -7,6 +7,7 @@ import bip32 from copy import deepcopy from importlib import reload +from typing import Tuple reload(reference) G = ECKey().set(1).get_pubkey() @@ -14,6 +15,18 @@ HRP="sp" #TODO: use clearly mock signatures, e.g. 010203040506... +def derive_silent_payment_key_pair(seed: bytes) -> Tuple[ECKey, ECKey, ECPubKey, ECPubKey]: + SCAN_KEY = "m/352h/0h/0h/1h/0" + SPEND_KEY = "m/352h/0h/0h/0h/0" + + master = bip32.BIP32.from_seed(seed) + scan = ECKey().set(master.get_privkey_from_path(SCAN_KEY)) + spend = ECKey().set(master.get_privkey_from_path(SPEND_KEY)) + Scan = scan.get_pubkey() + Spend = spend.get_pubkey() + + return scan, spend, Scan, Spend + def get_key_pair(index, seed=b'deadbeef', derivation='m/0h'): @@ -112,7 +125,7 @@ def generate_labeled_output_tests(): input_pub_keys = [I1, I2] recipient_bip32_seed = 'f00dbabe' - b_scan, b_spend, B_scan, B_spend = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + b_scan, b_spend, B_scan, B_spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) label_ints = [2, 3, 1001337] address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) @@ -236,7 +249,7 @@ def generate_single_output_outpoint_tests(): recipient['given']['vin'] = inputs - b_scan, b_spend, B_scan, B_spend = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + b_scan, b_spend, B_scan, B_spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) recipient['given']['key_material']['scan_priv_key'] = b_scan.get_bytes().hex() recipient['given']['key_material']['spend_priv_key'] = b_spend.get_bytes().hex() address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) @@ -298,11 +311,11 @@ def generate_multiple_output_tests(): recipient_one_bip32_seed = 'f00dbabe' recipient_two_bip32_seed = 'decafbad' - scan1, spend1, Scan1, Spend1 = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_one_bip32_seed)) + scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_one_bip32_seed)) address1 = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) addresses1 = [(address1, amount) for amount in [2.0, 3.0]] - scan2, spend2, Scan2, Spend2 = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_two_bip32_seed)) + scan2, spend2, Scan2, Spend2 = derive_silent_payment_key_pair(bytes.fromhex(recipient_two_bip32_seed)) address2 = reference.encode_silent_payment_address(Scan2, Spend2, hrp=HRP) addresses2 = [(address2, amount) for amount in [4.0, 5.0]] @@ -488,7 +501,7 @@ def generate_multiple_outputs_with_labels_tests(): input_pub_keys = [I1, I2] recipient_bip32_seed = 'f00dbabe' - scan1, spend1, Scan1, Spend1 = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) l1 = 1 l2 = 1337 @@ -660,7 +673,7 @@ def generate_single_output_input_tests(): sender['given']['vin'] = add_private_keys(deepcopy(inp), inputs[0]) recipient['given']['vin'] = inp - b_scan, b_spend, B_scan, B_spend = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + b_scan, b_spend, B_scan, B_spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) recipient['given']['key_material']['scan_priv_key'] = b_scan.get_bytes().hex() recipient['given']['key_material']['spend_priv_key'] = b_spend.get_bytes().hex() address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) @@ -723,7 +736,7 @@ def generate_change_tests(): input_priv_keys = [(i1, False), (i2, False)] input_pub_keys = [I1, I2] - scan0, spend0, Scan0, Spend0 = reference.derive_silent_payment_key_pair(bytes.fromhex(sender_bip32_seed)) + scan0, spend0, Scan0, Spend0 = derive_silent_payment_key_pair(bytes.fromhex(sender_bip32_seed)) sender_address = reference.encode_silent_payment_address(Scan0, Spend0, hrp=HRP) change_label = 0 change_labels = [change_label] @@ -731,7 +744,7 @@ def generate_change_tests(): recipient_bip32_seed = 'f00dbabe' seeds = [sender_bip32_seed, recipient_bip32_seed] - scan1, spend1, Scan1, Spend1 = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) addresses = [(address, 1.0), (change_address, 2.0)] @@ -773,7 +786,7 @@ def generate_change_tests(): rec['given']['vin'] = inputs rec['given']['outputs'] = output_pub_keys - scan, spend, Scan, Spend = reference.derive_silent_payment_key_pair(bytes.fromhex(seeds[i])) + scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(seeds[i])) add_to_wallet = reference.scanning( scan, Spend, @@ -816,7 +829,7 @@ def generate_unknown_segwit_ver_test(): ] sender_bip32_seed = 'deadbeef' recipient_bip32_seed = 'f00dbabe' - scan, spend, Scan, Spend = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) addresses = [(address, 1.0)] @@ -948,7 +961,7 @@ def generate_taproot_with_nums_point_test(): sender['given']['vin'] = add_private_keys(deepcopy(inp), inputs[0]) recipient['given']['vin'] = inp - b_scan, b_spend, B_scan, B_spend = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + b_scan, b_spend, B_scan, B_spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) recipient['given']['key_material']['scan_priv_key'] = b_scan.get_bytes().hex() recipient['given']['key_material']['spend_priv_key'] = b_spend.get_bytes().hex() address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) @@ -1005,7 +1018,7 @@ def generate_malleated_p2pkh_test(): ] sender_bip32_seed = 'deadbeef' recipient_bip32_seed = 'f00dbabe' - scan, spend, Scan, Spend = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) addresses = [(address, 1.0)] @@ -1107,7 +1120,7 @@ def generate_uncompressed_keys_tests(): input_pub_keys = [] recipient_bip32_seed = 'f00dbabe' - scan, spend, Scan, Spend = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() @@ -1207,7 +1220,7 @@ def generate_p2sh_tests(): ] sender_bip32_seed = 'deadbeef' recipient_bip32_seed = 'f00dbabe' - scan, spend, Scan, Spend = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) addresses = [(address, 1.0)] @@ -1337,7 +1350,7 @@ def generate_no_outputs_tests(): i2.negate() I2.negate() - scan, spend, Scan, Spend = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) addresses = [(address, 1.0)] @@ -1385,7 +1398,7 @@ def generate_no_outputs_tests(): regular_p2tr = I2.get_bytes(True).hex() # Decoy scriptpubkey - scan_decoy, spend_decoy, Scan_decoy, Spend_decoy = reference.derive_silent_payment_key_pair(bytes.fromhex("decafbad")) + scan_decoy, spend_decoy, Scan_decoy, Spend_decoy = derive_silent_payment_key_pair(bytes.fromhex("decafbad")) decoy_address = reference.encode_silent_payment_address(Scan_decoy, Spend_decoy, hrp=HRP) decoy_outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [(decoy_address, 1.0)], hrp=HRP) @@ -1409,7 +1422,7 @@ def generate_no_valid_inputs_tests(): sender_bip32_seed = 'deadbeef' recipient_bip32_seed = 'f00dbabe' - scan, spend, Scan, Spend = reference.derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) addresses = [(address, 1.0)] diff --git a/reference.py b/reference.py index e16d69d..7554ab2 100644 --- a/reference.py +++ b/reference.py @@ -1,11 +1,8 @@ #!/usr/bin/env python3 -import bip32 # type: ignore import hashlib import json -import struct -from io import BytesIO -from typing import List, Tuple, Dict, Union, cast +from typing import List, Tuple, Dict, cast from sys import argv from functools import reduce @@ -88,18 +85,6 @@ def get_input_hash(outpoints: List[COutPoint], sum_input_pubkeys: ECPubKey) -> b return TaggedHash("BIP0352/Inputs", lowest_outpoint.serialize() + cast(bytes, sum_input_pubkeys.get_bytes(False))) -def derive_silent_payment_key_pair(seed: bytes) -> Tuple[ECKey, ECKey, ECPubKey, ECPubKey]: - SCAN_KEY = "m/352h/0h/0h/1h/0" - SPEND_KEY = "m/352h/0h/0h/0h/0" - - master = bip32.BIP32.from_seed(seed) - scan = ECKey().set(master.get_privkey_from_path(SCAN_KEY)) - spend = ECKey().set(master.get_privkey_from_path(SPEND_KEY)) - Scan = scan.get_pubkey() - Spend = spend.get_pubkey() - - return scan, spend, Scan, Spend - def encode_silent_payment_address(B_scan: ECPubKey, B_m: ECPubKey, hrp: str = "tsp", version: int = 0) -> str: data = convertbits(B_scan.get_bytes(False) + B_m.get_bytes(False), 8, 5) From bc4de0868caa5a8b2c63250f7b0385b53c4cc769 Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 09:40:26 +0100 Subject: [PATCH 03/14] remove amount from tests Amount is not used for anything, so better to remove it. To ensure determinism with the tests, the addresses are now sorted lexicographically before generating outputs. This is necessary because the outputs will change based on the order in which addresses for the same recipient are created. For the receiver, this doesnt matter. This is only relevant for determinism in the test vectors. --- generate-test-vector.py | 110 ++++++++++++++++++---------------------- reference.py | 31 +++++------ 2 files changed, 61 insertions(+), 80 deletions(-) diff --git a/generate-test-vector.py b/generate-test-vector.py index 91d9556..d23bdae 100755 --- a/generate-test-vector.py +++ b/generate-test-vector.py @@ -137,7 +137,7 @@ def generate_labeled_output_tests(): for i, case in enumerate(label_ints): sender, recipient, test_case = new_test_case() address = reference.create_labeled_silent_payment_address(b_scan, B_spend, case, hrp=HRP) - addresses = [(address, 1.0)] + addresses = [address] inputs = [] for i, outpoint in enumerate(outpoints): @@ -161,15 +161,14 @@ def generate_labeled_output_tests(): deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addresses, hrp=HRP) sender['expected']['outputs'] = outputs - output_pub_keys = [r[0] for r in outputs] - recipient['given']['outputs'] = output_pub_keys + recipient['given']['outputs'] = outputs add_to_wallet = reference.scanning( b_scan, B_spend, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], labels={(reference.generate_label(b_scan, l)*G).get_bytes(False).hex():reference.generate_label(b_scan, l).hex() for l in label_ints}, ) for o in add_to_wallet: @@ -254,22 +253,21 @@ def generate_single_output_outpoint_tests(): recipient['given']['key_material']['spend_priv_key'] = b_spend.get_bytes().hex() address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) - sender['given']['recipients'].extend([(address, 1.0)]) + sender['given']['recipients'].extend([address]) recipient['expected']['addresses'].extend([address]) A_sum = sum(input_pub_keys) deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [(address, 1.0)], hrp=HRP) + outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [address], hrp=HRP) sender['expected']['outputs'] = outputs - output_pub_keys = [recipient[0] for recipient in outputs] - recipient['given']['outputs'] = output_pub_keys + recipient['given']['outputs'] = outputs add_to_wallet = reference.scanning( b_scan, B_spend, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], ) for o in add_to_wallet: @@ -313,11 +311,11 @@ def generate_multiple_output_tests(): scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_one_bip32_seed)) address1 = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) - addresses1 = [(address1, amount) for amount in [2.0, 3.0]] + addresses1 = [address1, address1] scan2, spend2, Scan2, Spend2 = derive_silent_payment_key_pair(bytes.fromhex(recipient_two_bip32_seed)) address2 = reference.encode_silent_payment_address(Scan2, Spend2, hrp=HRP) - addresses2 = [(address2, amount) for amount in [4.0, 5.0]] + addresses2 = [address2, address2] test_cases = [] @@ -351,15 +349,14 @@ def generate_multiple_output_tests(): deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addresses1, hrp=HRP) sender['expected']['outputs'] = outputs - output_pub_keys = [recipient[0] for recipient in outputs] - recipient1['given']['outputs'] = output_pub_keys + recipient1['given']['outputs'] = outputs add_to_wallet = reference.scanning( scan1, Spend1, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], ) for o in add_to_wallet: @@ -383,16 +380,15 @@ def generate_multiple_output_tests(): sender1['given']['recipients'] = addresses1 + addresses2 outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addresses1 + addresses2, hrp=HRP) sender1['expected']['outputs'] = outputs - output_pub_keys = [recipient[0] for recipient in outputs] - recipient1['given']['outputs'] = output_pub_keys - recipient2['given']['outputs'] = output_pub_keys + recipient1['given']['outputs'] = outputs + recipient2['given']['outputs'] = outputs add_to_wallet = reference.scanning( scan2, Spend2, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], ) for o in add_to_wallet: @@ -447,22 +443,21 @@ def generate_paying_to_self_test(): recipient['given']['spend_priv_key'] = b_spend.get_bytes().hex() address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) - sender['given']['recipients'].extend([(address, 1.0)]) + sender['given']['recipients'].extend([address]) recipient['expected']['addresses'].extend([address]) A_sum = sum(input_pub_keys) deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [(address, 1.0)], hrp=HRP) + outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [address], hrp=HRP) sender['expected']['outputs'] = outputs - output_pub_keys = [recipient[0] for recipient in outputs] - recipient['given']['outputs'] = output_pub_keys + recipient['given']['outputs'] = outputs add_to_wallet = reference.scanning( b_scan, B_spend, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], ) for o in add_to_wallet: @@ -509,9 +504,9 @@ def generate_multiple_outputs_with_labels_tests(): labels_three = [l1, l2] label_address_one = reference.create_labeled_silent_payment_address(scan1, Spend1, m=l1, hrp=HRP) label_address_two = reference.create_labeled_silent_payment_address(scan1, Spend1, m=l2, hrp=HRP) - addresses1 = [(address, 1.0), (label_address_one, 2.0)] - addresses2 = [(label_address_one, 3.0), (label_address_one, 4.0)] - addresses3 = [(address, 5.0), (label_address_one, 6.0), (label_address_two, 7.0), (label_address_two, 8.0)] + addresses1 = [address, label_address_one] + addresses2 = [label_address_one, label_address_one] + addresses3 = [address, label_address_one, label_address_two, label_address_two] test_cases = [] labels = [labels_one, labels_one, labels_three] @@ -546,15 +541,14 @@ def generate_multiple_outputs_with_labels_tests(): deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addrs, hrp=HRP) sender['expected']['outputs'] = outputs - output_pub_keys = [recipient[0] for recipient in outputs] - recipient['given']['outputs'] = output_pub_keys + recipient['given']['outputs'] = outputs add_to_wallet = reference.scanning( scan1, Spend1, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], labels={(reference.generate_label(scan1, l)*G).get_bytes(False).hex():reference.generate_label(scan1, l).hex() for l in labels[i]}, ) for o in add_to_wallet: @@ -678,23 +672,22 @@ def generate_single_output_input_tests(): recipient['given']['key_material']['spend_priv_key'] = b_spend.get_bytes().hex() address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) - sender['given']['recipients'].extend([(address, 1.0)]) + sender['given']['recipients'].extend([address]) recipient['expected']['addresses'].extend([address]) A_sum = sum([p if not inputs[0][i][1] or p.get_y()%2==0 else p * -1 for i, p in enumerate(inputs[1])]) deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(inputs[0], deterministic_nonce, [(address, 1.0)], hrp=HRP) + outputs = reference.create_outputs(inputs[0], deterministic_nonce, [address], hrp=HRP) sender['expected']['outputs'] = outputs - output_pub_keys = [recipient[0] for recipient in outputs] - recipient['given']['outputs'] = output_pub_keys + recipient['given']['outputs'] = outputs add_to_wallet = reference.scanning( b_scan, B_spend, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], ) for o in add_to_wallet: @@ -746,7 +739,7 @@ def generate_change_tests(): seeds = [sender_bip32_seed, recipient_bip32_seed] scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) - addresses = [(address, 1.0), (change_address, 2.0)] + addresses = [address, change_address] test_cases = [] sp_recipients = [[address, change_address]] @@ -778,13 +771,12 @@ def generate_change_tests(): outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addresses, hrp=HRP) sender['expected']['outputs'] = outputs - output_pub_keys = [recipient[0] for recipient in outputs] test_case['sending'].extend([sender]) labels = [change_labels, []] for i, rec in enumerate([rec1, rec2]): rec['given']['vin'] = inputs - rec['given']['outputs'] = output_pub_keys + rec['given']['outputs'] = outputs scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(seeds[i])) add_to_wallet = reference.scanning( @@ -792,7 +784,7 @@ def generate_change_tests(): Spend, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], labels={(reference.generate_label(scan, l)*G).get_bytes(False).hex():reference.generate_label(scan, l).hex() for l in labels[i]}, ) for o in add_to_wallet: @@ -831,7 +823,7 @@ def generate_unknown_segwit_ver_test(): recipient_bip32_seed = 'f00dbabe' scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) - addresses = [(address, 1.0)] + addresses = [address] recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() recipient['given']['key_material']['spend_priv_key'] = spend.get_bytes().hex() @@ -966,23 +958,22 @@ def generate_taproot_with_nums_point_test(): recipient['given']['key_material']['spend_priv_key'] = b_spend.get_bytes().hex() address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) - sender['given']['recipients'].extend([(address, 1.0)]) + sender['given']['recipients'].extend([address]) recipient['expected']['addresses'].extend([address]) A_sum = sum([p if p.get_y()%2==0 else p * -1 for p in included_pubkeys]) deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs([(inp[0], True) for inp in included_keys], deterministic_nonce, [(address, 1.0)], hrp=HRP) + outputs = reference.create_outputs([(inp[0], True) for inp in included_keys], deterministic_nonce, [address], hrp=HRP) sender['expected']['outputs'] = outputs - output_pub_keys = [recipient[0] for recipient in outputs] - recipient['given']['outputs'] = output_pub_keys + recipient['given']['outputs'] = outputs add_to_wallet = reference.scanning( b_scan, B_spend, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], ) for o in add_to_wallet: @@ -1020,7 +1011,7 @@ def generate_malleated_p2pkh_test(): recipient_bip32_seed = 'f00dbabe' scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) - addresses = [(address, 1.0)] + addresses = [address] sender['given']['recipients'] = addresses recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() @@ -1066,17 +1057,16 @@ def add_input(inputs, input_priv_keys, input_pub_keys, get_script_sig): sender['expected']['outputs'] = outputs sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - output_pub_keys = [recipient[0] for recipient in outputs] recipient['given']['vin'] = inputs - recipient['given']['outputs'] = output_pub_keys + recipient['given']['outputs'] = outputs add_to_wallet = reference.scanning( scan, Spend, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], labels={}, ) for o in add_to_wallet: @@ -1126,7 +1116,7 @@ def generate_uncompressed_keys_tests(): recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() recipient['given']['key_material']['spend_priv_key'] = spend.get_bytes().hex() recipient['expected']['addresses'] = [address] - addresses = [(address, 1.0)] + addresses = [address] # p2pkh compressed Key i = len(inputs) @@ -1175,18 +1165,17 @@ def generate_uncompressed_keys_tests(): sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) sender['given']['recipients'] = addresses - output_pub_keys = [recipient[0] for recipient in outputs] test_case['sending'].extend([sender]) recipient['given']['vin'] = inputs - recipient['given']['outputs'] = output_pub_keys + recipient['given']['outputs'] = outputs add_to_wallet = reference.scanning( scan, Spend, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], labels={}, ) for o in add_to_wallet: @@ -1222,7 +1211,7 @@ def generate_p2sh_tests(): recipient_bip32_seed = 'f00dbabe' scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) - addresses = [(address, 1.0)] + addresses = [address] sender['given']['recipients'] = addresses recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() @@ -1295,18 +1284,17 @@ def generate_p2sh_tests(): sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) sender['given']['recipients'] = addresses - output_pub_keys = [recipient[0] for recipient in outputs] test_case['sending'].extend([sender]) recipient['given']['vin'] = inputs - recipient['given']['outputs'] = output_pub_keys + recipient['given']['outputs'] = outputs add_to_wallet = reference.scanning( scan, Spend, A_sum, deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in output_pub_keys], + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], labels={}, ) for o in add_to_wallet: @@ -1352,7 +1340,7 @@ def generate_no_outputs_tests(): scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) - addresses = [(address, 1.0)] + addresses = [address] sender['given']['recipients'] = addresses recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() @@ -1400,11 +1388,11 @@ def generate_no_outputs_tests(): # Decoy scriptpubkey scan_decoy, spend_decoy, Scan_decoy, Spend_decoy = derive_silent_payment_key_pair(bytes.fromhex("decafbad")) decoy_address = reference.encode_silent_payment_address(Scan_decoy, Spend_decoy, hrp=HRP) - decoy_outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [(decoy_address, 1.0)], hrp=HRP) + decoy_outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [decoy_address], hrp=HRP) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [(address, 1.0)], hrp=HRP) + outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [address], hrp=HRP) sender['expected']['outputs'] = outputs - recipient['given']['outputs'] = [regular_p2tr] + [d[0] for d in decoy_outputs] + recipient['given']['outputs'] = [regular_p2tr] + decoy_outputs recipient['expected']['outputs'] = [] test_case['sending'].extend([sender]) @@ -1424,7 +1412,7 @@ def generate_no_valid_inputs_tests(): scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) - addresses = [(address, 1.0)] + addresses = [address] sender['given']['recipients'] = addresses recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() diff --git a/reference.py b/reference.py index 7554ab2..c47b591 100644 --- a/reference.py +++ b/reference.py @@ -112,7 +112,7 @@ def decode_silent_payment_address(address: str, hrp: str = "tsp") -> Tuple[ECPub return B_scan, B_spend -def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, recipients: List[Tuple[str, float]], hrp="tsp") -> List[Tuple[str, float]]: +def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, recipients: List[str], hrp="tsp") -> List[Tuple[str, float]]: G = ECKey().set(1).get_pubkey() negated_keys = [] for key, is_xonly in input_priv_keys: @@ -122,28 +122,25 @@ def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, negated_keys.append(k) a_sum = sum(negated_keys) - silent_payment_groups: Dict[ECPubKey, List[Tuple[ECPubKey, float]]] = {} + silent_payment_groups: Dict[ECPubKey, List[ECPubKey]] = {} for recipient in recipients: - addr, amount = recipient - B_scan, B_m = decode_silent_payment_address(addr, hrp=hrp) + B_scan, B_m = decode_silent_payment_address(recipient, hrp=hrp) if B_scan in silent_payment_groups: - silent_payment_groups[B_scan].append((B_m, amount)) + silent_payment_groups[B_scan].append(B_m) else: - silent_payment_groups[B_scan] = [(B_m, amount)] + silent_payment_groups[B_scan] = [B_m] outputs = [] - for B_scan, B_m_values in silent_payment_groups.items(): + sorted_groups = dict(sorted(silent_payment_groups.items())) + for B_scan, B_m_values in sorted_groups.items(): k = 0 ecdh_shared_secret = input_hash * a_sum * B_scan - # Sort B_m_values by amount to ensure determinism in the tests - # Note: the receiver can find the outputs regardless of the ordering, this - # sorting step is only for testing - B_m_values.sort(key=lambda x: x[1]) - for B_m, amount in B_m_values: + B_m_sorted = sorted(B_m_values) + for B_m in B_m_sorted: t_k = TaggedHash("BIP0352/SharedSecret", ecdh_shared_secret.get_bytes(False) + ser_uint32(k)) P_km = B_m + t_k * G - outputs.append((P_km.get_bytes().hex(), amount)) + outputs.append(P_km.get_bytes().hex()) k += 1 return outputs @@ -228,17 +225,13 @@ def scanning(b_scan: ECKey, B_spend: ECPubKey, A_sum: ECPubKey, input_hash: byte is_p2tr(vin.prevout), )) input_pub_keys.append(pubkey) - + sending_outputs = [] if (len(input_pub_keys) > 0): A_sum = reduce(lambda x, y: x + y, input_pub_keys) input_hash = get_input_hash([vin.outpoint for vin in vins], A_sum) - sending_outputs = [ - list(t) - for t in create_outputs(input_priv_keys, input_hash, given["recipients"], hrp="sp") - ] + sending_outputs = create_outputs(input_priv_keys, input_hash, given["recipients"], hrp="sp") # Check that for a given set of inputs, we were able to generate the expected outputs for the receiver - sending_outputs.sort(key=lambda x: cast(float, x[1])) assert sending_outputs == expected["outputs"], "Sending test failed" # Test receiving From 5d1f84877027fe699a1880f3d45af15ccc243a5c Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 09:58:01 +0100 Subject: [PATCH 04/14] add test for labels with multiple recipients --- generate-test-vector.py | 95 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/generate-test-vector.py b/generate-test-vector.py index d23bdae..75f4817 100755 --- a/generate-test-vector.py +++ b/generate-test-vector.py @@ -514,7 +514,7 @@ def generate_multiple_outputs_with_labels_tests(): comments = [ "Multiple outputs with labels: un-labeled and labeled address; same recipient", "Multiple outputs with labels: multiple outputs for labeled address; same recipient", - "Multiple outputs with labels: un-labeled, labeled, and multiple outputs for labeled address; multiple recipients", + "Multiple outputs with labels: un-labeled, labeled, and multiple outputs for labeled address; same recipient", ] for i, addrs in enumerate([addresses1, addresses2, addresses3]): sender, recipient, test_case = new_test_case() @@ -573,6 +573,98 @@ def generate_multiple_outputs_with_labels_tests(): return test_cases +def generate_more_labels_tests(): + + msg = hashlib.sha256(b'message').digest() + aux = hashlib.sha256(b'random auxiliary data').digest() + G = ECKey().set(1).get_pubkey() + recipient_test_cases = [] + outpoints = [ + ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), + ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0), + ] + sender_bip32_seed = 'deadbeef' + i1, I1 = get_key_pair(0, seed=bytes.fromhex(sender_bip32_seed)) + i2, I2 = get_key_pair(1, seed=bytes.fromhex(sender_bip32_seed)) + input_priv_keys = [(i1, False), (i2, False)] + input_pub_keys = [I1, I2] + + recipient_bip32_seed = 'f00dbabe' + scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + address = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) + scan2, spend2, Scan2, Spend2 = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed) + b'01') + address2 = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) + l1 = 1 + l2 = 1337 + labels = [[l1, l2]] + label_address_one = reference.create_labeled_silent_payment_address(scan1, Spend1, m=l1, hrp=HRP) + label_address_two = reference.create_labeled_silent_payment_address(scan1, Spend1, m=l2, hrp=HRP) + label_address_one2 = reference.create_labeled_silent_payment_address(scan2, Spend2, m=l1, hrp=HRP) + label_address_two2 = reference.create_labeled_silent_payment_address(scan2, Spend2, m=l2, hrp=HRP) + addresses1 = [label_address_one, address, address2, label_address_one, label_address_one2, label_address_one, label_address_two2, label_address_two, label_address_two, label_address_two2] + + test_cases = [] + sp_addresses = [[address, label_address_one, label_address_two]] + comments = [ + "Multiple outputs with labels: multiple outputs per multiple lables; multiple recipients", + ] + for a, addrs in enumerate([addresses1]): + sender, recipient, test_case = new_test_case() + + inputs = [] + for i, outpoint in enumerate(outpoints): + inputs += [{ + 'txid': outpoint[0], + 'vout': outpoint[1], + 'scriptSig': get_p2pkh_scriptsig(input_pub_keys[i], input_priv_keys[i][0]), + 'txinwitness': '', + 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(input_pub_keys[i])}}, + }] + + sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) + recipient['given']['vin'] = inputs + + recipient['given']['key_material']['scan_priv_key'] = scan1.get_bytes().hex() + recipient['given']['key_material']['spend_priv_key'] = spend1.get_bytes().hex() + sender['given']['recipients'] = addrs + recipient['expected']['addresses'] = sp_addresses[a] + recipient['given']['labels'] = labels[a] + A_sum = sum(input_pub_keys) + deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) + outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addrs, hrp=HRP) + sender['expected']['outputs'] = outputs + recipient['given']['outputs'] = outputs + + add_to_wallet = reference.scanning( + scan1, + Spend1, + A_sum, + deterministic_nonce, + [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], + labels={(reference.generate_label(scan1, l)*G).get_bytes(False).hex():reference.generate_label(scan1, l).hex() for l in labels[a]}, + ) + for o in add_to_wallet: + + pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) + full_private_key = spend1.add( + bytes.fromhex(o['priv_key_tweak']) + ) + if full_private_key.get_pubkey().get_y()%2 != 0: + full_private_key.negate() + + sig = full_private_key.sign_schnorr(msg, aux) + assert pubkey.verify_schnorr(sig, msg) + o['signature'] = sig.hex() + + recipient['expected']['outputs'] = add_to_wallet + test_case['sending'].extend([sender]) + test_case['receiving'].extend([recipient]) + test_case["comment"] = comments[a] + test_cases.append(test_case) + + return test_cases + + def generate_single_output_input_tests(): msg = hashlib.sha256(b'message').digest() @@ -1470,6 +1562,7 @@ def generate_no_valid_inputs_tests(): generate_multiple_output_tests() + generate_labeled_output_tests() + generate_multiple_outputs_with_labels_tests() + + generate_more_labels_tests() + generate_change_tests() + generate_taproot_with_nums_point_test() + generate_malleated_p2pkh_test() + From a026b44fa9f6a6e24bda4dd07696694d3aa5af0f Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 09:59:03 +0100 Subject: [PATCH 05/14] fix typing errors in reference --- reference.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/reference.py b/reference.py index c47b591..1f489c6 100644 --- a/reference.py +++ b/reference.py @@ -87,8 +87,8 @@ def get_input_hash(outpoints: List[COutPoint], sum_input_pubkeys: ECPubKey) -> b def encode_silent_payment_address(B_scan: ECPubKey, B_m: ECPubKey, hrp: str = "tsp", version: int = 0) -> str: - data = convertbits(B_scan.get_bytes(False) + B_m.get_bytes(False), 8, 5) - return bech32_encode(hrp, [version] + data, Encoding.BECH32M) + data = convertbits(cast(bytes, B_scan.get_bytes(False)) + cast(bytes, B_m.get_bytes(False)), 8, 5) + return bech32_encode(hrp, [version] + cast(List[int], data), Encoding.BECH32M) def generate_label(b_scan: ECKey, m: int) -> bytes: @@ -105,7 +105,9 @@ def create_labeled_silent_payment_address(b_scan: ECKey, B_spend: ECPubKey, m: i def decode_silent_payment_address(address: str, hrp: str = "tsp") -> Tuple[ECPubKey, ECPubKey]: - version, data = decode(hrp, address) + _, data = decode(hrp, address) + if data is None: + return ECPubKey(), ECPubKey() B_scan = ECPubKey().set(data[:33]) B_spend = ECPubKey().set(data[33:]) From 45f72cbc76b04435986116bc117e8e324060d35b Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 10:01:29 +0100 Subject: [PATCH 06/14] update tests Run ./generate-test-vectors.py with all the new changes: 1. Remove amounts 2. Add new labels test case 3. Different outputs for tests with multiple outputs for the same recipient --- send_and_receive_test_vectors.json | 519 +++++++++++++---------------- 1 file changed, 223 insertions(+), 296 deletions(-) diff --git a/send_and_receive_test_vectors.json b/send_and_receive_test_vectors.json index 8d6dcf2..b21e109 100644 --- a/send_and_receive_test_vectors.json +++ b/send_and_receive_test_vectors.json @@ -31,18 +31,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1", - 1.0 - ] + "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1" ] } } @@ -130,18 +124,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1", - 1.0 - ] + "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1" ] } } @@ -229,18 +217,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6", - 1.0 - ] + "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6" ] } } @@ -328,18 +310,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38", - 1.0 - ] + "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38" ] } } @@ -427,18 +403,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566", - 1.0 - ] + "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566" ] } } @@ -526,18 +496,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb", - 1.0 - ] + "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb" ] } } @@ -625,18 +589,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1", - 1.0 - ] + "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1" ] } } @@ -724,18 +682,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0", - 1.0 - ] + "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0" ] } } @@ -823,18 +775,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a", - 1.0 - ] + "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a" ] } } @@ -922,26 +868,14 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 2.0 - ], - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 3.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - 2.0 - ], - [ - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - 3.0 - ] + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" ] } } @@ -974,10 +908,10 @@ } ], "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -1037,42 +971,18 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 2.0 - ], - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 3.0 - ], - [ - "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn", - 4.0 - ], - [ - "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn", - 5.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn", + "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn" ] }, "expected": { "outputs": [ - [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - 2.0 - ], - [ - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - 3.0 - ], - [ - "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - 4.0 - ], - [ - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", - 5.0 - ] + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" ] } } @@ -1105,10 +1015,10 @@ } ], "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -1161,10 +1071,10 @@ } ], "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" ], "key_material": { "spend_priv_key": "9902c3c56e84002a7cd410113a9ab21d142be7f53cf5200720bb01314c5eb920", @@ -1224,18 +1134,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjex54dmqmmv6rw353tsuqhs99ydvadxzrsy9nuvk74epvee55drs734pqq", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjex54dmqmmv6rw353tsuqhs99ydvadxzrsy9nuvk74epvee55drs734pqq" ] }, "expected": { "outputs": [ - [ - "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a", - 1.0 - ] + "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a" ] } } @@ -1330,18 +1234,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqsg59z2rppn4qlkx0yz9sdltmjv3j8zgcqadjn4ug98m3t6plujsq9qvu5n", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqsg59z2rppn4qlkx0yz9sdltmjv3j8zgcqadjn4ug98m3t6plujsq9qvu5n" ] }, "expected": { "outputs": [ - [ - "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c", - 1.0 - ] + "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c" ] } } @@ -1436,18 +1334,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgq7c2zfthc6x3a5yecwc52nxa0kfd20xuz08zyrjpfw4l2j257yq6qgnkdh5", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgq7c2zfthc6x3a5yecwc52nxa0kfd20xuz08zyrjpfw4l2j257yq6qgnkdh5" ] }, "expected": { "outputs": [ - [ - "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951", - 1.0 - ] + "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951" ] } } @@ -1542,26 +1434,14 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ], - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", - 2.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj" ] }, "expected": { "outputs": [ - [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - 1.0 - ], - [ - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", - 2.0 - ] + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c" ] } } @@ -1658,26 +1538,14 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", - 3.0 - ], - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", - 4.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj" ] }, "expected": { "outputs": [ - [ - "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - 3.0 - ], - [ - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", - 4.0 - ] + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c" ] } } @@ -1774,42 +1642,18 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 5.0 - ], - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", - 6.0 - ], - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5", - 7.0 - ], - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5", - 8.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5" ] }, "expected": { "outputs": [ - [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - 5.0 - ], - [ - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", - 6.0 - ], - [ - "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", - 7.0 - ], - [ - "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b", - 8.0 - ] + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", + "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e" ] } } @@ -1842,10 +1686,10 @@ } ], "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", - "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", - "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b" + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", + "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -1860,16 +1704,150 @@ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj" ], + "outputs": [] + } + } + ] + }, + { + "comment": "Multiple outputs with labels: multiple outputs per multiple lables; 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": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "sp1qq0gdy774euwamngny6pkr07nce9u7nv8caf9l8m5pyceunuhkey32q6v49je657lfz3vgfuwjehv93rrw9cfe5ahghq6ngylvuyvfv9czc8fympr", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "sp1qq0gdy774euwamngny6pkr07nce9u7nv8caf9l8m5pyceunuhkey32q3w9q2dudvhtgas7cd9uj2trmsjmlfhtv8gd5vkcj8urrfpynqcau5dzc03", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5", + "sp1qq0gdy774euwamngny6pkr07nce9u7nv8caf9l8m5pyceunuhkey32q3w9q2dudvhtgas7cd9uj2trmsjmlfhtv8gd5vkcj8urrfpynqcau5dzc03" + ] + }, + "expected": { "outputs": [ + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", + "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "89544a5ba58822b4dd2a73b77e01b45c9f8a6d3ba8e52c9edfaec81893e2f09a", + "ed37ca4025c6a85ee12c5ce1e4651ea3ba706db78d78424c288a9ae5c5fab547", + "dac96ebe893ccbe905c308246f36a2896075ffc183530b62e4e02eb79952f679", + "0195159a57026d15b35ec5ff54c9d21f3190dd0324eb33cafddb3fd039ed881e", + "e31a193bbe1638f6e8d71a274d2d7d35652b53f5e286d84fed34b7e20c8f517a", + "d248959d8523b9509c237129702065b8cb93c70627064abed02d278eca0e7873" + ] + } + } + ], + "receiving": [ + { + "given": { + "vin": [ { - "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a", - "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912" + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "vout": 0, + "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac" + } + } }, { - "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", - "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914", - "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75" + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "vout": 0, + "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", + "txinwitness": "", + "prevout": { + "scriptPubKey": { + "hex": "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" + } + } + } + ], + "outputs": [ + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", + "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "89544a5ba58822b4dd2a73b77e01b45c9f8a6d3ba8e52c9edfaec81893e2f09a", + "ed37ca4025c6a85ee12c5ce1e4651ea3ba706db78d78424c288a9ae5c5fab547", + "dac96ebe893ccbe905c308246f36a2896075ffc183530b62e4e02eb79952f679", + "0195159a57026d15b35ec5ff54c9d21f3190dd0324eb33cafddb3fd039ed881e", + "e31a193bbe1638f6e8d71a274d2d7d35652b53f5e286d84fed34b7e20c8f517a", + "d248959d8523b9509c237129702065b8cb93c70627064abed02d278eca0e7873" + ], + "key_material": { + "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", + "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" + }, + "labels": [ + 1, + 1337 + ] + }, + "expected": { + "addresses": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5" + ], + "outputs": [ + { + "pub_key": "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "priv_key_tweak": "736f05e4e3072c3b8656bedef2e9bf54cbcaa2b6fe5320d3e86f5b96874dda71", + "signature": "2e61bb3d79418ecf55f68847cf121bfc12d397b39d1da8643246b2f0a9b96c3daa4bfe9651beb5c9ce20e1f29282c4566400a4b45ee6657ec3b18fdc554da0b4" + }, + { + "pub_key": "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "priv_key_tweak": "191f41b5b8014d6ba4038eac6db11569a8259042218488994d1cc299970bab06", + "signature": "2bb294a78450e2b97f95c16e5161984b8239bd951cc43ceced37af00f6ac26013b0b46d86598f913c95dbbe952945fc30faef97a0c8406e7db59a7c363584ac9" + }, + { + "pub_key": "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", + "priv_key_tweak": "7fcfa2102d4c4dfbf4f9c3e4941af8de7b8cdc01977f57cb1ee03841160c6c1c", + "signature": "ebb32a0caf9c75aa4803cbf0db4ec97d4d62eed24044f7aefa179323a88f441075d2b4582aaa3400659459b2fc04837d2baa309d157b3d61873bd596d1b3a531" + }, + { + "pub_key": "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "priv_key_tweak": "4e3352fbe0505c25e718d96007c259ef08db34f8c844e4ff742d9855ff03805a", + "signature": "6eeae1ea9eb826e3d0e812f65937100e0836ea188c04f36fabc4981eda29de8d3d3529390a0a8b3d830f7bca4f5eae5994b9788ddaf05ad259ffe26d86144b4b" + }, + { + "pub_key": "89544a5ba58822b4dd2a73b77e01b45c9f8a6d3ba8e52c9edfaec81893e2f09a", + "priv_key_tweak": "9dfadb91df9e04706eac530bb720fcb706d849503961fa584bbd73affbfbec88", + "signature": "678c43d1104cbdf8d0f05e04f0bf0ae23a31285b19d13765e5d2f14a73a7fcd1041cd41385ca8ad1eba6067baa8de6df9e2c846d618723e9b97a487b9fcb2e84" } ] } @@ -1908,26 +1886,14 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ], - [ - "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqlv6saelkk5snl4wfutyxrchpzzwm8rjp3z6q7apna59z9huq4x754e5atr", - 2.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", + "sp1qqw6vczcfpdh5nf5y2ky99kmqae0tr30hgdfg88parz50cp80wd2wqqlv6saelkk5snl4wfutyxrchpzzwm8rjp3z6q7apna59z9huq4x754e5atr" ] }, "expected": { "outputs": [ - [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - 1.0 - ], - [ - "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff", - 2.0 - ] + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff" ] } } @@ -2080,18 +2046,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d", - 1.0 - ] + "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d" ] } } @@ -2202,18 +2162,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "4612cdbf845c66c7511d70aab4d9aed11e49e48cdb8d799d787101cdd0d53e4f", - 1.0 - ] + "4612cdbf845c66c7511d70aab4d9aed11e49e48cdb8d799d787101cdd0d53e4f" ] } } @@ -2324,18 +2278,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6", - 1.0 - ] + "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" ] } } @@ -2446,18 +2394,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6", - 1.0 - ] + "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" ] } }, @@ -2502,18 +2444,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6", - 1.0 - ] + "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" ] } } @@ -2581,7 +2517,7 @@ ] }, { - "comment": "Ignore unrelated outputs", + "comment": "Recipient ignores unrelated outputs", "sending": [ { "given": { @@ -2612,18 +2548,12 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - 1.0 - ] + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" ] } } @@ -2706,10 +2636,7 @@ } ], "recipients": [ - [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - 1.0 - ] + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { From 580715a4e9d1858f557d2a6f50c007250e139e39 Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 10:03:04 +0100 Subject: [PATCH 07/14] clean up whitespace --- reference.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/reference.py b/reference.py index 1f489c6..2b75421 100644 --- a/reference.py +++ b/reference.py @@ -71,12 +71,12 @@ def get_pubkey_from_input(vin: VinInfo) -> ECPubKey: if (internal_key == NUMS_H.to_bytes(32, 'big')): # Skip if NUMS_H return ECPubKey() - + pubkey = ECPubKey().set(vin.prevout[2:]) if (pubkey.valid) & (pubkey.compressed): - return pubkey - - + return pubkey + + return ECPubKey() From 3f978a753f363c207977f903a9bc372fb504a6f8 Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 10:09:58 +0100 Subject: [PATCH 08/14] add comment for reference --- reference.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/reference.py b/reference.py index 2b75421..2440e09 100644 --- a/reference.py +++ b/reference.py @@ -133,11 +133,15 @@ def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, silent_payment_groups[B_scan] = [B_m] outputs = [] - sorted_groups = dict(sorted(silent_payment_groups.items())) - for B_scan, B_m_values in sorted_groups.items(): + for B_scan, B_m_values in silent_payment_groups.items(): k = 0 ecdh_shared_secret = input_hash * a_sum * B_scan + # Order doesn't matter for creating/finding the outputs. The only reason + # we sort here is for determinism in the test vectors. When sending to multiple + # addresses belonging to the same recipient (i.e. same scan pubkey), different orderings + # of the spend pubkeys will create different outputs, + # e.g. hash(shared_secret || 1) + Bm vs hash(shared_secret || 2) + Bm B_m_sorted = sorted(B_m_values) for B_m in B_m_sorted: t_k = TaggedHash("BIP0352/SharedSecret", ecdh_shared_secret.get_bytes(False) + ser_uint32(k)) From beceb2b7e0038eae77f89ad14d0b741f0da4ec7f Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 10:10:07 +0100 Subject: [PATCH 09/14] update tests --- send_and_receive_test_vectors.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/send_and_receive_test_vectors.json b/send_and_receive_test_vectors.json index b21e109..e22216e 100644 --- a/send_and_receive_test_vectors.json +++ b/send_and_receive_test_vectors.json @@ -908,10 +908,10 @@ } ], "outputs": [ - "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -979,10 +979,10 @@ }, "expected": { "outputs": [ - "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" ] } } @@ -1015,10 +1015,10 @@ } ], "outputs": [ - "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -1071,10 +1071,10 @@ } ], "outputs": [ - "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" ], "key_material": { "spend_priv_key": "9902c3c56e84002a7cd410113a9ab21d142be7f53cf5200720bb01314c5eb920", From e3044d88cbf228216cf7cc70dd3666a408b86473 Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 09:40:26 +0100 Subject: [PATCH 10/14] remove amount from tests Amount is not used for anything, so better to remove it. To ensure determinism with the tests, the addresses are now sorted lexicographically before generating outputs. This is necessary because the outputs will change based on the order in which addresses for the same recipient are created. For the receiver, this doesnt matter. This is only relevant for determinism in the test vectors. --- generate-test-vector.py | 2 +- reference.py | 13 +++++------- send_and_receive_test_vectors.json | 34 +++++++++++++++--------------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/generate-test-vector.py b/generate-test-vector.py index 75f4817..ef96f6c 100755 --- a/generate-test-vector.py +++ b/generate-test-vector.py @@ -504,7 +504,7 @@ def generate_multiple_outputs_with_labels_tests(): labels_three = [l1, l2] label_address_one = reference.create_labeled_silent_payment_address(scan1, Spend1, m=l1, hrp=HRP) label_address_two = reference.create_labeled_silent_payment_address(scan1, Spend1, m=l2, hrp=HRP) - addresses1 = [address, label_address_one] + addresses1 = [label_address_one, address] addresses2 = [label_address_one, label_address_one] addresses3 = [address, label_address_one, label_address_two, label_address_two] diff --git a/reference.py b/reference.py index 2440e09..93a3416 100644 --- a/reference.py +++ b/reference.py @@ -114,7 +114,7 @@ def decode_silent_payment_address(address: str, hrp: str = "tsp") -> Tuple[ECPub return B_scan, B_spend -def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, recipients: List[str], hrp="tsp") -> List[Tuple[str, float]]: +def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, recipients: List[str], hrp="tsp") -> List[str]: G = ECKey().set(1).get_pubkey() negated_keys = [] for key, is_xonly in input_priv_keys: @@ -137,13 +137,10 @@ def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, k = 0 ecdh_shared_secret = input_hash * a_sum * B_scan - # Order doesn't matter for creating/finding the outputs. The only reason - # we sort here is for determinism in the test vectors. When sending to multiple - # addresses belonging to the same recipient (i.e. same scan pubkey), different orderings - # of the spend pubkeys will create different outputs, - # e.g. hash(shared_secret || 1) + Bm vs hash(shared_secret || 2) + Bm - B_m_sorted = sorted(B_m_values) - for B_m in B_m_sorted: + # Order doesn't matter for creating/finding the outputs. However, different orderings + # may produce different generated outputs, if sending to multiple silent payment addresses belong to the + # same sender but different labels + for B_m in B_m_values: t_k = TaggedHash("BIP0352/SharedSecret", ecdh_shared_secret.get_bytes(False) + ser_uint32(k)) P_km = B_m + t_k * G outputs.append(P_km.get_bytes().hex()) diff --git a/send_and_receive_test_vectors.json b/send_and_receive_test_vectors.json index e22216e..a2306f8 100644 --- a/send_and_receive_test_vectors.json +++ b/send_and_receive_test_vectors.json @@ -1434,14 +1434,14 @@ } ], "recipients": [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj" + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" ] }, "expected": { "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c" + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" ] } } @@ -1474,8 +1474,8 @@ } ], "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c" + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -1492,14 +1492,14 @@ ], "outputs": [ { - "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a", - "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912" + "pub_key": "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "priv_key_tweak": "9d5fd3b91cac9ddfea6fc2e6f9386f680e6cee623cda02f53706306c081de87f", + "signature": "db0dfacc98b6a6fcc67cc4631f080b1ca38c60d8c397f2f19843f8f95ec91594b24e47c5bd39480a861c1209f7e3145c440371f9191fb96e324690101eac8e8e" }, { - "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", - "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914", - "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75" + "pub_key": "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "priv_key_tweak": "d97e442d110c0bdd31161a7bb6e7862e038d02a09b1484dfbb463f2e0f7c9230", + "signature": "29bd25d0f808d7fcd2aa6d5ed206053899198397506c301b218a9e47a3d7070af03e903ff718978d50d1b6b9af8cc0e313d84eda5d5b1e8e85e5516d630bbeb9" } ] } @@ -1650,10 +1650,10 @@ }, "expected": { "outputs": [ - "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", - "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", - "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", - "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e" + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", + "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b" ] } } @@ -2690,4 +2690,4 @@ } ] } -] \ No newline at end of file +] From f346402e8165f2ad26547b33194625bce36388f1 Mon Sep 17 00:00:00 2001 From: josibake Date: Tue, 2 Apr 2024 11:50:28 +0200 Subject: [PATCH 11/14] ensure tests are not order dependent different orderings of the recipient addresses will produce different outputs in some scenarios. for sending and receiving, this does not matter. however, for implementations only testing sending e.g. testing that they can generate the correct outputs from an input set, this is fragile and leads to confusion and wasted time. essentially, the tester needs to fiddle with the order of the recipient addresses until the correct permutation is hit (when in reality any permutation is valid). --- reference.py | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/reference.py b/reference.py index 93a3416..5677f03 100644 --- a/reference.py +++ b/reference.py @@ -5,6 +5,7 @@ from typing import List, Tuple, Dict, cast from sys import argv from functools import reduce +from itertools import permutations # local files from bech32m import convertbits, bech32_encode, decode, Encoding @@ -114,7 +115,7 @@ def decode_silent_payment_address(address: str, hrp: str = "tsp") -> Tuple[ECPub return B_scan, B_spend -def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, recipients: List[str], hrp="tsp") -> List[str]: +def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, recipients: List[str], hrp="tsp", permute=True) -> List[str]: G = ECKey().set(1).get_pubkey() negated_keys = [] for key, is_xonly in input_priv_keys: @@ -134,18 +135,31 @@ def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, outputs = [] for B_scan, B_m_values in silent_payment_groups.items(): - k = 0 ecdh_shared_secret = input_hash * a_sum * B_scan - # Order doesn't matter for creating/finding the outputs. However, different orderings - # may produce different generated outputs, if sending to multiple silent payment addresses belong to the - # same sender but different labels - for B_m in B_m_values: - t_k = TaggedHash("BIP0352/SharedSecret", ecdh_shared_secret.get_bytes(False) + ser_uint32(k)) - P_km = B_m + t_k * G - outputs.append(P_km.get_bytes().hex()) - k += 1 - return outputs + # Order doesn't matter for creating/finding the outputs. However, different orderings of the recipient addresses + # will produce different generated outputs if sending to multiple silent payment addresses belong to the + # same sender but with different labels. To account for this in testing, set `perumute=True`. This will generate + # all possible sets of outputs (based on all possible orderings of recipient addresses). + # + # NOTE: THIS IS A TEST ONLY FEATURE AND NOT RELEVANT FOR NORMAL SENDING/RECEIVING + if permute: + for permutation in permutations(B_m_values, len(B_m_values)): + k = 0 + for B_m in permutation: + t_k = TaggedHash("BIP0352/SharedSecret", ecdh_shared_secret.get_bytes(False) + ser_uint32(k)) + P_km = B_m + t_k * G + outputs.append(P_km.get_bytes().hex()) + k += 1 + else: + k = 0 + for B_m in B_m_values: + t_k = TaggedHash("BIP0352/SharedSecret", ecdh_shared_secret.get_bytes(False) + ser_uint32(k)) + P_km = B_m + t_k * G + outputs.append(P_km.get_bytes().hex()) + k += 1 + + return list(set(outputs)) def scanning(b_scan: ECKey, B_spend: ECPubKey, A_sum: ECPubKey, input_hash: bytes, outputs_to_check: List[ECPubKey], labels: Dict[str, str] = {}) -> List[Dict[str, str]]: @@ -233,9 +247,10 @@ def scanning(b_scan: ECKey, B_spend: ECPubKey, A_sum: ECPubKey, input_hash: byte if (len(input_pub_keys) > 0): A_sum = reduce(lambda x, y: x + y, input_pub_keys) input_hash = get_input_hash([vin.outpoint for vin in vins], A_sum) - sending_outputs = create_outputs(input_priv_keys, input_hash, given["recipients"], hrp="sp") + sending_outputs = create_outputs(input_priv_keys, input_hash, given["recipients"], hrp="sp", permute=False) # Check that for a given set of inputs, we were able to generate the expected outputs for the receiver - assert sending_outputs == expected["outputs"], "Sending test failed" + for output in sending_outputs: + assert output in expected["outputs"], "Sending test failed" # Test receiving msg = hashlib.sha256(b"message").digest() From 39848c19da4cca7e3776fab4fbe9c9354ecee414 Mon Sep 17 00:00:00 2001 From: josibake Date: Tue, 2 Apr 2024 13:42:32 +0200 Subject: [PATCH 12/14] make smallest outpoint an ignored output --- generate-test-vector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate-test-vector.py b/generate-test-vector.py index ef96f6c..362a46d 100755 --- a/generate-test-vector.py +++ b/generate-test-vector.py @@ -1190,8 +1190,8 @@ def generate_uncompressed_keys_tests(): aux = hashlib.sha256(b'random auxiliary data').digest() outpoints = [ - ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0), + ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 1), ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 2), ] From 8b999d5dbefe3abc569a7820696ae56342d02cfc Mon Sep 17 00:00:00 2001 From: josibake Date: Thu, 21 Mar 2024 10:01:29 +0100 Subject: [PATCH 13/14] update tests Run ./generate-test-vectors.py with all the new changes: 1. Remove amounts 2. All possible permutations in outputs for tests with multiple outputs for the same recipient 3. Make smallest outpoint an excluded input --- send_and_receive_test_vectors.json | 182 +++++++---------------------- 1 file changed, 44 insertions(+), 138 deletions(-) diff --git a/send_and_receive_test_vectors.json b/send_and_receive_test_vectors.json index a2306f8..b4119ab 100644 --- a/send_and_receive_test_vectors.json +++ b/send_and_receive_test_vectors.json @@ -874,8 +874,8 @@ }, "expected": { "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" ] } } @@ -908,8 +908,8 @@ } ], "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" ], @@ -979,8 +979,8 @@ }, "expected": { "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" ] @@ -1015,8 +1015,8 @@ } ], "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" ], @@ -1071,8 +1071,8 @@ } ], "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" ], @@ -1441,7 +1441,9 @@ "expected": { "outputs": [ "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c" ] } } @@ -1475,7 +1477,9 @@ ], "outputs": [ "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -1650,10 +1654,18 @@ }, "expected": { "outputs": [ + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e", "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b", + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed", + "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", + "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", - "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", - "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b" + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701" ] } } @@ -1686,10 +1698,18 @@ } ], "outputs": [ + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b", "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed", "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", - "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e" + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -1716,138 +1736,24 @@ "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": [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", - "sp1qq0gdy774euwamngny6pkr07nce9u7nv8caf9l8m5pyceunuhkey32q6v49je657lfz3vgfuwjehv93rrw9cfe5ahghq6ngylvuyvfv9czc8fympr", - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", - "sp1qq0gdy774euwamngny6pkr07nce9u7nv8caf9l8m5pyceunuhkey32q3w9q2dudvhtgas7cd9uj2trmsjmlfhtv8gd5vkcj8urrfpynqcau5dzc03", - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5", - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5", - "sp1qq0gdy774euwamngny6pkr07nce9u7nv8caf9l8m5pyceunuhkey32q3w9q2dudvhtgas7cd9uj2trmsjmlfhtv8gd5vkcj8urrfpynqcau5dzc03" - ] - }, - "expected": { - "outputs": [ - "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", - "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", - "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", - "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", - "89544a5ba58822b4dd2a73b77e01b45c9f8a6d3ba8e52c9edfaec81893e2f09a", - "ed37ca4025c6a85ee12c5ce1e4651ea3ba706db78d78424c288a9ae5c5fab547", - "dac96ebe893ccbe905c308246f36a2896075ffc183530b62e4e02eb79952f679", - "0195159a57026d15b35ec5ff54c9d21f3190dd0324eb33cafddb3fd039ed881e", - "e31a193bbe1638f6e8d71a274d2d7d35652b53f5e286d84fed34b7e20c8f517a", - "d248959d8523b9509c237129702065b8cb93c70627064abed02d278eca0e7873" - ] - } - } - ], - "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": [ - "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", - "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", - "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", - "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", - "89544a5ba58822b4dd2a73b77e01b45c9f8a6d3ba8e52c9edfaec81893e2f09a", - "ed37ca4025c6a85ee12c5ce1e4651ea3ba706db78d78424c288a9ae5c5fab547", - "dac96ebe893ccbe905c308246f36a2896075ffc183530b62e4e02eb79952f679", - "0195159a57026d15b35ec5ff54c9d21f3190dd0324eb33cafddb3fd039ed881e", - "e31a193bbe1638f6e8d71a274d2d7d35652b53f5e286d84fed34b7e20c8f517a", - "d248959d8523b9509c237129702065b8cb93c70627064abed02d278eca0e7873" - ], - "key_material": { - "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", - "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" - }, - "labels": [ - 1, - 1337 - ] - }, - "expected": { - "addresses": [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5" - ], - "outputs": [ - { - "pub_key": "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", - "priv_key_tweak": "736f05e4e3072c3b8656bedef2e9bf54cbcaa2b6fe5320d3e86f5b96874dda71", - "signature": "2e61bb3d79418ecf55f68847cf121bfc12d397b39d1da8643246b2f0a9b96c3daa4bfe9651beb5c9ce20e1f29282c4566400a4b45ee6657ec3b18fdc554da0b4" + "pub_key": "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "priv_key_tweak": "9d5fd3b91cac9ddfea6fc2e6f9386f680e6cee623cda02f53706306c081de87f", + "signature": "db0dfacc98b6a6fcc67cc4631f080b1ca38c60d8c397f2f19843f8f95ec91594b24e47c5bd39480a861c1209f7e3145c440371f9191fb96e324690101eac8e8e" }, { - "pub_key": "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", - "priv_key_tweak": "191f41b5b8014d6ba4038eac6db11569a8259042218488994d1cc299970bab06", - "signature": "2bb294a78450e2b97f95c16e5161984b8239bd951cc43ceced37af00f6ac26013b0b46d86598f913c95dbbe952945fc30faef97a0c8406e7db59a7c363584ac9" + "pub_key": "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "priv_key_tweak": "d97e442d110c0bdd31161a7bb6e7862e038d02a09b1484dfbb463f2e0f7c9230", + "signature": "29bd25d0f808d7fcd2aa6d5ed206053899198397506c301b218a9e47a3d7070af03e903ff718978d50d1b6b9af8cc0e313d84eda5d5b1e8e85e5516d630bbeb9" }, { - "pub_key": "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", - "priv_key_tweak": "7fcfa2102d4c4dfbf4f9c3e4941af8de7b8cdc01977f57cb1ee03841160c6c1c", - "signature": "ebb32a0caf9c75aa4803cbf0db4ec97d4d62eed24044f7aefa179323a88f441075d2b4582aaa3400659459b2fc04837d2baa309d157b3d61873bd596d1b3a531" + "pub_key": "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed", + "priv_key_tweak": "e9616d6d0de7012ecc003c1d5133382c1d7692350bbedde1bf1fef0eeea1d441", + "signature": "7a9ff514d8b42014f0dde5a5f800eb9fc876680cac89af0777cfbb59af2bb5e3c16cc807edfcfaf70a910dbcdda8a66fda6a03c1757559bdae3304aadf78e638" }, { "pub_key": "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", "priv_key_tweak": "4e3352fbe0505c25e718d96007c259ef08db34f8c844e4ff742d9855ff03805a", "signature": "6eeae1ea9eb826e3d0e812f65937100e0836ea188c04f36fabc4981eda29de8d3d3529390a0a8b3d830f7bca4f5eae5994b9788ddaf05ad259ffe26d86144b4b" - }, - { - "pub_key": "89544a5ba58822b4dd2a73b77e01b45c9f8a6d3ba8e52c9edfaec81893e2f09a", - "priv_key_tweak": "9dfadb91df9e04706eac530bb720fcb706d849503961fa584bbd73affbfbec88", - "signature": "678c43d1104cbdf8d0f05e04f0bf0ae23a31285b19d13765e5d2f14a73a7fcd1041cd41385ca8ad1eba6067baa8de6df9e2c846d618723e9b97a487b9fcb2e84" } ] } @@ -2241,7 +2147,7 @@ "given": { "vin": [ { - "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", "vout": 0, "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", "txinwitness": "", @@ -2253,7 +2159,7 @@ "private_key": "eadc78165ff1f8ea94ad7cfdc54990738a4c53f6e0507b42154201b8e5dff3b1" }, { - "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", "vout": 0, "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b974104782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c3799373233387c5343bf58e23269e903335b958a12182f9849297321e8d710e49a8727129cab", "txinwitness": "", @@ -2293,7 +2199,7 @@ "given": { "vin": [ { - "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", + "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", "vout": 0, "scriptSig": "483046022100ad79e6801dd9a8727f342f31c71c4912866f59dc6e7981878e92c5844a0ce929022100fb0d2393e813968648b9753b7e9871d90ab3d815ebf91820d704b19f4ed224d621025a1e61f898173040e20616d43e9f496fba90338a39faa1ed98fcbaeee4dd9be5", "txinwitness": "", @@ -2304,7 +2210,7 @@ } }, { - "txid": "a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", + "txid": "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", "vout": 0, "scriptSig": "473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b974104782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c3799373233387c5343bf58e23269e903335b958a12182f9849297321e8d710e49a8727129cab", "txinwitness": "", From 404fe33e9318f200959e76875505edec7be10e90 Mon Sep 17 00:00:00 2001 From: josibake Date: Sat, 27 Apr 2024 12:59:03 +0200 Subject: [PATCH 14/14] cleanup `generate-test-vector` general refactor, namely: * reduce reused code * make test generation deterministic * only generate multiple output sets for sender * give each candidate set as its own result * add new test case for lexicographic sort * remove test related code from reference.py --- generate-test-vector.py | 1175 +++++++--------------------- reference.py | 65 +- send_and_receive_test_vectors.json | 580 ++++++++------ 3 files changed, 646 insertions(+), 1174 deletions(-) mode change 100644 => 100755 reference.py diff --git a/generate-test-vector.py b/generate-test-vector.py index 362a46d..4e210f3 100755 --- a/generate-test-vector.py +++ b/generate-test-vector.py @@ -6,15 +6,11 @@ from bitcoin_utils import deser_txid, hash160, COutPoint import bip32 from copy import deepcopy -from importlib import reload -from typing import Tuple -reload(reference) - -G = ECKey().set(1).get_pubkey() -sending_test_vectors = [] +from typing import Tuple, List +from itertools import permutations HRP="sp" -#TODO: use clearly mock signatures, e.g. 010203040506... + def derive_silent_payment_key_pair(seed: bytes) -> Tuple[ECKey, ECKey, ECPubKey, ECPubKey]: SCAN_KEY = "m/352h/0h/0h/1h/0" SPEND_KEY = "m/352h/0h/0h/0h/0" @@ -27,7 +23,6 @@ def derive_silent_payment_key_pair(seed: bytes) -> Tuple[ECKey, ECKey, ECPubKey, return scan, spend, Scan, Spend - def get_key_pair(index, seed=b'deadbeef', derivation='m/0h'): master = bip32.BIP32.from_seed(seed) @@ -36,12 +31,6 @@ def get_key_pair(index, seed=b'deadbeef', derivation='m/0h'): return d, P -def add_private_keys(inputs, input_priv_keys): - for x, i in enumerate(inputs): - i['private_key'] = input_priv_keys[x][0].get_bytes().hex() - - return inputs - def get_p2pkh_scriptsig(pub_key, priv_key): msg = hashlib.sha256(b'message').digest() sig = priv_key.sign_ecdsa(msg, low_s=False, rfc6979=True).hex() @@ -73,7 +62,108 @@ def serialize_witness_stack(stack_items): result += f'{size:02x}' + item return result -def new_test_case(): +def unique_permutations(lst): + # Generate all permutations of the list + perms = permutations(lst, len(lst)) + unique_perms = set(perms) + unique_perms_list = list(unique_perms) + return unique_perms_list + +def create_output_permutations(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, recipients: List[str], hrp="tsp") -> List[List[str]]: + G = ECKey().set(1).get_pubkey() + negated_keys = [] + for key, is_xonly in input_priv_keys: + k = ECKey().set(key.get_bytes()) + if is_xonly and k.get_pubkey().get_y() % 2 != 0: + k.negate() + negated_keys.append(k) + + a_sum = sum(negated_keys) + all_outputs = [] + for permutation in unique_permutations(recipients): + silent_payment_groups: Dict[ECPubKey, List[ECPubKey]] = {} + for recipient in permutation: + B_scan, B_m = reference.decode_silent_payment_address(recipient, hrp=hrp) + if B_scan in silent_payment_groups: + silent_payment_groups[B_scan].append(B_m) + else: + silent_payment_groups[B_scan] = [B_m] + outputs = [] + for B_scan, B_m_values in silent_payment_groups.items(): + ecdh_shared_secret = input_hash * a_sum * B_scan + k = 0 + for B_m in B_m_values: + t_k = TaggedHash("BIP0352/SharedSecret", ecdh_shared_secret.get_bytes(False) + reference.ser_uint32(k)) + P_km = B_m + t_k * G + outputs.append(P_km.get_bytes().hex()) + k += 1 + all_outputs.append(outputs) + set_of_sets = set() + for s in all_outputs: + set_of_sets.add(frozenset(s)) + return [sorted(list(fz)) for fz in set_of_sets] + +def custom_sort(sublist): + return tuple(sorted(sublist)) + +def sort_list_of_lists(list_of_lists): + return sorted(list_of_lists, key=custom_sort) + +def generated_test_case(inputs, scan_key, spend_key, addrs, labels, comment): + test_case = { + "comment": "", + "sending": [], + "receiving": [], + } + test_case['sending'].append(generated_sender(inputs, addrs)) + # For the sender, there may be more than one valid output set, so we pick + # the first one to test for the recipient (although any would be valid) + test_case['receiving'].append(generated_recipient(inputs, test_case['sending'][0]['expected']['outputs'][0], scan_key, spend_key, labels)) + test_case['comment'] = deepcopy(comment) + return deepcopy(test_case) + +def generated_sender(inputs, addrs,): + sender = { + "given": { + "vin": [], + "recipients": [] + }, + "expected": { + "outputs": [], + } + } + sender['given']['vin'] = deepcopy(inputs) + sender['given']['recipients'] = deepcopy(addrs) + vins = [ + reference.VinInfo( + outpoint=COutPoint(hash=deser_txid(input["txid"]), n=input["vout"]), + scriptSig=bytes.fromhex(input["scriptSig"]), + txinwitness=reference.CTxInWitness().deserialize(reference.from_hex(input["txinwitness"])), + prevout=bytes.fromhex(input["prevout"]["scriptPubKey"]["hex"]), + private_key=ECKey().set(bytes.fromhex(input["private_key"])), + ) + for input in inputs + ] + + input_pubkeys = [] + input_priv_keys = [] + for input in vins: + input_pubkey = reference.get_pubkey_from_input(input) + if input_pubkey.valid: + input_pubkeys.append(input_pubkey) + input_priv_keys.append((input.private_key, reference.is_p2tr(input.prevout))) + if len(input_pubkeys) == 0: + sender['expected']['outputs'] = [[]] + else: + A_sum = sum(input_pubkeys) + input_hash = reference.get_input_hash([o.outpoint for o in vins], A_sum) + outputs = create_output_permutations(input_priv_keys, input_hash, addrs, hrp=HRP) + sender['expected']['outputs'] = sort_list_of_lists(outputs) + return deepcopy(sender) + +def generated_recipient(inputs, output_set: List[str], scan_key: ECKey, spend_key: ECKey, labels: List[int]): + msg = hashlib.sha256(b'message').digest() + aux = hashlib.sha256(b'random auxiliary data').digest() recipient = { "given": { "vin": [], @@ -89,30 +179,75 @@ def new_test_case(): "outputs": [], } } - sender = { - "given": { - "vin": [], - "recipients": [] - }, - "expected": { - "outputs": [] - } - } - test_case = { - "comment": "", - "sending": [], - "receiving": [], - } - return sender, recipient, test_case + vins = [ + reference.VinInfo( + outpoint=COutPoint(hash=deser_txid(input["txid"]), n=input["vout"]), + scriptSig=bytes.fromhex(input["scriptSig"]), + txinwitness=reference.CTxInWitness().deserialize(reference.from_hex(input["txinwitness"])), + prevout=bytes.fromhex(input["prevout"]["scriptPubKey"]["hex"]), + private_key=ECKey(), + ) + for input in inputs + ] + recipient['given']['outputs'] = deepcopy(sorted(output_set)) + recipient['given']['key_material']['scan_priv_key'] = scan_key.get_bytes().hex() + recipient['given']['key_material']['spend_priv_key'] = spend_key.get_bytes().hex() + recipient['given']['labels'] = deepcopy(labels) + addr = reference.encode_silent_payment_address( + scan_key.get_pubkey(), + spend_key.get_pubkey(), + hrp="sp", + ) + recipient['expected']['addresses'].append(addr) + label_cache = dict() + for label in labels: + label_tweak = reference.generate_label(scan_key, label) + label_pubkey = ECKey().set(label_tweak).get_pubkey() + label_cache[label_pubkey.get_bytes(False).hex()] = label_tweak.hex() + l_addr = reference.create_labeled_silent_payment_address(scan_key, spend_key.get_pubkey(), label, hrp="sp") + recipient['expected']['addresses'].append(l_addr) + input_pubkeys = [] + for input in vins: + input_pubkey = reference.get_pubkey_from_input(input) + if input_pubkey.valid: + input_pubkeys.append(input_pubkey) + if len(input_pubkeys) == 0: + recipient['expected']['outputs'] = [] + else: + A_sum = sum(input_pubkeys) + input_hash = reference.get_input_hash([o.outpoint for o in vins], A_sum) + found_outputs = reference.scanning( + scan_key, + spend_key.get_pubkey(), + A_sum, + input_hash, + [ECPubKey().set(bytes.fromhex(p)) for p in output_set], + label_cache, + ) + for found in found_outputs: + pubkey = ECPubKey().set(bytes.fromhex(found['pub_key'])) + full_private_key = spend_key.add( + bytes.fromhex(found['priv_key_tweak']) + ) + if full_private_key.get_pubkey().get_y()%2 != 0: + full_private_key.negate() -# In[10]: + sig = full_private_key.sign_schnorr(msg, aux) + assert pubkey.verify_schnorr(sig, msg) + found['signature'] = sig.hex() + recipient['expected']['outputs'] = sorted([dict(sorted(f.items())) for f in found_outputs], key=lambda d: d['pub_key']) + + inputs_receiver = deepcopy(inputs) + for input in inputs_receiver: + del input['private_key'] + recipient['given']['vin'] = deepcopy(inputs_receiver) + return deepcopy(recipient) + +# Test cases def generate_labeled_output_tests(): - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - G = ECKey().set(1).get_pubkey() test_cases = [] outpoints = [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), @@ -123,81 +258,31 @@ def generate_labeled_output_tests(): i2, I2 = get_key_pair(1, seed=bytes.fromhex(sender_bip32_seed)) input_priv_keys = [(i1, False), (i2, False)] input_pub_keys = [I1, I2] + inputs = [] + for i, outpoint in enumerate(outpoints): + inputs += [{ + 'txid': outpoint[0], + 'vout': outpoint[1], + 'scriptSig': get_p2pkh_scriptsig(input_pub_keys[i], input_priv_keys[i][0]), + 'txinwitness': '', + 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(input_pub_keys[i])}}, + 'private_key': input_priv_keys[i][0].get_bytes().hex(), + }] recipient_bip32_seed = 'f00dbabe' b_scan, b_spend, B_scan, B_spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) label_ints = [2, 3, 1001337] address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) - labeled_addresses = [ - reference.create_labeled_silent_payment_address(b_scan, B_spend, case, hrp=HRP) for case in label_ints - ] - recipient_addresses = [address] + labeled_addresses comments = ["Receiving with labels: label with even parity", "Receiving with labels: label with odd parity", "Receiving with labels: large label integer"] for i, case in enumerate(label_ints): - sender, recipient, test_case = new_test_case() address = reference.create_labeled_silent_payment_address(b_scan, B_spend, case, hrp=HRP) addresses = [address] - - inputs = [] - for i, outpoint in enumerate(outpoints): - inputs += [{ - 'txid': outpoint[0], - 'vout': outpoint[1], - 'scriptSig': get_p2pkh_scriptsig(input_pub_keys[i], input_priv_keys[i][0]), - 'txinwitness': '', - 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(input_pub_keys[i])}}, - }] - - sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - sender['given']['recipients'] = addresses - recipient['given']['vin'] = inputs - recipient['given']['key_material']['scan_priv_key'] = b_scan.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = b_spend.get_bytes().hex() - recipient['expected']['addresses'] = recipient_addresses - recipient['given']['labels'] = label_ints - - A_sum = sum(input_pub_keys) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addresses, hrp=HRP) - sender['expected']['outputs'] = outputs - recipient['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - b_scan, - B_spend, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - labels={(reference.generate_label(b_scan, l)*G).get_bytes(False).hex():reference.generate_label(b_scan, l).hex() for l in label_ints}, - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = b_spend.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient['expected']['outputs'] = add_to_wallet - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - test_case["comment"] = comments[i] - test_cases.append(test_case) - + test_cases.append(generated_test_case(inputs, b_scan, b_spend, addresses, labels=label_ints, comment=comments[i])) return test_cases - def generate_single_output_outpoint_tests(): - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - G = ECKey().set(1).get_pubkey() outpoint_test_cases = [ [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), @@ -215,6 +300,10 @@ def generate_single_output_outpoint_tests(): ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 7), ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 3) ], + [ + ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 1), + ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 256) + ], ] sender_bip32_seed = 'deadbeef' @@ -229,73 +318,27 @@ def generate_single_output_outpoint_tests(): "Simple send: two inputs", "Simple send: two inputs, order reversed", "Simple send: two inputs from the same transaction", - "Simple send: two inputs from the same transaction, order reversed" + "Simple send: two inputs from the same transaction, order reversed", + "Outpoint ordering byte-lexicographically vs. vout-integer", ] + b_scan, b_spend, B_scan, B_spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + address = reference.encode_silent_payment_address(B_scan, B_spend, hrp="sp") for i, outpoints in enumerate(outpoint_test_cases): - sender, recipient, test_case = new_test_case() - test_case["comment"] = comments[i] - inputs = [] - for i, outpoint in enumerate(outpoints): + for j, outpoint in enumerate(outpoints): inputs += [{ 'txid': outpoint[0], 'vout': outpoint[1], - 'scriptSig': get_p2pkh_scriptsig(input_pub_keys[i], input_priv_keys[i][0]), + 'scriptSig': get_p2pkh_scriptsig(input_pub_keys[j], input_priv_keys[j][0]), 'txinwitness': '', - 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(input_pub_keys[i])}}, + 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(input_pub_keys[j])}}, + 'private_key': input_priv_keys[j][0].get_bytes().hex(), }] - sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - - recipient['given']['vin'] = inputs - - b_scan, b_spend, B_scan, B_spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) - recipient['given']['key_material']['scan_priv_key'] = b_scan.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = b_spend.get_bytes().hex() - address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) - - sender['given']['recipients'].extend([address]) - recipient['expected']['addresses'].extend([address]) - - A_sum = sum(input_pub_keys) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [address], hrp=HRP) - sender['expected']['outputs'] = outputs - recipient['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - b_scan, - B_spend, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = b_spend.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient['expected']['outputs'] = add_to_wallet - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - test_cases.append(test_case) - + test_cases.append(generated_test_case(inputs, b_scan, b_spend, [address], labels=[], comment=comments[i])) return test_cases - def generate_multiple_output_tests(): - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - G = ECKey().set(1).get_pubkey() - recipient_test_cases = [] outpoints = [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0), @@ -306,24 +349,6 @@ def generate_multiple_output_tests(): input_priv_keys = [(i1, False), (i2, False)] input_pub_keys = [I1, I2] - recipient_one_bip32_seed = 'f00dbabe' - recipient_two_bip32_seed = 'decafbad' - - scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_one_bip32_seed)) - address1 = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) - addresses1 = [address1, address1] - - scan2, spend2, Scan2, Spend2 = derive_silent_payment_key_pair(bytes.fromhex(recipient_two_bip32_seed)) - address2 = reference.encode_silent_payment_address(Scan2, Spend2, hrp=HRP) - addresses2 = [address2, address2] - - test_cases = [] - - sender, recipient1, test_case = new_test_case() - sender1 = deepcopy(sender) - recipient2 = deepcopy(recipient1) - test_case2 = deepcopy(test_case) - inputs = [] for i, outpoint in enumerate(outpoints): inputs += [{ @@ -332,159 +357,28 @@ def generate_multiple_output_tests(): 'scriptSig': get_p2pkh_scriptsig(input_pub_keys[i], input_priv_keys[i][0]), 'txinwitness': '', 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(input_pub_keys[i])}}, + 'private_key': input_priv_keys[i][0].get_bytes().hex(), }] - sender1['given']['vin'] = sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - sender['given']['recipients'] = addresses1 - recipient1['given']['vin'] = inputs - recipient2['given']['vin'] = inputs - recipient1['given']['key_material']['scan_priv_key'] = scan1.get_bytes().hex() - recipient1['given']['key_material']['spend_priv_key'] = spend1.get_bytes().hex() - recipient1['expected']['addresses'] = [address1] - recipient2['given']['key_material']['scan_priv_key'] = scan2.get_bytes().hex() - recipient2['given']['key_material']['spend_priv_key'] = spend2.get_bytes().hex() - recipient2['expected']['addresses'] = [address2] - - A_sum = sum(input_pub_keys) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addresses1, hrp=HRP) - sender['expected']['outputs'] = outputs - recipient1['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - scan1, - Spend1, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = spend1.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient1['expected']['outputs'] = add_to_wallet - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient1]) - test_case["comment"] = "Multiple outputs: multiple outputs, same recipient" - test_cases.append(test_case) - - sender1['given']['recipients'] = addresses1 + addresses2 - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addresses1 + addresses2, hrp=HRP) - sender1['expected']['outputs'] = outputs - recipient1['given']['outputs'] = outputs - recipient2['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - scan2, - Spend2, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = spend2.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient2['expected']['outputs'] = add_to_wallet - test_case2['sending'].extend([sender1]) - test_case2['receiving'].extend([recipient1, recipient2]) - test_case2["comment"] = "Multiple outputs: multiple outputs, multiple recipients" - test_cases.append(test_case2) - - return test_cases - - -# In[13]: + recipient_one_bip32_seed = 'f00dbabe' + recipient_two_bip32_seed = 'decafbad' + scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_one_bip32_seed)) + address1 = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) + addresses1 = [address1, address1] -def generate_paying_to_self_test(): + scan2, spend2, Scan2, Spend2 = derive_silent_payment_key_pair(bytes.fromhex(recipient_two_bip32_seed)) + address2 = reference.encode_silent_payment_address(Scan2, Spend2, hrp=HRP) + addresses2 = [address1, address2, address2] - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - outpoints = [ - ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), - ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0) + test_cases = [ + generated_test_case(inputs, scan1, spend1, addresses1, labels=[], comment="Multiple outputs: multiple outputs, same recipient"), + generated_test_case(inputs, scan2, spend2, addresses2, labels=[], comment="Multiple outputs: multiple outputs, multiple recipients"), ] - - sender_bip32_seed = 'deadbeef' - recipient_bip32_seed = 'deadbeef' - i1, I1 = get_key_pair(0, seed=bytes.fromhex(sender_bip32_seed)) - i2, I2 = get_key_pair(1) - input_priv_keys = [(i1, False), (i2, False)] - input_pub_keys = [I1, I2] - - sender, recipient, test_case = new_test_case() - sender['given']['outpoints'] = outpoints - recipient['given']['outpoints'] = outpoints - sender['given']['input_priv_keys'].extend([i1.get_bytes().hex(), i2.get_bytes().hex()]) - recipient['given']['input_pub_keys'].extend([I1.get_bytes(False).hex(), I2.get_bytes(False).hex()]) - - b_scan, b_spend, B_scan, B_spend = create_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) - recipient['given']['bip32_seed'] = recipient_bip32_seed - recipient['given']['scan_priv_key'] = b_scan.get_bytes().hex() - recipient['given']['spend_priv_key'] = b_spend.get_bytes().hex() - address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) - - sender['given']['recipients'].extend([address]) - recipient['expected']['addresses'].extend([address]) - - A_sum = sum(input_pub_keys) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [address], hrp=HRP) - sender['expected']['outputs'] = outputs - recipient['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - b_scan, - B_spend, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = b_spend.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient['expected']['outputs'] = add_to_wallet - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - - return test_case - + return test_cases def generate_multiple_outputs_with_labels_tests(): - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - G = ECKey().set(1).get_pubkey() - recipient_test_cases = [] outpoints = [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0), @@ -509,167 +403,30 @@ def generate_multiple_outputs_with_labels_tests(): addresses3 = [address, label_address_one, label_address_two, label_address_two] test_cases = [] - labels = [labels_one, labels_one, labels_three] - sp_addresses = [[address, label_address_one], [address, label_address_one], [address, label_address_one, label_address_two]] - comments = [ - "Multiple outputs with labels: un-labeled and labeled address; same recipient", - "Multiple outputs with labels: multiple outputs for labeled address; same recipient", - "Multiple outputs with labels: un-labeled, labeled, and multiple outputs for labeled address; same recipient", - ] - for i, addrs in enumerate([addresses1, addresses2, addresses3]): - sender, recipient, test_case = new_test_case() - - inputs = [] - for i, outpoint in enumerate(outpoints): - inputs += [{ - 'txid': outpoint[0], - 'vout': outpoint[1], - 'scriptSig': get_p2pkh_scriptsig(input_pub_keys[i], input_priv_keys[i][0]), - 'txinwitness': '', - 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(input_pub_keys[i])}}, - }] - - sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - recipient['given']['vin'] = inputs - - recipient['given']['key_material']['scan_priv_key'] = scan1.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = spend1.get_bytes().hex() - sender['given']['recipients'] = addrs - recipient['expected']['addresses'] = sp_addresses[i] - recipient['given']['labels'] = labels[i] - A_sum = sum(input_pub_keys) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addrs, hrp=HRP) - sender['expected']['outputs'] = outputs - recipient['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - scan1, - Spend1, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - labels={(reference.generate_label(scan1, l)*G).get_bytes(False).hex():reference.generate_label(scan1, l).hex() for l in labels[i]}, - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = spend1.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient['expected']['outputs'] = add_to_wallet - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - test_case["comment"] = comments[i] - test_cases.append(test_case) - - return test_cases - - -def generate_more_labels_tests(): - - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - G = ECKey().set(1).get_pubkey() - recipient_test_cases = [] - outpoints = [ - ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), - ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0), - ] - sender_bip32_seed = 'deadbeef' - i1, I1 = get_key_pair(0, seed=bytes.fromhex(sender_bip32_seed)) - i2, I2 = get_key_pair(1, seed=bytes.fromhex(sender_bip32_seed)) - input_priv_keys = [(i1, False), (i2, False)] - input_pub_keys = [I1, I2] - - recipient_bip32_seed = 'f00dbabe' - scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) - address = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) - scan2, spend2, Scan2, Spend2 = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed) + b'01') - address2 = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) - l1 = 1 - l2 = 1337 - labels = [[l1, l2]] - label_address_one = reference.create_labeled_silent_payment_address(scan1, Spend1, m=l1, hrp=HRP) - label_address_two = reference.create_labeled_silent_payment_address(scan1, Spend1, m=l2, hrp=HRP) - label_address_one2 = reference.create_labeled_silent_payment_address(scan2, Spend2, m=l1, hrp=HRP) - label_address_two2 = reference.create_labeled_silent_payment_address(scan2, Spend2, m=l2, hrp=HRP) - addresses1 = [label_address_one, address, address2, label_address_one, label_address_one2, label_address_one, label_address_two2, label_address_two, label_address_two, label_address_two2] - - test_cases = [] - sp_addresses = [[address, label_address_one, label_address_two]] - comments = [ - "Multiple outputs with labels: multiple outputs per multiple lables; multiple recipients", - ] - for a, addrs in enumerate([addresses1]): - sender, recipient, test_case = new_test_case() - - inputs = [] - for i, outpoint in enumerate(outpoints): - inputs += [{ - 'txid': outpoint[0], - 'vout': outpoint[1], - 'scriptSig': get_p2pkh_scriptsig(input_pub_keys[i], input_priv_keys[i][0]), - 'txinwitness': '', - 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(input_pub_keys[i])}}, - }] - - sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - recipient['given']['vin'] = inputs - - recipient['given']['key_material']['scan_priv_key'] = scan1.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = spend1.get_bytes().hex() - sender['given']['recipients'] = addrs - recipient['expected']['addresses'] = sp_addresses[a] - recipient['given']['labels'] = labels[a] - A_sum = sum(input_pub_keys) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addrs, hrp=HRP) - sender['expected']['outputs'] = outputs - recipient['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - scan1, - Spend1, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - labels={(reference.generate_label(scan1, l)*G).get_bytes(False).hex():reference.generate_label(scan1, l).hex() for l in labels[a]}, - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = spend1.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient['expected']['outputs'] = add_to_wallet - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - test_case["comment"] = comments[a] - test_cases.append(test_case) + inputs = [] + for i, outpoint in enumerate(outpoints): + inputs += [{ + 'txid': outpoint[0], + 'vout': outpoint[1], + 'scriptSig': get_p2pkh_scriptsig(input_pub_keys[i], input_priv_keys[i][0]), + 'txinwitness': '', + 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(input_pub_keys[i])}}, + 'private_key': input_priv_keys[i][0].get_bytes().hex(), + }] + test_cases.append( + generated_test_case(inputs, scan1, spend1, addresses1, labels_one, "Multiple outputs with labels: un-labeled and labeled address; same recipient") + ) + test_cases.append( + generated_test_case(inputs, scan1, spend1, addresses2, labels_one, "Multiple outputs with labels: multiple outputs for labeled address; same recipient") + ) + test_cases.append( + generated_test_case(inputs, scan1, spend1, addresses3, labels_three, "Multiple outputs with labels: un-labeled, labeled, and multiple outputs for labeled address; same recipients") + ) return test_cases - def generate_single_output_input_tests(): - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - G = ECKey().set(1).get_pubkey() outpoints = [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0) @@ -729,8 +486,6 @@ def generate_single_output_input_tests(): "Single recipient: taproot input with odd y-value and non-taproot input" ] for i, inputs in enumerate([address_reuse, taproot_only, taproot_only_with_odd_y, mixed, mixed_with_odd_y]): - sender, recipient, test_case = new_test_case() - inp = [] for x, (key, is_taproot) in enumerate(inputs[0]): pub_key = inputs[1][x] @@ -741,6 +496,7 @@ def generate_single_output_input_tests(): "scriptSig": "", "txinwitness": get_p2tr_witness(key), "prevout": {"scriptPubKey": {"hex": get_p2tr_scriptPubKey(pub_key)}}, + "private_key": key.get_bytes().hex(), }] else: inp += [{ @@ -749,68 +505,17 @@ def generate_single_output_input_tests(): "scriptSig": get_p2pkh_scriptsig(pub_key, key), "txinwitness": "", "prevout": {"scriptPubKey": {"hex": get_p2pkh_scriptPubKey(pub_key)}}, + "private_key": key.get_bytes().hex(), }] - priv_keys = [] - for (priv_key, is_taproot) in inputs[0]: - priv_keys += [priv_key.get_bytes().hex()] - - - sender['given']['vin'] = add_private_keys(deepcopy(inp), inputs[0]) - recipient['given']['vin'] = inp - b_scan, b_spend, B_scan, B_spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) - recipient['given']['key_material']['scan_priv_key'] = b_scan.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = b_spend.get_bytes().hex() address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) - - sender['given']['recipients'].extend([address]) - recipient['expected']['addresses'].extend([address]) - - A_sum = sum([p if not inputs[0][i][1] or p.get_y()%2==0 else p * -1 for i, p in enumerate(inputs[1])]) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - - outputs = reference.create_outputs(inputs[0], deterministic_nonce, [address], hrp=HRP) - sender['expected']['outputs'] = outputs - recipient['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - b_scan, - B_spend, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = b_spend.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient['expected']['outputs'] = add_to_wallet - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - test_case["comment"] = comments[i] - test_cases.append(test_case) + test_cases.append(generated_test_case(inp, b_scan, b_spend, [address], labels=[], comment=comments[i])) return test_cases - def generate_change_tests(): - sender, recipient, test_case = new_test_case() - - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - G = ECKey().set(1).get_pubkey() - recipient_test_cases = [] outpoints = [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0), @@ -821,31 +526,6 @@ def generate_change_tests(): input_priv_keys = [(i1, False), (i2, False)] input_pub_keys = [I1, I2] - scan0, spend0, Scan0, Spend0 = derive_silent_payment_key_pair(bytes.fromhex(sender_bip32_seed)) - sender_address = reference.encode_silent_payment_address(Scan0, Spend0, hrp=HRP) - change_label = 0 - change_labels = [change_label] - change_address = reference.create_labeled_silent_payment_address(scan0, Spend0, m=change_label, hrp=HRP) - - recipient_bip32_seed = 'f00dbabe' - seeds = [sender_bip32_seed, recipient_bip32_seed] - scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) - address = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) - addresses = [address, change_address] - - test_cases = [] - sp_recipients = [[address, change_address]] - - rec1, rec2 = deepcopy(recipient), deepcopy(recipient) - rec1['given']['key_material']['scan_priv_key'] = scan0.get_bytes().hex() - rec1['given']['key_material']['spend_priv_key'] = spend0.get_bytes().hex() - rec2['given']['key_material']['scan_priv_key'] = scan1.get_bytes().hex() - rec2['given']['key_material']['spend_priv_key'] = spend1.get_bytes().hex() - rec1['expected']['addresses'] = [sender_address, change_address] - rec1['given']['labels'] = change_labels - rec2['expected']['addresses'] = [address] - - inputs = [] for i, outpoint in enumerate(outpoints): inputs += [{ @@ -854,120 +534,23 @@ def generate_change_tests(): 'scriptSig': get_p2pkh_scriptsig(input_pub_keys[i], input_priv_keys[i][0]), 'txinwitness': '', 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(input_pub_keys[i])}}, + 'private_key': input_priv_keys[i][0].get_bytes().hex(), }] - sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - sender['given']['recipients'] = addresses - A_sum = sum(input_pub_keys) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addresses, hrp=HRP) - sender['expected']['outputs'] = outputs - - - test_case['sending'].extend([sender]) - labels = [change_labels, []] - for i, rec in enumerate([rec1, rec2]): - rec['given']['vin'] = inputs - rec['given']['outputs'] = outputs - - scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(seeds[i])) - add_to_wallet = reference.scanning( - scan, - Spend, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - labels={(reference.generate_label(scan, l)*G).get_bytes(False).hex():reference.generate_label(scan, l).hex() for l in labels[i]}, - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = spend.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - rec['expected']['outputs'] = add_to_wallet - test_case['receiving'].extend([rec]) - test_case["comment"] = "Single recipient: use silent payments for sender change" - test_cases.append(test_case) - return test_cases - - -# In[17]: - - -def generate_unknown_segwit_ver_test(): - sender, recipient, test_case = new_test_case() + scan0, spend0, Scan0, Spend0 = derive_silent_payment_key_pair(bytes.fromhex(sender_bip32_seed)) + change_address = reference.create_labeled_silent_payment_address(scan0, Spend0, m=0, hrp=HRP) - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - outpoints = [ - ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), - ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0), - ] - sender_bip32_seed = 'deadbeef' recipient_bip32_seed = 'f00dbabe' - scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) - address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) - addresses = [address] - - recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = spend.get_bytes().hex() - - inputs = [] - input_priv_keys = [] - input_pub_keys = [] - - ## included - # p2pkh - i = len(inputs) - priv, pub = get_key_pair(i, seed=bytes.fromhex(sender_bip32_seed)) - inputs += [{ - 'prevout': list(outpoints[i]) + [get_p2pkh_scriptsig(pub, priv), ""], - 'scriptPubKey': get_p2pkh_scriptPubKey(pub), - }] - input_priv_keys += [(priv, False)] - input_pub_keys += [pub] - - # unknown segwit version - i = len(inputs) - priv, pub = get_key_pair(i, seed=bytes.fromhex(sender_bip32_seed)) - sig = priv.sign_ecdsa(msg, low_s=False, rfc6979=True).hex() - inputs += [{ - 'prevout': list(outpoints[i]) + ["", serialize_witness_stack([sig, pub.get_bytes(False).hex()])], - 'scriptPubKey': "5214" + hash160(pub.get_bytes(False)), - }] - input_priv_keys += [(priv, False)] - input_pub_keys += [pub] + scan1, spend1, Scan1, Spend1 = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) + address = reference.encode_silent_payment_address(Scan1, Spend1, hrp=HRP) + addresses = [address, change_address] - sender['given']['recipients'] = addresses - sender['given']['inputs'] = add_private_keys(deepcopy(inputs), input_priv_keys) - # TODO: encode failure of sending explicitly - test_case['sending'].extend([sender]) - - recipient['given']['inputs'] = inputs - # create plausible outputs - # a) using all detected keys - outputs_a = reference.create_outputs(input_priv_keys, reference.hash_outpoints(outpoints), addresses, hrp=HRP) - # b) using only p2pkh input - outputs_b = reference.create_outputs(input_priv_keys[:1], reference.hash_outpoints(outpoints), addresses, hrp=HRP) - recipient['given']['outputs'] = [outputs_a[0][0], outputs_b[0][0]] - test_case['receiving'].extend([recipient]) - - test_case["comment"] = "Skipped tx: unknown segwit version input" + test_case = generated_test_case(inputs, scan0, spend0, addresses, labels=[0], comment="Single recipient: use silent payments for sender change") + test_case['receiving'].append(generated_recipient(inputs, test_case['sending'][0]['expected']['outputs'][0], scan1, spend1, labels=[])) return [test_case] def generate_taproot_with_nums_point_test(): - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - G = ECKey().set(1).get_pubkey() outpoints = [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0), @@ -987,7 +570,7 @@ def generate_taproot_with_nums_point_test(): if I2.get_y()%2 != 0: i2.negate() I2.negate() - + if I3.get_y()%2 == 0: i3.negate() I3.negate() @@ -999,13 +582,11 @@ def generate_taproot_with_nums_point_test(): test_cases = [] comments = [ - "Single receipient: taproot input with NUMS point" + "Single recipient: taproot input with NUMS point" ] included_keys = [] included_pubkeys = [] for i, inputs in enumerate([nums_point]): - sender, recipient, test_case = new_test_case() - inp = [] for x, (key, internal_key, add_annex) in enumerate(inputs[0]): pub_key = inputs[1][x] @@ -1017,6 +598,7 @@ def generate_taproot_with_nums_point_test(): "scriptSig": "", "txinwitness": get_p2tr_witness(key), "prevout": {"scriptPubKey": {"hex": get_p2tr_scriptPubKey(pub_key)}}, + "private_key": key.get_bytes().hex(), }] included_keys += [(key, True)] included_pubkeys += [pub_key] @@ -1029,6 +611,7 @@ def generate_taproot_with_nums_point_test(): tap_tweak = TaggedHash("TapTweak", internal_key_bytes + leaf_hash) tweaked_key = ECPubKey().set(internal_key_bytes).tweak_add(tap_tweak) control_block = "c1" + internal_key_bytes.hex() + msg = hashlib.sha256(b'message').digest() sig = key.sign_schnorr(msg).hex() stack = [sig, script, control_block] if (add_annex): @@ -1039,60 +622,18 @@ def generate_taproot_with_nums_point_test(): "scriptSig": "", "txinwitness": serialize_witness_stack(stack), "prevout": {"scriptPubKey": {"hex": get_p2tr_scriptPubKey(tweaked_key)}}, + "private_key": key.get_bytes().hex(), }] # Notice that the keys are not added to included list because they should be skipped since they use NUMS_POINT - sender['given']['vin'] = add_private_keys(deepcopy(inp), inputs[0]) - recipient['given']['vin'] = inp - b_scan, b_spend, B_scan, B_spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) - recipient['given']['key_material']['scan_priv_key'] = b_scan.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = b_spend.get_bytes().hex() address = reference.encode_silent_payment_address(B_scan, B_spend, hrp=HRP) - sender['given']['recipients'].extend([address]) - recipient['expected']['addresses'].extend([address]) - - A_sum = sum([p if p.get_y()%2==0 else p * -1 for p in included_pubkeys]) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - - outputs = reference.create_outputs([(inp[0], True) for inp in included_keys], deterministic_nonce, [address], hrp=HRP) - sender['expected']['outputs'] = outputs - recipient['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - b_scan, - B_spend, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = b_spend.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient['expected']['outputs'] = add_to_wallet - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - test_case["comment"] = comments[i] - test_cases.append(test_case) + test_cases.append(generated_test_case(inp, b_scan, b_spend, [address], labels=[], comment=comments[i])) return test_cases def generate_malleated_p2pkh_test(): - sender, recipient, test_case = new_test_case() - - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() outpoints = [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), @@ -1105,11 +646,6 @@ def generate_malleated_p2pkh_test(): address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) addresses = [address] - sender['given']['recipients'] = addresses - recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = spend.get_bytes().hex() - recipient['expected']['addresses'] = [address] - inputs = [] input_priv_keys = [] input_pub_keys = [] @@ -1123,12 +659,13 @@ def add_input(inputs, input_priv_keys, input_pub_keys, get_script_sig): 'scriptSig': get_script_sig(pub, priv), 'txinwitness': '', 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(pub)}}, + 'private_key': priv.get_bytes().hex(), }] input_priv_keys += [(priv, False)] input_pub_keys += [pub] ## All inputs should be included - + # p2pkh add_input(inputs, input_priv_keys, input_pub_keys, get_p2pkh_scriptsig) @@ -1142,53 +679,14 @@ def add_input(inputs, input_priv_keys, input_pub_keys, get_script_sig): fake_script = get_p2pkh_scriptsig(fake_pub, fake_priv) add_input(inputs, input_priv_keys, input_pub_keys, lambda pub, priv: "5163" + get_p2pkh_scriptsig(pub, priv) + "67" + fake_script + "68") - sender['given']['recipients'] = addresses - A_sum = sum([p if not input_priv_keys[i][1] or p.get_y()%2==0 else p * -1 for i, p in enumerate(input_pub_keys)]) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, addresses, hrp=HRP) - sender['expected']['outputs'] = outputs - sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - - - recipient['given']['vin'] = inputs - recipient['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - scan, - Spend, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - labels={}, - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = spend.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient['expected']['outputs'] = add_to_wallet - - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - test_case["comment"] = "Pubkey extraction from malleated p2pkh" - test_cases = [] - test_cases.append(test_case) + test_cases = [ + generated_test_case(inputs, scan, spend, addresses, labels=[], comment="Pubkey extraction from malleated p2pkh") + ] return test_cases def generate_uncompressed_keys_tests(): - sender, recipient, test_case = new_test_case() msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() - outpoints = [ ("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d", 0), ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), @@ -1204,10 +702,6 @@ def generate_uncompressed_keys_tests(): recipient_bip32_seed = 'f00dbabe' scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) - - recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = spend.get_bytes().hex() - recipient['expected']['addresses'] = [address] addresses = [address] # p2pkh compressed Key @@ -1219,6 +713,7 @@ def generate_uncompressed_keys_tests(): 'scriptSig': get_p2pkh_scriptsig(pub, priv), 'txinwitness': '', 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(pub)}}, + 'private_key': priv.get_bytes().hex(), }] input_priv_keys += [(priv, False)] input_pub_keys += [pub] @@ -1233,6 +728,7 @@ def generate_uncompressed_keys_tests(): 'scriptSig': get_p2pkh_scriptsig(pub, priv), 'txinwitness': '', 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(pub)}}, + 'private_key': priv.get_bytes().hex(), }] input_priv_keys += [(priv, False)] @@ -1247,53 +743,18 @@ def generate_uncompressed_keys_tests(): 'scriptSig': '', 'txinwitness': serialize_witness_stack([sig, pub.get_bytes(False).hex()]), 'prevout': {'scriptPubKey': {'hex': "0014" + reference.hash160(pub.get_bytes(False)).hex()}}, + 'private_key': priv.get_bytes().hex(), }] input_priv_keys += [(priv, False)] - A_sum = sum([p for p in input_pub_keys]) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys[:1], deterministic_nonce, addresses, hrp=HRP) - sender['expected']['outputs'] = outputs - sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - sender['given']['recipients'] = addresses - - - test_case['sending'].extend([sender]) - recipient['given']['vin'] = inputs - recipient['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - scan, - Spend, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - labels={}, - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = spend.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient['expected']['outputs'] = add_to_wallet - - test_case['receiving'].extend([recipient]) - test_case["comment"] = "P2PKH and P2WPKH Uncompressed Keys are skipped" - return [test_case] + test_cases = [ + generated_test_case(inputs, scan, spend, addresses, labels=[], comment="P2PKH and P2WPKH Uncompressed Keys are skipped") + ] + return test_cases def generate_p2sh_tests(): - sender, recipient, test_case = new_test_case() - msg = hashlib.sha256(b'message').digest() - aux = hashlib.sha256(b'random auxiliary data').digest() + msg = hashlib.sha256(b'message').digest() outpoints = [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 1), @@ -1305,15 +766,10 @@ def generate_p2sh_tests(): address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) addresses = [address] - sender['given']['recipients'] = addresses - recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = spend.get_bytes().hex() - recipient['expected']['addresses'] = [address] - inputs = [] input_priv_keys = [] input_pub_keys = [] - + # p2sh(p2wpkh) compressed i = len(inputs) priv, pub = get_key_pair(i, seed=bytes.fromhex(sender_bip32_seed)) @@ -1325,7 +781,8 @@ def generate_p2sh_tests(): 'vout': outpoints[i][1], 'scriptSig': f'{(len(p2wpkh)//2):0x}'+p2wpkh, 'txinwitness': serialize_witness_stack([sig, pub.get_bytes(False).hex()]), - 'prevout': { 'scriptPubKey': { 'hex': p2sh } } + 'prevout': { 'scriptPubKey': { 'hex': p2sh } }, + 'private_key': priv.get_bytes().hex(), }] input_priv_keys += [(priv, False)] input_pub_keys += [pub] @@ -1342,7 +799,8 @@ def generate_p2sh_tests(): 'vout': outpoints[i][1], 'scriptSig': f'{(len(p2wpkh)//2):0x}'+p2wpkh, 'txinwitness': serialize_witness_stack([sig, pub.get_bytes(False).hex()]), - 'prevout': { 'scriptPubKey': { 'hex': p2sh } } + 'prevout': { 'scriptPubKey': { 'hex': p2sh } }, + 'private_key': priv.get_bytes().hex(), }] input_priv_keys += [(priv, False)] @@ -1365,52 +823,17 @@ def generate_p2sh_tests(): 'vout': outpoints[i][1], 'scriptSig': f"00{''.join(sigs)}4c{(len(multisig_script)//2):0x}{multisig_script}", 'txinwitness': '', - 'prevout': { 'scriptPubKey': { 'hex': p2sh } } + 'prevout': { 'scriptPubKey': { 'hex': p2sh } }, + 'private_key': keys[0][0].get_bytes().hex(), }] input_priv_keys += [(keys[0][0], False)] - A_sum = sum([p for p in input_pub_keys]) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - outputs = reference.create_outputs(input_priv_keys[:1], deterministic_nonce, addresses, hrp=HRP) - sender['expected']['outputs'] = outputs - sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - sender['given']['recipients'] = addresses - - - test_case['sending'].extend([sender]) - recipient['given']['vin'] = inputs - recipient['given']['outputs'] = outputs - - add_to_wallet = reference.scanning( - scan, - Spend, - A_sum, - deterministic_nonce, - [ECPubKey().set(bytes.fromhex(pub)) for pub in outputs], - labels={}, - ) - for o in add_to_wallet: - - pubkey = ECPubKey().set(bytes.fromhex(o['pub_key'])) - full_private_key = spend.add( - bytes.fromhex(o['priv_key_tweak']) - ) - if full_private_key.get_pubkey().get_y()%2 != 0: - full_private_key.negate() - - sig = full_private_key.sign_schnorr(msg, aux) - assert pubkey.verify_schnorr(sig, msg) - o['signature'] = sig.hex() - - recipient['expected']['outputs'] = add_to_wallet - - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - test_case["comment"] = "Skip invalid P2SH inputs" - return [test_case] + test_cases = [ + generated_test_case(inputs, scan, spend, addresses, labels=[], comment="Skip invalid P2SH inputs") + ] + return test_cases def generate_no_outputs_tests(): - sender, recipient, test_case = new_test_case() outpoints = [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), @@ -1430,14 +853,7 @@ def generate_no_outputs_tests(): i2.negate() I2.negate() - scan, spend, Scan, Spend = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) - address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) - addresses = [address] - - sender['given']['recipients'] = addresses - recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = spend.get_bytes().hex() - recipient['expected']['addresses'] = [address] + scan, spend, _, _ = derive_silent_payment_key_pair(bytes.fromhex(recipient_bip32_seed)) inputs = [] input_priv_keys = [] @@ -1450,7 +866,8 @@ def generate_no_outputs_tests(): 'vout': outpoints[i][1], 'scriptSig': "", 'txinwitness': get_p2tr_witness(i1), - 'prevout': { "scriptPubKey": { "hex": get_p2tr_scriptPubKey(I1) } } + 'prevout': { "scriptPubKey": { "hex": get_p2tr_scriptPubKey(I1) } }, + 'private_key': i1.get_bytes().hex(), }] input_priv_keys += [(i1, True)] tr_input_pub_keys += [I1] @@ -1463,37 +880,26 @@ def generate_no_outputs_tests(): 'scriptSig': get_p2pkh_scriptsig(pub, priv), 'txinwitness': '', 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(pub)}}, + 'private_key': priv.get_bytes().hex(), }] input_priv_keys += [(priv, False)] pkpkh_input_pub_keys += [pub] - sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - sender['given']['recipients'] = addresses - recipient['given']['vin'] = inputs - - A_sum = sum([p if p.get_y()%2==0 else p * -1 for p in tr_input_pub_keys] + pkpkh_input_pub_keys) - deterministic_nonce = reference.get_input_hash([COutPoint(deser_txid(o[0]), o[1]) for o in outpoints], A_sum) - # Regular taproot scriptpubkey regular_p2tr = I2.get_bytes(True).hex() # Decoy scriptpubkey - scan_decoy, spend_decoy, Scan_decoy, Spend_decoy = derive_silent_payment_key_pair(bytes.fromhex("decafbad")) + _, _, Scan_decoy, Spend_decoy = derive_silent_payment_key_pair(bytes.fromhex("decafbad")) decoy_address = reference.encode_silent_payment_address(Scan_decoy, Spend_decoy, hrp=HRP) - decoy_outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [decoy_address], hrp=HRP) - outputs = reference.create_outputs(input_priv_keys, deterministic_nonce, [address], hrp=HRP) - sender['expected']['outputs'] = outputs - recipient['given']['outputs'] = [regular_p2tr] + decoy_outputs - recipient['expected']['outputs'] = [] - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - test_case["comment"] = "Recipient ignores unrelated outputs" - return [test_case] + test_cases = [ + generated_test_case(inputs, scan, spend, [decoy_address], labels=[], comment="Recipient ignores unrelated outputs") + ] + test_cases[0]['receiving'][0]['given']['outputs'].append(regular_p2tr) + return test_cases def generate_no_valid_inputs_tests(): - sender, recipient, test_case = new_test_case() outpoints = [ ("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16", 0), @@ -1506,11 +912,6 @@ def generate_no_valid_inputs_tests(): address = reference.encode_silent_payment_address(Scan, Spend, hrp=HRP) addresses = [address] - sender['given']['recipients'] = addresses - recipient['given']['key_material']['scan_priv_key'] = scan.get_bytes().hex() - recipient['given']['key_material']['spend_priv_key'] = spend.get_bytes().hex() - recipient['expected']['addresses'] = [address] - inputs = [] input_priv_keys = [] @@ -1522,7 +923,8 @@ def generate_no_valid_inputs_tests(): 'vout': outpoints[i][1], 'scriptSig': get_p2pkh_scriptsig(pub, priv), 'txinwitness': '', - 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(pub)}} + 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(pub)}}, + 'private_key': priv.get_bytes().hex(), }] input_priv_keys += [(priv, False)] @@ -1534,26 +936,20 @@ def generate_no_valid_inputs_tests(): 'vout': outpoints[i][1], 'scriptSig': get_p2pkh_scriptsig(pub, priv), 'txinwitness': '', - 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(pub)}} + 'prevout': {'scriptPubKey': {'hex': get_p2pkh_scriptPubKey(pub)}}, + 'private_key': priv.get_bytes().hex(), }] input_priv_keys += [(priv, False)] - sender['given']['vin'] = add_private_keys(deepcopy(inputs), input_priv_keys) - sender['given']['recipients'] = addresses - recipient['given']['vin'] = inputs - # Regular taproot pubkeys regular_p2tr1 = get_key_pair(i, seed=bytes.fromhex(sender_bip32_seed))[1].get_bytes(True).hex() regular_p2tr2 = get_key_pair(i+1, seed=bytes.fromhex(sender_bip32_seed))[1].get_bytes(True).hex() - sender['expected']['outputs'] = [] - recipient['given']['outputs'] = [regular_p2tr1, regular_p2tr2] - recipient['expected']['outputs'] = [] - - test_case['sending'].extend([sender]) - test_case['receiving'].extend([recipient]) - test_case["comment"] = "No valid inputs, sender generates no outputs" - return [test_case] + test_cases = [ + generated_test_case(inputs, scan, spend, addresses, labels=[], comment="No valid inputs, sender generates no outputs"), + ] + test_cases[0]['receiving'][0]['given']['outputs'] = [regular_p2tr1, regular_p2tr2] + return test_cases with open("send_and_receive_test_vectors.json", "w") as f: json.dump( @@ -1562,7 +958,6 @@ def generate_no_valid_inputs_tests(): generate_multiple_output_tests() + generate_labeled_output_tests() + generate_multiple_outputs_with_labels_tests() + - generate_more_labels_tests() + generate_change_tests() + generate_taproot_with_nums_point_test() + generate_malleated_p2pkh_test() + diff --git a/reference.py b/reference.py old mode 100644 new mode 100755 index 5677f03..c98dac8 --- a/reference.py +++ b/reference.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 +# For running the test vectors, run this script: +# ./reference.py send_and_receive_test_vectors.json import hashlib import json from typing import List, Tuple, Dict, cast -from sys import argv +from sys import argv, exit from functools import reduce from itertools import permutations @@ -82,7 +84,7 @@ def get_pubkey_from_input(vin: VinInfo) -> ECPubKey: def get_input_hash(outpoints: List[COutPoint], sum_input_pubkeys: ECPubKey) -> bytes: - lowest_outpoint = sorted(outpoints, key=lambda outpoint: (outpoint.hash, outpoint.n))[0] + lowest_outpoint = sorted(outpoints, key=lambda outpoint: outpoint.serialize())[0] return TaggedHash("BIP0352/Inputs", lowest_outpoint.serialize() + cast(bytes, sum_input_pubkeys.get_bytes(False))) @@ -115,7 +117,7 @@ def decode_silent_payment_address(address: str, hrp: str = "tsp") -> Tuple[ECPub return B_scan, B_spend -def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, recipients: List[str], hrp="tsp", permute=True) -> List[str]: +def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, recipients: List[str], hrp="tsp") -> List[str]: G = ECKey().set(1).get_pubkey() negated_keys = [] for key, is_xonly in input_priv_keys: @@ -136,28 +138,12 @@ def create_outputs(input_priv_keys: List[Tuple[ECKey, bool]], input_hash: bytes, outputs = [] for B_scan, B_m_values in silent_payment_groups.items(): ecdh_shared_secret = input_hash * a_sum * B_scan - - # Order doesn't matter for creating/finding the outputs. However, different orderings of the recipient addresses - # will produce different generated outputs if sending to multiple silent payment addresses belong to the - # same sender but with different labels. To account for this in testing, set `perumute=True`. This will generate - # all possible sets of outputs (based on all possible orderings of recipient addresses). - # - # NOTE: THIS IS A TEST ONLY FEATURE AND NOT RELEVANT FOR NORMAL SENDING/RECEIVING - if permute: - for permutation in permutations(B_m_values, len(B_m_values)): - k = 0 - for B_m in permutation: - t_k = TaggedHash("BIP0352/SharedSecret", ecdh_shared_secret.get_bytes(False) + ser_uint32(k)) - P_km = B_m + t_k * G - outputs.append(P_km.get_bytes().hex()) - k += 1 - else: - k = 0 - for B_m in B_m_values: - t_k = TaggedHash("BIP0352/SharedSecret", ecdh_shared_secret.get_bytes(False) + ser_uint32(k)) - P_km = B_m + t_k * G - outputs.append(P_km.get_bytes().hex()) - k += 1 + k = 0 + for B_m in B_m_values: + t_k = TaggedHash("BIP0352/SharedSecret", ecdh_shared_secret.get_bytes(False) + ser_uint32(k)) + P_km = B_m + t_k * G + outputs.append(P_km.get_bytes().hex()) + k += 1 return list(set(outputs)) @@ -188,6 +174,7 @@ def scanning(b_scan: ECKey, B_spend: ECPubKey, A_sum: ECPubKey, input_hash: byte }) outputs_to_check.remove(output) k += 1 + break else: output.negate() m_G_sub = output - P_k @@ -208,6 +195,10 @@ def scanning(b_scan: ECKey, B_spend: ECPubKey, A_sum: ECPubKey, input_hash: byte if __name__ == "__main__": + if len(argv) != 2 or argv[1] in ('-h', '--help'): + print("Usage: ./reference.py send_and_receive_test_vectors.json") + exit(0) + with open(argv[1], "r") as f: test_data = json.loads(f.read()) @@ -247,10 +238,15 @@ def scanning(b_scan: ECKey, B_spend: ECPubKey, A_sum: ECPubKey, input_hash: byte if (len(input_pub_keys) > 0): A_sum = reduce(lambda x, y: x + y, input_pub_keys) input_hash = get_input_hash([vin.outpoint for vin in vins], A_sum) - sending_outputs = create_outputs(input_priv_keys, input_hash, given["recipients"], hrp="sp", permute=False) - # Check that for a given set of inputs, we were able to generate the expected outputs for the receiver - for output in sending_outputs: - assert output in expected["outputs"], "Sending test failed" + sending_outputs = create_outputs(input_priv_keys, input_hash, given["recipients"], hrp="sp") + + # Note: order doesn't matter for creating/finding the outputs. However, different orderings of the recipient addresses + # will produce different generated outputs if sending to multiple silent payment addresses belonging to the + # same sender but with different labels. Because of this, expected["outputs"] contains all possible valid output sets, + # based on all possible permutations of recipient address orderings. Must match exactly one of the possible output sets. + assert(any(set(sending_outputs) == set(lst) for lst in expected["outputs"])), "Sending test failed" + else: + assert(sending_outputs == expected["outputs"][0] == []), "Sending test failed" # Test receiving msg = hashlib.sha256(b"message").digest() @@ -326,7 +322,14 @@ def scanning(b_scan: ECKey, B_spend: ECPubKey, A_sum: ECPubKey, input_hash: byte assert pub_key.verify_schnorr(sig, msg), f"Invalid signature for {pub_key}" output["signature"] = sig.hex() - # Check if the found output public keys match the expected output public keys - assert add_to_wallet == expected["outputs"], "Receiving test failed" + # Note: order doesn't matter for creating/finding the outputs. However, different orderings of the recipient addresses + # will produce different generated outputs if sending to multiple silent payment addresses belonging to the + # same sender but with different labels. Because of this, expected["outputs"] contains all possible valid output sets, + # based on all possible permutations of recipient address orderings. Must match exactly one of the possible found output + # sets in expected["outputs"] + generated_set = {frozenset(d.items()) for d in add_to_wallet} + expected_set = {frozenset(d.items()) for d in expected["outputs"]} + assert generated_set == expected_set, "Receive test failed" + print("All tests passed") diff --git a/send_and_receive_test_vectors.json b/send_and_receive_test_vectors.json index b4119ab..f9b205b 100644 --- a/send_and_receive_test_vectors.json +++ b/send_and_receive_test_vectors.json @@ -36,7 +36,9 @@ }, "expected": { "outputs": [ - "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1" + [ + "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1" + ] ] } } @@ -83,8 +85,8 @@ ], "outputs": [ { - "pub_key": "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1", "priv_key_tweak": "f438b40179a3c4262de12986c0e6cce0634007cdc79c1dcd3e20b9ebc2e7eef6", + "pub_key": "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1", "signature": "74f85b856337fbe837643b86f462118159f93ac4acc2671522f27e8f67b079959195ccc7a5dbee396d2909f5d680d6e30cda7359aa2755822509b70d6b0687a1" } ] @@ -129,7 +131,9 @@ }, "expected": { "outputs": [ - "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1" + [ + "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1" + ] ] } } @@ -176,8 +180,8 @@ ], "outputs": [ { - "pub_key": "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1", "priv_key_tweak": "f438b40179a3c4262de12986c0e6cce0634007cdc79c1dcd3e20b9ebc2e7eef6", + "pub_key": "3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1", "signature": "74f85b856337fbe837643b86f462118159f93ac4acc2671522f27e8f67b079959195ccc7a5dbee396d2909f5d680d6e30cda7359aa2755822509b70d6b0687a1" } ] @@ -222,7 +226,9 @@ }, "expected": { "outputs": [ - "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6" + [ + "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6" + ] ] } } @@ -269,8 +275,8 @@ ], "outputs": [ { - "pub_key": "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6", "priv_key_tweak": "4851455bfbe1ab4f80156570aa45063201aa5c9e1b1dcd29f0f8c33d10bf77ae", + "pub_key": "79e71baa2ba3fc66396de3a04f168c7bf24d6870ec88ca877754790c1db357b6", "signature": "10332eea808b6a13f70059a8a73195808db782012907f5ba32b6eae66a2f66b4f65147e2b968a1678c5f73d57d5d195dbaf667b606ff80c8490eac1f3b710657" } ] @@ -315,7 +321,9 @@ }, "expected": { "outputs": [ - "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38" + [ + "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38" + ] ] } } @@ -362,8 +370,8 @@ ], "outputs": [ { - "pub_key": "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38", "priv_key_tweak": "ab0c9b87181bf527879f48db9f14a02233619b986f8e8f2d5d408ce68a709f51", + "pub_key": "f4c2da807f89cb1501f1a77322a895acfb93c28e08ed2724d2beb8e44539ba38", "signature": "398a9790865791a9db41a8015afad3a47d60fec5086c50557806a49a1bc038808632b8fe679a7bb65fc6b455be994502eed849f1da3729cd948fc7be73d67295" } ] @@ -371,6 +379,101 @@ } ] }, + { + "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": [ + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + ] + }, + "expected": { + "outputs": [ + [ + "a85ef8701394b517a4b35217c4bd37ac01ebeed4b008f8d0879f9e09ba95319c" + ] + ] + } + } + ], + "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" + } + ] + } + } + ] + }, { "comment": "Single recipient: multiple UTXOs from the same public key", "sending": [ @@ -408,7 +511,9 @@ }, "expected": { "outputs": [ - "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566" + [ + "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566" + ] ] } } @@ -455,8 +560,8 @@ ], "outputs": [ { - "pub_key": "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566", "priv_key_tweak": "f032695e2636619efa523fffaa9ef93c8802299181fd0461913c1b8daf9784cd", + "pub_key": "548ae55c8eec1e736e8d3e520f011f1f42a56d166116ad210b3937599f87f566", "signature": "f238386c5d5e5444f8d2c75aabbcb28c346f208c76f60823f5de3b67b79e0ec72ea5de2d7caec314e0971d3454f122dda342b3eede01b3857e83654e36b25f76" } ] @@ -501,7 +606,9 @@ }, "expected": { "outputs": [ - "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb" + [ + "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb" + ] ] } } @@ -548,8 +655,8 @@ ], "outputs": [ { - "pub_key": "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb", "priv_key_tweak": "3fb9ce5ce1746ced103c8ed254e81f6690764637ddbc876ec1f9b3ddab776b03", + "pub_key": "de88bea8e7ffc9ce1af30d1132f910323c505185aec8eae361670421e749a1fb", "signature": "c5acd25a8f021a4192f93bc34403fd8b76484613466336fb259c72d04c169824f2690ca34e96cee86b69f376c8377003268fda56feeb1b873e5783d7e19bcca5" } ] @@ -594,7 +701,9 @@ }, "expected": { "outputs": [ - "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1" + [ + "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1" + ] ] } } @@ -641,8 +750,8 @@ ], "outputs": [ { - "pub_key": "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1", "priv_key_tweak": "f5382508609771068ed079b24e1f72e4a17ee6d1c979066bf1d4e2a5676f09d4", + "pub_key": "77cab7dd12b10259ee82c6ea4b509774e33e7078e7138f568092241bf26b99f1", "signature": "ff65833b8fd1ed3ef9d0443b4f702b45a3f2dd457ba247687e8207745c3be9d2bdad0ab3f07118f8b2efc6a04b95f7b3e218daf8a64137ec91bd2fc67fc137a5" } ] @@ -687,7 +796,9 @@ }, "expected": { "outputs": [ - "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0" + [ + "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0" + ] ] } } @@ -734,8 +845,8 @@ ], "outputs": [ { - "pub_key": "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0", "priv_key_tweak": "b40017865c79b1fcbed68896791be93186d08f47e416b289b8c063777e14e8df", + "pub_key": "30523cca96b2a9ae3c98beb5e60f7d190ec5bc79b2d11a0b2d4d09a608c448f0", "signature": "d1edeea28cf1033bcb3d89376cabaaaa2886cbd8fda112b5c61cc90a4e7f1878bdd62180b07d1dfc8ffee1863c525a0c7b5bcd413183282cfda756cb65787266" } ] @@ -780,7 +891,9 @@ }, "expected": { "outputs": [ - "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a" + [ + "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a" + ] ] } } @@ -827,8 +940,8 @@ ], "outputs": [ { - "pub_key": "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a", "priv_key_tweak": "a2f9dd05d1d398347c885d9c61a64d18a264de6d49cea4326bafc2791d627fa7", + "pub_key": "359358f59ee9e9eec3f00bdf4882570fd5c182e451aa2650b788544aff012a3a", "signature": "96038ad233d8befe342573a6e54828d863471fb2afbad575cc65271a2a649480ea14912b6abbd3fbf92efc1928c036f6e3eef927105af4ec1dd57cb909f360b8" } ] @@ -874,8 +987,10 @@ }, "expected": { "outputs": [ - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + [ + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ] ] } } @@ -909,9 +1024,7 @@ ], "outputs": [ "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -925,14 +1038,14 @@ ], "outputs": [ { - "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a", - "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912" - }, - { - "pub_key": "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", "priv_key_tweak": "d97e442d110c0bdd31161a7bb6e7862e038d02a09b1484dfbb463f2e0f7c9230", + "pub_key": "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", "signature": "29bd25d0f808d7fcd2aa6d5ed206053899198397506c301b218a9e47a3d7070af03e903ff718978d50d1b6b9af8cc0e313d84eda5d5b1e8e85e5516d630bbeb9" + }, + { + "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a", + "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912" } ] } @@ -971,7 +1084,6 @@ } ], "recipients": [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn", "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn" @@ -979,10 +1091,11 @@ }, "expected": { "outputs": [ - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" + [ + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ] ] } } @@ -1015,66 +1128,9 @@ } ], "outputs": [ - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" - ], - "key_material": { - "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", - "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" - }, - "labels": [] - }, - "expected": { - "addresses": [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" - ], - "outputs": [ - { - "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a", - "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912" - }, - { - "pub_key": "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - "priv_key_tweak": "d97e442d110c0bdd31161a7bb6e7862e038d02a09b1484dfbb463f2e0f7c9230", - "signature": "29bd25d0f808d7fcd2aa6d5ed206053899198397506c301b218a9e47a3d7070af03e903ff718978d50d1b6b9af8cc0e313d84eda5d5b1e8e85e5516d630bbeb9" - } - ] - } - }, - { - "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", - "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a" + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" ], "key_material": { "spend_priv_key": "9902c3c56e84002a7cd410113a9ab21d142be7f53cf5200720bb01314c5eb920", @@ -1088,14 +1144,14 @@ ], "outputs": [ { - "pub_key": "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", - "priv_key_tweak": "2f17ea873a0047fc01ba8010fef0969e76d0e4283f600d48f735098b1fee6eb9", - "signature": "c26f4e3cf371b90b840f48ea0e761b5ec31883ed55719f9ef06a90e282d85f565790ab780a3f491bc2668cc64e944dca849d1022a878cdadb8d168b8da4a6da3" - }, - { - "pub_key": "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", "priv_key_tweak": "72cd082cccb633bf85240a83494b32dc943a4d05647a6686d23ad4ca59c0ebe4", + "pub_key": "2e847bb01d1b491da512ddd760b8509617ee38057003d6115d00ba562451323a", "signature": "38745f3d9f5eef0b1cfb17ca314efa8c521efab28a23aa20ec5e3abb561d42804d539906dce60c4ee7977966184e6f2cab1faa0e5377ceb7148ec5218b4e7878" + }, + { + "priv_key_tweak": "2f17ea873a0047fc01ba8010fef0969e76d0e4283f600d48f735098b1fee6eb9", + "pub_key": "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "signature": "c26f4e3cf371b90b840f48ea0e761b5ec31883ed55719f9ef06a90e282d85f565790ab780a3f491bc2668cc64e944dca849d1022a878cdadb8d168b8da4a6da3" } ] } @@ -1103,7 +1159,7 @@ ] }, { - "comment": "Receiving with labels: label with odd parity", + "comment": "Receiving with labels: label with even parity", "sending": [ { "given": { @@ -1139,7 +1195,9 @@ }, "expected": { "outputs": [ - "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a" + [ + "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a" + ] ] } } @@ -1193,8 +1251,8 @@ ], "outputs": [ { - "pub_key": "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a", "priv_key_tweak": "51d4e9d0d482b5700109b4b2e16ff508269b03d800192a043d61dca4a0a72a52", + "pub_key": "d014d4860f67d607d60b1af70e0ee236b99658b61bb769832acbbe87c374439a", "signature": "c30fa63bad6f0a317f39a773a5cbf0b0f8193c71dfebba05ee6ae4ed28e3775e6e04c3ea70a83703bb888122855dc894cab61692e7fd10c9b3494d479a60785e" } ] @@ -1239,7 +1297,9 @@ }, "expected": { "outputs": [ - "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c" + [ + "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c" + ] ] } } @@ -1293,8 +1353,8 @@ ], "outputs": [ { - "pub_key": "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c", "priv_key_tweak": "6024ae214876356b8d917716e7707d267ae16a0fdb07de2a786b74a7bbcddead", + "pub_key": "67626aebb3c4307cf0f6c39ca23247598fabf675ab783292eb2f81ae75ad1f8c", "signature": "a86d554d0d6b7aa0907155f7e0b47f0182752472fffaeddd68da90e99b9402f166fd9b33039c302c7115098d971c1399e67c19e9e4de180b10ea0b9d6f0db832" } ] @@ -1303,7 +1363,7 @@ ] }, { - "comment": "Receiving with labels: label with odd parity", + "comment": "Receiving with labels: large label integer", "sending": [ { "given": { @@ -1339,7 +1399,9 @@ }, "expected": { "outputs": [ - "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951" + [ + "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951" + ] ] } } @@ -1393,8 +1455,8 @@ ], "outputs": [ { - "pub_key": "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951", "priv_key_tweak": "e336b92330c33030285ce42e4115ad92d5197913c88e06b9072b4a9b47c664a2", + "pub_key": "7efa60ce78ac343df8a013a2027c6c5ef29f9502edcbd769d2c21717fecc5951", "signature": "c9e80dd3bdd25ca2d352ce77510f1aed37ba3509dc8cc0677f2d7c2dd04090707950ce9dd6c83d2a428063063aff5c04f1744e334f661f2fc01b4ef80b50f739" } ] @@ -1403,7 +1465,7 @@ ] }, { - "comment": "Multiple outputs with labels: multiple outputs for labeled address; same recipient", + "comment": "Multiple outputs with labels: un-labeled and labeled address; same recipient", "sending": [ { "given": { @@ -1440,10 +1502,14 @@ }, "expected": { "outputs": [ - "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c" + [ + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ], + [ + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca" + ] ] } } @@ -1476,10 +1542,8 @@ } ], "outputs": [ - "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c" + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -1496,14 +1560,14 @@ ], "outputs": [ { - "pub_key": "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "priv_key_tweak": "9d5fd3b91cac9ddfea6fc2e6f9386f680e6cee623cda02f53706306c081de87f", - "signature": "db0dfacc98b6a6fcc67cc4631f080b1ca38c60d8c397f2f19843f8f95ec91594b24e47c5bd39480a861c1209f7e3145c440371f9191fb96e324690101eac8e8e" + "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914", + "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75" }, { - "pub_key": "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - "priv_key_tweak": "d97e442d110c0bdd31161a7bb6e7862e038d02a09b1484dfbb463f2e0f7c9230", - "signature": "29bd25d0f808d7fcd2aa6d5ed206053899198397506c301b218a9e47a3d7070af03e903ff718978d50d1b6b9af8cc0e313d84eda5d5b1e8e85e5516d630bbeb9" + "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a", + "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", + "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912" } ] } @@ -1548,8 +1612,10 @@ }, "expected": { "outputs": [ - "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c" + [ + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c" + ] ] } } @@ -1582,8 +1648,8 @@ } ], "outputs": [ - "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c" + "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -1600,14 +1666,14 @@ ], "outputs": [ { - "pub_key": "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "priv_key_tweak": "9d5fd3b91cac9ddfea6fc2e6f9386f680e6cee623cda02f53706306c081de87f", - "signature": "db0dfacc98b6a6fcc67cc4631f080b1ca38c60d8c397f2f19843f8f95ec91594b24e47c5bd39480a861c1209f7e3145c440371f9191fb96e324690101eac8e8e" - }, - { - "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914", + "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75" + }, + { + "priv_key_tweak": "9d5fd3b91cac9ddfea6fc2e6f9386f680e6cee623cda02f53706306c081de87f", + "pub_key": "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", + "signature": "db0dfacc98b6a6fcc67cc4631f080b1ca38c60d8c397f2f19843f8f95ec91594b24e47c5bd39480a861c1209f7e3145c440371f9191fb96e324690101eac8e8e" } ] } @@ -1615,7 +1681,7 @@ ] }, { - "comment": "Multiple outputs with labels: multiple outputs for labeled address; same recipient", + "comment": "Multiple outputs with labels: un-labeled, labeled, and multiple outputs for labeled address; same recipients", "sending": [ { "given": { @@ -1654,18 +1720,78 @@ }, "expected": { "outputs": [ - "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", - "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e", - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b", - "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", - "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed", - "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", - "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", - "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", - "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701" + [ + "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" + ] ] } } @@ -1698,62 +1824,46 @@ } ], "outputs": [ - "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", - "602e10e6944107c9b48bd885b493676578c935723287e0ab2f8b7f136862568e", - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "f4569fc5f69c10f0082cfbb8e072e6266ec55f69fba8cffca4cbb4c144b7e59b", - "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", - "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed", - "7ee1543ed5d123ffa66fbebc128c020173eb490d5fa2ba306e0c9573a77db8f3", - "3c54444944d176437644378c23efb999ab6ab1cacdfe1dc1537b607e3df330e2", "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", - "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701" + "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", + "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", "scan_priv_key": "0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c" }, "labels": [ - 1 + 1, + 1337 ] }, "expected": { "addresses": [ "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv", - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj" + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqaxww2fnhrx05cghth75n0qcj59e3e2anscr0q9wyknjxtxycg07y3pevyj", + "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjyh2ju7hd5gj57jg5r9lev3pckk4n2shtzaq34467erzzdfajfggty6aa5" ], - "outputs": [] - } - } - ] - }, - { - "comment": "Multiple outputs with labels: multiple outputs per multiple lables; multiple recipients", - "sending": [ - { - "given": { - "vin": [ + "outputs": [ { - "pub_key": "83dc944e61603137294829aed56c74c9b087d80f2c021b98a7fae5799000696c", - "priv_key_tweak": "9d5fd3b91cac9ddfea6fc2e6f9386f680e6cee623cda02f53706306c081de87f", - "signature": "db0dfacc98b6a6fcc67cc4631f080b1ca38c60d8c397f2f19843f8f95ec91594b24e47c5bd39480a861c1209f7e3145c440371f9191fb96e324690101eac8e8e" + "priv_key_tweak": "4e3352fbe0505c25e718d96007c259ef08db34f8c844e4ff742d9855ff03805a", + "pub_key": "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", + "signature": "6eeae1ea9eb826e3d0e812f65937100e0836ea188c04f36fabc4981eda29de8d3d3529390a0a8b3d830f7bca4f5eae5994b9788ddaf05ad259ffe26d86144b4b" }, { - "pub_key": "e976a58fbd38aeb4e6093d4df02e9c1de0c4513ae0c588cef68cda5b2f8834ca", - "priv_key_tweak": "d97e442d110c0bdd31161a7bb6e7862e038d02a09b1484dfbb463f2e0f7c9230", - "signature": "29bd25d0f808d7fcd2aa6d5ed206053899198397506c301b218a9e47a3d7070af03e903ff718978d50d1b6b9af8cc0e313d84eda5d5b1e8e85e5516d630bbeb9" + "priv_key_tweak": "43100f89f1a6bf10081c92b473ffc57ceac7dbed600b6aba9bb3976f17dbb914", + "pub_key": "39f42624d5c32a77fda80ff0acee269afec601d3791803e80252ae04e4ffcf4c", + "signature": "15c92509b67a6c211ebb4a51b7528d0666e6720de2343b2e92cfb97942ca14693c1f1fdc8451acfdb2644039f8f5c76114807fdc3d3a002d8a46afab6756bd75" }, { - "pub_key": "3edf1ff6657c6e69568811bd726a7a7f480493aa42161acfe8dd4f44521f99ed", - "priv_key_tweak": "e9616d6d0de7012ecc003c1d5133382c1d7692350bbedde1bf1fef0eeea1d441", - "signature": "7a9ff514d8b42014f0dde5a5f800eb9fc876680cac89af0777cfbb59af2bb5e3c16cc807edfcfaf70a910dbcdda8a66fda6a03c1757559bdae3304aadf78e638" + "priv_key_tweak": "bf709f98d4418f8a67e738154ae48818dad44689cd37fbc070891a396dd1c633", + "pub_key": "ae1a780c04237bd577283c3ddb2e499767c3214160d5a6b0767e6b8c278bd701", + "signature": "42a19fd8a63dde1824966a95d65a28203e631e49bf96ca5dae1b390e7a0ace2cc8709c9b0c5715047032f57f536a3c80273cbecf4c05be0b5456c183fa122c06" }, { - "pub_key": "006a02c308ccdbf3ac49f0638f6de128f875db5a213095cf112b3b77722472ae", - "priv_key_tweak": "4e3352fbe0505c25e718d96007c259ef08db34f8c844e4ff742d9855ff03805a", - "signature": "6eeae1ea9eb826e3d0e812f65937100e0836ea188c04f36fabc4981eda29de8d3d3529390a0a8b3d830f7bca4f5eae5994b9788ddaf05ad259ffe26d86144b4b" + "priv_key_tweak": "736f05e4e3072c3b8656bedef2e9bf54cbcaa2b6fe5320d3e86f5b96874dda71", + "pub_key": "ca64abe1e0f737823fb9a94f597eed418fb2df77b1317e26b881a14bb594faaa", + "signature": "2e61bb3d79418ecf55f68847cf121bfc12d397b39d1da8643246b2f0a9b96c3daa4bfe9651beb5c9ce20e1f29282c4566400a4b45ee6657ec3b18fdc554da0b4" } ] } @@ -1798,8 +1908,10 @@ }, "expected": { "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff" + [ + "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + ] ] } } @@ -1832,8 +1944,8 @@ } ], "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff" + "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" ], "key_material": { "spend_priv_key": "b8f87388cbb41934c50daca018901b00070a5ff6cc25a7e9e716a9d5b9e4d664", @@ -1850,8 +1962,8 @@ ], "outputs": [ { - "pub_key": "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff", "priv_key_tweak": "80cd767ed20bd0bb7d8ea5e803f8c381293a62e8a073cf46fb0081da46e64e1f", + "pub_key": "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff", "signature": "7fbd5074cf1377273155eefafc7c330cb61b31da252f22206ac27530d2b2567040d9af7808342ed4a09598c26d8307446e4ed77079e6a2e61fea736e44da5f5a" } ] @@ -1884,8 +1996,8 @@ } ], "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", - "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff" + "be368e28979d950245d742891ae6064020ba548c1e2e65a639a8bb0675d95cff", + "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -1899,8 +2011,8 @@ ], "outputs": [ { - "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", "priv_key_tweak": "33ce085c3c11eaad13694aae3c20301a6c83382ec89a7cde96c6799e2f88805a", + "pub_key": "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac", "signature": "335667ca6cae7a26438f5cfdd73b3d48fa832fa9768521d7d5445f22c203ab0d74ed85088f27d29959ba627a4509996676f47df8ff284d292567b1beef0e3912" } ] @@ -1909,7 +2021,7 @@ ] }, { - "comment": "Single receipient: taproot input with NUMS point", + "comment": "Single recipient: taproot input with NUMS point", "sending": [ { "given": { @@ -1957,7 +2069,9 @@ }, "expected": { "outputs": [ - "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d" + [ + "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d" + ] ] } } @@ -2015,8 +2129,8 @@ ], "outputs": [ { - "pub_key": "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d", "priv_key_tweak": "3ddec3232609d348d6b8b53123b4f40f6d4f5398ca586f087b0416ec3b851496", + "pub_key": "79e79897c52935bfd97fc6e076a6431a0c7543ca8c31e0fc3cf719bb572c842d", "signature": "d7d06e3afb68363031e4eb18035c46ceae41bdbebe7888a4754bc9848c596436869aeaecff0527649a1f458b71c9ceecec10b535c09d01d720229aa228547706" } ] @@ -2073,7 +2187,9 @@ }, "expected": { "outputs": [ - "4612cdbf845c66c7511d70aab4d9aed11e49e48cdb8d799d787101cdd0d53e4f" + [ + "4612cdbf845c66c7511d70aab4d9aed11e49e48cdb8d799d787101cdd0d53e4f" + ] ] } } @@ -2131,8 +2247,8 @@ ], "outputs": [ { - "pub_key": "4612cdbf845c66c7511d70aab4d9aed11e49e48cdb8d799d787101cdd0d53e4f", "priv_key_tweak": "10bde9781def20d7701e7603ef1b1e5e71c67bae7154818814e3c81ef5b1a3d3", + "pub_key": "4612cdbf845c66c7511d70aab4d9aed11e49e48cdb8d799d787101cdd0d53e4f", "signature": "6137969f810e9e8ef6c9755010e808f5dd1aed705882e44d7f0ae64eb0c509ec8b62a0671bee0d5914ac27d2c463443e28e999d82dc3d3a4919f093872d947bb" } ] @@ -2189,7 +2305,9 @@ }, "expected": { "outputs": [ - "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" + [ + "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" + ] ] } } @@ -2247,8 +2365,8 @@ ], "outputs": [ { - "pub_key": "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6", "priv_key_tweak": "688fa3aeb97d2a46ae87b03591921c2eaf4b505eb0ddca2733c94701e01060cf", + "pub_key": "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6", "signature": "72e7ad573ac23255d4651d5b0326a200496588acb7a4894b22092236d5eda6a0a9a4d8429b022c2219081fefce5b33795cae488d10f5ea9438849ed8353624f2" } ] @@ -2305,57 +2423,9 @@ }, "expected": { "outputs": [ - "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" - ] - } - }, - { - "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": [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" - ] - }, - "expected": { - "outputs": [ - "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" + [ + "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6" + ] ] } } @@ -2413,8 +2483,8 @@ ], "outputs": [ { - "pub_key": "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6", "priv_key_tweak": "688fa3aeb97d2a46ae87b03591921c2eaf4b505eb0ddca2733c94701e01060cf", + "pub_key": "67fee277da9e8542b5d2e6f32d660a9bbd3f0e107c2d53638ab1d869088882d6", "signature": "72e7ad573ac23255d4651d5b0326a200496588acb7a4894b22092236d5eda6a0a9a4d8429b022c2219081fefce5b33795cae488d10f5ea9438849ed8353624f2" } ] @@ -2454,12 +2524,14 @@ } ], "recipients": [ - "sp1qqgste7k9hx0qftg6qmwlkqtwuy6cycyavzmzj85c6qdfhjdpdjtdgqjuexzk6murw56suy3e0rd2cgqvycxttddwsvgxe2usfpxumr70xc9pkqwv" + "sp1qqgrz6j0lcqnc04vxccydl0kpsj4frfje0ktmgcl2t346hkw30226xqupawdf48k8882j0strrvcmgg2kdawz53a54dd376ngdhak364hzcmynqtn" ] }, "expected": { "outputs": [ - "f207162b1a7abc51c42017bef055e9ec1efc3d3567cb720357e2b84325db33ac" + [ + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8" + ] ] } } @@ -2492,8 +2564,8 @@ } ], "outputs": [ - "782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338", - "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8" + "841792c33c9dc6193e76744134125d40add8f2f4a96475f28ba150be032d64e8", + "782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" ], "key_material": { "spend_priv_key": "9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3", @@ -2546,7 +2618,9 @@ ] }, "expected": { - "outputs": [] + "outputs": [ + [] + ] } } ], @@ -2596,4 +2670,4 @@ } ] } -] +] \ No newline at end of file