From 8edc7da5a2825f1540df2d70b2a2f17b98958eb2 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 29 Sep 2024 11:59:57 +0000 Subject: [PATCH] BCH cashaddr: full support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BCH addresses are now displayed in cashaddr format by default. This may be overridden on the command line with --cashaddr=0, or in the config file by setting `bch_cashaddr` to false - In tracking wallet views, the ‘h’ key toggles between legacy and cashaddr address display - Transaction views display BCH addresses in both formats simultaneously - The --usecashaddr=0 daemon option is no longer required and should be omitted Testing: $ test/unit_tests.py -v cashaddr bip_hd.multicoin $ test/gentest.py --coin=bch -v --type=C 1 test/ref/bitcoin_cash/bchwallet.dump $ test/cmdtest.py -e bch_txview_cashaddr1 bch_txview_cashaddr2 $ test/cmdtest.py --coin=bch -e txsend regtest.view autosign_automount $ test/cmdtest.py --coin=bch -n ref3_addr --- mmgen/addr.py | 11 +++- mmgen/addrfile.py | 4 +- mmgen/addrlist.py | 2 +- mmgen/bip_hd/__init__.py | 2 +- mmgen/cfg.py | 4 ++ mmgen/data/mmgen.cfg | 3 + mmgen/data/version | 2 +- mmgen/opts.py | 1 + mmgen/proto/bch/params.py | 50 ++++++++++++++++ mmgen/proto/btc/daemon.py | 1 - mmgen/proto/btc/tw/addresses.py | 3 + mmgen/proto/btc/tw/prune.py | 3 + mmgen/proto/btc/tw/txhistory.py | 3 + mmgen/proto/btc/tw/unspent.py | 3 + mmgen/proto/btc/tx/info.py | 15 ++++- mmgen/protocol.py | 1 + mmgen/tw/view.py | 8 +++ test/cmdtest_py_d/ct_base.py | 6 +- test/cmdtest_py_d/ct_misc.py | 25 ++++++++ test/cmdtest_py_d/ct_ref_3seed.py | 24 +++++++- test/cmdtest_py_d/ct_regtest.py | 60 +++++++++++++++++-- test/gentest.py | 2 +- .../bitcoin_cash/895108-BCH[2.65913].rawtx | 5 ++ test/ref/bitcoin_cash/bchwallet-testnet.dump | 57 ++++++++++++++++++ test/ref/bitcoin_cash/bchwallet.dump | 57 ++++++++++++++++++ test/scrambletest.py | 3 +- test/test-release.d/cfg.sh | 13 +++- test/unit_tests_d/ut_bip_hd.py | 2 +- test/unit_tests_d/ut_cashaddr.py | 52 +++++++++++++++- 29 files changed, 397 insertions(+), 25 deletions(-) create mode 100644 test/ref/bitcoin_cash/895108-BCH[2.65913].rawtx create mode 100644 test/ref/bitcoin_cash/bchwallet-testnet.dump create mode 100644 test/ref/bitcoin_cash/bchwallet.dump diff --git a/mmgen/addr.py b/mmgen/addr.py index 96a6cc61..a3349950 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -157,9 +157,14 @@ class CoinAddr(HiliteStr, InitErrors, MMGenObject): try: ap = proto.decode_addr(addr) assert ap, f'coin address {addr!r} could not be parsed' - me = str.__new__(cls, addr) - me.views = [addr] - me.view_pref = 0 + if hasattr(ap, 'addr'): + me = str.__new__(cls, ap.addr) + me.views = ap.views + me.view_pref = ap.view_pref + else: + me = str.__new__(cls, addr) + me.views = [addr] + me.view_pref = 0 me.addr_fmt = ap.fmt me.bytes = ap.bytes me.ver_bytes = ap.ver_bytes diff --git a/mmgen/addrfile.py b/mmgen/addrfile.py index 4f6d1a1e..8a6be44f 100755 --- a/mmgen/addrfile.py +++ b/mmgen/addrfile.py @@ -86,7 +86,7 @@ class AddrFile(MMGenObject): coin = proto.coin mmtype = self.parent.al_id.mmtype lbl_p2 = ':'.join( - ([] if coin in ('BTC', 'BCH') else [coin]) + ([] if coin == 'BTC' or (coin == 'BCH' and not self.cfg.cashaddr) else [coin]) + ([] if mmtype == 'E' or (mmtype == 'L' and not proto.testnet) else [mmtype.name.upper()]) + ([proto.network.upper()] if proto.testnet else []) ) @@ -206,6 +206,8 @@ class AddrFile(MMGenObject): """ label examples: - Bitcoin legacy mainnet: no label + - BCH legacy mainnet (no cashaddr): no label + - BCH legacy mainnet (cashaddr): 'BCH' - Bitcoin legacy testnet: 'LEGACY:TESTNET' - Bitcoin Segwit: 'SEGWIT' - Bitcoin Segwit testnet: 'SEGWIT:TESTNET' diff --git a/mmgen/addrlist.py b/mmgen/addrlist.py index d11e9431..25f6d37e 100755 --- a/mmgen/addrlist.py +++ b/mmgen/addrlist.py @@ -122,7 +122,7 @@ class AddrListIDStr(HiliteStr): ret = fmt_str.format(s) else: proto = addrlist.proto - coin = 'BTC' if proto.coin == 'BCH' else proto.coin + coin = 'BTC' if proto.coin == 'BCH' and not addrlist.cfg.cashaddr else proto.coin mmtype = addrlist.al_id.mmtype ret = '{}{}{}[{}]'.format( addrlist.al_id.sid, diff --git a/mmgen/bip_hd/__init__.py b/mmgen/bip_hd/__init__.py index 723adb6e..fe464978 100644 --- a/mmgen/bip_hd/__init__.py +++ b/mmgen/bip_hd/__init__.py @@ -136,7 +136,7 @@ def check_privkey(key_int): class BipHDConfig(Lockable): - supported_coins = ('btc', 'eth', 'doge', 'ltc') + supported_coins = ('btc', 'eth', 'doge', 'ltc', 'bch') def __init__(self, base_cfg, coin, network, addr_type, from_path, no_path_checks): diff --git a/mmgen/cfg.py b/mmgen/cfg.py index b3e67f91..84ab06f1 100755 --- a/mmgen/cfg.py +++ b/mmgen/cfg.py @@ -195,6 +195,9 @@ class Config(Lockable): carol = False regtest_user = '' + # altcoin: + cashaddr = True + # Monero: monero_wallet_rpc_user = 'monero' monero_wallet_rpc_password = '' @@ -273,6 +276,7 @@ class Config(Lockable): 'subseeds', 'testnet', 'usr_randchars', + 'bch_cashaddr', 'bch_max_tx_fee', 'btc_max_tx_fee', 'eth_max_tx_fee', diff --git a/mmgen/data/mmgen.cfg b/mmgen/data/mmgen.cfg index b613ef77..5e20a101 100644 --- a/mmgen/data/mmgen.cfg +++ b/mmgen/data/mmgen.cfg @@ -115,6 +115,9 @@ ## Altcoin options ## ##################### +# Set this to false to prefer legacy BCH address format: +# bch_cashaddr true + # Set the maximum transaction fee for BCH: # bch_max_tx_fee 0.1 diff --git a/mmgen/data/version b/mmgen/data/version index 94188a74..0fe77a34 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -15.0.0 +15.1.dev1 diff --git a/mmgen/opts.py b/mmgen/opts.py index 2378542a..0356dafa 100755 --- a/mmgen/opts.py +++ b/mmgen/opts.py @@ -42,6 +42,7 @@ long_opts_data = { --, --accept-defaults Accept defaults at all prompts --, --coin=c Choose coin unit. Default: BTC. Current choice: {cu_dfl} --, --token=t Specify an ERC20 token by address or symbol +--, --cashaddr=0|1 Display BCH addresses in cashaddr format (default: 1) --, --color=0|1 Disable or enable color output (default: 1) --, --columns=N Force N columns of output with certain commands --, --scroll Use the curses-like scrolling interface for diff --git a/mmgen/proto/bch/params.py b/mmgen/proto/bch/params.py index f9b4e350..d2a90718 100755 --- a/mmgen/proto/bch/params.py +++ b/mmgen/proto/bch/params.py @@ -12,7 +12,11 @@ proto.bch.params: Bitcoin Cash protocol """ +from ...protocol import decoded_addr_multiview +from ...addr import CoinAddr from ..btc.params import mainnet, _finfo +from ..btc.common import b58chk_decode, b58chk_encode +from .cashaddr import cashaddr_decode_addr, cashaddr_encode_addr, cashaddr_addr_types class mainnet(mainnet): is_fork_of = 'Bitcoin' @@ -25,6 +29,50 @@ class mainnet(mainnet): coin_amt = 'BCHAmt' max_tx_fee = '0.1' ignore_daemon_version = False + cashaddr_pfx = 'bitcoincash' + cashaddr = True + + def decode_addr(self, addr): + if len(addr) >= 42: # cashaddr + if addr.islower(): + pass + elif addr.isupper(): + addr = addr.lower() + else: + raise ValueError(f'{addr}: address has mixed case!') + if ':' in addr: + assert addr.startswith(self.cashaddr_pfx), f'{addr}: address has invalid prefix!' + else: + addr = f'{self.cashaddr_pfx}:{addr}' + dec = cashaddr_decode_addr(addr) + ver_bytes = self.addr_fmt_to_ver_bytes[dec.addr_type] + return decoded_addr_multiview( + dec.bytes, + ver_bytes, + dec.addr_type, + addr, + [dec.payload, b58chk_encode(ver_bytes+dec.bytes)] if len(dec.bytes) == self.addr_len else + [dec.payload], + 0) + else: + dec = self.decode_addr_bytes(b58chk_decode(addr)) + enc = cashaddr_encode_addr( + cashaddr_addr_types[dec.fmt], + len(dec.bytes), + self.cashaddr_pfx, + dec.bytes) + return decoded_addr_multiview(*dec, enc.addr, [enc.payload, addr], 1) + + def pubhash2addr(self, pubhash, addr_type): + return CoinAddr( + self, + cashaddr_encode_addr( + cashaddr_addr_types[addr_type], + len(pubhash), + self.cashaddr_pfx, + pubhash).addr + if self.cfg.cashaddr else + b58chk_encode(self.addr_fmt_to_ver_bytes[addr_type] + pubhash)) def pubhash2redeem_script(self,pubhash): raise NotImplementedError @@ -35,6 +83,8 @@ class mainnet(mainnet): class testnet(mainnet): addr_ver_info = { '6f': 'p2pkh', 'c4': 'p2sh' } wif_ver_num = { 'std': 'ef' } + cashaddr_pfx = 'bchtest' class regtest(testnet): halving_interval = 150 + cashaddr_pfx = 'bchreg' diff --git a/mmgen/proto/btc/daemon.py b/mmgen/proto/btc/daemon.py index d3546c84..8a261cc4 100755 --- a/mmgen/proto/btc/daemon.py +++ b/mmgen/proto/btc/daemon.py @@ -79,7 +79,6 @@ class bitcoin_core_daemon(CoinDaemon): ['--pid='+self.pidfile, self.use_pidfile], ['--daemon', self.platform in ('linux', 'darwin') and not self.opt.no_daemonize], ['--fallbackfee=0.0002', self.coin == 'BTC' and self.network == 'regtest'], - ['--usecashaddr=0', self.coin == 'BCH'], ['--deprecatedrpc=create_bdb', self.coin == 'BTC' and self.opt.bdb_wallet], ['--mempoolreplacement=1', self.coin == 'LTC'], ['--txindex=1', self.coin == 'LTC' or self.network == 'regtest'], diff --git a/mmgen/proto/btc/tw/addresses.py b/mmgen/proto/btc/tw/addresses.py index fd40df1a..1f90cb3d 100755 --- a/mmgen/proto/btc/tw/addresses.py +++ b/mmgen/proto/btc/tw/addresses.py @@ -27,6 +27,9 @@ class BitcoinTwAddresses(TwAddresses,BitcoinTwRPC): 'Filters: show [E]mpty addrs, [u]sed addrs, all [L]abels', 'View/Print: pager [v]iew, [w]ide pager view, [p]rint{s}', 'Actions: [q]uit menu, r[e]draw, add [l]abel:'] + prompt_fs_repl = { + 'BCH': (1, 'Column options: toggle [D]ays/date/confs/block, cas[h]addr') + } key_mappings = { 'a':'s_amt', 'A':'s_age', diff --git a/mmgen/proto/btc/tw/prune.py b/mmgen/proto/btc/tw/prune.py index 4241687e..429a2a2c 100755 --- a/mmgen/proto/btc/tw/prune.py +++ b/mmgen/proto/btc/tw/prune.py @@ -23,6 +23,9 @@ class BitcoinTwAddressesPrune(BitcoinTwAddresses,TwAddressesPrune): 'Filters: show [E]mpty addrs, [U]sed addrs, all [L]abels', 'View/Actions: pager [v]iew, [w]ide view, r[e]draw{s}', 'Pruning: [q]uit pruning, [p]rune, [u]nprune, [c]lear prune list:'] + prompt_fs_repl = { + 'BCH': (1, 'Column options: toggle [D]ays/date/confs/block, cas[h]addr') + } key_mappings = { 'a':'s_amt', 'A':'s_age', diff --git a/mmgen/proto/btc/tw/txhistory.py b/mmgen/proto/btc/tw/txhistory.py index 10b2dd2e..079d6ca9 100755 --- a/mmgen/proto/btc/tw/txhistory.py +++ b/mmgen/proto/btc/tw/txhistory.py @@ -240,6 +240,9 @@ class BitcoinTwTxHistory(TwTxHistory,BitcoinTwRPC): 'Column options: toggle [D]ays/date/confs/block, tx[i]d, [T]otal amt', 'View/Print: pager [v]iew, full pager [V]iew, [p]rint, full [P]rint{s}', 'Filters/Actions: show [u]nconfirmed, [q]uit menu, r[e]draw:'] + prompt_fs_repl = { + 'BCH': (1, 'Column options: toggle [D]ate/confs, cas[h]addr, tx[i]d, [T]otal amt') + } key_mappings = { 'A':'s_age', 'n':'s_blockheight', diff --git a/mmgen/proto/btc/tw/unspent.py b/mmgen/proto/btc/tw/unspent.py index 6fafad2f..365814e1 100755 --- a/mmgen/proto/btc/tw/unspent.py +++ b/mmgen/proto/btc/tw/unspent.py @@ -33,6 +33,9 @@ class BitcoinTwUnspentOutputs(TwUnspentOutputs): 'Column options: toggle [D]ays/date/confs/block, gr[o]up, show [m]mgen addr', 'View options: pager [v]iew, [w]ide pager view{s}', 'Actions: [q]uit menu, [p]rint, r[e]draw, add [l]abel:'] + prompt_fs_repl = { + 'BCH': (1, 'Column options: toggle [D]ate/confs, cas[h]addr, gr[o]up, show [m]mgen addr') + } key_mappings = { 't':'s_txid', 'a':'s_amt', diff --git a/mmgen/proto/btc/tx/info.py b/mmgen/proto/btc/tx/info.py index 7fa264be..8f9206bb 100755 --- a/mmgen/proto/btc/tx/info.py +++ b/mmgen/proto/btc/tx/info.py @@ -89,6 +89,8 @@ class TxInfo(TxInfo): get_mmid_fmt(e, is_input), e.amt.fmt(iwidth=iwidth,color=True), tx.dcoin ) + if have_bch: + yield '{:3} [{}]\n'.format('', e.addr.hl(vp2, color=False)) else: col1_w = len(str(len(io))) + 1 for n,e in enumerate(io_sorted()): @@ -100,8 +102,12 @@ class TxInfo(TxInfo): if is_input: yield (n+1, 'tx,vout:', f'{e.txid.hl()},{red(str(e.vout))}') yield ('', 'address:', f'{e.addr.hl(vp1)} {mmid_fmt}') + if have_bch: + yield ('', '', f'[{e.addr.hl(vp2, color=False)}]') else: yield (n+1, 'address:', f'{e.addr.hl(vp1)} {mmid_fmt}') + if have_bch: + yield ('', '', f'[{e.addr.hl(vp2, color=False)}]') if e.comment: yield ('', 'comment:', e.comment.hl()) yield ('', 'amount:', f'{e.amt.hl()} {tx.dcoin}') @@ -112,7 +118,14 @@ class TxInfo(TxInfo): yield '\n'.join('{:>{w}} {:<8} {}'.format(*d,w=col1_w) for d in gen()) + '\n\n' tx = self.tx - vp1 = 0 + + if self.cfg._proto.coin == 'BCH': + have_bch = True + vp1 = 1 if not self.cfg.cashaddr else not self.cfg._proto.cashaddr + vp2 = (vp1 + 1) % 2 + else: + have_bch = False + vp1 = 0 return ( 'Displaying inputs and outputs in {} sort order'.format({'raw':'raw','addr':'address'}[sort]) diff --git a/mmgen/protocol.py b/mmgen/protocol.py index cbb2c74c..ded7ac47 100755 --- a/mmgen/protocol.py +++ b/mmgen/protocol.py @@ -27,6 +27,7 @@ from .objmethods import MMGenObject decoded_wif = namedtuple('decoded_wif',['sec','pubkey_type','compressed']) decoded_addr = namedtuple('decoded_addr', ['bytes', 'ver_bytes', 'fmt']) +decoded_addr_multiview = namedtuple('mv_decoded_addr', ['bytes', 'ver_bytes', 'fmt', 'addr', 'views', 'view_pref']) parsed_addr = namedtuple('parsed_addr',['ver_bytes','data']) _finfo = namedtuple('fork_info',['height','hash','name','replayable']) diff --git a/mmgen/tw/view.py b/mmgen/tw/view.py index 0e5ae946..31c41c9d 100755 --- a/mmgen/tw/view.py +++ b/mmgen/tw/view.py @@ -90,6 +90,7 @@ class TwView(MMGenObject,metaclass=AsyncInit): sort_key = 'age' display_hdr = () display_body = () + prompt_fs_repl = {} nodata_msg = '[no data for requested parameters]' cols = 0 term_height = 0 @@ -122,6 +123,8 @@ class TwView(MMGenObject,metaclass=AsyncInit): age_fmts_date_dependent = ('days','date','date_time') _age_fmt = 'confs' + bch_addr_fmts = ('cashaddr', 'legacy') + age_col_params = { 'confs': (7, 'Confs'), 'block': (8, 'Block'), @@ -195,7 +198,12 @@ class TwView(MMGenObject,metaclass=AsyncInit): from .ctl import TwCtl self.twctl = await TwCtl(cfg,proto,mode='w') self.amt_keys = {'amt':'iwidth','amt2':'iwidth2'} if self.has_amt2 else {'amt':'iwidth'} + if repl := self.prompt_fs_repl.get(self.proto.coin): + self.prompt_fs_in[repl[0]] = repl[1] self.prompt_fs = '\n'.join(self.prompt_fs_in) + if self.proto.coin == 'BCH': + self.key_mappings.update({'h': 'd_addr_view_pref'}) + self.addr_view_pref = 1 if not self.cfg.cashaddr else not self.proto.cashaddr @property def age_w(self): diff --git a/test/cmdtest_py_d/ct_base.py b/test/cmdtest_py_d/ct_base.py index 1a7c96a8..aec6a2a8 100755 --- a/test/cmdtest_py_d/ct_base.py +++ b/test/cmdtest_py_d/ct_base.py @@ -54,8 +54,7 @@ class CmdTestBase: self.usr_rand_arg = f'-r{self.usr_rand_chars}' self.tn_ext = ('','.testnet')[self.proto.testnet] self.coin = self.proto.coin.lower() - self.bch_legacy = self.coin == 'bch' - self.fork = 'btc' if self.bch_legacy else self.coin + self.fork = 'btc' if self.coin == 'bch' and not cfg.cashaddr else self.coin self.altcoin_pfx = '' if self.fork == 'btc' else f'-{self.proto.coin}' if len(self.tmpdir_nums) == 1: self.tmpdir_num = self.tmpdir_nums[0] @@ -115,3 +114,6 @@ class CmdTestBase: def noop(self): return 'ok' + + def _cashaddr_opt(self, val): + return [f'--cashaddr={val}'] if self.proto.coin == 'BCH' else [] diff --git a/test/cmdtest_py_d/ct_misc.py b/test/cmdtest_py_d/ct_misc.py index 83b4844c..98f0f2f0 100755 --- a/test/cmdtest_py_d/ct_misc.py +++ b/test/cmdtest_py_d/ct_misc.py @@ -63,6 +63,10 @@ class CmdTestMisc(CmdTestBase): passthru_opts = ('daemon_data_dir','rpc_port') cmd_group = ( ('rpc_backends', 'RPC backends'), + ('bch_txview_legacy1', "'mmgen-tool --coin=bch --cashaddr=0 txview terse=0'"), + ('bch_txview_legacy2', "'mmgen-tool --coin=bch --cashaddr=0 txview terse=1'"), + ('bch_txview_cashaddr1', "'mmgen-tool --coin=bch --cashaddr=1 txview terse=0'"), + ('bch_txview_cashaddr2', "'mmgen-tool --coin=bch --cashaddr=1 txview terse=1'"), ('xmrwallet_txview', "'mmgen-xmrwallet' txview"), ('xmrwallet_txlist', "'mmgen-xmrwallet' txlist"), ('coin_daemon_info', "'examples/coin-daemon-info.py'"), @@ -79,6 +83,27 @@ class CmdTestMisc(CmdTestBase): t = self.spawn_chk('mmgen-tool',[f'--rpc-backend={b}','daemon_version'],extra_desc=f'({b})') return t + def _bch_txview(self, view_pref, terse, expect): + if cfg.no_altcoin: + return 'skip' + tx = 'test/ref/bitcoin_cash/895108-BCH[2.65913].rawtx' + t = self.spawn('mmgen-tool', ['--coin=bch', f'--cashaddr={view_pref}', 'txview', tx, f'terse={terse}']) + #t = self.spawn('mmgen-tool', ['--coin=bch', '--longhelp']) + t.expect(expect) + return t + + def bch_txview_legacy1(self): + return self._bch_txview(0, 0, '[qzuffa536e0eqfwz3smapckhlw9wge4p5spvx5j7h7]') + + def bch_txview_legacy2(self): + return self._bch_txview(0, 1, '[qzuffa536e0eqfwz3smapckhlw9wge4p5spvx5j7h7]') + + def bch_txview_cashaddr1(self): + return self._bch_txview(1, 0, '[1HpynST7vkLn8yNtdrqPfeghexZk4sdB3W]') + + def bch_txview_cashaddr2(self): + return self._bch_txview(1, 1, '[1HpynST7vkLn8yNtdrqPfeghexZk4sdB3W]') + def xmrwallet_txview(self,op='txview'): if cfg.no_altcoin: return 'skip' diff --git a/test/cmdtest_py_d/ct_ref_3seed.py b/test/cmdtest_py_d/ct_ref_3seed.py index 45a7b27a..ac7aa7bf 100755 --- a/test/cmdtest_py_d/ct_ref_3seed.py +++ b/test/cmdtest_py_d/ct_ref_3seed.py @@ -214,8 +214,8 @@ class CmdTestRef3Seed(CmdTestBase,CmdTestShared): class CmdTestRef3Addr(CmdTestRef3Seed): 'generated reference address, key and password files for 128-, 192- and 256-bit seeds' - networks = ('btc', 'btc_tn', 'ltc', 'ltc_tn') - passthru_opts = ('coin', 'testnet') + networks = ('btc', 'btc_tn', 'ltc', 'ltc_tn', 'bch', 'bch_tn') + passthru_opts = ('coin', 'testnet', 'cashaddr') tmpdir_nums = [26, 27, 28] shared_deps = ['mmdat', pwfile] @@ -224,6 +224,7 @@ class CmdTestRef3Addr(CmdTestRef3Seed): 'sids': ('FE3C6545', '1378FC64', '98831F3A'), 'refaddrgen_legacy_1': { 'btc': ('B230 7526 638F 38CB','A9DC 5A13 12CB 1317'), + 'bch': ('026D AFE0 8C60 6CFF','B406 4937 D884 6E48'), 'ltc': ('2B23 5E97 848A B961','AEC3 E774 0B21 0202'), }, 'refaddrgen_segwit_1': { @@ -236,14 +237,17 @@ class CmdTestRef3Addr(CmdTestRef3Seed): }, 'refaddrgen_compressed_1': { 'btc': ('95EB 8CC0 7B3B 7856','16E6 6170 154D 2202'), + 'bch': ('C560 A343 CEAB 118E','3F56 8DC5 0383 CD78'), 'ltc': ('35D5 8ECA 9A42 46C3','15B3 5492 D3D3 6854'), }, 'refkeyaddrgen_legacy_1': { 'btc': ('CF83 32FB 8A8B 08E2','1F67 B73A FF8C 5D15'), + 'bch': ('6909 4C64 119A 7681','7E48 5071 5E41 D1AE'), 'ltc': ('1896 A26C 7F14 2D01','FA0E CD4E ADAF DBF4'), }, 'refkeyaddrgen_compressed_1': { 'btc': ('E43A FA46 5751 720A','FDEE 8E45 1C0A 02AD'), + 'bch': ('7068 9B37 8ABF 3E31','C688 29A5 BA4C 21B2'), 'ltc': ('7603 2FE3 2145 FFAD','3FE0 5A8E 5FBE FF3E'), }, 'refkeyaddrgen_segwit_1': { @@ -265,10 +269,12 @@ class CmdTestRef3Addr(CmdTestRef3Seed): 'ref_hex2bip39_24_passwdgen_1': '91AF E735 A31D 72A0', 'refaddrgen_legacy_2': { 'btc': ('8C17 A5FA 0470 6E89','764C 66F9 7502 AAEA'), + 'bch': ('8117 24B6 3FDA 6B40','E58C A8A4 C371 66AE'), 'ltc': ('2B77 A009 D5D0 22AD','51D1 979D 0A35 F24B'), }, 'refaddrgen_compressed_2': { 'btc': ('2615 8401 2E98 7ECA','A386 EE07 A356 906D'), + 'bch': ('3364 0F9D 8355 2A53','3451 F741 0A8A FA56'), 'ltc': ('197C C48C 3C37 AB0F','8DDC 5FE3 BFF9 1226'), }, 'refaddrgen_segwit_2': { @@ -281,10 +287,12 @@ class CmdTestRef3Addr(CmdTestRef3Seed): }, 'refkeyaddrgen_legacy_2': { 'btc': ('9648 5132 B98E 3AD9','1BD3 5A36 D51C 256D'), + 'bch': ('C4D8 7C36 DC77 F8C2','953D 245C 8CFF AC72'), 'ltc': ('DBD4 FAB6 7E46 CD07','8822 3FDF FEC0 6A8C'), }, 'refkeyaddrgen_compressed_2': { 'btc': ('6D6D 3D35 04FD B9C3','94BF 4BCF 10B2 394B'), + 'bch': ('3E7F C369 2AB9 BD58','0C99 14CD 5ADE 6782'), 'ltc': ('F5DA 9D60 6798 C4E9','7918 88DE 9096 DD7A'), }, 'refkeyaddrgen_segwit_2': { @@ -306,10 +314,12 @@ class CmdTestRef3Addr(CmdTestRef3Seed): 'ref_hex2bip39_24_passwdgen_2': '0E8E 23C9 923F 7C2D', 'refaddrgen_legacy_3': { 'btc': ('6FEF 6FB9 7B13 5D91','424E 4326 CFFE 5F51'), + 'bch': ('E580 43BB 0F96 AA93','630E 174A 8DDE 1BCE'), 'ltc': ('AD52 C3FE 8924 AAF0','4EBE 2E85 E969 1B30'), }, 'refaddrgen_compressed_3': { 'btc': ('A33C 4FDE F515 F5BC','6C48 AA57 2056 C8C8'), + 'bch': ('E37B AF41 7997 A28C','0D5D 9A58 D6E9 92EE'), 'ltc': ('3FC0 8F03 C2D6 BD19','4C0A 49B6 2DD1 1BE0'), }, 'refaddrgen_segwit_3': { @@ -322,10 +332,12 @@ class CmdTestRef3Addr(CmdTestRef3Seed): }, 'refkeyaddrgen_legacy_3': { 'btc': ('9F2D D781 1812 8BAD','88CC 5120 9A91 22C2'), + 'bch': ('A0EE B039 48F4 24AE','B014 E0AB 5F87 EC64'), 'ltc': ('B804 978A 8796 3ED4','98B5 AC35 F334 0398'), }, 'refkeyaddrgen_compressed_3': { 'btc': ('420A 8EB5 A9E2 7814','F43A CB4A 81F3 F735'), + 'bch': ('33E7 5C06 88CF 2792','6E09 FF73 B7C8 00D4'), 'ltc': ('8D1C 781F EB7F 44BC','05F3 5C68 FD31 FCEF'), }, 'refkeyaddrgen_segwit_3': { @@ -379,8 +391,12 @@ class CmdTestRef3Addr(CmdTestRef3Seed): def refaddrgen_compressed(self): return self.call_addrgen('compressed') def refaddrgen_segwit(self): + if cfg.coin == 'BCH': + return 'skip' return self.call_addrgen('segwit') def refaddrgen_bech32(self): + if cfg.coin == 'BCH': + return 'skip' return self.call_addrgen('bech32') def refkeyaddrgen_legacy(self): @@ -388,8 +404,12 @@ class CmdTestRef3Addr(CmdTestRef3Seed): def refkeyaddrgen_compressed(self): return self.call_addrgen('compressed', 'keyaddrgen') def refkeyaddrgen_segwit(self): + if cfg.coin == 'BCH': + return 'skip' return self.call_addrgen('segwit', 'keyaddrgen') def refkeyaddrgen_bech32(self): + if cfg.coin == 'BCH': + return 'skip' return self.call_addrgen('bech32', 'keyaddrgen') def pwgen(self, ftype, id_str, pwfmt=None, pwlen=None, extra_opts=[], stdout=False): diff --git a/test/cmdtest_py_d/ct_regtest.py b/test/cmdtest_py_d/ct_regtest.py index 25fdf005..47262ad5 100755 --- a/test/cmdtest_py_d/ct_regtest.py +++ b/test/cmdtest_py_d/ct_regtest.py @@ -24,6 +24,8 @@ import os, json, time, re from decimal import Decimal from mmgen.proto.btc.regtest import MMGenRegtest +from mmgen.proto.bch.cashaddr import b32a +from mmgen.proto.btc.common import b58a from mmgen.color import yellow from mmgen.util import msg_r,die,gmsg,capfirst,fmt_list from mmgen.protocol import init_proto @@ -394,6 +396,7 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): 'view': ( 'viewing addresses and unspent outputs', ('alice_listaddresses_scroll', 'listaddresses (--scroll, interactive=1)'), + ('alice_listaddresses_cashaddr', 'listaddresses (BCH cashaddr)'), ('alice_listaddresses_empty', 'listaddresses (no data)'), ('alice_listaddresses_menu', 'listaddresses (menu items)'), ('alice_listaddresses1', 'listaddresses'), @@ -404,6 +407,7 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): ('alice_twview_days', 'twview (age_fmt=days)'), ('alice_twview_date', 'twview (age_fmt=date)'), ('alice_twview_date_time', 'twview (age_fmt=date_time)'), + ('alice_twview_interactive_cashaddr', 'twview (interactive=1, BCH cashaddr)'), ('alice_txcreate_info', 'txcreate -i'), ('alice_txcreate_info_term', 'txcreate -i (pexpect_spawn)'), ('bob_send_to_alice_2addr', 'sending a TX to 2 addresses in Alice’s wallet'), @@ -763,10 +767,10 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): return self.user_bal('alice',rtBals[8]) def bob_bal1(self): - return self.user_bal('bob',rtFundAmt) + return self.user_bal('bob', rtFundAmt, self._cashaddr_opt(0)) def bob_bal2(self): - return self.user_bal('bob',rtBals[0]) + return self.user_bal('bob', rtBals[0], self._cashaddr_opt(1)) def bob_bal2a(self): return self.user_bal('bob',rtBals[0],args=['showempty=1','age_fmt=confs']) @@ -827,11 +831,21 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): def bob_twview2(self): sid1 = self._get_user_subsid('bob','29L') - return self.user_twview('bob',chk=(sid1+':C:2','0.29'),sort='twmmid') + return self.user_twview( + 'bob', + opts = self._cashaddr_opt(0), + chk = (f'{sid1}:C:2', '0.29'), + sort = 'twmmid', + expect = rf'[{b58a}]{{8}}' if self.proto.coin == 'BCH' else None) def bob_twview3(self): sid2 = self._get_user_subsid('bob','127S') - return self.user_twview('bob',chk=(sid2+':C:3','0.127'),sort='amt') + return self.user_twview( + 'bob', + opts = self._cashaddr_opt(1), + chk = (f'{sid2}:C:3', '0.127'), + sort = 'amt', + expect = rf'[{b32a}]{{8}}' if self.proto.coin == 'BCH' else None) def bob_subwallet_txcreate(self): sid1 = self._get_user_subsid('bob','29L') @@ -873,13 +887,17 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): def bob_txhist1(self): return self.user_txhist('bob', + opts = self._cashaddr_opt(1), args = ['sort=age'], - expect = fr'\s1\).*\s{rtFundAmt}\s' ) + expect = fr'\s1\).*\s{rtFundAmt}\s', + expect2 = rf'[{b32a}]{{8}}' if self.proto.coin == 'BCH' else None) def bob_txhist2(self): return self.user_txhist('bob', + opts = self._cashaddr_opt(0), args = ['sort=blockheight','reverse=1','age_fmt=block'], - expect = fr'\s1\).*:{self.dfl_mmtype}:1\s' ) + expect = fr'\s1\).*:{self.dfl_mmtype}:1\s', + expect2 = rf'[{b58a}]{{8}}' if self.proto.coin == 'BCH' else None) def bob_txhist3(self): return self.user_txhist('bob', @@ -903,6 +921,13 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): self.get_file_with_ext('out',delete_all=True) t = self.spawn('mmgen-tool', ['--bob',f'--outdir={self.tmpdir}','txhist','age_fmt=date_time','interactive=true'] ) + if self.proto.coin == 'BCH': + for expect, resp in ( + (rf'[{b32a}]{{8}}', 'h'), + (rf'[{b58a}]{{8}}', 'h') + ): + t.expect(expect, regex=True) + t.expect('draw:\b', resp, regex=True) for resp in ('u','i','t','a','m','T','A','r','r','D','D','D','D','p','P','n','V'): t.expect('draw:\b',resp,regex=True) if t.pexpect_spawn: @@ -1673,6 +1698,18 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): t.expect(prompt, s) return t + def alice_listaddresses_cashaddr(self): + if self.proto.coin != 'BCH': + return 'skip' + prompt = 'abel:\b' + expect = ( + [rf'[{b32a}]{{8}}'], + [prompt, 'h'], + [rf'[{b58a}]{{8}}'], + [prompt, 'q'] + ) + return self._alice_listaddresses_interactive(expect=expect) + def alice_listaddresses_empty(self): return self._alice_listaddresses_interactive(expect_menu='uuEq') @@ -1731,6 +1768,17 @@ class CmdTestRegtest(CmdTestBase,CmdTestShared): args = ['age_fmt=date_time'], expect = (rtAmts[0],pat_date_time) ) + def alice_twview_interactive_cashaddr(self): + if self.proto.coin != 'BCH': + return 'skip' + t = self.spawn('mmgen-tool', ['--alice', 'twview', 'interactive=true']) + prompt = 'abel:\b' + t.expect(rf'[{b32a}]{{8}}', regex=True) + t.expect(prompt, 'h') + t.expect(rf'[{b58a}]{{8}}', regex=True) + t.expect(prompt, 'q') + return t + def alice_txcreate_info(self,pexpect_spawn=False): t = self.spawn('mmgen-txcreate',['--alice','-Bi'],pexpect_spawn=pexpect_spawn) pats = ( diff --git a/test/gentest.py b/test/gentest.py index e3cd7e6f..48848fe3 100755 --- a/test/gentest.py +++ b/test/gentest.py @@ -294,7 +294,7 @@ def do_ab_test(proto,scfg,addr_type,gen1,kg2,ag,tool,cache_data): sec = PrivKey(proto,in_bytes,compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type) data = kg1.gen_data(sec) addr1 = ag.to_addr(data) - view_pref = 0 + view_pref = 1 if proto.coin == 'BCH' else 0 tinfo = ( in_bytes, sec, sec.wif, type(kg1).__name__, type(kg2).__name__ if kg2 else tool.desc ) def do_msg(): diff --git a/test/ref/bitcoin_cash/895108-BCH[2.65913].rawtx b/test/ref/bitcoin_cash/895108-BCH[2.65913].rawtx new file mode 100644 index 00000000..99606f44 --- /dev/null +++ b/test/ref/bitcoin_cash/895108-BCH[2.65913].rawtx @@ -0,0 +1,5 @@ +0bd373 +BCH MAINNET 895108 2.65913 20240926_071550 1000000 +0200000002eb4f3508ac2ca1ec5c2851274214aee247dfc60ae3c739bae926216f13b8ed4e0000000000ffffffffd751a1d02474628bffca80020a4f430a5ab97e52c8eb0327255bce8f406844610700000000ffffffff030859de01000000001976a9140d5ea98cdb1d5c41bbb73750c579352b63cb8b6e88aca029fb0d000000001976a914f956b8114948fb7d8b651fc23fef66e0087412f388ac63b07167010000001976a914295e2be7fb7ae4a00d8e0ed267da0e00f14eb00588ac00000000 +[{'vout': 0, 'txid': '4eedb8136f2126e9ba39c7e30ac6df47e2ae14422751285ceca12cac08354feb', 'scriptPubKey': '76a914b894f691d65f9025c28c37d0e2d7fb8ae466a1a488ac', 'comment': 'Alice\u2019s allowance', 'amt': '37.52425504', 'addr': 'bitcoincash:qzuffa536e0eqfwz3smapckhlw9wge4p5spvx5j7h7', 'confs': 574214, 'mmid': 'EB5572C5:L:1', 'sequence': 4294967295}, {'vout': 7, 'txid': '614468408fce5b252703ebc8527eb95a0a434f0a0280caff8b627424d0a151d7', 'scriptPubKey': '76a91460e025db040aaa5ed9a7a8051cb9e82f58df489588ac', 'comment': '', 'amt': '25.44058763', 'addr': 'bitcoincash:qpswqfwmqs925hke575q289eaqh43h6gj54ysy3xae', 'confs': 676627, 'sequence': 4294967295}] +[{'addr': 'bitcoincash:qqx4a2vvmvw4csdmkum4p3tex54k8jutdctzd50x4u', 'amt': '0.31349', 'is_chg': False}, {'addr': 'bitcoincash:qru4dwq3f9y0klvtv50uy0l0vmsqsaqj7vugke5esy', 'amt': '2.34564', 'is_chg': False, 'mmid': 'EB5572C5:L:7'}, {'addr': 'bitcoincash:qq54u2l8ldawfgqd3c8dye76pcq0zn4sq5letrxcdf', 'amt': '60.30471267', 'is_chg': True, 'mmid': 'EB5572C5:L:8'}] diff --git a/test/ref/bitcoin_cash/bchwallet-testnet.dump b/test/ref/bitcoin_cash/bchwallet-testnet.dump new file mode 100644 index 00000000..def9aacf --- /dev/null +++ b/test/ref/bitcoin_cash/bchwallet-testnet.dump @@ -0,0 +1,57 @@ +# Wallet dump created by Bitcoin Cash Node v27.1.0-9f9aa5a6e +# * Created on 2024-09-28T09:13:38Z +# * Best block at time of backup was 0 (000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943), +# mined on 2011-02-02T23:16:42Z + +# extended private masterkey: tprv8ZgxMBicQKsPexYGMbCXgorSb94LSNHruGV8TcYGnvvAmfYhMsFgeLQNcZNGjpFNGZECug2pNiGx9CxgPfmm1MBai3ChRco9zvMhGgjCrbc + +cVp8wiSLfBHmFTM8ANuNQaGSYdaeGzQPTjzy35cctkD5NYpWVedV 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qqtw9k8rrz5yuuu8mpxqrrlt05nslxp3qqsswccy0t hdkeypath=m/0'/0'/636' +cRLvPdrsqR3izskvzcz4cS8pGzLTsnMDA6Lo6vPepYhp8vEc7NnD 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qq57qrhm3r3w0cjjtr865l7qq47ed66gqqtz3dcqh2 hdkeypath=m/0'/0'/668' +cNUKQrbW6y9usj4rn8cX1LTP4f3rDmLcouuQcex1d8UykdyNvGqN 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qp2f2fvxccmpdnkzc3h252d3gupzj9y9qqw7qa6wus hdkeypath=m/0'/0'/650' +cRvhFvDo4DQ6xeFDyG8N6kfgff3FS2sJL2yRCYWeG9rFHHhBMqD5 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qp74u03veyxpwr0fr6mn5vamvr5gd8ufqqttu5hvz9 hdkeypath=m/0'/1'/438' +cUxwL9tKy83GX5zUHtGbsfZK23Y4CQ6hKdqbCiMkLJpNMvRyboV2 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qrrdu293cnrdy0eyfc29pugulk339ddpqqnz5h86m4 hdkeypath=m/0'/0'/572' +cUxUVnJ1CzoTCA4nsC1zKWJHwYYhNCGghc3iaLecnS5Yez57DTKV 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzju897ltu2gwuy029grdesgp64dwv49qq7g4u4jrp hdkeypath=m/0'/1'/650' +cNeXwV6a2XnvkmhxRJFjTBrZuFrmR8geii1AWzK8TyPxMd9R5yHX 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qql35gw4lcvjn7y0rtajtjvar566rpwzqqwg9v8wz6 hdkeypath=m/0'/1'/26' +cNY9Aw3KUXbu2fVoNHJKEeyae2dcu6wkrehuUY5FtDPATaoyGqqy 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qqr52pjym9zx3q660ya989shtrs3dlk5qqjskuc2cz hdkeypath=m/0'/0'/245' +cS8qYyAg8QGRGn1HPTpaT5yogQdhxy7EG2tJvhrNRVF4SSpevwcp 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzycyx62gt2r9aa40zqry38mzcdplfkeqq79tkmya8 hdkeypath=m/0'/0'/59' +cTdMWafCwAd9o5UDNQNwXfVRcAvybo98NguKyxDVCDRLrDRax3TL 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qz4rxgqy0duzf5kxhd7te53edqca9qklqqtf0jv8dc hdkeypath=m/0'/1'/575' +cPtJLThtyGbj8QnmiuXiQhX6A4Mdb8DPyRYMYrQ4cLnKeRxXLdF6 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzpwh9dh6j6g0juc50cl9dslcgmcs4hmqq9djlmk5j hdkeypath=m/0'/1'/425' +cRzHF8okeRPFVMSewpAjWqfiWgjkjFxZqAEVPjiFt5ZCdyZUr3C7 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qp97umelqwpukmtaf72y083dy54lqmqdqy7yjcvjmj hdkeypath=m/0'/1'/559' +cS99oNTaQB5RBpHDSHJEenyjmJhF9KyWDtq96J91wKHkBmbU8ANq 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qpc5qjz6lt0v5cjeg5z9h0u65sd3lpe9qydu3d30uf hdkeypath=m/0'/1'/765' +cTkaVbTNPfYEZvVQum7vf7RWYy4hwqrg4BD1cnk2TE8fSe5LDiex 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qqqdmv4u2l8g3vttakq8a0vdpgw0v2fsqyutg60vpd hdkeypath=m/0'/1'/29' +cNZqJhwScX3TrAF4pMcZnzku9QoLmfbamuxCVMVmZNJkubRyv4ht 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qz7vmyvj805g7ahnmrz623qg9t49jh6mqy8xhz29az hdkeypath=m/0'/1'/491' +cV5FaqsEYyYWokVNRMGgoweUneU8AJhVJ8F8PrSc5XsL3g8fUT4A 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qz0u3kgqq5xrkj8zch8v28jypdvtaur5qyjfv33a4r hdkeypath=m/0'/1'/190' +cUyaVudNPss23dKjJUfs4cqKDnvh1taigAhtHDRXone75VWmMbMN 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qp9mkm0u9jcuf8yzmntk497d8vccfw5hqy40lxngtk hdkeypath=m/0'/0'/978' +cNLhJ4nomtwsvDKoFXGDondDZJFgfF2e8XTxDPERs9zP9NPqUDKg 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qpx96famcmfr7g7370qufkh58ffmwp4nqylaj6tsw2 hdkeypath=m/0'/0'/564' +cR3JBZBk7RtbEwt3VMKqQimWZXKyhuidU5pwpLoY2GGNLphSnrsP 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qp5recsz4w0lsqxkudzjwe4syhqqpr8xqyx06hqhdt hdkeypath=m/0'/1'/329' +cRkrg9gFU8e51RBW8ZXZYoEoktWRYLV3RZFL5aEdpfk6wkparAHD 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qqk48mrkq8hjzysdz2y53vs6u4ulkt8aqy7he5yr9x hdkeypath=m/0'/0'/569' +cMzNpPeeXtYTVswwULSF6AAoa2nUBHb6myufP7AF4hoovMU4RAZh 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qr5fceuvff9khx5304aqkvxhzwmemngpqgcykgc7ts hdkeypath=m/0'/1'/495' +cSJ4aztJyTfzsR2yRhQXBtYm6hoANLQHmV1fRodnepWaNps7N5Fy 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzveklahtds9u4p0pt04f3vz4gznsccdqgqd9zn7gy hdkeypath=m/0'/1'/583' +cTrDo25RNhpGw4j7Kn8oTVfYioJ7kVKPt52Vu8mbWQA7LZfQ9uTY 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qrd4ctzd9lfqksyxyh4rk3ydvs9ncfs4qgln04njqf hdkeypath=m/0'/0'/985' +cRxbuanYahFoZxykkFJPCYpHLkhzGznmcDiE3pjJAbaALDy7t4iX 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzg4jh3lakadpm0vmenms59javvevcfmqgtprztuje hdkeypath=m/0'/1'/48' +cQcnMRahdsvAEUZo6GGMhCi3KJdhZiTE7PLjvoZFSWUHDZSZVpHz 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzxmlct4rjemuw7l3mecc7lehkt8hxzjqg0ry8h6z8 hdkeypath=m/0'/1'/635' +cV9hcAoazBJTRuDoBhDfMgrb5b484g2UechpaCCbbLhEvxktGVit 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qq95ljjemaf528krs6xgja9zpzd6656eqgg6f7atfa hdkeypath=m/0'/1'/387' +cNc5Y7fwUFumaWxnq63FGACVK2Uy5aUCMCeqzEGEzLFBaU4oqLxW 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qrhqfqsetnxmc9ajwsfjwluefqy6uytsqgahzal7wx hdkeypath=m/0'/0'/353' +cTmGh4sv7gDJUDe8PBsZQ77KoKpEZrpz9MTM6mxeTgSC5ikYbLNK 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qqeqttck033lrgac0smvnq44cryfqw5fqgfws59ujw hdkeypath=m/0'/0'/384' +cUNh8FEFYuJYEb2dfRst1SwMPonbBbUxy5QGP2ednbxm9KahzY67 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qrspfwz5mwlgc55vxmjls2xy5yd3tkvwqga5xut3sc hdkeypath=m/0'/0'/493' +cVQeD6M16HKPsR5BZdiKHk576cxoT1emU43w7R8XDQkeCi2oGg5y 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzeq0wr07r75mkygukase2ye7dxg42asqgwhh8uhpa hdkeypath=m/0'/1'/442' +cVCah2gzytEK4iBXVJLeTNHmvmGzLNqFLrR5dR1XtstMsCgwyCgC 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qz8cavad8ujsj5p8mnuycg6efcsupxlpqg0wd35j2e hdkeypath=m/0'/1'/687' +cUFejVszCwodxV3B3ppB9CSbBc6yJ9e9RjGR4aK6Z5LPXnbdCEv2 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qrm9hrvw2stpnlkl0f6rje75ltv4ftpzqvvgawxhcd hdkeypath=m/0'/1'/469' +cQEYTwzo22pEwE7T1zWATsL4eZ7gYYanQR3XxdLdo8RGpqd1KSeZ 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qqtgazx0dcyypeaxy50x00fve5y3zkeeqvrl76ku5k hdkeypath=m/0'/1'/910' +cRoFNNQYRm5V72cokeAjoWUYuCjieZTjTvd6743m8cmYXazSp22m 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzenr7dmrt68tcjm7daawn6srkcy76jtqvn92m6de2 hdkeypath=m/0'/1'/287' +cSK2tKxkNZQojqtohor5yL7dSb4edeuG5qMh8JzUJeLZvN8cxukt 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qq2w0d3vw5202hse28p0la9rvcne5j24qv6t4hj58d hdkeypath=m/0'/0'/555' +cVPsuH8v4PpLJieDFMYyXuRAVmFkVmRTHBGxEeGELo4QMPz8bYyZ 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qp8nn4zyy5v8su6v3c6sf2hq0nrxeqzuqvqs4h5xam hdkeypath=m/0'/0'/858' +cPGthbqMbX6kCUXG72kZfYWZX1cwWhLUaRvKj1LeNTZTEQkEQKez 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzy7flccw4g3nhh4mksxwwuz2smx85naqvcealz8pc hdkeypath=m/0'/0'/89' +cV76D5Z8UxuskGaerD5VYR6oies1HUHDvq8eLuQ9dnEnrupiHesN 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qqeug07w2n6gjrnzflr8dp2x4ku3794dqv46y80v4e hdkeypath=m/0'/1'/849' +cPi5RxfS2HHMWMt3YnY5x2JnLLis5wtYoX7uf8swYb2DaC93ja83 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzn0nkz9klkctg3s08zgf7xwexung27qqv33s57lfa hdkeypath=m/0'/0'/445' +cTmE2XrXjay4pN6rSQqGdQ61RfH71716i9Gr41XRE1H5am998HWT 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qq9vkfzflckgrzndj5um4xnxtzkwl5wtqvrurey9a6 hdkeypath=m/0'/1'/97' +cQLSaL6A4taGdsWLGWQK2RrAjsRHE3iKQ9bQ4sNFFFJaPqeSzEdW 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qrje963zup3k4w0usvfcfm6xwm6rmah3qvp3y5p5f2 hdkeypath=m/0'/1'/807' +cUwbXhFJzLvDM6cahsmjteUSPB9R86pNjRWbGiidcdnHQtZcjffS 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qpzqpt9nnqvrpzp4kydxrl87jq5qv2glqs3lty3jqv hdkeypath=m/0'/0'/76' +cNnhDbhrvxMsmr8NQwuBubg2LqtdmyVF1cZCCUNZXtkQvCFzeisB 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qql8htcgd7fmk94q443jptzn0ulv06zqqsl0k7pxlh hdkeypath=m/0'/0'/51' +cUqbctTgaD19iZ8FJjKMGr5YdqCXs3ti3KHqtZNPNryVaa9EA2RD 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qzz6rhxw92daxjawff92c2ghxfjjwkmqqszz0s68te hdkeypath=m/0'/0'/26' +cQXB8bGdcPjyNVnrPwUKCdJcx4yNJoYauAGE4pZvcB8ZrAe1Z8ZT 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qqex9m5ty059pgqwryfffm9dzuapfg4rqskq3hqm0s hdkeypath=m/0'/1'/896' +cNReXczzABpyPxPBAPK1ddwwV1aoy2xnqeMt9mXGH7EpqEiiisqm 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qr6gf7vx08rq0uchl0wwsh0jlnp9l6ayqsds94yuqd hdkeypath=m/0'/1'/712' +cTZ1XaNjpTnr5dgCGAkeV4NZCxZhoYwRrJoenmAqbgWC2yzA1vLi 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qrhyd89258ultrjf3hsk72lg3n7wx3lxqsrvpgx04e hdkeypath=m/0'/1'/224' +cSnbnKjTEinAxVMk4i7qDzAeC7duseKvVLninzCiVskSVjwSmAv4 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qp05qf87mkfgxs0hpgvgdchtj3zqex8wqs8h8954e9 hdkeypath=m/0'/1'/717' +cVLJtk7kFFCAfEvj3ULNr59ogFhprhY4LmBSaKyepDtssuBV4aoj 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qz6pxp99qtvewl6zxa90268y6nwx3mhhqsfkf4d7ms hdkeypath=m/0'/0'/779' +cTrScwV6E1uRtrcNufcDPJyord3AXgGiAz7HHPk88d5AFD6WVbUx 2024-09-28T09:13:20Z reserve=1 # addr=bchtest:qqeyp4gqhd3thj235xw8rkjxyg2hh7leqs55paumr5 hdkeypath=m/0'/1'/565' diff --git a/test/ref/bitcoin_cash/bchwallet.dump b/test/ref/bitcoin_cash/bchwallet.dump new file mode 100644 index 00000000..1774a4c6 --- /dev/null +++ b/test/ref/bitcoin_cash/bchwallet.dump @@ -0,0 +1,57 @@ +# Wallet dump created by Bitcoin Cash Node v27.1.0-9f9aa5a6e +# * Created on 2024-09-28T09:01:27Z +# * Best block at time of backup was 0 (000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f), +# mined on 2009-01-03T18:15:05Z + +# extended private masterkey: xprv9s21ZrQH143K3rtmqd4EARwck2w9PMoozEdPXdN3q7G34xYWJUi6f78acEx3go1648yunqrmRzcbjQDYuFTPAXnG1CUjqc7LZnkwMNg3QBG + +KzVH2yXbj8oKGPiBqqwze9CyfkE5bsCUVCt6hd8nVxwZg7MCjaVh 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qryzwf3zl5vl4txc4v03yuzymenfrfwmqqye26zhtv hdkeypath=m/0'/0'/13' +KzF25zJ1SEZrWFbc1V7GnCrXWgUFCHXirpi5UDNZ83PTd524Qz9B 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qqw6uwxf096nmgfucnqjdlgnhu9jxyswqyuejq9cac hdkeypath=m/0'/0'/9' +L4t5RMzGSUps2pVtFS2gfx1W9hTaLXKkLfdsowt6de3NUvd9g6kv 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qzjz6zrkjxcm0t7xv2p6d5ylrvnrj5x4pqrxczzt6d hdkeypath=m/0'/0'/3' +L3WHKc7JY9QtnPQmrmk6w8rbMugBpy2ydNhzYpgoG6vxzTofxTAQ 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qz4vyfzutpp53nfnzqtwasavh9zvr45r9vequueu05 hdkeypath=m/0'/0'/4' +L3C7osemZLh9dzUmtBpL3xz2zgkFuY1wt5ykFAevZigudUsSjHDd 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qz7uunmyx67nvujz9qg59yh2dedv4q6v9sddgmywe7 hdkeypath=m/0'/0'/2' +L1ua9H1EJq6bGTgrj4SjSipEBMPhM4naE2udaGZxeYyXxmgEDWSG 2024-09-28T09:00:51Z hdseed=1 # addr=bitcoincash:qzaatxgp2p6dqjppu43jxwpsllyh94s6xy4dgksysv hdkeypath=s +L2PZUxH9n9J6dLu8MUvKzpngcg1EJLQy9QfWfFtBj5yLQb1Uqr5L 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qpy40kh7nkfd9heuc5f0fy2yq9s400quxv8y4xntu6 hdkeypath=m/0'/0'/21' +L3cPRuFrvewas5WyAksyYnNmjBJzkEsFtrTqKKJKEz5VpfW2FPcW 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qpneya8njpm6n6mfsrcj3s9evr9k67jyxs5gfrgzcj hdkeypath=m/0'/0'/17' +KxRmkfp4cJy4inmxy7sPVjrqN6F8d2pxJoZf7rBfyYc4KK7wokdh 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qqa4ezt3gzf3j7uqcjgudntpwvlxvp5d8vjqav3rht hdkeypath=m/0'/0'/20' +KzM9HmLtuD8skinau2eUtkMeaqjokiTXVgrYPzCw7ySJfSa6cQsb 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qz9aw6gv4d69v8lmd7srn2gcwda2t0a085w8xunskq hdkeypath=m/0'/0'/6' +L2Xze59SbhtNUwv1JwUMzzA5RQB4dw3j8FZZdrt9QC7ErSB5w4L1 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qpmx2se4glcfc83mtm4eglthhyqm7ee52ss72alnmy hdkeypath=m/0'/0'/19' +KwdDU4G4rppt773ois2BULKGP8Lr6PoBVtWjfMGYD8WJAbq1Fk67 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qrq6cm6cml3xyn63c7lgdncqszqka7a0tu4zlkfynh hdkeypath=m/0'/0'/0' +Kxfb92ePatCnDcjpJhdtqvyWfE63KWNtqiNpbmzuecFQRvPB2giB 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qzuq9zummldyc5xjxsqgauprm2rqqjmqvvy9jcflq3 hdkeypath=m/0'/0'/12' +L1FpyeQFFmk1YkFC4FU4WhTjDETi9TRYDxVqZC48phUigfJnCGLf 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qq2vn4qf04pwjrfddn8zwx0afqv9u674dul0p9tqyz hdkeypath=m/0'/0'/7' +KxeABzNJ32G81B2KYis3QgCmPH2LmgrvsLKUyTbFTa9TBBpMXpGU 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qpm786fm57k3qr9m2yrpj33exgvedrha0ua9pk5dfz hdkeypath=m/0'/0'/8' +KwfUszgfu1SLJAydoHDz6HYCsj5x7yeLUfEkWkRBk6pHHQmdvdNT 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qzz2gms7w3g0u2jqrgwqf00pnpw4xv5w3g68pn0lwl hdkeypath=m/0'/0'/10' +KyFEkJhebt7ZnzaLTGBokkeKECj3eUHSMLVz16LmQiKE9YkYjnDx 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qzudzz8um9thpe3n6sx6y2s83g3f5gffnvzvydmrd2 hdkeypath=m/0'/0'/15' +KzjQPk5xVmsqfTai9Qnwsw8PrUxSjoLT9TqXi2iXrCHAPqFEN9KL 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qz2wp0tsd2222hf2nw23rhvjp8cgyext4gnuruujts hdkeypath=m/0'/0'/16' +KwrEu8kc7FWbAJnfpGniYVvLkCyQXdsZANtYmfXEzRnTUoNTFkL5 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qr5d0gr79c5gcy6f46x3f997aw98ttaxkszemm2sc5 hdkeypath=m/0'/0'/5' +L1QrY5tecZ6pQ7FjaUG3ZFWPy55qm7JRjivaPzp7tUHorLVpxsts 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qzf8e846e070d4ms6rkrrcwfhfvqgzvhhvjemgw90y hdkeypath=m/0'/0'/14' +L4fjz4VZL8MyhSXXnmqCaPpJ1gpDJSWVkEj9JwGqHNh3ftFt7fSK 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qq5j965zc0ynlyx7gu8a3ay6ynwpgjcwmuge4nx9d7 hdkeypath=m/0'/0'/11' +Kx5doQ9MZZNNjdaR9AjV18SFojQf3dDyvqR8xgu5f99kaPFrxfih 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qqgjaznt55w8p6445jvlp3l99vpd58pqugvgdw54m6 hdkeypath=m/0'/0'/18' +Ky4UDQL9uJn9BsgqhEpyZzCwKM4S3VZbjj7MizaZv4qoktSco4SK 2024-09-28T09:00:51Z reserve=1 # addr=bitcoincash:qqq5jwnp0lhszkly4href262wnl3e8dxaqx67edhwj hdkeypath=m/0'/0'/1' +L4Pg3BFXMD4GxfsDujY56is1GDt7TuFUzq9D8EpzmZAMoxG5VGe1 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qzq2l67xz9zver23lyhqdn6j4fxa0v32qqgy7tuxpx hdkeypath=m/0'/1'/392' +KzmaLSVCL5RythXGPUw4uyJQDS2j5qXxcLUxWkQbDsRpKTiMyr9h 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qqt30vesj72xegyxhhu5vph9s474gjt9qq0vcxvp8k hdkeypath=m/0'/1'/217' +KxNiUoG6xoWn1dBqDW6QU5n1hSD1rdPNPPfv8BqnMsdPDViYtDMG 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qzmrdx6m0lmn5qxcg2p7yghkndwtusunqqzhy0a937 hdkeypath=m/0'/0'/991' +Kwf7frjVxR8geZwMGr4YY4rd3urTyThTQmPwmzZHsRmJYopuVEdR 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qzjp3udaawrwzjpp0ha3r2u8v0cpcmvaqqqgrhvhvp hdkeypath=m/0'/1'/146' +L3RXLas6ZmBKENVQB9PCgFqZqvpCaJH8yc5SD29e3ysfViaodV2K 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qqa267qf9a07z6la89hvr6dppyckzfkwqq20wrhypu hdkeypath=m/0'/0'/965' +L1Uyd6thQJnRbFr9ytWPJz1ogZLZNdhfTgmpDt16FwANedb7oteS 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qzkxc8c5ull9qc485yyjzh06tcmkw4l7qq9seqpqy7 hdkeypath=m/0'/0'/963' +L2LpY1dUY5VL9bDqc11zLzCSJKEmfkAZNaJRwjrbk2HJFKkM2fmr 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qzvrcr3f46czytk3wr3runlqwznv9p3zqy9lzjvetm hdkeypath=m/0'/1'/927' +KweRCR3RkpfhZvRRMFBpq16z3XpB29N6UguN8VWjD4PQr96kmqVP 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qzllfzpwshvhjxe98y7vq3sm0ulz42peqy2ngg2xpz hdkeypath=m/0'/0'/268' +L3GYWNvrReSpL6ED6bmN6NmVoPrJeQg2363BBZNAbfJXE8ofTcjP 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qzgtmde25a7q29tpu4jf38dvkcujw4j2qyh0l62alz hdkeypath=m/0'/0'/332' +L3VwEfgMaWViYkY2xdvZknGMNHSWbfqjrokpzX5KAL2w347Tahho 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qzvqr39nmtylzhzpf7cjjxzgjv5syx6dqyl38cuyt3 hdkeypath=m/0'/0'/884' +KydYvd86K9d88CdxWbTNrM3gnoos4jvKENfgiDtPraKiEYmv4MNX 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qpwssgatqta6nx5ru58r20w8qcrzaxrjqyvvz5q7n5 hdkeypath=m/0'/0'/876' +L2Ka61jjXQvNKFsZXJyB6YhYcSeSSpKqPy6zW5ABVXftxT9Bqjwu 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qraclt7lpx6um64s3ruyqtznt0hhn6vnqyyjf04z6y hdkeypath=m/0'/1'/260' +L2Bko5eZ4nSSowtZZuUUZHTQEkr5sHvzjGprkG9QwGqpf7AUhHV3 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qr78tq3cgdv5h0ywqep89fyhy34l9t4dqyc5dcenvm hdkeypath=m/0'/1'/865' +L45JdFaTJe4TagqcsMCo7WHfKHyitKDG6eC5BoeiVF7yqfU4BG44 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qpzu3sq2ljmvmwr0rjdxjg0xal26yyh2qynadya0r5 hdkeypath=m/0'/0'/704' +L4ZfyviTt2Js2nH2X6ogGNoLnoauma9VfZYioga9f5iJzp1yKBsj 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qqff4nfsr7zwt3rw79u75gtnr72velcxqgn30n9dn9 hdkeypath=m/0'/1'/745' +Kz3oWqWx8p4bYT11rQG6GjADNC5ykuUtpRb7pBWQogQDGRYBQzyc 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qz85l6lfl7d7qzg9c3nmtg958kvfzqsgqgvv2pr0zs hdkeypath=m/0'/0'/456' +L5SZrXNW8Z9yvYdUkruhBNwcKEjJVHXT5zH78HXDYGCjM1HDoPRC 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qpcp3jvcxu89a5lxmdkns29ejxxn53fgqgv0l9ksna hdkeypath=m/0'/1'/515' +KzLmpFiekUUpwchKLw14m1nDsx5UVvzvEwU6PY7qCb8bFFdfgVue 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qzv0v5wq7vqxeq9fuz5rm8f7azu2g43wqgvrlkxzyf hdkeypath=m/0'/1'/885' +L4g6VoesU45xfbD1dpjKn4bSEQz26dumPD4vsTc4FFAyhphwxnbh 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qzp565dxv8efvmc3uh4z4h0ylds0cn36qgegl9fm5p hdkeypath=m/0'/0'/741' +Kxwbnh2SjK5yoZwPTZ69VP4J6fnTEYPFtPbys9S7xU8xFxq512Zm 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qqea3z09daw8tj25w47npuwx5lsp9njaqgwddrunhc hdkeypath=m/0'/1'/246' +L15Eg9gUugCAZvMRUi3282xb8Q9J7d5tiVawtJejZieHX457jgdk 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qrt7ej2m2hzu5yshuutm2pka6l8n0nnxqgqx92plfm hdkeypath=m/0'/1'/801' +L2K5S297cfQnEx4tkaSS1xan6N5HVoH5TARavLEgdQbcPhhPbEjT 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qqe2kanrhh720y00uk6c5w63rd43gr4hqgwkgrrn33 hdkeypath=m/0'/1'/737' +KyXyudwnHEDFvE1mVFY5m175Xw45BUF5bLMxJ1Xi5fHCqMoQ6kbk 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qqc0g42hx2czecul7u94ke7fh3mvh3ggqv3mrq8suu hdkeypath=m/0'/0'/518' +KyXJuaTB4JwhHFvp6pHHFhNUv5JHfjTc1CJpfX1SCvZqEkW6Mg4f 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qr9kx4a6puadxwtm3vjqtf5hkjaa56qwqv7nqnczgm hdkeypath=m/0'/1'/39' +KzcozomWBQgkGq1FB94UBqdBQeAaWHj6bP9wLwUVu7Vh2WiorTqF 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qp0p9t0075uf56nm2t2x2gc4xd7909ceqvccy6gsdp hdkeypath=m/0'/0'/376' +KwS6Ln2E3bFqYNW1y7AAfWXNSuJdTmksmi1a3nt4TULmRqg44rMC 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qpj7z3vx762z0m2cnf92xtc8yw5e65ftqv99x2u3nv hdkeypath=m/0'/1'/628' +L4BGf9Ri2ELjNgAyLipKX3j2vLst7t3jSK7XBhmk5cf2Z8SxAMEk 2024-09-28T09:00:52Z reserve=1 # addr=bitcoincash:qpmhuh97dpa00c2ha406fh7kcu6k68f0qvqnkj6d7c hdkeypath=m/0'/0'/892' diff --git a/test/scrambletest.py b/test/scrambletest.py index b0d5378e..751f3038 100755 --- a/test/scrambletest.py +++ b/test/scrambletest.py @@ -142,7 +142,8 @@ def make_coin_test_data(): coin, mmtype = id_str.split('_', 1) if '_' in id_str else (id_str, None) opts = list_gen( [f'--coin={coin}'], - [f'--type={mmtype}', mmtype] + [f'--type={mmtype}', mmtype], + [ '--cashaddr=0', coin == 'bch'] ) yield ('mmgen-addrgen', opts, [], [], test_data, 'address') diff --git a/test/test-release.d/cfg.sh b/test/test-release.d/cfg.sh index 4a023857..ed905fa8 100755 --- a/test/test-release.d/cfg.sh +++ b/test/test-release.d/cfg.sh @@ -197,10 +197,16 @@ init_tests() { " d_bch="overall operations with emulated RPC data (Bitcoin Cash Node)" - t_bch="- $cmdtest_py --coin=bch --exclude regtest" + t_bch=" + - $cmdtest_py --coin=bch --exclude regtest + - $cmdtest_py --coin=bch --cashaddr=0 ref3_addr + " d_bch_tn="overall operations with emulated RPC data (Bitcoin Cash Node testnet)" - t_bch_tn="- $cmdtest_py --coin=bch --testnet=1" + t_bch_tn=" + - $cmdtest_py --coin=bch --testnet=1 + - $cmdtest_py --coin=bch --testnet=1 --cashaddr=0 ref3_addr + " d_bch_rt="overall operations using the regtest network (Bitcoin Cash Node)" t_bch_rt="- $cmdtest_py --coin=bch regtest" @@ -286,6 +292,9 @@ init_tests() { a $gentest_py --coin=ltc --type=segwit 1 $REFDIR/litecoin/ltcwallet-segwit.dump a $gentest_py --coin=ltc --type=bech32 1 $REFDIR/litecoin/ltcwallet-bech32.dump a $gentest_py --coin=ltc --type=compressed --testnet=1 1 $REFDIR/litecoin/ltcwallet-testnet.dump + a $gentest_py --coin=bch --type=compressed --cashaddr=0 1 $REFDIR/bitcoin_cash/bchwallet.dump + a $gentest_py --coin=bch --type=compressed --cashaddr=1 1 $REFDIR/bitcoin_cash/bchwallet.dump + a $gentest_py --coin=bch --type=compressed --testnet=1 1 $REFDIR/bitcoin_cash/bchwallet-testnet.dump - # libsecp256k1 vs python-ecdsa: - $gentest_py --type=legacy 1:2 $rounds100x - $gentest_py --type=compressed 1:2 $rounds100x diff --git a/test/unit_tests_d/ut_bip_hd.py b/test/unit_tests_d/ut_bip_hd.py index c2cde3ea..39ac94ed 100755 --- a/test/unit_tests_d/ut_bip_hd.py +++ b/test/unit_tests_d/ut_bip_hd.py @@ -151,7 +151,7 @@ vectors_multicoin = { 'doge': 'DFX88RXpi4S4W24YVvuMgbdUcCAYNeEYGd', 'avax-c': '0x373731f4d885Fc7Da05498F9f0804a87A14F891b', 'ltc_bech32': 'ltc1q3uh5ga5cp9kkdfx6a52uymxj9keq4tpzep7er0', - 'bch_cashaddr': 'bitcoincash:qpqpcllprftg4s0chdgkpxhxv23wfymq3gj7n0a9vw', + 'bch_compressed': 'bitcoincash:qpqpcllprftg4s0chdgkpxhxv23wfymq3gj7n0a9vw', 'bsc_smart': '0x373731f4d885Fc7Da05498F9f0804a87A14F891b', 'bnb_beacon': 'bnb179c3ymltqm4utlp089zxqeta5dvn48a305rhe5', } diff --git a/test/unit_tests_d/ut_cashaddr.py b/test/unit_tests_d/ut_cashaddr.py index 304ba770..65a016d8 100755 --- a/test/unit_tests_d/ut_cashaddr.py +++ b/test/unit_tests_d/ut_cashaddr.py @@ -9,10 +9,23 @@ altcoin_dep = True from collections import namedtuple from mmgen.proto.bch.cashaddr import cashaddr_parse_addr, cashaddr_decode_addr, cashaddr_encode_addr +from mmgen.addr import CoinAddr -from ..include.common import vmsg +from ..include.common import cfg, vmsg + +from mmgen.protocol import init_proto +proto = init_proto(cfg, 'bch') # Source: https://upgradespecs.bitcoincashnode.org/cashaddr +alias_data = """ +1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a +1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy +16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r +3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq +3LDsS579y7sruadqu11beEJoTjdFiFCdX4 bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e +31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37 +""" + vec_data = """ F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9 20 0 bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2 @@ -78,6 +91,11 @@ class unit_tests: yield t(int(d[0]), int(d[1]), d[2], data) return list(gen()) + @property + def aliases(self): + t = namedtuple('aliases', ['legacy', 'cashaddr']) + return [t(*a.split()) for a in alias_data.splitlines() if a] + def encode(self, name, ut, desc='low-level address encoding'): data = None for v in self.vectors: @@ -99,3 +117,35 @@ class unit_tests: ret = cashaddr_decode_addr(v.addr) assert ret.bytes.hex() == v.data return True + + def coinaddr(self, name, ut, desc='CoinAddr class'): + for e in self.aliases: + for addr in ( + e.cashaddr.upper(), + e.cashaddr, + e.cashaddr.split(':')[1], + e.legacy, + ): + a = CoinAddr(proto, addr) + vmsg(addr) + assert e.legacy == a.views[1] + assert e.cashaddr == a.proto.cashaddr_pfx + ':' + a.views[0] + vmsg('') + return True + + def errors(self, name, ut, desc='error handling'): + # could do these in objtest.py: + def bad1(): a = CoinAddr(proto, self.aliases[0].cashaddr.replace('g','G')) + def bad2(): a = CoinAddr(proto, 'x' + self.aliases[0].cashaddr) + def bad3(): a = CoinAddr(proto, self.aliases[0].cashaddr[:-1]) + def bad4(): a = CoinAddr(proto, self.aliases[0].cashaddr[:-1]+'i') + def bad5(): a = CoinAddr(proto, self.aliases[0].cashaddr[:-1]+'x') + + ut.process_bad_data(( + ('case', 'ObjectInitError', 'mixed case', bad1), + ('prefix', 'ObjectInitError', 'invalid prefix', bad2), + ('data', 'ObjectInitError', 'too short', bad3), + ('b32 char', 'ObjectInitError', 'substring', bad4), + ('chksum', 'ObjectInitError', 'checksum', bad5), + )) + return True