From 396653f7457f3c3d443ea20da1cd77afe861db90 Mon Sep 17 00:00:00 2001 From: Ferdinando Ametrano Date: Mon, 7 Sep 2026 20:42:48 +0200 Subject: [PATCH] bip-0352: do not require the optional expected argument create_outputs and scanning declare expected with a default of None but dereference it unconditionally (expected.get(...)), so calling either without it raises AttributeError, although the parameter is documented optional. Guard the expected-value checks with `if expected is not None:`. The self-test, which always passes expected, is unaffected. --- bip-0352/reference.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/bip-0352/reference.py b/bip-0352/reference.py index 1671c25142..c334d32899 100755 --- a/bip-0352/reference.py +++ b/bip-0352/reference.py @@ -137,7 +137,8 @@ def create_outputs(input_priv_keys: List[Tuple[Scalar, bool]], outpoints: List[C if a_sum == 0: # Input privkeys sum is zero -> fail return [] - assert Scalar.from_bytes_checked(bytes.fromhex(expected.get("input_private_key_sum"))) == a_sum, "a_sum did not match expected input_private_key_sum" + if expected is not None: + assert Scalar.from_bytes_checked(bytes.fromhex(expected.get("input_private_key_sum"))) == a_sum, "a_sum did not match expected input_private_key_sum" input_hash_scalar = Scalar.from_bytes_checked(get_input_hash(outpoints, a_sum * G)) silent_payment_groups: Dict[GE, List[GE]] = {} for recipient in recipients: @@ -159,14 +160,15 @@ def create_outputs(input_priv_keys: List[Tuple[Scalar, bool]], outpoints: List[C outputs = [] for B_scan, B_m_values in silent_payment_groups.items(): ecdh_shared_secret = input_hash_scalar * a_sum * B_scan - expected_shared_secrets = expected.get("shared_secrets", {}) - # Find the recipient address that corresponds to this B_scan and get its index - for recipient_idx, recipient in enumerate(recipients): - recipient_B_scan = GE.from_bytes_compressed(bytes.fromhex(recipient["scan_pub_key"])) - if recipient_B_scan == B_scan: - expected_shared_secret_hex = expected_shared_secrets[recipient_idx] - assert ecdh_shared_secret.to_bytes_compressed().hex() == expected_shared_secret_hex, f"ecdh_shared_secret did not match expected, recipient {recipient_idx} ({recipient['address']}): expected={expected_shared_secret_hex}" - break + if expected is not None: + expected_shared_secrets = expected.get("shared_secrets", {}) + # Find the recipient address that corresponds to this B_scan and get its index + for recipient_idx, recipient in enumerate(recipients): + recipient_B_scan = GE.from_bytes_compressed(bytes.fromhex(recipient["scan_pub_key"])) + if recipient_B_scan == B_scan: + expected_shared_secret_hex = expected_shared_secrets[recipient_idx] + assert ecdh_shared_secret.to_bytes_compressed().hex() == expected_shared_secret_hex, f"ecdh_shared_secret did not match expected, recipient {recipient_idx} ({recipient['address']}): expected={expected_shared_secret_hex}" + break k = 0 for B_m in B_m_values: t_k = Scalar.from_bytes_checked(tagged_hash("BIP0352/SharedSecret", ecdh_shared_secret.to_bytes_compressed() + ser_uint32(k))) @@ -180,9 +182,10 @@ def create_outputs(input_priv_keys: List[Tuple[Scalar, bool]], outpoints: List[C def scanning(b_scan: Scalar, B_spend: GE, A_sum: GE, input_hash: bytes, outputs_to_check: List[bytes], labels: Dict[str, str] = None, expected: Dict[str, Any] = None) -> List[Dict[str, str]]: input_hash_scalar = Scalar.from_bytes_checked(input_hash) computed_tweak_point = input_hash_scalar * A_sum - assert computed_tweak_point.to_bytes_compressed().hex() == expected.get("tweak"), "tweak did not match expected" ecdh_shared_secret = input_hash_scalar * b_scan * A_sum - assert ecdh_shared_secret.to_bytes_compressed().hex() == expected.get("shared_secret"), "ecdh_shared_secret did not match expected shared_secret" + if expected is not None: + assert computed_tweak_point.to_bytes_compressed().hex() == expected.get("tweak"), "tweak did not match expected" + assert ecdh_shared_secret.to_bytes_compressed().hex() == expected.get("shared_secret"), "ecdh_shared_secret did not match expected shared_secret" k = 0 wallet = [] while True: