Skip to content

Commit dc1d312

Browse files
committed
Add tests generation material
1 parent e3c7677 commit dc1d312

File tree

11 files changed

+5471
-1
lines changed

11 files changed

+5471
-1
lines changed

bip-0375/README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,25 @@ This directory contains the complete reference implementation for BIP 375: Sendi
88

99
- **`reference.py`** - Minimal standalone BIP 375 validator with integrated test runner (executable)
1010

11+
### PSBTv2 Library for Silent Payments
12+
13+
- **`tests/psbt_sp/`** - Complete PSBT v2 package for Silent Payments
14+
- Full role-based implementation (Creator, Constructor, Updater, Signer, Input Finalizer, Extractor)
15+
- Serialization, crypto utilities, and BIP 352 integration
16+
- Used to generate test vectors
17+
1118
### Dependencies (from BIP 374)
1219

1320
- **`dleq_374.py`** - BIP 374 DLEQ proof implementation
1421
- **`secp256k1_374.py`** - Secp256k1 implementation
1522

23+
## **Testing**
24+
25+
### Test Infrastructure
26+
27+
- **`test_vectors.json`** - Test vectors with full cryptographic material (13 invalid + 4 valid)
28+
- **`tests/test_generator.py`** - Deterministic test vector generator producing`test_vectors.json`
29+
1630
## **Usage**
1731

1832
### Run Reference Implementation Tests
@@ -21,4 +35,12 @@ This directory contains the complete reference implementation for BIP 375: Sendi
2135
python reference.py # Run all tests using test_vectors.json
2236
python reference.py -f custom.json # Use custom test file
2337
python reference.py -v # Verbose mode with detailed errors
24-
```
38+
```
39+
40+
### Generate Test Vectors
41+
42+
```bash
43+
python tests/test_generator.py # Creates test_vectors.json in bip-0375 root directory
44+
```
45+
46+
**Note:** Demo implementations can be found in [bip375-examples](https://github.com/macgyver13/bip375-examples/)

bip-0375/tests/psbt_sp/__init__.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env python3
2+
"""
3+
PSBT Silent Payment (psbt_sp) Package
4+
5+
Clean Python package for BIP 375 - Sending Silent Payments with PSBTs
6+
Provides PSBT v2 implementation with silent payment extensions.
7+
"""
8+
9+
# Constants
10+
from .constants import PSBTFieldType
11+
12+
# Core PSBT functionality
13+
from .psbt import SilentPaymentPSBT, SilentPaymentAddress, ECDHShare, validate_psbt_silent_payments
14+
15+
# Cryptographic utilities
16+
from .crypto import Wallet
17+
18+
# Serialization utilities (for advanced usage)
19+
from .serialization import PSBTField, PSBTv2, compact_size_uint, create_witness_utxo, parse_psbt_bytes
20+
21+
# BIP 352 cryptographic functions
22+
from .bip352_crypto import (
23+
compute_label_tweak,
24+
compute_shared_secret_tweak,
25+
apply_label_to_spend_key,
26+
derive_silent_payment_output_pubkey,
27+
pubkey_to_p2wpkh_script
28+
)
29+
30+
# PSBT utilities
31+
from .psbt_utils import (
32+
extract_input_pubkey,
33+
extract_combined_input_pubkeys,
34+
check_ecdh_coverage,
35+
extract_scan_keys_from_outputs
36+
)
37+
38+
# File I/O
39+
from .psbt_io import save_psbt_to_file, load_psbt_from_file
40+
41+
# Role-based classes
42+
from .roles import (
43+
PSBTCreator,
44+
PSBTConstructor,
45+
PSBTUpdater,
46+
PSBTSigner,
47+
PSBTInputFinalizer,
48+
PSBTExtractor
49+
)
50+
51+
# Convenience re-exports of extractor methods
52+
extract_transaction = PSBTExtractor.extract_transaction
53+
save_transaction = PSBTExtractor.save_transaction
54+
55+
__version__ = "1.0.0"
56+
__author__ = "BIP 375 Implementation"
57+
__description__ = "PSBT v2 with BIP 375 Silent Payment extensions"
58+
59+
# Public API - what gets imported with "from psbt_sp import *"
60+
__all__ = [
61+
# Constants
62+
"PSBTFieldType",
63+
64+
# Core classes
65+
"SilentPaymentPSBT",
66+
"SilentPaymentAddress",
67+
"ECDHShare",
68+
"Wallet",
69+
70+
# Role-based classes
71+
"PSBTCreator",
72+
"PSBTConstructor",
73+
"PSBTUpdater",
74+
"PSBTSigner",
75+
"PSBTInputFinalizer",
76+
"PSBTExtractor",
77+
78+
# Serialization classes
79+
"PSBTField",
80+
"PSBTv2",
81+
82+
# BIP 352 crypto functions
83+
"compute_label_tweak",
84+
"compute_shared_secret_tweak",
85+
"apply_label_to_spend_key",
86+
"derive_silent_payment_output_pubkey",
87+
"pubkey_to_p2wpkh_script",
88+
89+
# PSBT utility functions
90+
"extract_input_pubkey",
91+
"extract_combined_input_pubkeys",
92+
"check_ecdh_coverage",
93+
"extract_scan_keys_from_outputs",
94+
95+
# Transaction functions
96+
"extract_transaction",
97+
"save_transaction",
98+
99+
# File I/O functions
100+
"save_psbt_to_file",
101+
"load_psbt_from_file",
102+
103+
# Other utilities
104+
"compact_size_uint",
105+
"create_witness_utxo",
106+
"parse_psbt_bytes",
107+
"validate_psbt_silent_payments",
108+
109+
# Package metadata
110+
"__version__",
111+
"__author__",
112+
"__description__"
113+
]
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#!/usr/bin/env python3
2+
"""
3+
BIP 352 Silent Payments Cryptographic Utilities
4+
5+
Pure cryptographic functions for BIP 352 silent payments protocol.
6+
These functions are independent of PSBT and can be used by wallets,
7+
receivers, and other silent payment implementations.
8+
"""
9+
10+
import hashlib
11+
import struct
12+
from secp256k1_374 import GE, G
13+
14+
15+
def compute_label_tweak(scan_privkey_bytes: bytes, label: int) -> int:
16+
"""
17+
Compute BIP 352 label tweak for modifying spend key
18+
19+
Formula: hash_BIP0352/Label(ser_256(b_scan) || ser_32(m))
20+
21+
Args:
22+
scan_privkey_bytes: Scan private key (32 bytes)
23+
label: Label integer (0 for change, > 0 for other purposes)
24+
25+
Returns:
26+
Scalar for point multiplication to modify spend key
27+
"""
28+
# BIP 352: ser_256(b_scan) || ser_32(m)
29+
label_bytes = struct.pack('<I', label) # 4 bytes little-endian
30+
31+
# Tagged hash: BIP0352/Label
32+
tag = b"BIP0352/Label"
33+
tag_hash = hashlib.sha256(tag).digest()
34+
35+
# hash_BIP0352/Label(b_scan || m)
36+
tagged_input = tag_hash + tag_hash + scan_privkey_bytes + label_bytes
37+
tweak_hash = hashlib.sha256(tagged_input).digest()
38+
tweak_scalar = int.from_bytes(tweak_hash, 'big') % GE.ORDER
39+
40+
return tweak_scalar
41+
42+
43+
def compute_shared_secret_tweak(ecdh_shared_secret_bytes: bytes, k: int = 0) -> int:
44+
"""
45+
Compute BIP 352 shared secret tweak for output derivation
46+
47+
Formula: t_k = hash_BIP0352/SharedSecret(ecdh_shared_secret || ser_32(k))
48+
49+
Args:
50+
ecdh_shared_secret_bytes: ECDH shared secret point (33 bytes compressed)
51+
k: Output index for this scan key (default 0)
52+
53+
Returns:
54+
Scalar tweak for deriving output public key
55+
"""
56+
k_bytes = k.to_bytes(4, 'big') # 4 bytes big-endian
57+
58+
# Tagged hash: BIP0352/SharedSecret
59+
tag_data = b"BIP0352/SharedSecret"
60+
tag_hash = hashlib.sha256(tag_data).digest()
61+
62+
tagged_input = tag_hash + tag_hash + ecdh_shared_secret_bytes + k_bytes
63+
tweak_hash = hashlib.sha256(tagged_input).digest()
64+
tweak_int = int.from_bytes(tweak_hash, 'big') % GE.ORDER
65+
66+
return tweak_int
67+
68+
69+
def apply_label_to_spend_key(spend_key_point: GE, scan_privkey_bytes: bytes, label: int) -> GE:
70+
"""
71+
Apply BIP 352 label to spend public key
72+
73+
Formula: B_m = B_spend + hash_BIP0352/Label(b_scan || m) * G
74+
75+
Args:
76+
spend_key_point: Base spend public key
77+
scan_privkey_bytes: Scan private key (32 bytes)
78+
label: Label integer
79+
80+
Returns:
81+
Modified spend public key B_m
82+
"""
83+
label_tweak = compute_label_tweak(scan_privkey_bytes, label)
84+
label_tweak_point = label_tweak * G
85+
return spend_key_point + label_tweak_point
86+
87+
88+
def derive_silent_payment_output_pubkey(
89+
spend_key_point: GE,
90+
ecdh_shared_secret_bytes: bytes,
91+
k: int = 0
92+
) -> GE:
93+
"""
94+
Derive final output public key for silent payment
95+
96+
Formula: P_k = B_m + t_k * G
97+
where t_k = hash_BIP0352/SharedSecret(ecdh_shared_secret || ser_32(k))
98+
99+
Args:
100+
spend_key_point: Spend public key (possibly label-modified B_m)
101+
ecdh_shared_secret_bytes: ECDH shared secret (33 bytes compressed)
102+
k: Output index for this scan key
103+
104+
Returns:
105+
Final output public key P_k
106+
"""
107+
tweak_int = compute_shared_secret_tweak(ecdh_shared_secret_bytes, k)
108+
tweak_point = tweak_int * G
109+
return spend_key_point + tweak_point
110+
111+
112+
def pubkey_to_p2wpkh_script(pubkey_point: GE) -> bytes:
113+
"""
114+
Convert public key to P2WPKH script
115+
116+
Args:
117+
pubkey_point: Public key point
118+
119+
Returns:
120+
P2WPKH script: OP_0 <20-byte-pubkey-hash>
121+
"""
122+
pubkey_bytes = pubkey_point.to_bytes_compressed()
123+
pubkey_hash = hashlib.new('ripemd160', hashlib.sha256(pubkey_bytes).digest()).digest()
124+
return b'\x00\x14' + pubkey_hash # OP_0 + 20 bytes
125+
126+
127+
def pubkey_to_p2tr_script(pubkey_point: GE) -> bytes:
128+
"""
129+
Convert public key to P2TR (Taproot) script
130+
131+
BIP 352 requires silent payment outputs to use P2TR (Taproot).
132+
133+
Formula: OP_1 <32-byte-x-only-pubkey>
134+
135+
Args:
136+
pubkey_point: Public key point
137+
138+
Returns:
139+
P2TR script: OP_1 (0x51) + 32 bytes (x-only public key)
140+
"""
141+
# Get x-only public key (32 bytes) - BIP 340/341
142+
pubkey_bytes = pubkey_point.to_bytes_compressed()
143+
x_only = pubkey_bytes[1:] # Remove first byte (02/03 parity), keep 32-byte x coordinate
144+
return b'\x51\x20' + x_only # OP_1 (0x51) + PUSH_32 (0x20) + 32 bytes
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/env python3
2+
"""
3+
BIP 174/370/375 PSBT Field Type Constants
4+
5+
This module defines all PSBT field type constants used across BIP 174 (PSBTv1),
6+
BIP 370 (PSBTv2), and BIP 375 (Silent Payments with PSBT).
7+
"""
8+
9+
10+
class PSBTFieldType:
11+
"""PSBT Field Type Constants for BIP 174/370/375"""
12+
13+
# =======================================================================
14+
# GLOBAL FIELDS (PSBT-wide fields)
15+
# =======================================================================
16+
17+
# Standard PSBT v2 global fields
18+
PSBT_GLOBAL_UNSIGNED_TX = 0x00
19+
PSBT_GLOBAL_XPUB = 0x01
20+
PSBT_GLOBAL_TX_VERSION = 0x02
21+
PSBT_GLOBAL_FALLBACK_LOCKTIME = 0x03
22+
PSBT_GLOBAL_INPUT_COUNT = 0x04
23+
PSBT_GLOBAL_OUTPUT_COUNT = 0x05
24+
PSBT_GLOBAL_TX_MODIFIABLE = 0x06
25+
PSBT_GLOBAL_VERSION = 0xfb
26+
PSBT_GLOBAL_PROPRIETARY = 0xfc
27+
28+
# BIP 375 Silent Payment global fields
29+
PSBT_GLOBAL_SP_ECDH_SHARE = 0x07
30+
PSBT_GLOBAL_SP_DLEQ = 0x08
31+
32+
# =======================================================================
33+
# INPUT FIELDS (Per-input fields)
34+
# =======================================================================
35+
36+
# Standard PSBT v2 input fields
37+
PSBT_IN_NON_WITNESS_UTXO = 0x00
38+
PSBT_IN_WITNESS_UTXO = 0x01
39+
PSBT_IN_PARTIAL_SIG = 0x02
40+
PSBT_IN_SIGHASH_TYPE = 0x03
41+
PSBT_IN_REDEEM_SCRIPT = 0x04
42+
PSBT_IN_WITNESS_SCRIPT = 0x05
43+
PSBT_IN_BIP32_DERIVATION = 0x06
44+
PSBT_IN_FINAL_SCRIPTSIG = 0x07
45+
PSBT_IN_FINAL_SCRIPTWITNESS = 0x08
46+
PSBT_IN_POR_COMMITMENT = 0x09
47+
PSBT_IN_RIPEMD160 = 0x0a
48+
PSBT_IN_SHA256 = 0x0b
49+
PSBT_IN_HASH160 = 0x0c
50+
PSBT_IN_HASH256 = 0x0d
51+
PSBT_IN_PREVIOUS_TXID = 0x0e
52+
PSBT_IN_OUTPUT_INDEX = 0x0f
53+
PSBT_IN_SEQUENCE = 0x10
54+
PSBT_IN_REQUIRED_TIME_LOCKTIME = 0x11
55+
PSBT_IN_REQUIRED_HEIGHT_LOCKTIME = 0x12
56+
PSBT_IN_TAP_KEY_SIG = 0x13
57+
PSBT_IN_TAP_SCRIPT_SIG = 0x14
58+
PSBT_IN_TAP_LEAF_SCRIPT = 0x15
59+
PSBT_IN_TAP_BIP32_DERIVATION = 0x16
60+
PSBT_IN_TAP_INTERNAL_KEY = 0x17
61+
PSBT_IN_TAP_MERKLE_ROOT = 0x18
62+
PSBT_IN_PROPRIETARY = 0xfc
63+
64+
# BIP 375 Silent Payment input fields
65+
PSBT_IN_SP_ECDH_SHARE = 0x1d
66+
PSBT_IN_SP_DLEQ = 0x1e
67+
68+
# =======================================================================
69+
# OUTPUT FIELDS (Per-output fields)
70+
# =======================================================================
71+
72+
# Standard PSBT v2 output fields
73+
PSBT_OUT_REDEEM_SCRIPT = 0x00
74+
PSBT_OUT_WITNESS_SCRIPT = 0x01
75+
PSBT_OUT_BIP32_DERIVATION = 0x02
76+
PSBT_OUT_AMOUNT = 0x03
77+
PSBT_OUT_SCRIPT = 0x04
78+
PSBT_OUT_TAP_INTERNAL_KEY = 0x05
79+
PSBT_OUT_TAP_TREE = 0x06
80+
PSBT_OUT_TAP_BIP32_DERIVATION = 0x07
81+
PSBT_OUT_PROPRIETARY = 0xfc
82+
83+
# BIP 375 Silent Payment output fields
84+
PSBT_OUT_SP_V0_INFO = 0x09
85+
PSBT_OUT_SP_V0_LABEL = 0x0a

0 commit comments

Comments
 (0)