feat(bip85): add gen-child command (v1.0.6)
Implements BIP85 child mnemonic derivation with full interoperability.
Features:
- Derives child BIP39 mnemonics (12/15/18/21/24 words) from master mnemonic
- BIP85 path: m/83696968'/39'/0'/{words}'/{index}'
- Supports optional master BIP39 passphrase
- Reuses existing input modes (--interactive, --mnemonic-stdin)
- Follows existing UX patterns (--off-screen, --file, PGP encryption)
- Offline-first with NetworkGuard protection
Testing:
- Adds deterministic regression tests for BIP85 spec compliance
- Verified against official BIP85 test vectors
- CLI smoke tests for end-to-end validation
Interoperability:
- Produces mnemonics compatible with Coldcard, Ian Coleman tool, etc.
- Test vector verified: 'girl mad pet galaxy egg matter matrix prison refuse sense ordinary nose'
Version bumped to v1.0.6
This commit is contained in:
91
tests/generate_bip85_vectors.py
Executable file
91
tests/generate_bip85_vectors.py
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate BIP85 test vectors for vectors.json
|
||||
|
||||
Run this from the repo root:
|
||||
python3 tests/generate_bip85_vectors.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add src to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
import pyhdwallet
|
||||
from bip_utils import Bip39SeedGenerator
|
||||
import json
|
||||
|
||||
# Test cases to generate
|
||||
test_cases = [
|
||||
{
|
||||
"description": "BIP85 test case 1: 12-word child, no passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "",
|
||||
"child_words": 12,
|
||||
"index": 0,
|
||||
},
|
||||
{
|
||||
"description": "BIP85 test case 2: 18-word child, no passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "",
|
||||
"child_words": 18,
|
||||
"index": 0,
|
||||
},
|
||||
{
|
||||
"description": "BIP85 test case 3: 24-word child, no passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "",
|
||||
"child_words": 24,
|
||||
"index": 0,
|
||||
},
|
||||
{
|
||||
"description": "BIP85 test case 4: 12-word child, WITH passphrase",
|
||||
"master_mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
"master_passphrase": "TREZOR",
|
||||
"child_words": 12,
|
||||
"index": 0,
|
||||
},
|
||||
]
|
||||
|
||||
print("Generating BIP85 test vectors...\n")
|
||||
print("=" * 80)
|
||||
|
||||
vectors = []
|
||||
|
||||
for case in test_cases:
|
||||
print(f"\n{case['description']}")
|
||||
print("-" * 80)
|
||||
|
||||
# Generate master seed
|
||||
master_seed = Bip39SeedGenerator(case["master_mnemonic"]).Generate(case["master_passphrase"])
|
||||
|
||||
# Derive child using BIP85
|
||||
path, child_mnemonic, entropy64, truncated_entropy = pyhdwallet.bip85_derive_child_mnemonic(
|
||||
master_seed,
|
||||
case["child_words"],
|
||||
case["index"]
|
||||
)
|
||||
|
||||
vector = {
|
||||
"description": case["description"],
|
||||
"master_mnemonic": case["master_mnemonic"],
|
||||
"master_passphrase": case["master_passphrase"],
|
||||
"child_words": case["child_words"],
|
||||
"index": case["index"],
|
||||
"bip85_path": path,
|
||||
"expected_entropy64_hex": entropy64.hex(),
|
||||
"expected_entropy_truncated_hex": truncated_entropy.hex(),
|
||||
"expected_child_mnemonic": child_mnemonic,
|
||||
}
|
||||
|
||||
vectors.append(vector)
|
||||
|
||||
print(f"Path: {path}")
|
||||
print(f"Entropy64: {entropy64.hex()}")
|
||||
print(f"Truncated: {truncated_entropy.hex()}")
|
||||
print(f"Child mnemonic: {child_mnemonic}")
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print("\nJSON output for vectors.json:\n")
|
||||
print(json.dumps(vectors, indent=2))
|
||||
Reference in New Issue
Block a user