Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion bip-0375.mediawiki
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ Use the provided [[bip-0375/test_runner.py|test runner]] to validate each test v
| one input / two sp outputs - output 0 has no label / output 1 uses label=0 convention for sp change
|-
| Can Finalize
| two sp outputs - output 0 uses label=3 / output 1 uses label=1
| three sp outputs (same scan key) - output 0 uses label=1, outputs 1 and 2 uses label=2 (same spend key)
|-
| Can Finalize
| two inputs using per-input ECDH shares - only eligible inputs contribute shares (P2SH excluded)
Expand Down Expand Up @@ -403,6 +403,8 @@ Use the provided [[bip-0375/test_runner.py|test runner]] to validate each test v

== Changelog ==

* '''0.1.2''' (2026-07-07):
** Correct labeled test vectors and reference: include labeled-spend key in PSBT_OUT_SP_V0_INFO.
* '''0.1.1''' (2026-04-16):
** Add test vectors and reference for validating Sending Silent Payments with PSBTs.
* '''0.1.0''' (2025-01-13):
Expand Down
41 changes: 24 additions & 17 deletions bip-0375/bip375_test_vectors.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"description": "BIP-375 Test Vectors",
"version": "1.1",
"version": "1.1.1",
"notes": [
"Generated by https://github.com/macgyver13/bip375-test-generator/",
"Each vector includes: base64-encoded psbt and description",
Expand Down Expand Up @@ -904,7 +904,7 @@
{
"output_index": 0,
"amount": 100000,
"sp_v0_info": "027a487fc19fb769877b8742d6ea18118f3c4e72b1ea8c6de602a7ad4a41dbe0680361e1b1e9de5e42cb2007f7ca54b9e0d57ed13938fad56d3f19e57513a8fce039",
"sp_v0_info": "027a487fc19fb769877b8742d6ea18118f3c4e72b1ea8c6de602a7ad4a41dbe06803ea8f228be2b716dacdefcb2810072874f00a476f5c07bd051917126cc64b3def",
"sp_v0_label": 1,
"script": "51202268cf529959f30ac8f7a5471ca4c67536a51f0e5b168af03eed4715edcc72e0"
},
Expand All @@ -918,7 +918,7 @@
},
{
"description": "can finalize: one input / two sp outputs - output 0 has no label / output 1 uses label=0 convention for sp change",
"psbt": "cHNidP8B+wQCAAAAAQIEAgAAAAEEAQEBBQECAQYBAAABDiAYpxdmOwurFLEqGncTI/8eQHndUy5d0T4o6hCBxwCYSgEPBAAAAAABAR/gkwQAAAAAABYAFCKactNKZFvTSWu79Qu7gckGP0+UIgICyBe7dSGvw16pbzv7Jw5utQ3f+lVgYnuWH+wA8pllCL9HMEQCICJa45kPVF365uoUKpN0arSqgSfjyBNY63wha08anrtiAiBl42BSGRArJQdeOVXz80zjvPKjXR4QFd1dw7/tPs81qQEBAwQBAAAAIgYCyBe7dSGvw16pbzv7Jw5utQ3f+lVgYnuWH+wA8pllCL8IAAAAgAAAAAABEAT+////Ih0CXw7Bs42ZhKfrYvFb5AMCBBDxQu301p42P1YVr9Kmts0hA84eNnXZDZkmSfqd2xH5AlSbs7AvVvbonMCgTIuMN92UIh0Ci3kOCYf96fDRI5jra+MMc3Z5PGR4ZaKgLy78lg2WZWghAlWBv6pzMVb2uY5xom9w/XazZ4ZJ9isfxed7jpq45hBQIh4CXw7Bs42ZhKfrYvFb5AMCBBDxQu301p42P1YVr9Kmts1ADolhPR9rJTbbj6kkIA2zpvjvyhciVWTVaxEuUIZbTTAuBY+m9hwp4jZqSnJp1FVaXGHWVVnb+/rJ4urf5TY31SIeAot5DgmH/enw0SOY62vjDHN2eTxkeGWioC8u/JYNlmVoQHlAgZ626fJYwVg4fApkzBb1F8fvRAkSLM6qlPTi/1N2xCR7/BcNmyY7Dj0ga2aMh+lLNN7q+gCheSsEeteIdbsAAQMIoIYBAAAAAAABBCJRIKBIDZZnnBk25au9SLqw4B/6yx7n0cnhaC8VQBaZZYXZAQlCAot5DgmH/enw0SOY62vjDHN2eTxkeGWioC8u/JYNlmVoA7RDRzNcY/UF5V4mirKA8xi0pqoUSS/zecNmih9U74ybAAEDCDDmAgAAAAAAAQQiUSB2Hb00kpiEpmcp69zbrPFnSC32LBTwmOTvGNcP4Grv0AEJQgJfDsGzjZmEp+ti8VvkAwIEEPFC7fTWnjY/VhWv0qa2zQK+EFEqlVNkHkB62OUJo0GEUqkC6FVqDboumD9eAPe8UQEKBAAAAAAA",
"psbt": "cHNidP8B+wQCAAAAAQIEAgAAAAEEAQEBBQECAQYBAAABDiAYpxdmOwurFLEqGncTI/8eQHndUy5d0T4o6hCBxwCYSgEPBAAAAAABAR/gkwQAAAAAABYAFCKactNKZFvTSWu79Qu7gckGP0+UIgICyBe7dSGvw16pbzv7Jw5utQ3f+lVgYnuWH+wA8pllCL9HMEQCICJa45kPVF365uoUKpN0arSqgSfjyBNY63wha08anrtiAiBl42BSGRArJQdeOVXz80zjvPKjXR4QFd1dw7/tPs81qQEBAwQBAAAAIgYCyBe7dSGvw16pbzv7Jw5utQ3f+lVgYnuWH+wA8pllCL8IAAAAgAAAAAABEAT+////Ih0CXw7Bs42ZhKfrYvFb5AMCBBDxQu301p42P1YVr9Kmts0hA84eNnXZDZkmSfqd2xH5AlSbs7AvVvbonMCgTIuMN92UIh0Ci3kOCYf96fDRI5jra+MMc3Z5PGR4ZaKgLy78lg2WZWghAlWBv6pzMVb2uY5xom9w/XazZ4ZJ9isfxed7jpq45hBQIh4CXw7Bs42ZhKfrYvFb5AMCBBDxQu301p42P1YVr9Kmts1ADolhPR9rJTbbj6kkIA2zpvjvyhciVWTVaxEuUIZbTTAuBY+m9hwp4jZqSnJp1FVaXGHWVVnb+/rJ4urf5TY31SIeAot5DgmH/enw0SOY62vjDHN2eTxkeGWioC8u/JYNlmVoQHlAgZ626fJYwVg4fApkzBb1F8fvRAkSLM6qlPTi/1N2xCR7/BcNmyY7Dj0ga2aMh+lLNN7q+gCheSsEeteIdbsAAQMIoIYBAAAAAAABBCJRIKBIDZZnnBk25au9SLqw4B/6yx7n0cnhaC8VQBaZZYXZAQlCAot5DgmH/enw0SOY62vjDHN2eTxkeGWioC8u/JYNlmVoA7RDRzNcY/UF5V4mirKA8xi0pqoUSS/zecNmih9U74ybAAEDCDDmAgAAAAAAAQQiUSDkKhgUiNj/9ucWH3FfXvD0fcbISAhIqtaERxvz6ljzUgEJQgJfDsGzjZmEp+ti8VvkAwIEEPFC7fTWnjY/VhWv0qa2zQLOCzIZpapd+PIc45PpYs3afVFUbaTTXDjtAYilludg8wEKBAAAAAAA",
"supplementary": {
"inputs": [
{
Expand Down Expand Up @@ -958,16 +958,16 @@
{
"output_index": 1,
"amount": 190000,
"sp_v0_info": "025f0ec1b38d9984a7eb62f15be403020410f142edf4d69e363f5615afd2a6b6cd037cd462358c69c40525247a8793944d9e67484b248fd84a1e7ee4f920b4da41b5",
"sp_v0_info": "025f0ec1b38d9984a7eb62f15be403020410f142edf4d69e363f5615afd2a6b6cd02ce0b3219a5aa5df8f21ce393e962cdda7d51546da4d35c38ed0188a596e760f3",
"sp_v0_label": 0,
"script": "5120761dbd34929884a66729ebdcdbacf167482df62c14f098e4ef18d70fe06aefd0"
"script": "5120e42a181488d8fff6e7161f715f5ef0f47dc6c8480848aad684471bf3ea58f352"
}
]
}
},
{
"description": "can finalize: two sp outputs - output 0 uses label=3 / output 1 uses label=1",
"psbt": "cHNidP8B+wQCAAAAAQIEAgAAAAEEAQEBBQECAQYBAAABDiAYpxdmOwurFLEqGncTI/8eQHndUy5d0T4o6hCBxwCYSgEPBAAAAAABAR/gkwQAAAAAABYAFCKactNKZFvTSWu79Qu7gckGP0+UIgICyBe7dSGvw16pbzv7Jw5utQ3f+lVgYnuWH+wA8pllCL9HMEQCICJa45kPVF365uoUKpN0arSqgSfjyBNY63wha08anrtiAiBl42BSGRArJQdeOVXz80zjvPKjXR4QFd1dw7/tPs81qQEBAwQBAAAAIgYCyBe7dSGvw16pbzv7Jw5utQ3f+lVgYnuWH+wA8pllCL8IAAAAgAAAAAABEAT+////Ih0Ci3kOCYf96fDRI5jra+MMc3Z5PGR4ZaKgLy78lg2WZWghAlWBv6pzMVb2uY5xom9w/XazZ4ZJ9isfxed7jpq45hBQIh4Ci3kOCYf96fDRI5jra+MMc3Z5PGR4ZaKgLy78lg2WZWhAeUCBnrbp8ljBWDh8CmTMFvUXx+9ECRIszqqU9OL/U3bEJHv8Fw2bJjsOPSBrZoyH6Us03ur6AKF5KwR614h1uwABAwighgEAAAAAAAEEIlEgyvcy9B51UZCclKM10VeOp7dmAnuIbN0cFwHZv8a3hIgBCUICi3kOCYf96fDRI5jra+MMc3Z5PGR4ZaKgLy78lg2WZWgDliRhtuNd91TQDUhtw53swgJxhFB2YU5cDtLJDyPT6b4BCgQDAAAAAAEDCDDmAgAAAAAAAQQiUSBNB6DTsPYafF7+NsLDxYfItphBhUYAjPgZu9pM4xgSIQEJQgKLeQ4Jh/3p8NEjmOtr4wxzdnk8ZHhloqAvLvyWDZZlaANaVzfUxngmF8xrDElrsrxiVVoyslQkT7ycnANd9GnlqwEKBAEAAAAA",
"description": "can finalize: three sp outputs (same scan key) - output 0 uses label=1, outputs 1 and 2 uses label=2 (same spend key)",
"psbt": "cHNidP8B+wQCAAAAAQIEAgAAAAEEAQEBBQEDAQYBAAABDiAYpxdmOwurFLEqGncTI/8eQHndUy5d0T4o6hCBxwCYSgEPBAAAAAABAR+AGgYAAAAAABYAFCKactNKZFvTSWu79Qu7gckGP0+UIgICyBe7dSGvw16pbzv7Jw5utQ3f+lVgYnuWH+wA8pllCL9HMEQCIFaplAuaU3aICoTcvxrXSZnHH6i4SXpbYSuzopSyB0k4AiA98iUFQysKS0I3s2sHC3R8ZpycvK7JZiE0PcjMfiNnYgEBAwQBAAAAIgYCyBe7dSGvw16pbzv7Jw5utQ3f+lVgYnuWH+wA8pllCL8IAAAAgAAAAAABEAT+////Ih0Ci3kOCYf96fDRI5jra+MMc3Z5PGR4ZaKgLy78lg2WZWghAlWBv6pzMVb2uY5xom9w/XazZ4ZJ9isfxed7jpq45hBQIh4Ci3kOCYf96fDRI5jra+MMc3Z5PGR4ZaKgLy78lg2WZWhAeUCBnrbp8ljBWDh8CmTMFvUXx+9ECRIszqqU9OL/U3bEJHv8Fw2bJjsOPSBrZoyH6Us03ur6AKF5KwR614h1uwABAwihhgEAAAAAAAEEIlEgG/+hD9qiUCwvDJKF/grxmlD4UV47bfv1behQ2ZkBRAABCUICi3kOCYf96fDRI5jra+MMc3Z5PGR4ZaKgLy78lg2WZWgD1DFYje5hqzo5clspnDdC3Gaghh3s9ZG0I7DlJwcPgjQBCgQBAAAAAAEDCDLmAgAAAAAAAQQiUSCITaZwjIK5tWyYvTvm5fU4lLsb/ZC/rPiUAlVw9r1TBQEJQgKLeQ4Jh/3p8NEjmOtr4wxzdnk8ZHhloqAvLvyWDZZlaAMhk60Mh1+sUkNzgCKQCnckxhdd2smUQWWqtFeq35xPPAEKBAIAAAAAAQMIooYBAAAAAAABBCJRINKR0zkoDgyG4scIpZH46OL4ktEqfP6FELZneZtxExyGAQlCAot5DgmH/enw0SOY62vjDHN2eTxkeGWioC8u/JYNlmVoAyGTrQyHX6xSQ3OAIpAKdyTGF13ayZRBZaq0V6rfnE88AQoEAgAAAAA=",
"supplementary": {
"inputs": [
{
Expand All @@ -977,8 +977,8 @@
"prevout_txid": "18a717663b0bab14b12a1a771323ff1e4079dd532e5dd13e28ea1081c700984a",
"prevout_index": 0,
"prevout_scriptpubkey": "0014229a72d34a645bd3496bbbf50bbb81c9063f4f94",
"amount": 300000,
"witness_utxo": "e093040000000000160014229a72d34a645bd3496bbbf50bbb81c9063f4f94",
"amount": 400000,
"witness_utxo": "801a060000000000160014229a72d34a645bd3496bbbf50bbb81c9063f4f94",
"sequence": 4294967294,
"signed": true
}
Expand All @@ -994,17 +994,24 @@
"outputs": [
{
"output_index": 0,
"amount": 100000,
"sp_v0_info": "028b790e0987fde9f0d12398eb6be30c7376793c647865a2a02f2efc960d96656803b44347335c63f505e55e268ab280f318b4a6aa14492ff379c3668a1f54ef8c9b",
"sp_v0_label": 3,
"script": "5120caf732f41e7551909c94a335d1578ea7b766027b886cdd1c1701d9bfc6b78488"
"amount": 100001,
"sp_v0_info": "028b790e0987fde9f0d12398eb6be30c7376793c647865a2a02f2efc960d96656803d431588dee61ab3a39725b299c3742dc66a0861decf591b423b0e527070f8234",
"sp_v0_label": 1,
"script": "51201bffa10fdaa2502c2f0c9285fe0af19a50f8515e3b6dfbf56de850d999014400"
},
{
"output_index": 1,
"amount": 190000,
"sp_v0_info": "028b790e0987fde9f0d12398eb6be30c7376793c647865a2a02f2efc960d96656803b44347335c63f505e55e268ab280f318b4a6aa14492ff379c3668a1f54ef8c9b",
"sp_v0_label": 1,
"script": "51204d07a0d3b0f61a7c5efe36c2c3c587c8b698418546008cf819bbda4ce3181221"
"amount": 190002,
"sp_v0_info": "028b790e0987fde9f0d12398eb6be30c7376793c647865a2a02f2efc960d966568032193ad0c875fac5243738022900a7724c6175ddac9944165aab457aadf9c4f3c",
"sp_v0_label": 2,
"script": "5120884da6708c82b9b56c98bd3be6e5f53894bb1bfd90bfacf894025570f6bd5305"
},
{
"output_index": 2,
"amount": 100002,
"sp_v0_info": "028b790e0987fde9f0d12398eb6be30c7376793c647865a2a02f2efc960d966568032193ad0c875fac5243738022900a7724c6175ddac9944165aab457aadf9c4f3c",
"sp_v0_label": 2,
"script": "5120d291d339280e0c86e2c708a591f8e8e2f892d12a7cfe8510b667799b71131c86"
}
]
}
Expand Down
18 changes: 11 additions & 7 deletions bip-0375/validator/validate_psbt.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def validate_dleq_proof(
) -> Tuple[bool, str]:
"""
Verify a DLEQ proof for silent payments

Checks:
- ECDH share and DLEQ proof lengths
- Verify DLEQ proof correctness
Expand Down Expand Up @@ -304,15 +304,19 @@ def validate_output_scripts(psbt: PSBT) -> Tuple[bool, str]:
output_index = struct.unpack("<I", output_index_bytes)[0]
outpoints.append(COutPoint(txid_int, output_index))

# Collect SP outputs and sort by (sp_info, output_idx) so k is assigned in
# lexicographic silent payment code order
sp_outputs = [
(output_map[PSBT_OUT_SP_V0_INFO], output_idx, output_map)
for output_idx, output_map in enumerate(psbt.o)
if PSBT_OUT_SP_V0_INFO in output_map
]
sp_outputs.sort(key=lambda entry: (entry[0], entry[1]))

# Track k values per scan key
scan_key_k_values = {}

# Validate each SP output
for output_idx, output_map in enumerate(psbt.o):
if PSBT_OUT_SP_V0_INFO not in output_map:
continue # Skip non-SP outputs

sp_info = output_map[PSBT_OUT_SP_V0_INFO]
for sp_info, output_idx, output_map in sp_outputs:
scan_pubkey_bytes = sp_info[:33]
spend_pubkey_bytes = sp_info[33:]

Expand Down