cmdtest.py regtest: enable deterministic testing with descriptor wallet

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

View file

@ -37,12 +37,15 @@ opts_data = {
-n, --setup-no-stop-daemon Don't stop daemon after setup is finished
-q, --quiet Produce quieter output
-v, --verbose Produce more verbose output
-w, --wallet-src=DIR Initialize miner and tracking wallets from wallets in
DIR instead of creating new ones
""",
'notes': """
AVAILABLE COMMANDS
setup - set up Bob and Alice regtest mode
create_wallets - create Miner wallet and Bob and Alices tracking wallets
start - start the regtest coin daemon
stop - stop the regtest coin daemon
generate N - mine N blocks (defaults to 1)

View file

@ -56,6 +56,7 @@ class MMGenRegtest(MMGenObject):
coins = ('btc', 'bch', 'ltc')
usr_cmds = (
'setup',
'create_wallets',
'generate',
'send',
'start',
@ -95,12 +96,27 @@ class MMGenRegtest(MMGenObject):
async def rpc(self):
return await rpc_init(self.cfg, self.proto, backend=None, daemon=self.d)
@property
async def miner_bip_hd_node(self):
if not hasattr(self, '_miner_bip_hd_node'):
from ...cfg import Config
from ...bip_hd import BipHDNode
data = await self.rpc_call('gethdkeys', {'private': True}, wallet='miner')
self._miner_bip_hd_node = BipHDNode.from_path(
Config({'regtest': True}),
None,
"m/{}'/1'/0'/0/0".format(44 if self.proto.coin == 'BCH' else 84),
xprv = data[0]['xprv'],
addr_type = 'compressed' if self.proto.coin == 'BCH' else 'bech32',
no_path_checks = True)
return self._miner_bip_hd_node
@property
async def miner_addr(self):
if not hasattr(self, '_miner_addr'):
self._miner_addr = (
self.bdb_miner_addrs[self.coin] if self.bdb_wallet else
await self.rpc_call('getnewaddress', wallet='miner'))
(await self.miner_bip_hd_node).address)
return self._miner_addr
@property
@ -108,7 +124,7 @@ class MMGenRegtest(MMGenObject):
if not hasattr(self, '_miner_wif'):
self._miner_wif = (
self.bdb_miner_wif if self.bdb_wallet else
None)
(await self.miner_bip_hd_node).privkey.wif)
return self._miner_wif
def create_hdseed_wif(self):
@ -150,7 +166,19 @@ class MMGenRegtest(MMGenObject):
descriptors = not self.bdb_wallet,
load_on_startup = False)
async def setup(self):
async def copy_wallet(self, user):
gmsg(f'Copying {capfirst(user)}’s tracking wallet')
from shutil import copytree
src = os.path.join(self.cfg.wallet_src, user)
if not os.path.exists(src):
die(2, f'Source wallet ‘{os.path.abspath(src)}’ does not exist!')
dest = os.path.join(self.d.network_datadir, 'wallets', user)
copytree(src, dest)
async def create_wallets(self):
await self.setup(create_wallets_only=True)
async def setup(self, create_wallets_only=False):
try:
os.makedirs(self.d.datadir)
@ -167,7 +195,19 @@ class MMGenRegtest(MMGenObject):
self.d.start(silent=True)
for user in ('miner', 'bob', 'alice'):
await self.create_wallet(user)
if self.cfg.wallet_src:
await self.copy_wallet(user)
else:
await self.create_wallet(user)
if create_wallets_only:
msg(f"Wallets are in '{self.d.network_datadir}/wallets'")
msg('Stopping regtest daemon')
await self.rpc_call('stop')
return
if self.cfg.wallet_src:
await self.load_wallets()
# BCH and LTC daemons refuse to set HD seed with empty blockchain ("in IBD" error),
# so generate a block:
@ -204,6 +244,9 @@ class MMGenRegtest(MMGenObject):
async def start_daemon(self, *, reindex=False, silent=True):
self.init_daemon(reindex=reindex)
self.d.start(silent=silent)
await self.load_wallets()
async def load_wallets(self):
for user in ('miner', 'bob', 'alice'):
msg(f'Loading {capfirst(user)}’s wallet')
await self.rpc_call('loadwallet', user, start_daemon=False)

View file

@ -60,14 +60,14 @@ class CmdTestChainsplit(CmdTestRegtest):
def split_fork(self):
self.coin = 'B2X'
t = self.spawn('mmgen-regtest', ['fork', 'btc'])
t = self.spawn('mmgen-regtest', self.regtest_opts + ['fork', 'btc'])
t.expect('Creating fork from coin')
t.expect('successfully created')
t.ok()
def split_start(self, coin):
self.coin = coin
t = self.spawn('mmgen-regtest', ['bob'])
t = self.spawn('mmgen-regtest', self.regtest_opts + ['bob'])
t.expect('Starting')
t.expect('done')
t.ok()

View file

@ -27,7 +27,7 @@ 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, suf, fmt_list, is_hex_str
from mmgen.util import msg_r, die, gmsg, capfirst, suf, fmt_list, is_hex_str, fmt
from mmgen.protocol import init_proto
from mmgen.addrlist import AddrList
from mmgen.wallet import Wallet, get_wallet_cls
@ -505,6 +505,7 @@ class CmdTestRegtest(CmdTestBase, CmdTestShared):
self.test_rbf = True # tests are non-coin-dependent, so run just once for BTC
if cfg.test_suite_deterministic:
self.deterministic = True
omsg('Deterministic testing enabled!')
self.spawn_env['MMGEN_BOGUS_SEND'] = ''
self.write_to_tmpfile('wallet_password', rt_pw)
@ -548,6 +549,17 @@ class CmdTestRegtest(CmdTestBase, CmdTestShared):
rmtree(joinpath(self.tr.data_dir, 'regtest'))
except:
pass
if self.deterministic:
action = 'Loading'
if not os.path.exists('wallets/alice'):
die(1, fmt("""
Saved tracking wallets are required for deterministic testing.
Run mmgen-regtest create_wallets, move or copy the resulting wallet
directory to the repository root, and re-run the test.
"""))
else:
action = 'Creating'
t = self.spawn(
'mmgen-regtest',
self.regtest_opts
@ -556,7 +568,7 @@ class CmdTestRegtest(CmdTestBase, CmdTestShared):
no_passthru_opts = True)
t.expect('Starting')
for _ in range(3):
t.expect('Creating')
t.expect(action)
for _ in range(17):
t.expect('Mined')
t.expect('Setup complete')

View file

@ -588,7 +588,7 @@ class CmdTestSwap(CmdTestSwapMethods, CmdTestRegtest, CmdTestAutosignThreaded):
addr = next(s for s in strip_ansi_escapes(t.read()).splitlines() if 'C:1 No' in s).split()[3]
t = self.spawn(
'mmgen-regtest',
[coin_arg, 'send', addr, str(amt)],
self.regtest_opts + [coin_arg, 'send', addr, str(amt)],
no_passthru_opts = ['coin'],
no_msg = True)
return t