BipHDNode.from_path(): support initialization from xprv instead of seed

This commit is contained in:
The MMGen Project 2026-08-22 18:34:47 +00:00
commit c85dd68312
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
3 changed files with 34 additions and 8 deletions

View file

@ -368,6 +368,7 @@ class BipHDNode(Lockable):
seed,
path_str,
*,
xprv = None,
coin = None,
addr_type = None,
no_path_checks = False):
@ -378,11 +379,20 @@ class BipHDNode(Lockable):
proto = init_proto(base_cfg, coin or 'btc')
res = MasterNode(base_cfg, seed).init_cfg(
coin = proto.coin,
addr_type = addr_type or proto.dfl_mmtype,
no_path_checks = no_path_checks,
from_path = True)
if xprv:
assert seed == None, 'BipHDNode.from_path(): `seed` must be None if `xprv` is specified'
res = BipHDNode.from_extended_key(
base_cfg = base_cfg,
coin = coin or proto.coin,
xkey_b58 = xprv,
no_path_checks = no_path_checks,
addr_type = addr_type or proto.dfl_mmtype)
else:
res = MasterNode(base_cfg, seed).init_cfg(
coin = proto.coin,
addr_type = addr_type or proto.dfl_mmtype,
no_path_checks = no_path_checks,
from_path = True)
for s in path:
for suf in ("'", 'h'):
@ -404,7 +414,7 @@ class BipHDNode(Lockable):
@staticmethod
# ‘addr_type’ is required for broken coins with duplicate version bytes across BIP protocols
# (i.e. Dogecoin)
def from_extended_key(base_cfg, coin, xkey_b58, *, addr_type=None):
def from_extended_key(base_cfg, coin, xkey_b58, *, addr_type=None, no_path_checks=False):
xk = Bip32ExtendedKey(xkey_b58)
if xk.public:
@ -425,7 +435,7 @@ class BipHDNode(Lockable):
network = xk.network,
addr_type = addr_type or addr_types[xk.bip_proto],
from_path = False,
no_path_checks = False)
no_path_checks = no_path_checks)
new.par_print = xk.par_print
new.depth = xk.depth

View file

@ -5,6 +5,10 @@
# https://github.com/MetaMask/slip44/blob/main/slip44.json (1bc984bee)
# https://github.com/ebellocchia/bip_utils (5649541c6)
"""
bip_hd.chainparams: BIP-44/49/84, SLIP-44 hierarchical-deterministic key derivation library
"""
from collections import namedtuple
def parse_data():
@ -132,7 +136,7 @@ IDX CHAIN CURVE NW ADDR_CLS VB_PRV VB_PUB VB_WIF VB_ADDR DFL_PA
[bip-84]
IDX CHAIN CURVE NW ADDR_CLS VB_PRV VB_PUB VB_WIF VB_ADDR DFL_PATH NAME
0 BTC x m P2WPKH 04b2430c 04b24746 80 h:bc x Bitcoin
1 LTC x T P2WPKH 0436ef7d 0436f6e1 ef h:tltc x Litecoin TestNet
1 - x T P2WPKH 0436ef7d 0436f6e1 ef h:tb x TestNet (all coins)
2 LTC x m P2WPKH 04b2430c 04b24746 b0 h:ltc x Litecoin
[bip-86]

View file

@ -192,6 +192,16 @@ class unit_tests:
vmsg('seed: 98831F3A (default derivation)')
m = MasterNode(cfg, self._seed)
master = m.init_cfg('btc', network=None, addr_type='bech32')
vmsg(f' {master.xprv=}')
chain_from_master_xprv = BipHDNode.from_path(
cfg,
None,
"m/84'/0'/0'/0",
no_path_checks = True,
addr_type = 'bech32',
xprv = master.xprv)
purpose = m.init_cfg(coin='btc', addr_type='bech32').derive_private()
vmsg(f' {purpose.address=}')
@ -210,7 +220,9 @@ class unit_tests:
chain3 = m.to_coin_type(coin='btc', addr_type='bech32').to_chain(0, public=True)
assert chain3.address == chain1.address
assert chain1.address == chain_from_master_xprv.address
vmsg(f' {chain1.address=}')
vmsg(f' {chain_from_master_xprv.address=}')
a = BipHDNode.from_extended_key(cfg, 'btc', chain2.xpub)
b = BipHDNode.from_extended_key(cfg, 'btc', chain2.xprv)