From ea1e8d1228db50c9776998ff50d270af52b35540 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 26 Jan 2024 10:54:03 +0000 Subject: [PATCH] bip_hd: a minimal, easy-to-use BIP-32/BIP-44 implementation - this is a work in progress, only a few coins are currently supported Testing: $ test/unit_tests.py -v bip_hd Examples: $ PYTHONPATH=. examples/bip_hd.py --- examples/bip_hd.py | 88 ++ mmgen/bip_hd/__init__.py | 504 ++++++++++ mmgen/bip_hd/chainparams.py | 1219 +++++++++++++++++++++++++ mmgen/data/version | 2 +- scripts/create-bip-hd-chain-params.py | 115 +++ setup.cfg | 3 +- test/cmdtest_py_d/ct_misc.py | 14 + test/ref/altcoin/slip44-mini.json | 1 + test/unit_tests_d/ut_bip_hd.py | 419 +++++++++ 9 files changed, 2363 insertions(+), 2 deletions(-) create mode 100755 examples/bip_hd.py create mode 100644 mmgen/bip_hd/__init__.py create mode 100644 mmgen/bip_hd/chainparams.py create mode 100755 scripts/create-bip-hd-chain-params.py create mode 100644 test/ref/altcoin/slip44-mini.json create mode 100755 test/unit_tests_d/ut_bip_hd.py diff --git a/examples/bip_hd.py b/examples/bip_hd.py new file mode 100755 index 00000000..53f2b480 --- /dev/null +++ b/examples/bip_hd.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2024 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen-wallet +# https://gitlab.com/mmgen/mmgen-wallet + +""" +examples/bip_hd.py: Usage examples for the MMGen BIP-32/-44 hierarchical/deterministic library +""" + +from mmgen.cfg import Config +from mmgen.util import fmt +from mmgen.bip39 import bip39 +from mmgen.bip_hd import MasterNode,BipHDNode + +cfg = Config() + +bip39_mnemonic = 'cat swing flag economy stadium alone churn speed unique patch report train' + +seed = bip39().generate_seed(bip39_mnemonic.split()) + +m = MasterNode(cfg, seed) + +# Derive sample path: + +# to_chain() derives default chain for coin/addr_type pair: +dfl_pub_chain = m.to_chain(idx=0, coin='ltc', addr_type='bech32') +dfl_chg_chain = m.to_chain(idx=1, coin='ltc', addr_type='bech32') + +print(f'Default path (LTC, bech32):\n') +print(f' public chain xpub:\n {dfl_pub_chain.xpub}\n') +print(f' internal chain xpub:\n {dfl_chg_chain.xpub}\n') +print(f' public chain addr 0:\n {dfl_pub_chain.derive_public(0).address}\n') +print(f' public chain addr 1:\n {dfl_pub_chain.derive_public(1).address}\n') + +# Derive sample path using path string: + +dfl_pub_chain_from_path = BipHDNode.from_path( + base_cfg = cfg, + seed = seed, + # purpose=84 (bech32 [BIP-84]), coin_type=2 (LTC mainnet [SLIP-44]), account=0, chain=0 (public) + # as per BIP-44, ‘purpose’, ‘coin_type’ and ‘account’ are hardened, while ‘chain’ is not + path_str = "m/84'/2'/0'/0", + coin = 'ltc', + addr_type = 'bech32') + +assert dfl_pub_chain_from_path.xpub == dfl_pub_chain.xpub + +# Derive sample path step-by-step: + +# Configure master node with coin/addr_type pair: +master = m.init_cfg(coin='ltc', addr_type='bech32') + +# ‘idx’ and ‘hardened’ args may be omitted at depths where defaults exist: +purpose = master.derive_private() # ‘idx’ is auto-computed from addr_type (BIP-44/49/84) +coin_type = purpose.derive_private() # ‘idx’ is auto-computed from coin/network (SLIP-44) +account = coin_type.derive_private(idx=0) +pub_chain = account.derive_public(idx=0) + +assert pub_chain.xpub == dfl_pub_chain.xpub + +# Initialize node from xpub: +pub_chain_from_xpub = BipHDNode.from_extended_key(cfg, 'ltc', pub_chain.xpub) + +assert pub_chain_from_xpub.xpub == pub_chain.xpub + +# To derive arbitrary BIP-32 paths, ignoring BIP-44, specify ‘no_path_checks’ +nonstd_path = BipHDNode.from_path( + base_cfg = cfg, + seed = seed, + path_str = "m/111'/222/333/444", + coin = 'eth', + addr_type = 'E', + no_path_checks = True) + +print(f'Non-standard path (ETH):\n') +print(f' xpub:\n {nonstd_path.xpub}\n') +print(f' WIF key:\n {nonstd_path.privkey.wif}\n') +print(f' address:\n {nonstd_path.address}\n') + +# Display parsed xpub: +parsed_xpub = nonstd_path.key_extended(public=True) +print('Default path parsed xpub:\n') +print(fmt(str(parsed_xpub), indent=' ')) diff --git a/mmgen/bip_hd/__init__.py b/mmgen/bip_hd/__init__.py new file mode 100644 index 00000000..31c605f4 --- /dev/null +++ b/mmgen/bip_hd/__init__.py @@ -0,0 +1,504 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2024 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen-wallet +# https://gitlab.com/mmgen/mmgen-wallet + +""" +bip_hd: BIP-44/49/84, SLIP-44 hierarchical-deterministic key derivation library +""" + +# One motivation for this implementation: +# https://blog.unit410.com/bitcoin/bip32/bip39/kdf/2021/05/17/inconsistent-bip32-derivations.html + +import hmac +from collections import namedtuple + +from ..cfg import Config +from ..util import is_int, fmt +from ..base_obj import Lockable +from ..keygen import KeyGenerator, keygen_public_data +from ..addrgen import AddrGenerator +from ..addr import MMGenAddrType +from ..key import PrivKey +from ..protocol import CoinProtocol +from ..proto.btc.common import hash160, b58chk_encode, b58chk_decode +from ..proto.secp256k1.secp256k1 import pubkey_tweak_add, pubkey_check + +from . import chainparams +chainparams_data = chainparams.parse_data() + +secp256k1_order = CoinProtocol.Secp256k1.secp256k1_group_order +hardened_idx0 = 0x80000000 + +def get_chain_params(bipnum, chain): + return chainparams_data[f'bip-{bipnum}'][chain.upper()] + +def get_version_bytes(bip_proto, coin, public): + return getattr( + chainparams_data[f'bip-{bip_proto}'][coin], + 'vb_pub' if public else 'vb_prv') + +def parse_version_bytes(vb_hex): + e = chainparams_data['defaults'] + if vb_hex in (e.vb_pub, e.vb_prv): + return (None, e) + for bipnum in (49, 84, 86, 44): # search bip-44 last, since it has the most entries + for e in chainparams_data[f'bip-{bipnum}'].values(): + if vb_hex in (e.vb_pub, e.vb_prv): + return (bipnum, e) + else: + raise ValueError(f'0x{vb_hex}: unrecognized extended key version bytes') + +def compress_pubkey(pubkey_bytes): + # see: proto.secp256k1.keygen.pubkey_format() + return (b'\x02',b'\x03')[pubkey_bytes[-1] & 1] + pubkey_bytes[1:33] + +def decompress_pubkey(pubkey_bytes): + import ecdsa + return b'\x04' + ecdsa.VerifyingKey.from_string(pubkey_bytes, curve=ecdsa.curves.SECP256k1).to_string() + +class Bip32ExtendedKey(Lockable): + + def __init__(self, key_b58): + + try: + key = b58chk_decode(key_b58) + except Exception as e: + raise type(e)(f'invalid extended key: {e}') + + assert len(key) == 78, f'len(key) == {len(key)} (not 78)' + + # Serialization: + # ver_bytes | depth | par_print | idx | chaincode | serialized_key + # 0:4 (4) | 4 (1) | 5:9 (4) | 9:13 (4) | 13:45 (32) | 45(46): 33(32) + ver_hex = key[:4].hex() + bipnum, cp_entry = parse_version_bytes(ver_hex) + + public = ver_hex == cp_entry.vb_pub + idx_raw = int.from_bytes(key[9:13]) + + self.base58 = key_b58 + self.ver_bytes = key[:4] + self.depth = key[4] + self.par_print = key[5:9] + self.idx = idx_raw if idx_raw < hardened_idx0 else idx_raw - hardened_idx0 + self.chaincode = key[13:45] + self.key = key[45 if public else 46:] + self.hardened = idx_raw >= hardened_idx0 or self.depth == 0 + self.bip_proto = bipnum or 44 + self.network = cp_entry.network if bipnum else 'mainnet' + self.public = public + self.coin = cp_entry.chain if bipnum and cp_entry.chain != 'BTC' else '-' + + if self.public: + if not key[45] in (2, 3): + raise ValueError(f'0x{key[45]:02x}: invalid first byte for public key data (not 2 or 3)') + elif key[45]: + raise ValueError(f'0x{key[45]:02x}: invalid first byte for private key data (not zero)') + + if self.depth == 0: + if self.par_print != bytes(4): + raise ValueError(f'{self.par_print.hex()}: non-zero parent fingerprint at depth 0') + if idx_raw: + raise ValueError(f'{idx_raw}: non-zero index at depth 0') + + def __str__(self): + return fmt(f""" + base58: {self.base58} + ver_bytes: {self.ver_bytes.hex()} + depth: {self.depth} [{bip_hd_nodes[self.depth].desc}] + par_print: {self.par_print.hex()} + idx: {self.idx} + chaincode: {self.chaincode.hex()} + key: {self.key.hex()} + hardened: {self.hardened} + bip_proto: {self.bip_proto} + network: {self.network} + public: {self.public} + coin: {self.coin} + """) + +def get_bip_by_addr_type(addr_type): + return ( + 84 if addr_type.name == 'bech32' else + 49 if addr_type.name == 'segwit' else + 44) + +def check_privkey(key_int): + if key_int == 0: + raise ValueError('private key is zero!') + elif key_int >= secp256k1_order: + raise ValueError(f'{key_int:x}: private key >= group order!') + +class BipHDConfig(Lockable): + + supported_coins = ('btc', 'eth', 'doge', 'ltc') + + def __init__(self, base_cfg, coin, network, addr_type, from_path, no_path_checks): + + if not coin.lower() in self.supported_coins: + raise ValueError(f'bip_hd: coin {coin.upper()} not supported') + + base_cfg = Config({ + '_clone': base_cfg, + 'coin': coin, + 'network': network, + 'type': addr_type or None, + 'quiet': True + }) + + dfl_type = base_cfg._proto.dfl_mmtype + addr_type = MMGenAddrType( + proto = base_cfg._proto, + id_str = base_cfg.type or ('C' if dfl_type == 'L' else dfl_type)) + + self.base_cfg = base_cfg + self.addr_type = addr_type + self.kg = KeyGenerator(base_cfg, base_cfg._proto, addr_type.pubkey_type) + self.ag = AddrGenerator(base_cfg, base_cfg._proto, addr_type) + self.bip_proto = get_bip_by_addr_type(addr_type) + self.from_path = from_path + self.no_path_checks = no_path_checks + +class MasterNode(Lockable): + desc = 'Unconfigured Bip32 Master Node' + _use_class_attr = True + + def __init__(self, base_cfg, bytes_data): + + H = hmac.digest(b'Bitcoin seed', bytes_data, 'sha512') + + self.par_print = bytes(4) + self.depth = 0 + self.key = H[:32] + self.chaincode = H[32:] + self.idx = 0 + self.hardened = True + self.public = False + self.base_cfg = base_cfg + + check_privkey(int.from_bytes(self.key)) + + def init_cfg(self, coin=None, network=None, addr_type=None, from_path=False, no_path_checks=False): + + new = BipHDNodeMaster() + + new.cfg = BipHDConfig(self.base_cfg, coin, network, addr_type, from_path, no_path_checks) + new.par_print = self.par_print + new.depth = self.depth + new.key = self.key + new.chaincode = self.chaincode + new.idx = self.idx + new.hardened = self.hardened + new.public = self.public + + new._lock() + return new + + def to_coin_type(self, coin=None, network=None, addr_type=None): + return self.init_cfg(coin, network, addr_type).to_coin_type() + + def to_chain(self, idx, coin=None, network=None, addr_type=None, hardened=False, public=False): + return self.init_cfg(coin, network, addr_type).to_chain( + idx = idx, + hardened = hardened, + public = public) + +class BipHDNode(Lockable): + _autolock = False + _generated_pubkey = None + _set_ok = ('_generated_pubkey',) + + def check_param(self, name, val): + cls = type(self) + if val is None: + if not hasattr(cls, name): + raise ValueError(f'‘{name}’ at depth {self.depth} ({self.desc!r}) must be set') + elif hasattr(cls, name) and val != getattr(cls, name): + raise ValueError( + '{}: invalid value for ‘{}’ at depth {} ({!r}) (must be {})'.format( + val, name, self.depth, self.desc, + 'None' if getattr(cls, name) is None else f'None or {getattr(cls, name)}') + ) + + def set_params(self, cfg, idx, hardened): + self.check_param('idx', idx) + self.check_param('hardened', hardened) + return ( + type(self).idx if idx is None else idx, + type(self).hardened if hardened is None else hardened) + + @property + def privkey(self): + assert not self.public + return PrivKey( + self.cfg.base_cfg._proto, + self.key, + compressed = self.cfg.addr_type.compressed, + pubkey_type = self.cfg.addr_type.pubkey_type) + + @property + def pubkey_bytes(self): + if self.public: + return self.key + elif self.cfg.addr_type.compressed: + return self.priv2pub().pubkey + else: + return compress_pubkey(self.priv2pub().pubkey) + + def priv2pub(self): + if not self._generated_pubkey: + self._generated_pubkey = self.cfg.kg.gen_data(self.privkey) + return self._generated_pubkey + + @property + def address(self): + return self.cfg.ag.to_addr( + keygen_public_data( + pubkey = self.key if self.cfg.addr_type.compressed else decompress_pubkey(self.key), + viewkey_bytes = None, + pubkey_type = self.cfg.addr_type.pubkey_type, + compressed = self.cfg.addr_type.compressed) + if self.public else + self.priv2pub() + ) + + # Extended keys can be identified by the Hash160 (RIPEMD160 after SHA256) of the serialized ECDSA + # public key K, ignoring the chain code. This corresponds exactly to the data used in traditional + # Bitcoin addresses. It is not advised to represent this data in base58 format though, as it may be + # interpreted as an address that way (and wallet software is not required to accept payment to the + # chain key itself). + @property + def id(self): + return hash160(self.pubkey_bytes) + + # The first 32 bits of the identifier are called the key fingerprint. + @property + def fingerprint(self): + return self.id[:4] + + @property + def xpub(self): + return self.key_extended(public=True, as_str=True) + + @property + def xprv(self): + return self.key_extended(public=False, as_str=True) + + def key_extended(self, public, as_str=False): + if self.public and not public: + raise ValueError('cannot create extended private key for public node!') + ret = b58chk_encode( + bytes.fromhex(get_version_bytes(self.cfg.bip_proto, self.cfg.base_cfg.coin, public)) + + int.to_bytes(self.depth, length=1) + + self.par_print + + int.to_bytes(self.idx + (hardened_idx0 if self.hardened and self.depth else 0), length=4) + + self.chaincode + + (self.pubkey_bytes if public else b'\x00' + self.key) + ) + return ret if as_str else Bip32ExtendedKey(ret) + + def derive_public(self, idx=None): + return self.derive(idx=idx, hardened=False, public=True) + + def derive_private(self, idx=None, hardened=None): + return self.derive(idx=idx, hardened=hardened, public=False) + + def derive(self, idx, hardened, public): + + if self.public and not public: + raise ValueError('cannot derive private node from public node!') + + new = bip_hd_nodes[self.depth + 1]() + + new.depth = self.depth + 1 + new.cfg = self.cfg + new.par_print = self.fingerprint + new.public = public + + if new.cfg.no_path_checks: + new.idx, new.hardened = (idx, hardened) + else: + if new.public and type(new).hardened: + raise ValueError( + f'‘public’ requested, but node of depth {new.depth} ({new.desc}) must be hardened!') + new.idx, new.hardened = new.set_params(new.cfg, idx, hardened) + + key_in = b'\x00' + self.key if new.hardened else self.pubkey_bytes + + I = hmac.digest( + self.chaincode, + key_in + ((hardened_idx0 if new.hardened else 0) + new.idx).to_bytes(length=4), + 'sha512') + + pk_addend_bytes = I[:32] + new.chaincode = I[32:] + + if new.public: + new.key = pubkey_tweak_add(key_in, pk_addend_bytes) # checks range of pk_addend + else: + pk_addend = int.from_bytes(pk_addend_bytes) + check_privkey(pk_addend) + key_int = (int.from_bytes(self.key) + pk_addend) % secp256k1_order + check_privkey(key_int) + new.key = int.to_bytes(key_int, length=32) + + new._lock() + return new + + @staticmethod + def from_path( + base_cfg, + seed, + path_str, + coin = None, + addr_type = None, + no_path_checks = False): + + path = path_str.lower().split('/') + if path.pop(0) != 'm': + raise ValueError(f'{path_str}: invalid path string (first component is not "m")') + + res = MasterNode(base_cfg, seed).init_cfg( + coin = coin or 'btc', + addr_type = addr_type or 'compressed', + no_path_checks = no_path_checks, + from_path = True) + + for s in path: + for suf in ("'", 'h'): + if s.endswith(suf): + idx = s.removesuffix(suf) + hardened = True + break + else: + idx = s + hardened = False + + if not is_int(idx): + raise ValueError(f'invalid path component {s!r}') + + res = res.derive(int(idx), hardened, public=False) + + return res + + @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): + xk = Bip32ExtendedKey(xkey_b58) + + if xk.public: + pubkey_check(xk.key) + else: + check_privkey(int.from_bytes(xk.key)) + + addr_types = { + 84: 'bech32', + 49: 'segwit', + 44: None + } + + new = bip_hd_nodes[xk.depth]() + + new.cfg = BipHDConfig( + base_cfg, + coin, + xk.network, + addr_type or addr_types[xk.bip_proto], + False, + False) + + new.par_print = xk.par_print + new.depth = xk.depth + new.key = xk.key + new.chaincode = xk.chaincode + new.idx = xk.idx + new.hardened = xk.hardened + new.public = xk.public + + new._lock() + return new + +class BipHDNodeMaster(BipHDNode): + desc = 'Bip32 Master Node' + hardened = True + idx = None + + def to_coin_type(self): + # purpose coin_type + return self.derive_private().derive_private() + + def to_chain(self, idx, hardened=False, public=False): + # purpose coin_type account #0 chain + return self.derive_private().derive_private().derive_private(idx=0).derive( + idx = idx, + hardened = False if public else hardened, + public = public) + +class BipHDNodePurpose(BipHDNode): + desc = 'Purpose' + hardened = True + + def set_params(self, cfg, idx, hardened): + self.check_param('hardened', hardened) + if idx not in (None, cfg.bip_proto): + raise ValueError( + f'index for path component {self.desc!r} with address type {cfg.addr_type!r} ' + f'must be {cfg.bip_proto}, not {idx}') + return (cfg.bip_proto, type(self).hardened) + +class BipHDNodeCoinType(BipHDNode): + desc = 'Coin Type' + hardened = True + + def set_params(self, cfg, idx, hardened): + self.check_param('hardened', hardened) + chain_idx = get_chain_params( + bipnum = get_bip_by_addr_type(cfg.addr_type), + chain = cfg.base_cfg.coin).idx + if idx not in (None, chain_idx): + raise ValueError( + f'index {idx} at depth {self.depth} ({self.desc}) does not match ' + f'chain index {chain_idx} for coin {cfg.base_cfg.coin!r}') + return (chain_idx, type(self).hardened) + + def to_chain(self, idx, hardened=False, public=False): + # account #0 chain + return self.derive_private(idx=0).derive( + idx = idx, + hardened = False if public else hardened, + public = public) + +class BipHDNodeAccount(BipHDNode): + desc = 'Account' + hardened = True + +class BipHDNodeChain(BipHDNode): + desc = 'Chain' + hardened = False + + def set_params(self, cfg, idx, hardened): + self.check_param('hardened', hardened) + if idx not in (0, 1): + raise ValueError( + f'at depth {self.depth} ({self.desc}), ‘idx’ must be either 0 (external) or 1 (internal)') + return (idx, type(self).hardened) + +class BipHDNodeAddrIdx(BipHDNode): + desc = 'Address Index' + hardened = False + +bip_hd_nodes = { + 0: BipHDNodeMaster, + 1: BipHDNodePurpose, + 2: BipHDNodeCoinType, + 3: BipHDNodeAccount, + 4: BipHDNodeChain, + 5: BipHDNodeAddrIdx +} diff --git a/mmgen/bip_hd/chainparams.py b/mmgen/bip_hd/chainparams.py new file mode 100644 index 00000000..4ad09890 --- /dev/null +++ b/mmgen/bip_hd/chainparams.py @@ -0,0 +1,1219 @@ +#!/usr/bin/env python3 +# +# Created using scripts/create-bip-hd-chain-params.py +# Source data: +# https://github.com/MetaMask/slip44/blob/main/slip44.json (1bc984bee) +# https://github.com/ebellocchia/bip_utils (5649541c6) + +from collections import namedtuple + +def parse_data(): + + _d = namedtuple( + 'bip_hd_data', + 'idx chain curve network addr_cls vb_prv vb_pub vb_wif vb_addr def_path name') + _u = namedtuple( + 'bip_hd_data_partial', + 'idx chain name') + + def parse_line(line): + l = line.split() + + if l[2] == '-': + return _u( + idx = int(l[0]), + chain = l[1], + name = ' '.join(l[3:]), + ) + else: + return _d( + idx = int(l[0]), + chain = l[1], + curve = defaults.curve if l[2] == 'x' else l[2], + network = 'mainnet' if l[3] == 'm' else 'testnet' if l[3] == 'T' else None, + addr_cls = l[4], + vb_prv = defaults.vb_prv if l[5] == 'x' else l[5], + vb_pub = defaults.vb_pub if l[6] == 'x' else l[6], + vb_wif = l[7], + vb_addr = l[8], + def_path = defaults.def_path if l[9] == 'x' else l[9], + name = ' '.join(l[10:]), + ) + + out = {} + for line in _data_in.strip().splitlines(): + if not line or line.startswith('IDX'): + continue + if line.startswith('['): + key = line[1:-1] + continue + p = parse_line(line) + if key in out: + out[key][p[1]] = p + elif key == 'defaults': + out['defaults'] = p + defaults = p + else: + out[key] = {p[1]: p} + + return out + +_data_in = """ + +[defaults] +IDX CHAIN CURVE NW ADDR_CLS VB_PRV VB_PUB VB_WIF VB_ADDR DFL_PATH NAME +0 - secp - - 0488ade4 0488b21e - - 0'/0/0 - + +[bip-44] +IDX CHAIN CURVE NW ADDR_CLS VB_PRV VB_PUB VB_WIF VB_ADDR DFL_PATH NAME +0 BTC x m P2PKH x x 80 00 x Bitcoin +1 --- x T P2PKH 04358394 043587cf ef 1d25 x Testnet (all coins) +2 LTC x m P2PKH x x b0 spec x Litecoin +3 DOGE x m P2PKH 02fac398 02facafd 9e 1e x Dogecoin +5 DASH x m P2PKH x x cc 4c x Dash +60 ETH x m Eth x x - - x Ethereum +61 ETC x m Eth x x - - x Ether Classic +74 ICX x m Icx x x - - x ICON +77 XVG x m P2PKH x x 9e 1e x Verge Currency +118 ATOM x m Atom x x - h:stafi x Atom +128 XMR x m Xmr x x - - x Monero +133 ZEC x m P2PKH x x 80 1cb8 x Zcash +144 XRP x m Xrp x x - - x Ripple +145 BCH x m BchP2PKH x x 80 spec x Bitcoin Cash +148 XLM edw m Xlm x x - spec 0' Stellar Lumens +165 XNO blk m Nano x x - - 0' Nano +194 EOS x m Eos x x - - x EOS +195 TRX x m Trx x x - - x Tron +236 BSV x m P2PKH x x 80 00 x BitcoinSV +283 ALGO edw m Algo x x - - 0'/0'/0' Algorand +313 ZIL x m Zil x x - - x Zilliqa +330 LUNA x m Atom x x - h:terra x Terra +354 DOT edw m SubstrateEd25519 x x - spec 0'/0'/0' Polkadot +397 NEAR edw m Near x x - - 0' NEAR Protocol +429 ERG x T ErgoP2PKH 04358394 043587cf - spec x Ergo +434 KSM edw m SubstrateEd25519 x x - spec 0'/0'/0' Kusama +459 KAVA x m Atom x x - h:kava x Kava +461 FIL x m FilSecp256k1 x x - - x Filecoin +494 BAND x m Atom x x - h:band x Band +500 THETA x m Eth x x - - x Theta +501 SOL edw m Sol x x - - 0' Solana +508 EGLD edw m Egld x x - - 0'/0'/0' MultiversX +529 SCRT x m Atom x x - h:secret x Secret Network +567 NCG x m Eth x x - - x Nine Chronicles +637 APTOS edw m Aptos x x - - 0'/0'/0' Aptos +714 BNB x m Atom x x - h:bnb x Binance +784 SUI edw m Sui x x - - 0'/0'/0' Sui +818 VET x m Eth x x - - x VeChain Token +888 NEO nist m Neo x x - spec x NEO +996 OKT x m Okex x x - - x OKChain Token +1023 ONE x m One x x - - x HARMONY-ONE (Legacy) +1024 ONT nist m Neo x x - spec x Ontology +1729 XTZ edw m Xtz x x - spec 0'/0' Tezos +1815 ADA khol m AdaByronIcarus 0f4331d4 x - spec x Cardano +9000 AVAX x m AvaxXChain x x - - x Avalanche +52752 CELO x m Eth x x - - x Celo +314159 PI edw m Xlm x x - spec 0' Pi Network + +[bip-49] +IDX CHAIN CURVE NW ADDR_CLS VB_PRV VB_PUB VB_WIF VB_ADDR DFL_PATH NAME +0 BTC x m P2SH 049d7878 049d7cb2 80 05 x Bitcoin +1 ZEC x T P2SH 044a4e28 044a5262 ef 1cba x Zcash TestNet +2 LTC x m P2SH 049d7878 049d7cb2 b0 spec x Litecoin +3 DOGE x m P2SH 02fac398 02facafd 9e 16 x Dogecoin +5 DASH x m P2SH 049d7878 049d7cb2 cc 10 x Dash +133 ZEC x m P2SH 049d7878 049d7cb2 80 1cbd x Zcash +145 XEC x m BchP2SH 049d7878 049d7cb2 80 spec x eCash +236 BSV x m P2SH 049d7878 049d7cb2 80 05 x BitcoinSV + +[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 +2 LTC x m P2WPKH 04b2430c 04b24746 b0 h:ltc x Litecoin + +[bip-86] +IDX CHAIN CURVE NW ADDR_CLS VB_PRV VB_PUB VB_WIF VB_ADDR DFL_PATH NAME +0 BTC x m P2TR x x 80 h:bc x Bitcoin +1 BTC x T P2TR 04358394 043587cf ef h:tb x Bitcoin TestNet + +[bip-44-unsupported] +IDX CHAIN NAME +4 RDD - Reddcoin +6 PPC - Peercoin +7 NMC - Namecoin +8 FTC - Feathercoin +9 XCP - Counterparty +10 BLK - Blackcoin +11 NSR - NuShares +12 NBT - NuBits +13 MZC - Mazacoin +14 VIA - Viacoin +15 XCH - ClearingHouse +16 RBY - Rubycoin +17 GRS - Groestlcoin +18 DGC - Digitalcoin +19 CCN - Cannacoin +20 DGB - DigiByte +21 --- - Open Assets +22 MONA - Monacoin +23 CLAM - Clams +24 XPM - Primecoin +25 NEOS - Neoscoin +26 JBS - Jumbucks +27 ZRC - ziftrCOIN +28 VTC - Vertcoin +29 NXT - NXT +30 BURST - Burst +31 MUE - MonetaryUnit +32 ZOOM - Zoom +33 VASH - Virtual Cash +34 CDN - Canada eCoin +35 SDC - ShadowCash +36 PKB - ParkByte +37 PND - Pandacoin +38 START - StartCOIN +39 MOIN - MOIN +40 EXP - Expanse +41 EMC2 - Einsteinium +42 DCR - Decred +43 XEM - NEM +44 PART - Particl +45 ARG - Argentum (dead) +46 --- - Libertas +47 --- - Posw coin +48 SHR - Shreeji +49 GCR - Global Currency Reserve (GCRcoin) +50 NVC - Novacoin +51 AC - Asiacoin +52 BTCD - BitcoinDark +53 DOPE - Dopecoin +54 TPC - Templecoin +55 AIB - AIB +56 EDRC - EDRCoin +57 SYS - Syscoin +58 SLR - Solarcoin +59 SMLY - Smileycoin +62 PSB - Pesobit +63 LDCN - Landcoin (dead) +64 --- - Open Chain +65 XBC - Bitcoinplus +66 IOP - Internet of People +67 NXS - Nexus +68 INSN - InsaneCoin +69 OK - OKCash +70 BRIT - BritCoin +71 CMP - Compcoin +72 CRW - Crown +73 BELA - BelaCoin +75 FJC - FujiCoin +76 MIX - MIX +78 EFL - Electronic Gulden +79 CLUB - ClubCoin +80 RICHX - RichCoin +81 POT - Potcoin +82 QRK - Quarkcoin +83 TRC - Terracoin +84 GRC - Gridcoin +85 AUR - Auroracoin +86 IXC - IXCoin +87 NLG - Gulden +88 BITB - BitBean +89 BTA - Bata +90 XMY - Myriadcoin +91 BSD - BitSend +92 UNO - Unobtanium +93 MTR - MasterTrader +94 GB - GoldBlocks +95 SHM - Saham +96 CRX - Chronos +97 BIQ - Ubiquoin +98 EVO - Evotion +99 STO - SaveTheOcean +100 BIGUP - BigUp +101 GAME - GameCredits +102 DLC - Dollarcoins +103 ZYD - Zayedcoin +104 DBIC - Dubaicoin +105 STRAT - Stratis +106 SH - Shilling +107 MARS - MarsCoin +108 UBQ - Ubiq +109 PTC - Pesetacoin +110 NRO - Neurocoin +111 ARK - ARK +112 USC - UltimateSecureCashMain +113 THC - Hempcoin +114 LINX - Linx +115 ECN - Ecoin +116 DNR - Denarius +117 PINK - Pinkcoin +119 PIVX - Pivx +120 FLASH - Flashcoin +121 ZEN - Zencash +122 PUT - Putincoin +123 ZNY - BitZeny +124 UNIFY - Unify +125 XST - StealthCoin +126 BRK - Breakout Coin +127 VC - Vcash +129 VOX - Voxels +130 NAV - NavCoin +131 FCT - Factom Factoids +132 EC - Factom Entry Credits +134 LSK - Lisk +135 STEEM - Steem +136 XZC - ZCoin +137 RBTC - RSK +138 --- - Giftblock +139 RPT - RealPointCoin +140 LBC - LBRY Credits +141 KMD - Komodo +142 BSQ - bisq Token +143 RIC - Riecoin +146 NEBL - Neblio +147 ZCL - ZClassic +149 NLC2 - NoLimitCoin2 +150 WHL - WhaleCoin +151 ERC - EuropeCoin +152 DMD - Diamond +153 BTM - Bytom +154 BIO - Biocoin +155 XWCC - Whitecoin Classic +156 BTG - Bitcoin Gold +157 BTC2X - Bitcoin 2x +158 SSN - SuperSkynet +159 TOA - TOACoin +160 BTX - Bitcore +161 ACC - Adcoin +162 BCO - Bridgecoin +163 ELLA - Ellaism +164 PIRL - Pirl +166 VIVO - Vivo +167 FRST - Firstcoin +168 HNC - Helleniccoin +169 BUZZ - BUZZ +170 MBRS - Ember +171 HC - Hcash +172 HTML - HTMLCOIN +173 ODN - Obsidian +174 ONX - OnixCoin +175 RVN - Ravencoin +176 GBX - GoByte +177 BTCZ - BitcoinZ +178 POA - Poa +179 NYC - NewYorkCoin +180 MXT - MarteXcoin +181 WC - Wincoin +182 MNX - Minexcoin +183 BTCP - Bitcoin Private +184 MUSIC - Musicoin +185 BCA - Bitcoin Atom +186 CRAVE - Crave +187 STAK - STRAKS +188 WBTC - World Bitcoin +189 LCH - LiteCash +190 EXCL - ExclusiveCoin +191 --- - Lynx +192 LCC - LitecoinCash +193 XFE - Feirm +196 KOBO - Kobocoin +197 HUSH - HUSH +198 BAN - Banano +199 ETF - ETF +200 OMNI - Omni +201 BIFI - BitcoinFile +202 UFO - Uniform Fiscal Object +203 CNMC - Cryptonodes +204 BCN - Bytecoin +205 RIN - Ringo +206 ATP - Alaya +207 EVT - everiToken +208 ATN - ATN +209 BIS - Bismuth +210 NEET - NEETCOIN +211 BOPO - BopoChain +212 OOT - Utrum +213 ALIAS - Alias +214 MONK - Monkey Project +215 BOXY - BoxyCoin +216 FLO - Flo +217 MEC - Megacoin +218 BTDX - BitCloud +219 XAX - Artax +220 ANON - ANON +221 LTZ - LitecoinZ +222 BITG - Bitcoin Green +223 ICP - Internet Computer (DFINITY) +224 SMART - Smartcash +225 XUEZ - XUEZ +226 HLM - Helium +227 WEB - Webchain +228 ACM - Actinium +229 NOS - NOS Stable Coins +230 BITC - BitCash +231 HTH - Help The Homeless Coin +232 TZC - Trezarcoin +233 VAR - Varda +234 IOV - IOV +235 FIO - FIO +237 DXN - DEXON +238 QRL - Quantum Resistant Ledger +239 PCX - ChainX +240 LOKI - Loki +241 --- - Imagewallet +242 NIM - Nimiq +243 SOV - Sovereign Coin +244 JCT - Jibital Coin +245 SLP - Simple Ledger Protocol +246 EWT - Energy Web +247 UC - Ulord +248 EXOS - EXOS +249 ECA - Electra +250 SOOM - Soom +251 XRD - Redstone +252 FREE - FreeCoin +253 NPW - NewPowerCoin +254 BST - BlockStamp +255 --- - SmartHoldem +256 NANO - Bitcoin Nano +257 BTCC - Bitcoin Core +258 --- - Zen Protocol +259 ZEST - Zest +260 ABT - ArcBlock +261 PION - Pion +262 DT3 - DreamTeam3 +263 ZBUX - Zbux +264 KPL - Kepler +265 TPAY - TokenPay +266 ZILLA - ChainZilla +267 ANK - Anker +268 BCC - BCChain +269 HPB - HPB +270 ONE - ONE +271 SBC - SBC +272 IPC - IPChain +273 DMTC - Dominantchain +274 OGC - Onegram +275 SHIT - Shitcoin +276 ANDES - Andescoin +277 AREPA - Arepacoin +278 BOLI - Bolivarcoin +279 RIL - Rilcoin +280 HTR - Hathor Network +281 ACME - Accumulate +282 BRAVO - BRAVO +284 BZX - Bitcoinzero +285 GXX - GravityCoin +286 HEAT - HEAT +287 XDN - DigitalNote +288 FSN - FUSION +289 CPC - Capricoin +290 BOLD - Bold +291 IOST - IOST +292 TKEY - Tkeycoin +293 USE - Usechain +294 BCZ - BitcoinCZ +295 IOC - Iocoin +296 ASF - Asofe +297 MASS - MASS +298 FAIR - FairCoin +299 NUKO - Nekonium +300 GNX - Genaro Network +301 DIVI - Divi Project +302 CMT - Community +303 EUNO - EUNO +304 IOTX - IoTeX +305 ONION - DeepOnion +306 8BIT - 8Bit +307 ATC - AToken Coin +308 BTS - Bitshares +309 CKB - Nervos CKB +310 UGAS - Ultrain +311 ADS - Adshares +312 ARA - Aura +314 MOAC - MOAC +315 SWTC - SWTC +316 VNSC - vnscoin +317 PLUG - Pl^g +318 MAN - Matrix AI Network +319 ECC - ECCoin +320 RPD - Rapids +321 RAP - Rapture +322 GARD - Hashgard +323 ZER - Zero +324 EBST - eBoost +325 SHARD - Shard +326 MRX - Metrix Coin +327 CMM - Commercium +328 BLOCK - Blocknet +329 AUDAX - AUDAX +331 ZPM - zPrime +332 KUVA - Kuva Utility Note +333 MEM - MemCoin +334 CS - Credits +335 SWIFT - SwiftCash +336 FIX - FIX +337 CPC - CPChain +338 VGO - VirtualGoodsToken +339 DVT - DeVault +340 N8V - N8VCoin +341 MTNS - OmotenashiCoin +342 BLAST - BLAST +343 DCT - DECENT +344 AUX - Auxilium +345 USDP - USDP +346 HTDF - HTDF +347 YEC - Ycash +348 QLC - QLC Chain +349 TEA - Icetea Blockchain +350 ARW - ArrowChain +351 MDM - Medium +352 CYB - Cybex +353 LTO - LTO Network +355 AEON - Aeon +356 RES - Resistance +357 AYA - Aryacoin +358 DAPS - Dapscoin +359 CSC - CasinoCoin +360 VSYS - V Systems +361 NOLLAR - Nollar +362 XNOS - NOS +363 CPU - CPUchain +364 LAMB - Lambda Storage Chain +365 VCT - ValueCyber +366 CZR - Canonchain +367 ABBC - ABBC +368 HET - HET +369 XAS - Asch +370 VDL - Vidulum +371 MED - MediBloc +372 ZVC - ZVChain +373 VESTX - Vestx +374 DBT - DarkBit +375 SEOS - SuperEOS +376 MXW - Maxonrow +377 ZNZ - ZENZO +378 XCX - XChain +379 SOX - SonicX +380 NYZO - Nyzo +381 ULC - ULCoin +382 RYO - Ryo Currency +383 KAL - Kaleidochain +384 XSN - Stakenet +385 DOGEC - DogeCash +386 BMV - Bitcoin Matteo's Vision +387 QBC - Quebecoin +388 IMG - ImageCoin +389 QOS - QOS +390 PKT - PKT +391 LHD - LitecoinHD +392 CENNZ - CENNZnet +393 HSN - Hyper Speed Network +394 CRO - Crypto Chain +395 UMBRU - Umbru +396 EVER - Everscale +398 XPC - XPChain +399 ZOC - 01coin +400 NIX - NIX +401 UC - Utopiacoin +402 GALI - Galilel +403 OLT - Oneledger +404 XBI - XBI +405 DONU - DONU +406 EARTHS - Earths +407 HDD - HDDCash +408 SUGAR - Sugarchain +409 AILE - AileCoin +410 TENT - TENT +411 TAN - Tangerine Network +412 AIN - AIN +413 MSR - Masari +414 SUMO - Sumokoin +415 ETN - Electroneum +416 BYTZ - BYTZ +417 WOW - Wownero +418 XTNC - XtendCash +419 LTHN - Lethean +420 NODE - NodeHost +421 AGM - Argoneum +422 CCX - Conceal Network +423 TNET - Title Network +424 TELOS - TelosCoin +425 AION - Aion +426 BC - Bitcoin Confidential +427 KTV - KmushiCoin +428 ZCR - ZCore +430 PESO - Criptopeso +431 BTC2 - Bitcoin 2 +432 XRPHD - XRPHD +433 WE - WE Coin +435 PCN - Peepcoin +436 NCH - NetCloth +437 ICU - CHIPO +438 FNSA - FINSCHIA +439 DTP - DeVault Token Protocol +440 BTCR - Bitcoin Royale +441 AERGO - AERGO +442 XTH - Dothereum +443 LV - Lava +444 PHR - Phore +445 VITAE - Vitae +446 COCOS - Cocos-BCX +447 DIN - Dinero +448 SPL - Simplicity +449 YCE - MYCE +450 XLR - Solaris +451 KTS - Klimatas +452 DGLD - DGLD +453 XNS - Insolar +454 EM - EMPOW +455 SHN - ShineBlocks +456 SEELE - Seele +457 AE - æternity +458 ODX - ObsidianX +460 GLEEC - GLEEC +462 RUTA - Rutanio +463 CSDT - CSDT +464 ETI - EtherInc +465 ZSLP - Zclassic Simple Ledger Protocol +466 ERE - EtherCore +467 DX - DxChain Token +468 CPS - Capricoin+ +469 BTH - Bithereum +470 MESG - MESG +471 FIMK - FIMK +472 AR - Arweave +473 OGO - Origo +474 ROSE - Oasis Network +475 BARE - BARE Network +476 GLEEC - GleecBTC +477 CLR - Color Coin +478 RNG - Ring +479 OLO - Tool Global +480 PEXA - Pexa +481 MOON - Mooncoin +482 OCEAN - Ocean Protocol +483 BNT - Bluzelle Native +484 AMO - AMO Blockchain +485 FCH - FreeCash +486 LAT - PlatON +487 COIN - Bitcoin Bank +488 VEO - Amoveo +489 CCA - Counos Coin +490 GFN - Graphene +491 BIP - Minter Network +492 KPG - Kunpeng Network +493 FIN - FINL Chain +495 DROP - Dropil +496 BHT - Bluehelix Chain +497 LYRA - Scrypta +498 CS - Credits +499 RUPX - Rupaya +502 THT - ThoughtAI +503 CFX - Conflux +504 KUMA - Kumacoin +505 HASH - Provenance +506 CSPR - Casper +507 EARTH - EARTH +509 CHI - Xaya +510 KOTO - Koto +511 OTC - θ +512 XRD - Radiant +513 SEELEN - Seele-N +514 AETH - AETH +515 DNA - Idena +516 VEE - Virtual Economy Era +517 SIERRA - SierraCoin +518 LET - Linkeye +519 BSC - Bitcoin Smart Contract +520 BTCV - BitcoinVIP +521 ABA - Dabacus +522 SCC - StakeCubeCoin +523 EDG - Edgeware +524 AMS - AmsterdamCoin +525 GOSS - GOSSIP Coin +526 BU - BUMO +527 GRAM - GRAM +528 YAP - Yapstone +530 NOVO - Novo +531 GHOST - Ghost +532 HST - HST +533 PRJ - ProjectCoin +534 YOU - YOUChain +535 XHV - Haven Protocol +536 BYND - Beyondcoin +537 JOYS - Joys Digital +538 VAL - Valorbit +539 FLOW - Flow +540 SMESH - Spacemesh Coin +541 SCDO - SCDO +542 IQS - IQ-Cash +543 BIND - Compendia +544 COINEVO - Coinevo +545 SCRIBE - Scribe +546 HYN - Hyperion +547 BHP - BHP +548 BBC - BigBang Core +549 MKF - MarketFinance +550 XDC - XinFin +551 STR - Straightedge +552 SUM - Sumcoin +553 HBC - HuobiChain +554 --- - reserved +555 BCS - Bitcoin Smart +556 KTS - Kratos +557 LKR - Lkrcoin +558 TAO - Tao +559 XWC - Whitecoin +560 DEAL - DEAL +561 NTY - Nexty +562 TOP - TOP NetWork +563 --- - reserved +564 AG - Agoric +565 CICO - Coinicles +566 IRIS - Irisnet +568 LRG - Large Coin +569 SERO - Super Zero Protocol +570 BDX - Beldex +571 CCXX - Counos X +572 SLS - Saluscoin +573 SRM - Serum +574 --- - reserved +575 VIVT - VIDT Datalink +576 BPS - BitcoinPoS +577 NKN - NKN +578 ICL - ILCOIN +579 BONO - Bonorum +580 PLC - PLATINCOIN +581 DUN - Dune +582 DMCH - Darmacash +583 CTC - Creditcoin +584 KELP - Haidai Network +585 GBCR - GoldBCR +586 XDAG - XDAG +587 PRV - Incognito Privacy +588 SCAP - SafeCapital +589 TFUEL - Theta Fuel +590 GTM - Gentarium +591 RNL - RentalChain +592 GRIN - Grin +593 MWC - MimbleWimbleCoin +594 DOCK - Dock +595 POLYX - Polymesh +596 DIVER - Divergenti +597 XEP - Electra Protocol +598 APN - Apron +599 TFC - Turbo File Coin +600 UTE - Unit-e +601 MTC - Metacoin +602 NC - NobodyCash +603 XINY - Xinyuehu +604 DYN - Dynamo +605 BUFS - Buffer +606 STOS - Stratos +607 TON - TON +608 TAFT - TAFT +609 HYDRA - HYDRA +610 NOR - Noir +611 --- - Manta Network Private Asset +612 --- - Calamari Network Private Asset +613 WCN - Widecoin +614 OPT - Optimistic Ethereum +615 PSWAP - PolkaSwap +616 VAL - Validator +617 XOR - Sora +618 SSP - SmartShare +619 DEI - DeimosX +620 --- - reserved +621 ZERO - Singularity +622 ALPHA - AlphaDAO +623 BDECO - BDCashProtocol Ecosystem +624 NOBL - Nobility +625 EAST - Eastcoin +626 KDA - Kadena +627 SOUL - Phantasma +628 LORE - Gitopia +629 FNR - Fincor +630 NEXUS - Nexus +631 QTZ - Quartz +632 MAS - Massa +633 CALL - Callchain +634 VAL - Validity +635 POKT - Pocket Network +636 EMIT - EMIT +638 ADON - ADON +639 BTSG - BitSong +640 LFC - Leofcoin +641 KCS - KuCoin Shares +642 KCC - KuCoin Community Chain +643 AZERO - Aleph Zero +644 TREE - Tree +645 LX - Lynx +646 XLN - Lunarium +647 CIC - CIC Chain +648 ZRB - Zarb +649 --- - reserved +650 UCO - Archethic +651 SFX - Safex Cash +652 SFT - Safex Token +653 WSFX - Wrapped Safex Cash +654 USDG - US Digital Gold +655 WMP - WAMP +656 EKTA - Ekta +657 YDA - YadaCoin +659 KOIN - Koinos +660 PIRATE - PirateCash +661 UNQ - Unique +663 SFRX - EtherGem Sapphire +666 ACT - Achain +667 PRKL - Perkle +668 SSC - SelfSell +669 GC - GateChain +670 PLGR - Pledger +671 MPLGR - Pledger +672 KNOX - Knox +673 ZED - ZED +674 CNDL - Candle +675 WLKR - Walker Crypto Innovation Index +676 WLKRR - Walker +677 YUNGE - Yunge +678 Voken - Voken +679 APL - Apollo +680 Evrynet - Evrynet +681 NENG - Nengcoin +682 CHTA - Cheetahcoin +683 ALEO - Aleo Network +685 OAS - Oasys +686 KAR - Karura Network +688 CET - CoinEx Chain +690 KLV - KleverChain +694 VTBC - VTB Community +698 VEIL - Veil +699 GTB - GotaBit +700 XDAI - xDai +701 COM - Commercio +702 CCC - Commercio Cash Credit +707 MCOIN - Moneta Coin +710 FURY - Highbury +711 CHC - Chaincoin +712 SERF - Serfnet +713 XTL - Katal Chain +715 SIN - Sinovate +716 DLN - Delion +717 BONTE - Bontecoin +718 PEER - Peer +719 ZET - Zetacoin +720 ABY - Artbyte +721 PGX - Mirai Chain +722 IL8P - InfiniLooP +724 XVC - Vanillacash +725 MCX - MultiCash +727 BLU - BluCrates +730 HEALIOS - Tenacity +731 BMK - Bitmark +734 DENTX - DENTNet +737 ATOP - Financial Blockchain +747 CFG - Centrifuge +750 XPRT - Persistence +753 --- - Age X25519 Encryption +754 --- - Age NIST Encryption +757 HONEY - HoneyWood +768 BALLZ - Ballzcoin +770 COSA - Cosanta +771 BR - BR +775 PLSR - Pulsar Coin +776 KEY - Keymaker Coin +777 BTW - Bitcoin World +780 PLCUC - PLC Ultima Classic +781 PLCUX - PLC Ultima X +782 PLCU - PLC Ultima +783 SMARTBC - SMART Blockchain +786 UIDD - UIDD +787 ACA - Acala +788 BNC - Bifrost +789 TAU - Lamden +799 PDEX - Polkadex +800 BEET - Beetle Coin +801 DST - DSTRA +802 CY - Cyberyen +804 ZKS - zkSync +808 QVT - Qvolta +809 SDN - Shiden Network +810 ASTR - Astar Network +811 --- - reserved +813 MEER - Qitmeer +819 REEF - Reef +820 CLO - Callisto +822 BDB - BigchainDB +827 ACE - Endurance +828 CCN - ComputeCoin +829 BBA - BBACHAIN +831 CRUZ - cruzbit +832 SAPP - Sapphire +833 777 - Jackpot +834 KYAN - Kyanite +835 AZR - Azzure +836 CFL - CryptoFlow +837 DASHD - Dash Diamond +838 TRTT - Trittium +839 UCR - Ultra Clear +840 PNY - Peony +841 BECN - Beacon +842 MONK - Monk +843 SAGA - CryptoSaga +844 SUV - Suvereno +845 ESK - EskaCoin +846 OWO - OneWorld Coin +847 PEPS - PEPS Coin +848 BIR - Birake +849 MOBIC - MobilityCoin +850 FLS - Flits +852 DSM - Desmos +853 PRCY - PRCY Coin +858 HVH - HAVAH +866 MOB - MobileCoin +868 IF - Infinitefuture +877 NAM - Namada +878 SCR - Scorum Network +880 LUM - Lum Network +883 ZBC - ZooBC +886 ADF - AD Token +889 TOMO - TOMO +890 XSEL - Seln +896 LKSC - LKSCoin +898 AS - Assetchain +899 XEC - eCash +900 LMO - Lumeneo +901 NXT - NxtMeta +904 HNT - Helium +907 FIS - StaFi +909 SGE - Saage +911 GERT - Gert +913 VARA - Vara Network +916 META - Metadium +917 FRA - Findora +919 CCD - Concordium +921 AVN - Avian Network +925 DIP - Dipper Network +928 GHM - HermitMatrixNetwork +931 RUNE - THORChain (RUNE) +941 --- - reserved +945 UNLOCK - Jasiri protocol +955 LTP - LifetionCoin +958 --- - KickSoccer +960 VKAX - Vkax +966 MATIC - Matic +968 UNW - UNW +970 TWINS - TWINS +977 TLOS - Telos +981 TAFECO - Taf ECO Chain +985 AU - Autonomy +987 VCG - VipCoin +988 XAZAB - Xazab core +989 AIOZ - AIOZ +990 CORE - Coreum +991 PEC - Phoenix +992 UNT - Unit +993 XRB - X Currency +994 QUAI - Quai Network +995 CAPS - Ternoa +997 SUM - Solidum +998 LBTC - Lightning Bitcoin +999 BCD - Bitcoin Diamond +1000 BTN - Bitcoin New +1001 TT - ThunderCore +1002 BKT - BanKitt +1003 NODL - Nodle +1004 PCOIN - PCOIN +1005 TAO - Bittensor +1006 HSK - HashKey Chain +1007 FTM - Fantom +1008 RPG - RPG +1009 LAKE - iconLake +1010 HT - Huobi ECO Chain +1011 ELV - Eluvio +1012 JOC - Japan Open Chain +1013 BIC - Beincrypto +1016 --- - reserved +1020 EVC - Evrice +1022 XRD - Radix DLT +1025 CZZ - Classzz +1026 KEX - Kira Exchange Token +1027 MCM - Mochimo +1028 PLS - Pulse Coin +1032 BTCR - BTCR +1042 MFID - Moonfish ID +1111 BBC - Big Bitcoin +1116 CORE - Core +1120 RISE - RISE +1122 CMT - CyberMiles Token +1128 ETSC - Ethereum Social +1129 DFI - DeFiChain +1130 DFI - DeFiChain EVM Network +1137 $DAG - Constellation Labs +1145 CDY - Bitcoin Candy +1155 ENJ - Enjin Coin +1170 HOO - Hoo Smart Chain +1234 ALPH - Alephium +1236 --- - Masca +1237 --- - Nostr +1280 --- - Kudos Setler +1284 GLMR - Moonbeam +1285 MOVR - Moonriver +1298 WPC - Wpc +1308 WEI - WEI +1337 DFC - Defcoin +1348 ISLM - IslamicCoin +1397 HYC - Hycon +1410 TENTSLP - TENT Simple Ledger Protocol +1510 XSC - XT Smart Chain +1512 AAC - Double-A Chain +1524 --- - Taler +1533 BEAM - Beam +1551 SDK - Sovereign SDK +1555 APC - Apc Chain +1616 ELF - AELF +1618 AUDL - AUDL +1620 ATH - Atheios +1627 LUME - Lume Web +1642 NEW - Newton +1657 BTA - Btachain +1668 NEOX - Neoxa +1669 MEWC - Meowcoin +1688 BCX - BitcoinX +1776 LBTC - Liquid BTC +1777 BBP - Biblepay +1784 JPYS - JPY Stablecoin +1789 VEGA - Vega Protocol +1818 CUBE - Cube Chain Native Token +1856 TES - Teslacoin +1888 ZTX - Zetrix +1899 XEC - eCash token +1901 CLC - Classica +1907 BITCI - Bitcicoin +1919 VIPS - VIPSTARCOIN +1926 CITY - City Coin +1955 XX - xx coin +1977 XMX - Xuma +1984 TRTL - TurtleCoin +1985 SLRT - Solarti Chain +1986 QTH - Qing Tong Horizon +1987 EGEM - EtherGem +1988 MIRA - Mira Chain +1989 HODL - HOdlcoin +1990 PHL - Placeholders +1991 SC - Sia +1996 MYT - Mineyourtime +1997 POLIS - Polis +1998 XMCC - Monoeci +1999 COLX - ColossusXT +2000 GIN - GinCoin +2001 MNP - MNPCoin +2002 MLN - Miraland +2017 KIN - Kin +2018 EOSC - EOSClassic +2019 GBT - GoldBean Token +2020 PKC - PKC +2021 SKT - Sukhavati +2022 XHT - Xinghuo Token +2023 COC - Chat On Chain +2024 USBC - Universal Ledger USBC +2046 ANY - Any +2048 MCASH - MCashChain +2049 TRUE - TrueChain +2050 MOVO - Movo Smart Chain +2086 KILT - KILT Spiritnet +2109 SAMA - Exosama Network +2112 IoTE - IoTE +2125 BAY - BitBay +2137 XRG - Ergon +2182 CHZ - Chiliz +2199 SAMA - Moonsama Network +2221 ASK - ASK +2222 CWEB - Coinweb +2285 --- - Qiyi Chain +2301 QTUM - QTUM +2302 ETP - Metaverse +2303 GXC - GXChain +2304 CRP - CranePay +2305 ELA - Elastos +2338 SNOW - Snowblossom +2365 XIN - Mixin +2500 NEXI - Nexi +2570 AOA - Aurora +2718 NAS - Nebulas +2894 REOSC - REOSC Ecosystem +2941 BND - Blocknode +3000 SM - Stealth Message +3003 LUX - LUX +3030 HBAR - Hedera HBAR +3077 COS - Contentos +3276 CCC - CodeChain +3333 SXP - Solar +3377 ROI - ROIcoin +3381 DYN - Dynamic +3383 SEQ - Sequence +3552 DEO - Destocoin +3564 DST - DeStream +3601 CY - Cybits +3757 MPC - Partisia Blockchain +4040 FC8 - FCH Network +4096 YEE - YeeCo +4218 IOTA - IOTA +4219 SMR - Shimmer +4242 AXE - Axe +4343 XYM - Symbol +4444 C4E - Chain4Energy +4919 XVM - Venidium +4999 BXN - BlackFort Exchange Network +5006 SBC - Senior Blockchain +5248 FIC - FIC +5353 HNS - Handshake +5404 ISK - ISKRA +5467 ALTME - ALTME +5555 FUND - Unification +5757 STX - Stacks +5895 VOW - VowChain VOW +5920 SLU - SILUBIUM +6060 GO - GoChain GO +6174 MOI - My Own Internet +6532 UM - Penumbra +6599 RSC - Royal Sports City +6666 BPA - Bitcoin Pizza +6688 SAFE - SAFE +6779 COTI - COTI +6969 ROGER - TheHolyrogerCoin +7027 ELLA - Ella the heart +7028 AA - Arthera +7091 TOPL - Topl +7331 KLY - KLYNTAR +7341 SHFT - Shyft +7518 MEV - MEVerse +7576 ADIL - ADIL Chain +7777 BTV - Bitvote +8000 SKY - Skycoin +8080 --- - DSRV +8181 BOC - BeOne Chain +8192 PAC - pacprotocol +8217 KLAY - KLAY +8339 BTQ - BitcoinQuark +8444 XCH - Chia +8520 --- - reserved +8680 PLMNT - Planetmint +8866 GGX - Golden Gate +8886 GGXT - Golden Gate Sydney +8888 SBTC - Super Bitcoin +8964 NULS - NULS +8997 BBC - Babacoin +8998 JGC - JagoanCoin +8999 BTP - Bitcoin Pay +9001 ARB1 - Arbitrum +9002 BOBA - Boba +9003 LOOP - Loopring +9004 STRK - StarkNet +9005 AVAXC - Avalanche C-Chain +9006 BSC - Binance Smart Chain +9797 NRG - Energi +9888 BTF - Bitcoin Faith +9999 GOD - Bitcoin God +10000 FO - FIBOS +10111 DHP - dHealth +10226 RTM - Raptoreum +10291 XRC - XRhodium +10507 NUM - Numbers Protocol +10605 XPI - Lotus +11111 ESS - Essentia One +11742 VARCH - InvArch +11743 TNKR - Tinkernet +12345 IPOS - IPOS +12586 MINA - Mina +13107 BTY - BitYuan +13108 YCC - Yuan Chain Coin +14001 WAX - Worldwide Asset Exchange +15845 SDGO - SanDeGo +16181 XTX - Totem Live Network +16754 ARDR - Ardor +18000 MTR - Meter +19165 SAFE - Safecoin +19167 FLUX - Flux +19169 RITO - Ritocoin +19788 ML - Mintlayer +20036 XND - ndau +21004 C4EI - c4ei +21888 PAC - Pactus +22504 PWR - PWRcoin +23000 EPIC - Epic Cash +25252 BELL - Bellcoin +25718 CHX - Own +29223 NEXA - Nexa +30001 --- - reserved +31102 ESN - EtherSocial Network +31337 --- - ThePower +33416 TEO - Trust Eth reOrigin +33878 BTCS - Bitcoin Stake +34952 BTT - ByteTrade +37992 FXTC - FixedTradeCoin +39321 AMA - Amabig +42069 FACT - FACT0RN +43028 AXIV - AXIV +49262 EVE - evan +49344 STASH - STASH +61616 TH - TianHe +65536 KETH - Krypton World +69420 GRLC - Garlicoin +70007 GWL - Gewel +77777 ZYN - Wethio +88888 RYO - c0ban +99999 WICC - Waykichain +100500 HOME - HomeCoin +101010 STC - Starcoin +105105 STRAX - Strax +111111 KAS - Kaspa +161803 APTA - Bloqs4Good +200625 AKA - Akroma +200665 GENOM - GENOM +246529 ATS - ARTIS sigma1 +261131 ZAMA - Zama +333332 VALUE - Value Chain +333333 3333 - Pi Value Consensus +424242 X42 - x42 +534352 SCR - Scroll +666666 VITE - Vite +888888 SEA - Second Exchange Alliance +999999 WTC - WaltonChain +1048576 AMAX - Armonia Meta Chain +1171337 ILT - iOlite +1313114 ETHO - Etho Protocol +1313500 XERO - Xerom +1712144 LAX - LAPO +3924011 EPK - EPIK Protocol +4741444 HYD - Hydra Token +5249353 BCO - BitcoinOre +5249354 BHD - BitcoinHD +5264462 PTN - PalletOne +5655640 VLX - Velas +5718350 WAN - Wanchain +5741564 WAVES - Waves +5741565 WEST - Waves Enterprise +6382179 ABC - Abcmint +6517357 CRM - Creamcoin +7171666 BROCK - Bitrock +7562605 SEM - Semux +7567736 ION - ION +7777777 FCT - FirmaChain +7825266 WGR - WGR +7825267 OBSR - OBServer +8163271 AFS - ANFS +11259375 LBR - 0L +15118976 XDS - XDS +61717561 AQUA - Aquachain +88888888 HATCH - Hatch +91927009 kUSD - kUSD +99999996 GENS - GENS +99999997 EQ - EQ +99999998 FLUID - Fluid Chains +99999999 QKC - QuarkChain +608589380 FVDC - ForumCoin +1179993420 --- - Fuel + +""" diff --git a/mmgen/data/version b/mmgen/data/version index 93070951..2591aece 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -14.1.dev3 +14.1.dev4 diff --git a/scripts/create-bip-hd-chain-params.py b/scripts/create-bip-hd-chain-params.py new file mode 100755 index 00000000..6fcff636 --- /dev/null +++ b/scripts/create-bip-hd-chain-params.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +import json + +from bip_utils.slip.slip44 import Slip44 +from bip_utils.bip.conf.bip44.bip44_conf import Bip44Conf +from bip_utils.bip.conf.bip49.bip49_conf import Bip49Conf +from bip_utils.bip.conf.bip84.bip84_conf import Bip84Conf +from bip_utils.bip.conf.bip86.bip86_conf import Bip86Conf + +import script_init +from mmgen.cfg import Config +from mmgen.main import launch + +opts_data = { + 'text': { + 'desc': 'Aggregate bip_lib and SLIP-44 data into a bip_hd chainparams table', + 'usage':'[opts] infile', + 'options': """ +-h, --help Print this help message. +""", + 'notes': """ +source: https://github.com/MetaMask/slip44/blob/main/slip44.json +""" + } +} + +cfg = Config(opts_data=opts_data) + +def curve_clsname_abbr(s): + return { + 'Bip32Slip10Secp256k1': 'x', + 'Bip32Slip10Ed25519': 'edw', + 'Bip32Slip10Ed25519Blake2b': 'blk', + 'Bip32Slip10Nist256p1': 'nist', + 'Bip32KholawEd25519': 'khol', + }.get(s,s) + +fs2 = '{:5} {:2} {:16} {:8} {:8} {:6} {:8} {:8}' +hdr2 = fs2.format('CURVE','NW','ADDR_CLS','VB_PRV','VB_PUB','VB_WIF','VB_ADDR','DFL_PATH') + +dfl_vb_prv = '0488ade4' +dfl_vb_pub = '0488b21e' +dfl_curve = 'secp' +dfl_dfl_path = "0'/0/0" + +def get_bip_utils_data(bipnum,n): + name,v = bip_utils_data[bipnum][n] + #pexit(v.__dict__) + vb_prv = v.m_key_net_ver.m_priv_net_ver.hex() + vb_pub = v.m_key_net_ver.m_pub_net_ver.hex() + ap = v.m_addr_params + return fs2.format( + curve_clsname_abbr(v.m_bip32_cls.__name__), + 'T' if v.m_is_testnet else 'm', + v.m_addr_cls.__name__.removesuffix('AddrEncoder'), + 'x' if vb_prv == dfl_vb_prv else vb_prv, + 'x' if vb_pub == dfl_vb_pub else vb_pub, + v.m_wif_net_ver.hex() if isinstance(v.m_wif_net_ver,bytes) else '-', + ap['net_ver'].hex() if 'net_ver' in ap else 'h:'+ap['hrp'] if 'hrp' in ap else 'spec' if ap else '-', + 'x' if v.m_def_path == dfl_dfl_path else v.m_def_path, + ) + +def gen(): + + def format_data(bipnum,n,sym,name): + return fs.format( + n, + sym if sym else '---', + get_bip_utils_data(bipnum,n) if bipnum else '-', + name if name else '---') + + fs = '{:<6} {:6} {:1} {}' + + yield f'[defaults]' + yield fs.format('IDX','CHAIN',hdr2,'NAME') + yield fs.format('0', '-', fs2.format(dfl_curve, '-', '-', dfl_vb_prv, dfl_vb_pub, '-', '-', dfl_dfl_path), '-') + + yield f'\n[bip-44]' + yield fs.format('IDX','CHAIN',hdr2,'NAME') + for k,v in slip44_data.items(): + if int(k) in bip_utils_data[44]: + yield format_data(44,int(k),v['symbol'],v['name']) + + for bipnum in (49, 84, 86): + yield f'\n[bip-{bipnum}]' + yield fs.format('IDX','CHAIN',hdr2,'NAME') + for n,v in sorted(bip_utils_data[bipnum].items()): + nd = v[1].m_coin_names + yield format_data(bipnum,n,nd.m_abbr,nd.m_name) + + yield f'\n[bip-44-unsupported]' + yield fs.format('IDX','CHAIN','','NAME') + for k,v in slip44_data.items(): + if not int(k) in bip_utils_data[44]: + yield format_data(None,int(k),v['symbol'],v['name']) + +def main(): + + global slip44_data, bip_utils_data + + if len(cfg._args) != 1: + cfg._opts.usage() + + with open(cfg._args[0]) as fh: + slip44_data = json.loads(fh.read()) + + bip_utils_data = { + n:{v.m_coin_idx:(k,v) for k,v in globals()[f'Bip{n}Conf'].__dict__.items() if not k.startswith('_')} + for n in (44, 49, 84, 86) + } + + print('\n'.join(gen())) + +launch(func=main) diff --git a/setup.cfg b/setup.cfg index 71813926..c89e1a79 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,7 +35,7 @@ classifiers = Development Status :: 5 - Production/Stable [options] -python_requires = >=3.8 +python_requires = >=3.9 include_package_data = True install_requires = @@ -53,6 +53,7 @@ install_requires = packages = mmgen mmgen.altcoin + mmgen.bip_hd mmgen.contrib mmgen.data mmgen.help diff --git a/test/cmdtest_py_d/ct_misc.py b/test/cmdtest_py_d/ct_misc.py index 51414453..20d900d4 100755 --- a/test/cmdtest_py_d/ct_misc.py +++ b/test/cmdtest_py_d/ct_misc.py @@ -34,6 +34,7 @@ class CmdTestDev(CmdTestBase): networks = ('btc',) cmd_group = ( ('compute_file_chksum', 'scripts/compute-file-chksum.py'), + ('create_bip_hd_chain_params', 'scripts/create-bip-hd-chain-params.py'), ) tmpdir_nums = [99] color = True @@ -46,6 +47,15 @@ class CmdTestDev(CmdTestBase): t.expect('3df942') return t + def create_bip_hd_chain_params(self): + t = self._spawn('scripts/create-bip-hd-chain-params.py', ['test/ref/altcoin/slip44-mini.json']) + t.expect('[defaults]') + t.expect(r"secp.*0488ade4.*0488b21e.*0'\/0\/0",regex=True) + t.expect('[bip-44]') + t.expect('[bip-49]') + t.match_expect_list(['0','BTC','x','m','P2SH','049d7878','049d7cb2','80','05','x','Bitcoin','1']) + return t + class CmdTestMisc(CmdTestBase): 'miscellaneous tests (RPC backends, xmrwallet_txview, term)' networks = ('btc',) @@ -56,6 +66,7 @@ class CmdTestMisc(CmdTestBase): ('xmrwallet_txview', "'mmgen-xmrwallet' txview"), ('xmrwallet_txlist', "'mmgen-xmrwallet' txlist"), ('coin_daemon_info', "'examples/coin-daemon-info.py'"), + ('examples_bip_hd', "'examples/bip_hd.py'"), ('term_echo', "term.set('echo')"), ('term_cleanup', 'term.register_cleanup()'), ) @@ -87,6 +98,9 @@ class CmdTestMisc(CmdTestBase): def xmrwallet_txlist(self): return self.xmrwallet_txview(op='txlist') + def examples_bip_hd(self): + return self.spawn('examples/bip_hd.py',cmd_dir='.') + def coin_daemon_info(self): if cfg.no_altcoin: coins = ['btc'] diff --git a/test/ref/altcoin/slip44-mini.json b/test/ref/altcoin/slip44-mini.json new file mode 100644 index 00000000..b4c74a93 --- /dev/null +++ b/test/ref/altcoin/slip44-mini.json @@ -0,0 +1 @@ +{"0":{"index":"0","hex":"0x80000000","symbol":"BTC","name":"Bitcoin"},"1":{"index":"1","hex":"0x80000001","symbol":"","name":"Testnet (all coins)"},"2":{"index":"2","hex":"0x80000002","symbol":"LTC","name":"Litecoin"},"3":{"index":"3","hex":"0x80000003","symbol":"DOGE","name":"Dogecoin"},"5":{"index":"5","hex":"0x80000005","symbol":"DASH","name":"Dash"},"7":{"index":"7","hex":"0x80000007","symbol":"NMC","name":"Namecoin"},"99999999":{"index":"99999999","hex":"0x85f5e0ff","symbol":"QKC","name":"QuarkChain"},"608589380":{"index":"608589380","hex":"0xa4465644","symbol":"FVDC","name":"ForumCoin"},"1179993420":{"index":"1179993420","hex":"0xc655454c","symbol":"","name":"Fuel"}} diff --git a/test/unit_tests_d/ut_bip_hd.py b/test/unit_tests_d/ut_bip_hd.py new file mode 100755 index 00000000..de7ddac5 --- /dev/null +++ b/test/unit_tests_d/ut_bip_hd.py @@ -0,0 +1,419 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet +# Copyright (C)2013-2024 The MMGen Project +# Licensed under the GNU General Public License, Version 3: +# https://www.gnu.org/licenses +# Public project repositories: +# https://github.com/mmgen/mmgen-wallet +# https://gitlab.com/mmgen/mmgen-wallet + +""" +test.unit_tests_d.ut_bip_hd: bip_hd unit test for the MMGen suite +""" + +from mmgen.color import gray,pink,blue +from mmgen.util import fmt +from mmgen.bip_hd import Bip32ExtendedKey,BipHDConfig,BipHDNode,MasterNode,get_chain_params + +from ..include.common import cfg,vmsg + +# Source: BIP-32 +vectors_bip32 = [ +{ + 'seed': '000102030405060708090a0b0c0d0e0f', + "m": { + 'xpub': 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8', + 'xprv': 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi', + }, + "m/0'": { + 'xpub': 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw', + 'xprv': 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7', + }, + "m/0'/1": { + 'xpub': 'xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ', + 'xprv': 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs', + }, + "m/0'/1/2'": { + 'xpub': 'xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5', + 'xprv': 'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM', + }, + "m/0'/1/2'/2": { + 'xpub': 'xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV', + 'xprv': 'xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334', + }, + "m/0'/1/2'/2/1000000000": { + 'xpub': 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy', + 'xprv': 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76', + }, +},{ + 'seed': 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542', + 'm': { + 'xpub': 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB', + 'xprv': 'xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U', + }, + "m/0": { + 'xpub': 'xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH', + 'xprv': 'xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt', + }, + "m/0/2147483647'": { + 'xpub': 'xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a', + 'xprv': 'xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9', + }, + "m/0/2147483647'/1": { + 'xpub': 'xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon', + 'xprv': 'xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef', + }, + "m/0/2147483647'/1/2147483646'": { + 'xpub': 'xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL', + 'xprv': 'xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc', + }, + "m/0/2147483647'/1/2147483646'/2": { + 'xpub': 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt', + 'xprv': 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j', + }, +},{ + 'comment': 'These vectors test for the retention of leading zeros. See bitpay/bitcore-lib#47 and iancoleman/bip39#58 for more information.', + 'seed': '4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be', + 'm': { + 'xpub': 'xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13', + 'xprv': 'xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6', + }, + "m/0'": { + 'xpub': 'xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y', + 'xprv': 'xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L', + }, +},{ + 'comment': 'These vectors test for the retention of leading zeros. See btcsuite/btcutil#172 for more information.', + 'seed': '3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678', + "m": { + 'xpub': 'xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa', + 'xprv': 'xprv9s21ZrQH143K48vGoLGRPxgo2JNkJ3J3fqkirQC2zVdk5Dgd5w14S7fRDyHH4dWNHUgkvsvNDCkvAwcSHNAQwhwgNMgZhLtQC63zxwhQmRv', + }, + "m/0'": { + 'xpub': 'xpub69AUMk3qDBi3uW1sXgjCmVjJ2G6WQoYSnNHyzkmdCHEhSZ4tBok37xfFEqHd2AddP56Tqp4o56AePAgCjYdvpW2PU2jbUPFKsav5ut6Ch1m', + 'xprv': 'xprv9vB7xEWwNp9kh1wQRfCCQMnZUEG21LpbR9NPCNN1dwhiZkjjeGRnaALmPXCX7SgjFTiCTT6bXes17boXtjq3xLpcDjzEuGLQBM5ohqkao9G', + }, + "m/0'/1'": { + 'xpub': 'xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt', + 'xprv': 'xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1', + }, +}] + +# Source: BIP-32 +# These vectors test that invalid extended keys are recognized as invalid. +vectors_bip32_invalid = [ + ('xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm', 'pubkey version / prvkey mismatch'), + ('xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGTQQD3dC4H2D5GBj7vWvSQaaBv5cxi9gafk7NF3pnBju6dwKvH', 'prvkey version / pubkey mismatch'), + ('xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Txnt3siSujt9RCVYsx4qHZGc62TG4McvMGcAUjeuwZdduYEvFn', 'invalid pubkey prefix 04'), + ('xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGpWnsj83BHtEy5Zt8CcDr1UiRXuWCmTQLxEK9vbz5gPstX92JQ', 'invalid prvkey prefix 04'), + ('xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6N8ZMMXctdiCjxTNq964yKkwrkBJJwpzZS4HS2fxvyYUA4q2Xe4', 'invalid pubkey prefix 01'), + ('xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fEQ3Qen6J', 'invalid prvkey prefix 01'), + ('xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv', 'zero depth with non-zero parent fingerprint'), + ('xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ', 'zero depth with non-zero parent fingerprint'), + ('xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN', 'zero depth with non-zero index'), + ('xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8', 'zero depth with non-zero index'), + ('DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHGMQzT7ayAmfo4z3gY5KfbrZWZ6St24UVf2Qgo6oujFktLHdHY4', 'unknown extended key version'), + ('DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHPmHJiEDXkTiJTVV9rHEBUem2mwVbbNfvT2MTcAqj3nesx8uBf9', 'unknown extended key version'), + ('xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx', 'private key 0 not in 1..n-1'), + ('xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD5SDKr24z3aiUvKr9bJpdrcLg1y3G', 'private key n not in 1..n-1'), + ('xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY', 'invalid pubkey 02000000...07'), + ('xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL', 'invalid checksum'), +] + +# Source: bip_utils +vectors_derive = { + 'bech32': { + 0: 'bc1qwg77fxw0tkmc3h58tcnnpegxk7mp3h6ly44d3n', + 1: 'bc1q6g79y6kwpkufevv2njacvnqnsdxmen68jyvjde', + 2: 'bc1qknujpwlxc9e9e6avz50q5k90p552xy8g3qjd8u', + } +} + +# Source: bip_utils +vectors_addrfmt = { + 'pub': { + 'compressed': 'xpub6GJknXsmpFcEubJsddGacncHhyY5Bk9zMQKC8pC97vBVAphrchYxuoJsAqfZW2uEMfPr6umSPRrhuaA7zeuExkwuAWiUcKcXjSf437VMLwR', + 'segwit': 'ypub6aNved9dRKbMRjLfMbGPoXKgNN1tr86qi8WoBxSVr4mHX9fdzUxJFtEH63QZZxArk4f2fwFZQUQ7FRkNiBarTLu2Y69SRxzn68WysngXPrp', + 'bech32': 'zpub6urFg31yVogtpf3Y7aV3CLuxQUsEpZ2asqBx3fzYMuFRTFrMNKxn5B2QXAGMSuwVfEA9KSJr2CUs8vqbmhUCkzVvxysB4p6vybLS2CgQnze', + }, + 'prv': { + 'compressed': 'xprvA3KQP2Lsyt3wh7EQXbjaFefZ9whanHS8zBPbLRnXZaeWJ2Ni5AEiMzzPKbKHsG7Dn6hkhSkG8V4H4XUsjszxU4nd2sMnc5ag9sHLLYBqrr4', + 'segwit': 'yprvAMPaF7cjax34DFGCFZjPSPNwpLBQSfNzLubCPa2tHjEJeMLVSwe3i5uoEnUg4etxP3XEqr5ZJinjGkUJrte3xFNZ1jVKbjVaFJVHi4Msekw', + 'bech32': 'zprvAgruGXV5fS8bcAy51Yx2qCyDrT2kR6JjWcGMFHavoZiSaTXCpneXXNhvfsMLeyBmjzACqkpxB2KGCMAe85wUrW1dnenu6kVHCk4kXh7XFE6', + } +} + +# Source: Asgardex Wallet +vectors_multicoin = { + 'btc_bech32': 'bc1qwg77fxw0tkmc3h58tcnnpegxk7mp3h6ly44d3n', + 'eth': '373731f4d885Fc7Da05498F9f0804a87A14F891b', + 'doge': 'DFX88RXpi4S4W24YVvuMgbdUcCAYNeEYGd', + 'avax-c': '0x373731f4d885Fc7Da05498F9f0804a87A14F891b', + 'ltc_bech32': 'ltc1q3uh5ga5cp9kkdfx6a52uymxj9keq4tpzep7er0', + 'bch_cashaddr': 'bitcoincash:qpqpcllprftg4s0chdgkpxhxv23wfymq3gj7n0a9vw', + 'bsc_smart': '0x373731f4d885Fc7Da05498F9f0804a87A14F891b', + 'bnb_beacon': 'bnb179c3ymltqm4utlp089zxqeta5dvn48a305rhe5', +} + +def wif2addr(cfg,wif): + from mmgen.tool.coin import tool_cmd + return tool_cmd( + cfg = cfg.base_cfg, + cmdname = 'wif2addr', + proto = cfg.base_cfg._proto, + mmtype = cfg.addr_type).wif2addr(wif) + +class unit_tests: + + altcoin_deps = ('multicoin',) + + @property + def _seed(self): + if not hasattr(self,'__seed'): + with open('test/ref/98831F3A.bip39') as fh: + mnemonic = fh.read().strip() + from mmgen.bip39 import bip39 + self.__seed = bip39().generate_seed(mnemonic.split()) + return self.__seed + + def chainparams(self,name,ut): + for bipnum,idx,chain,addr_cls in ( + (44, 0, 'btc', 'P2PKH'), + (49, 0, 'btc', 'P2SH'), + (84, 0, 'btc', 'P2WPKH'), + (44, 60, 'eth', 'Eth'), + (44, 61, 'etc', 'Eth'), + (44, 2, 'ltc', 'P2PKH'), + (44, 3, 'doge', 'P2PKH'), + ): + res = get_chain_params(bipnum,chain) + assert res.idx == idx, res.idx + assert res.chain == chain.upper() + assert res.addr_cls == addr_cls + vmsg(f' {res}') + vmsg('') + return True + + def derive(self,name,ut): + vmsg('seed: 98831F3A (default derivation)') + + m = MasterNode(cfg,self._seed) + + purpose = m.init_cfg(coin='btc',addr_type='bech32').derive_private() + vmsg(f' {purpose.address=}') + + coin_type1 = purpose.derive_private() + + coin_type2 = m.to_coin_type('btc',addr_type='bech32') + assert coin_type1.address == coin_type2.address + vmsg(f' {coin_type1.address=}') + + acct = coin_type2.derive_private(idx=0) + chain1 = acct.derive_private(idx=0,hardened=False) + + chain2 = m.to_chain(idx=0,coin='btc',addr_type='bech32',public=False) + assert chain2.address == chain1.address + + chain3 = m.to_coin_type(coin='btc',addr_type='bech32').to_chain(0,public=True) + assert chain3.address == chain1.address + vmsg(f' {chain1.address=}') + + a = BipHDNode.from_extended_key(cfg,'btc',chain2.xpub) + b = BipHDNode.from_extended_key(cfg,'btc',chain2.xprv) + vmsg( + '\n xpub:\n' + + fmt(str(Bip32ExtendedKey(b.xpub)),indent=' ') + ) + assert a.xpub == b.xpub + + vmsg(' Addresses:') + for i in range(3): + res = chain1.derive_public(i) + vmsg(f' {i} {res.address}') + assert res.address == vectors_derive['bech32'][i] + res = chain1.derive_private(i) + assert res.address == vectors_derive['bech32'][i] + + vmsg('') + return True + + def derive_addrfmt(self,name,ut): + vmsg('seed: 98831F3A (default derivation)') + + m = MasterNode(cfg,self._seed) + + for addr_type in ('compressed','segwit','bech32'): + chk_xpub = vectors_addrfmt['pub'][addr_type] + chk_xprv = vectors_addrfmt['prv'][addr_type] + + res1 = m.to_chain(idx=0,coin='btc',addr_type=addr_type).derive_public(0) + vmsg(f' {addr_type}: {res1.xpub}') + assert res1.xpub == chk_xpub + + res2 = m.to_chain(idx=0,coin='btc',addr_type=addr_type).derive_private(0,False) + vmsg(f' {addr_type}: {res2.xprv}') + assert res2.xprv == chk_xprv + assert res2.xpub == chk_xpub + + assert res2.address == wif2addr(res2.cfg, res2.privkey.wif) + + vmsg('') + return True + + def path(self,name,ut): + + for vec in vectors_bip32: + seed = bytes.fromhex(vec['seed']) + vmsg(f'Seed: {vec["seed"]}') + + for n,path_str in enumerate(vec): + if path_str in ('seed','comment'): + continue + + path_arg = path_str.replace("'",'H') if n % 2 else path_str + node = BipHDNode.from_path(cfg,seed,path_arg,no_path_checks=True) + vmsg(' Path {} {}'.format(pink(path_str),blue('('+node.desc+')'))) + + for xkey_type in ('xpub','xprv'): + vmsg(f' {getattr(node,xkey_type)}') + assert getattr(node,xkey_type) == vec[path_str][xkey_type] + + vmsg('') + + return True + + def parse_extended(self,name,ut): + vmsg('Parsing and validating extended keys:\n') + + for vec in vectors_bip32: + seed = bytes.fromhex(vec['seed']) + vmsg(f' Seed: {vec["seed"]}') + + for n,path_str in enumerate(vec): + if path_str in ('seed','comment'): + continue + + vmsg(' Path {}'.format(pink(path_str))) + for xkey_type in ('xpub','xprv'): + xkey = vec[path_str][xkey_type] + vmsg(f' {xkey}') + node = BipHDNode.from_extended_key(cfg,'btc',xkey) + assert getattr(node,xkey_type) == xkey + + vmsg('') + + return True + + def multicoin(self,name,ut): + m = MasterNode(cfg,self._seed) + + fs = ' {:6} {:10} {}' + vmsg(fs.format('COIN','ADDR_TYPE','ADDR')) + for id_str,addr_chk in vectors_multicoin.items(): + ss = id_str.split('_') + coin = ss[0] + addr_type = ss[1] if len(ss) == 2 else None + if coin not in BipHDConfig.supported_coins: + vmsg(gray(fs.format(coin.upper(), (addr_type or ''), '[not supported yet]'))) + continue + node = m.to_chain(idx=0,coin=coin,addr_type=addr_type).derive_private(0) + xpub_parsed = node.key_extended(public=True) + xprv_parsed = node.key_extended(public=False) + addr = node.address + at_arg = 'compressed' if coin == 'doge' else None + from_xpub = BipHDNode.from_extended_key(node.cfg.base_cfg, coin, xpub_parsed.base58, addr_type=at_arg) + from_xprv = BipHDNode.from_extended_key(node.cfg.base_cfg, coin, xprv_parsed.base58, addr_type=at_arg) + assert from_xpub.xpub == node.xpub, f'{from_xpub.xpub=} != {node.xpub}' + assert from_xprv.xpub == node.xpub, f'{from_xprv.xpub=} != {node.xpub}' + assert from_xpub.address == addr, f'{from_xpub.address} != {addr}' + assert from_xprv.address == addr, f'{from_xprv.address} != {addr}' + addr_from_wif = wif2addr(node.cfg, node.privkey.wif) + proto = node.cfg.base_cfg._proto + if proto.base_proto == 'Ethereum': + addr = proto.checksummed_addr(node.address) + addr_from_wif = proto.checksummed_addr(addr_from_wif) + vmsg(fs.format(coin.upper(), (addr_type or 'auto'), addr)) + assert addr == addr_chk, f'{addr} != {addr_chk}' + assert addr == addr_from_wif, f'{addr} != {addr_from_wif}' + + vmsg('') + return True + + def errors(self,name,ut): + vmsg('Checking error handling:') + + m = MasterNode(cfg,self._seed) + m_btc = m.init_cfg(coin='btc',addr_type='bech32') + + purpose = m_btc.derive_private() + coin_type = purpose.derive_private() + acct = coin_type.derive_private(idx=0) + chain = acct.derive_private(idx=0,hardened=False) + + def bad01(): + m.to_chain(idx=0,coin='erq',addr_type='C') + def bad02(): + m_btc.derive_private(idx=0) + def bad03(): + m_btc.derive_private(hardened=False) + def bad04(): + purpose.derive_private(idx=8) + def bad05(): + purpose.derive_private(hardened=False) + def bad06(): + coin_type.derive_private() # no acct idx + def bad08(): + m_btc.derive_public() # must be private + def bad09(): + coin_type.derive_private(idx=8,hardened=False) + def bad10(): + acct.derive_private() + def bad11(): + chain.derive_private() + def bad12(): + chain.derive_private(hardened=True,idx=3) + + bad_data = ( + ('unsupported coin', 'ValueError', 'not supported', bad01), + ('depth 1 (purpose): idx not None', 'ValueError', 'index for path comp', bad02), + ('depth 1 (purpose): hardened False', 'ValueError', 'value for ‘hardened’', bad03), + ('depth 2 (coin type): idx mismatch', 'ValueError', 'index 8 at depth', bad04), + ('depth 2 (coin type): hardened False', 'ValueError', 'value for ‘hardened’', bad05), + ('depth 3 (account): idx not set', 'ValueError', 'must be set', bad06), + ('depth 1 (purpose): node not hardened', 'ValueError', 'must be hardened', bad08), + ('depth 3 (account): node not hardened', 'ValueError', 'value for ‘hardened’', bad09), + ('depth 4 (chain): idx not set', 'ValueError', 'must be either 0', bad10), + ('depth 5 (leaf node): idx not set', 'ValueError', 'must be set', bad11), + ('depth 5 (leaf node): hardened True', 'ValueError', 'must be None', bad12), + ) + + ut.process_bad_data(bad_data,pfx='') + vmsg('') + return True + + def parse_extended_errors(self,name,ut): + vmsg('Parsing and validating invalid extended keys:') + vec = vectors_bip32_invalid + func = [lambda m=n: BipHDNode.from_extended_key(cfg,'btc',vec[m][0]) for n in range(len(vec))] + exc = ( + 'first byte for public', + 'first byte for private', + 'first byte for public', + 'first byte for private', + 'first byte for public', + 'first byte for private', + 'non-zero parent fingerprint', + 'non-zero parent fingerprint', + 'non-zero index', + 'non-zero index', + 'unrecognized extended key v', + 'unrecognized extended key v', + 'private key is zero!', + 'private key >= group order!', + 'Public key could not be parsed', # extmod + 'incorrect checksum', + ) + ut.process_bad_data([(vec[n][1], 'ValueError', exc[n], func[n]) for n in range(len(vec))],pfx='') + vmsg('') + return True