Skip to content

Commit 897455d

Browse files
committed
BIP-375: skip ineligible inputs when combining ecdh shares
add fake ecdh share and dleq proof to P2SH input for valid test: two inputs using per-input ECDH shares - only eligible inputs contribute shares (P2SH excluded) remove unused return string from is_input_eligible
1 parent 6295a70 commit 897455d

3 files changed

Lines changed: 14 additions & 19 deletions

File tree

bip-0375/bip375_test_vectors.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,7 @@
10111011
},
10121012
{
10131013
"description": "can finalize: two inputs using per-input ECDH shares - only eligible inputs contribute shares (P2SH excluded)",
1014-
"psbt": "cHNidP8B+wQCAAAAAQIEAgAAAAEEAQIBBQEBAQYBAAABDiAYpxdmOwurFLEqGncTI/8eQHndUy5d0T4o6hCBxwCYSgEPBAAAAAABAR+ghgEAAAAAABYAFCKactNKZFvTSWu79Qu7gckGP0+UIgICyBe7dSGvw16pbzv7Jw5utQ3f+lVgYnuWH+wA8pllCL9IMEUCIQCLTLcGPIp7P5Ia4ABZbNd4jlRA4dY+8e4bzsjCrJQMogIgNw2OmoWgzI3SWwuwgfMaotzFugoiSOqGCoFlHKNKDGgBAQMEAQAAACIGAsgXu3Uhr8NeqW87+ycObrUN3/pVYGJ7lh/sAPKZZQi/CAAAAIAAAAAAARAE/v///yIdAnpIf8Gft2mHe4dC1uoYEY88TnKx6oxt5gKnrUpB2+BoIQPspP8Rtyji4PYM5iIpQ6b/VbnZX2J7+amdCEvIctUKWyIeAnpIf8Gft2mHe4dC1uoYEY88TnKx6oxt5gKnrUpB2+BoQIoTs5hVRfcr1uiXFK65CbPjVKhCqbuLVs0O3tId+KGZWYsxIopJ4L1+lc4QU/fFsorLVDpocHYA486Jgi7jICEAAQ4g0iImLyaHKbt8hMVYEppdaYYYXO9TXsFJwN9g3UqSwgQBDwQAAAAAAQBTAgAAAAFA5vqmEmjxehAQHWJVNYKIKuSFi+4TNj24D98oH4Jf/AAAAAAA/////wHwSQIAAAAAABepFPRfjMomjsJuS76JkXDxOCUNfPVehwAAAAABBEdSIQKHfKAvFEBZvYLQDhs5muN094pSzvehyjfyjXiLNA0veSEDTIekSHL14fAG002IoAlZKCvUU150d8PWjDFlmR6hs5ZSrgEQBP7///8AAQMIkF8BAAAAAAABBCJRIDJt9Q/goHt6y3IHC+s7Yy65rRW6aVzB5apqoAe2FG/YAQlCAnpIf8Gft2mHe4dC1uoYEY88TnKx6oxt5gKnrUpB2+BoA2HhseneXkLLIAf3ylS54NV+0Tk4+tVtPxnldROo/OA5AA==",
1014+
"psbt": "cHNidP8B+wQCAAAAAQIEAgAAAAEEAQIBBQEBAQYBAAABDiAYpxdmOwurFLEqGncTI/8eQHndUy5d0T4o6hCBxwCYSgEPBAAAAAABAR+ghgEAAAAAABYAFCKactNKZFvTSWu79Qu7gckGP0+UIgICyBe7dSGvw16pbzv7Jw5utQ3f+lVgYnuWH+wA8pllCL9IMEUCIQCLTLcGPIp7P5Ia4ABZbNd4jlRA4dY+8e4bzsjCrJQMogIgNw2OmoWgzI3SWwuwgfMaotzFugoiSOqGCoFlHKNKDGgBAQMEAQAAACIGAsgXu3Uhr8NeqW87+ycObrUN3/pVYGJ7lh/sAPKZZQi/CAAAAIAAAAAAARAE/v///yIdAnpIf8Gft2mHe4dC1uoYEY88TnKx6oxt5gKnrUpB2+BoIQPspP8Rtyji4PYM5iIpQ6b/VbnZX2J7+amdCEvIctUKWyIeAnpIf8Gft2mHe4dC1uoYEY88TnKx6oxt5gKnrUpB2+BoQIoTs5hVRfcr1uiXFK65CbPjVKhCqbuLVs0O3tId+KGZWYsxIopJ4L1+lc4QU/fFsorLVDpocHYA486Jgi7jICEAAQ4g0iImLyaHKbt8hMVYEppdaYYYXO9TXsFJwN9g3UqSwgQBDwQAAAAAAQBTAgAAAAFA5vqmEmjxehAQHWJVNYKIKuSFi+4TNj24D98oH4Jf/AAAAAAA/////wHwSQIAAAAAABepFPRfjMomjsJuS76JkXDxOCUNfPVehwAAAAABBEdSIQKHfKAvFEBZvYLQDhs5muN094pSzvehyjfyjXiLNA0veSEDTIekSHL14fAG002IoAlZKCvUU150d8PWjDFlmR6hs5ZSrgEQBP7///8iHQJ6SH/Bn7dph3uHQtbqGBGPPE5yseqMbeYCp61KQdvgaCECYototkRuaocCWXPZOvsZTkIKEPOhzLOK3YLUZKiUpCgiHgJ6SH/Bn7dph3uHQtbqGBGPPE5yseqMbeYCp61KQdvgaEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCJBfAQAAAAAAAQQiUSAybfUP4KB7estyBwvrO2Muua0VumlcweWqaqAHthRv2AEJQgJ6SH/Bn7dph3uHQtbqGBGPPE5yseqMbeYCp61KQdvgaANh4bHp3l5CyyAH98pUueDVftE5OPrVbT8Z5XUTqPzgOQA=",
10151015
"supplementary": {
10161016
"inputs": [
10171017
{

bip-0375/validator/inputs.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ def collect_input_ecdh_and_pubkey(
5050
for input_map in psbt.i:
5151
input_ecdh = input_map.get_by_key(PSBT_IN_SP_ECDH_SHARE, scan_key)
5252
if input_ecdh:
53+
if not is_input_eligible(input_map):
54+
continue # skip ineligible inputs
5355
ecdh_point = GE.from_bytes(input_ecdh)
5456
combined_ecdh = (
5557
ecdh_point if combined_ecdh is None else combined_ecdh + ecdh_point
@@ -71,7 +73,7 @@ def pubkey_from_eligible_input(input_map: BIP375PSBTMap) -> Optional[GE]:
7173
7274
Returns a GE point (public key), or None if not found
7375
"""
74-
if not is_input_eligible(input_map)[0]:
76+
if not is_input_eligible(input_map):
7577
return None
7678

7779
# Try BIP32 derivation first (key_data is the pubkey)
@@ -131,32 +133,32 @@ def _parse_non_witness_utxo(non_witness_utxo: bytes, output_index: int) -> bytes
131133
# ============================================================================
132134

133135

134-
def is_input_eligible(input_map: BIP375PSBTMap) -> Tuple[bool, str]:
136+
def is_input_eligible(input_map: BIP375PSBTMap) -> bool:
135137
"""Check if input is eligible for silent payments"""
136138
script_pubkey = _script_pubkey_from_psbt_input(input_map)
137139
assert script_pubkey is not None, (
138140
"scriptPubKey could not be extracted from PSBT input"
139141
)
140142

141143
if not _has_eligible_script_type(script_pubkey):
142-
return False, "ineligible input type"
144+
return False
143145

144146
NUMS_H = bytes.fromhex(
145147
"50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
146148
)
147149
if _is_p2tr(script_pubkey):
148150
tap_internal_key = input_map.get(PSBT_IN_TAP_INTERNAL_KEY)
149151
if tap_internal_key == NUMS_H:
150-
return False, "P2TR uses NUMS point H as internal key"
152+
return False
151153

152154
if _is_p2sh(script_pubkey):
153155
if PSBT_IN_REDEEM_SCRIPT in input_map:
154156
redeem_script = input_map[PSBT_IN_REDEEM_SCRIPT]
155157
if not _is_p2wpkh(redeem_script):
156-
return False, "P2SH is not P2SH-P2WPKH"
158+
return False
157159
else:
158-
assert False, "P2SH input missing PSBT_IN_REDEEM_SCRIPT"
159-
return True, None
160+
assert False
161+
return True
160162

161163

162164
def _has_eligible_script_type(script_pubkey: bytes) -> bool:

bip-0375/validator/validate_psbt.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -185,22 +185,15 @@ def validate_ecdh_coverage(psbt: PSBT) -> Tuple[bool, str]:
185185
# Verify per-input coverage for eligible inputs
186186
if scan_key_has_computed_output and not has_global_ecdh:
187187
for i, input_map in enumerate(psbt.i):
188-
is_eligible, _ = is_input_eligible(input_map)
189-
ecdh_share = input_map.get_by_key(PSBT_IN_SP_ECDH_SHARE, scan_key)
190-
# Disabled this check for now since it is not strictly forbidden by BIP-375
191-
if not is_eligible:
188+
if not is_input_eligible(input_map):
192189
continue
193-
# if not is_eligible and ecdh_share:
194-
# return (
195-
# False,
196-
# f"Input {i} has ECDH share but is ineligible for silent payments",
197-
# )
198-
if is_eligible and not ecdh_share:
190+
ecdh_share = input_map.get_by_key(PSBT_IN_SP_ECDH_SHARE, scan_key)
191+
if not ecdh_share:
199192
return (
200193
False,
201194
f"Output script set but eligible input {i} missing ECDH share",
202195
)
203-
if ecdh_share:
196+
else:
204197
# Verify per-input DLEQ proofs
205198
dleq_proof = input_map.get_by_key(PSBT_IN_SP_DLEQ, scan_key)
206199
if not dleq_proof:

0 commit comments

Comments
 (0)