420 lines
14 KiB
Python
420 lines
14 KiB
Python
# MMGen Wallet, a terminal-based cryptocurrency wallet
|
|
# Copyright (C)2013-2026 The MMGen Project <mmgen@tuta.io>
|
|
# 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.cmdtest_d.xmrswap: Monero swap tests for the cmdtest.py test suite
|
|
"""
|
|
|
|
import json
|
|
|
|
from .xmrwallet import udata
|
|
from .xmr_autosign import CmdTestXMRCompat
|
|
from .swap import CmdTestSwap, CmdTestSwapMethods, create_cross_methods
|
|
|
|
class CmdTestXMRSwapMethods:
|
|
|
|
def _swaptxcreate_ui(
|
|
self,
|
|
t,
|
|
prompt,
|
|
*,
|
|
tx_type = 'swap',
|
|
inputs = '1',
|
|
interactive_fee = None,
|
|
file_desc = 'Unsigned transaction',
|
|
tweaks = [],
|
|
auto_chg_addr = None,
|
|
auto_dest_addr = None,
|
|
reload_quote = False,
|
|
menu_prompt = None,
|
|
expect_pats = []):
|
|
if auto_chg_addr:
|
|
t.expect(rf'Using.*{auto_chg_addr}.*as change address.*: ', 'y', regex=True)
|
|
if tx_type == 'registration':
|
|
if auto_dest_addr:
|
|
t.expect(rf'Using.*{auto_dest_addr}.*as destination address.*: ', 'y', regex=True)
|
|
t.expect('continue: ', '\n')
|
|
t.expect(prompt, 'q')
|
|
t.expect('spend from: ', f'{inputs}\n')
|
|
elif tx_type in ('swap', 'xmr_swap'):
|
|
t.expect(prompt, 'q')
|
|
t.expect('to spend: ', f'{inputs}\n')
|
|
t.expect('continue: ', '\n')
|
|
if tx_type != 'xmr_swap':
|
|
t.expect('(Y/n): ', 'y') # fee OK?
|
|
t.expect('(Y/n): ', 'y') # change OK?
|
|
if tx_type != 'xmr_swap':
|
|
t.expect('(y/N): ', 'n') # add comment?
|
|
t.expect('view: ', 't')
|
|
for pat in expect_pats:
|
|
t.expect(pat, regex=True)
|
|
t.expect('to continue: ', '\n')
|
|
t.expect('(y/N): ', 'y') # save?
|
|
t.written_to_file('Unsigned automount transaction')
|
|
return t
|
|
|
|
def _swaptxcreate_refreq(
|
|
self,
|
|
*,
|
|
args,
|
|
inputs,
|
|
auto_chg_addr = None,
|
|
auto_dest_addr = None,
|
|
expect_pats = None,
|
|
exit_val = 0):
|
|
return self._user_txcreate(
|
|
'bob',
|
|
progname = 'swaptxcreate',
|
|
opts = ['--monero-wallet-rpc-password=abc', '--memoless'],
|
|
ui_handler = self._swaptxcreate_ui,
|
|
ui_handler_args = {
|
|
'prompt': self.rune_menu_prompt,
|
|
'tx_type': 'registration',
|
|
'inputs': inputs,
|
|
'auto_chg_addr': auto_chg_addr,
|
|
'auto_dest_addr': auto_dest_addr,
|
|
'expect_pats': expect_pats},
|
|
exit_val = exit_val,
|
|
output_args = args)
|
|
|
|
def _swaptxcreate_refnum(
|
|
self,
|
|
*,
|
|
args,
|
|
inputs,
|
|
tx_type = 'swap',
|
|
add_opts = [],
|
|
auto_chg_addr = None,
|
|
auto_dest_addr = None,
|
|
prompt = None,
|
|
expect_pats = None,
|
|
exit_val = 0):
|
|
data = json.loads(self.read_from_tmpfile('memo_info.json'))
|
|
return self._user_txcreate(
|
|
'bob',
|
|
progname = 'swaptxcreate',
|
|
opts = ['--monero-wallet-rpc-password=abc', f'--refnum={data["reference"]}'] + add_opts,
|
|
exit_val = exit_val,
|
|
ui_handler = self._swaptxcreate_ui,
|
|
ui_handler_args = {
|
|
'prompt': prompt or self.menu_prompt,
|
|
'tx_type': tx_type,
|
|
'inputs': inputs,
|
|
'auto_chg_addr': auto_chg_addr,
|
|
'auto_dest_addr': auto_dest_addr,
|
|
'expect_pats': expect_pats},
|
|
output_args = args)
|
|
|
|
def _swaptxsend_refnum(self, expect_pat, *, add_opts=[], is_xmr=False, no_wait=False):
|
|
return self._user_txsend(
|
|
'bob',
|
|
add_opts = ['--monero-wallet-rpc-password=abc'] + add_opts,
|
|
expect_pat = expect_pat,
|
|
is_xmr = is_xmr,
|
|
no_wait = no_wait,
|
|
need_rbf = True)
|
|
|
|
class CmdTestXMRSwap(CmdTestXMRSwapMethods, CmdTestSwap):
|
|
'Monero swap operations'
|
|
|
|
bdb_wallet = True
|
|
tmpdir_nums = [77]
|
|
networks = ('ltc',)
|
|
thornode_servers = ('rpc', 'swap')
|
|
cross_group = 'xmrswap_xmr'
|
|
cross_coin = 'xmr'
|
|
refreq_txid = 'ae04ecd8ff1f0c13861bd2d6123ce33ea03a731ccd142f5ca2f2450a54f215ba'
|
|
refreq_txid_xmr = 'fbbbd382edd2d5478e0316b6c31ef6a0bbad57bcebc9d8b30cc52e5dbf107622'
|
|
xmr_prompt = 'efresh balances:\b'
|
|
|
|
cmd_group_in = (
|
|
('subgroup.ltc_setup', []),
|
|
('subgroup.rune_setup', ['ltc_setup']),
|
|
('subgroup.autosign_setup', []),
|
|
('subgroup.xmr_setup', ['ltc_setup', 'autosign_setup']),
|
|
('subgroup.wait_loop', ['autosign_setup']),
|
|
('subgroup.swap', ['rune_setup', 'xmr_setup', 'wait_loop']),
|
|
('subgroup.xmr_swap', ['swap']),
|
|
('xmr_wait_loop_kill', ''),
|
|
('xmr_stop_daemons', ''))
|
|
|
|
cmd_subgroups = {
|
|
'ltc_setup': (
|
|
'setting up Bob’s MMGen wallet and tracking wallet',
|
|
('setup', 'regtest (Bob and Alice) mode setup'),
|
|
('walletconv_bob', 'MMGen wallet creation (Bob)'),
|
|
('addrgen_bob', 'address generation (Bob)'),
|
|
('addrimport_bob', 'importing Bob’s addresses'),
|
|
('bob_import_miner_addr', 'importing miner’s coinbase addr into Bob’s wallet'),
|
|
('fund_bob1', 'funding Bob’s wallet'),
|
|
('fund_bob2', 'funding Bob’s wallet'),
|
|
('fund_bob3', 'funding Bob’s wallet'),
|
|
('generate_ltc', 'mining a block'),
|
|
('bob_bal1', 'Bob’s balance'),
|
|
),
|
|
'rune_setup': (
|
|
'setting up Bob’s RUNE tracking wallet',
|
|
('rune_addrgen', 'generating RUNE addresses'),
|
|
('rune_addrimport', 'importing RUNE addresses'),
|
|
('rune_bal_refresh', 'refreshing balances in RUNE tracking wallet'),
|
|
),
|
|
'autosign_setup': (
|
|
'setting up offline autosigning',
|
|
('xmr_autosign_setup', ''),
|
|
('xmr_autosign_xmr_setup', ''),
|
|
),
|
|
'xmr_setup': (
|
|
'setting up miner’s and Bob’s XMR tracking wallets',
|
|
('xmr_addrimport_user', ''),
|
|
('xmr_gen_kafile_miner', ''),
|
|
('xmr_gen_vkafile_user', ''),
|
|
('xmr_create_wallet_miner', ''),
|
|
('xmr_mine_initial_coins', ''),
|
|
('xmr_fund_user1', ''),
|
|
('xmr_mine_blocks_10', ''),
|
|
('xmr_new_address_user1', ''),
|
|
('xmr_new_address_user2', ''),
|
|
('xmr_new_account_user', ''),
|
|
('xmr_mine_blocks_10', ''),
|
|
('xmr_user_listaddresses_refresh', ''),
|
|
),
|
|
'wait_loop': (
|
|
'starting the autosign wait loop',
|
|
('xmr_wait_loop_start_compat', ''),
|
|
),
|
|
'swap': (
|
|
'swap operations (LTC -> XMR)',
|
|
('remote_get_refnum', 'getting the reference number'),
|
|
('swaptxcreate_refreq1', 'creating an LTC->XMR registration transaction (auto chg, auto dest)'),
|
|
('swaptxcreate_abort1', 'aborting the transaction'),
|
|
('swaptxcreate_refreq2', 'creating an LTC->XMR registration transaction (auto chg, 1:0)'),
|
|
('swaptxcreate_abort2', 'aborting the transaction'),
|
|
('swaptxcreate_refreq3', 'creating an LTC->XMR registration transaction (auto chg, mmid dest)'),
|
|
('swaptxsend_refreq1', 'sending the transaction'),
|
|
('swaptxcreate_refreq4', 'creating an LTC->XMR registration transaction (auto chg, 1:1)'),
|
|
('swaptxsend_refreq2', 'sending the transaction'),
|
|
('swaptxcreate_refnum1', 'creating an LTC->XMR swap transaction (auto chg, auto dest)'),
|
|
('swaptxsend_refnum1', 'sending the swap transaction'),
|
|
('generate_ltc', 'generating a block'),
|
|
('swaptxcreate_refnum2', 'creating an LTC->XMR swap transaction (no chg, auto dest)'),
|
|
('swaptxsend_refnum2', 'sending the swap transaction'),
|
|
('generate_ltc', 'generating a block'),
|
|
('bob_bal2', 'Bob’s balance')),
|
|
'xmr_swap': (
|
|
'swap operations (XMR -> LTC)',
|
|
('remote_get_refnum_xmr', 'getting the reference number'),
|
|
('swaptxcreate_refreq7_bad', 'creating an XMR->LTC registration transaction (bad, no amount)'),
|
|
('swaptxcreate_refreq7', 'creating an XMR->LTC registration transaction (auto chg, auto dest)'),
|
|
('swaptxsend_refreq7', 'sending the transaction'),
|
|
('swaptxcreate_refnum7_bad', 'creating an XMR->LTC swap transaction (bad, no amount)'),
|
|
('swaptxcreate_refnum7', 'creating an XMR->LTC swap transaction (auto chg, auto dest)'),
|
|
('xmr_swaptxview_refnum7', ''),
|
|
('swaptxsend_refnum7', 'sending the transaction'),
|
|
('swaptxstatus_refnum7a', 'getting the transaction status (mempool)'),
|
|
('xmr_mine_blocks_10', ''),
|
|
('swaptxstatus_refnum7b', 'getting the transaction status (confirmations)'),
|
|
('swaptxstatus_refnum7c', 'getting the transaction status (--verbose)'))}
|
|
|
|
exec(create_cross_methods(cross_coin, cross_group, cmd_group_in, cmd_subgroups))
|
|
|
|
def __init__(self, cfg, trunner, cfgs, spawn):
|
|
|
|
super().__init__(cfg, trunner, cfgs, spawn)
|
|
|
|
self.spawn_env['MMGEN_TEST_SUITE_ENABLE_USER_PORT_SHIFT'] = '1'
|
|
|
|
if not trunner:
|
|
return
|
|
|
|
globals()[self.cross_group] = self.create_cross_runner(trunner, add_cfg={})
|
|
|
|
def fund_bob1(self):
|
|
return self.fund_wallet('bob', 500, mmtype='C')
|
|
|
|
def fund_bob2(self):
|
|
return self.fund_wallet('bob', 50, mmtype='S')
|
|
|
|
def fund_bob3(self):
|
|
return self.fund_wallet('bob', 25, mmtype='B')
|
|
|
|
def bob_bal1(self):
|
|
return self._bob_bal(0, '575')
|
|
|
|
def swaptxcreate_refreq1(self):
|
|
return self._swaptxcreate_refreq(
|
|
args = ['LTC', '5.4321', 'XMR'],
|
|
inputs = '3',
|
|
auto_chg_addr = '98831F3A:B:2',
|
|
auto_dest_addr = '98831F3A:M:1/0/1',
|
|
expect_pats = ['88u4waXXrPUD9EkCGMcFif', r'to spend:.*\b0\b.*RUNE'])
|
|
|
|
def swaptxcreate_abort1(self):
|
|
return self._txsend_abort()
|
|
|
|
def swaptxcreate_refreq2(self):
|
|
return self._swaptxcreate_refreq(
|
|
args = ['LTC', '5.4321', 'XMR', '1:0'],
|
|
inputs = '3',
|
|
auto_chg_addr = '98831F3A:B:2',
|
|
auto_dest_addr = '98831F3A:M:1/0/1',
|
|
expect_pats = ['88u4waXXrPUD9EkCGMcFif', r'to spend:.*\b0\b.*RUNE'])
|
|
|
|
def swaptxcreate_abort2(self):
|
|
return self._txsend_abort()
|
|
|
|
def swaptxcreate_refreq3(self):
|
|
return self._swaptxcreate_refreq(
|
|
args = ['LTC', '5.4321', 'XMR', '98831F3A:M:1/0/1'],
|
|
inputs = '3',
|
|
auto_chg_addr = '98831F3A:B:2',
|
|
expect_pats = ['88u4waXXrPUD9EkCGMcFif', r'to spend:.*\b0\b.*RUNE'])
|
|
|
|
def swaptxsend_refreq1(self):
|
|
return self._user_txsend('bob', need_rbf=True)
|
|
|
|
def swaptxcreate_refreq4(self):
|
|
return self._swaptxcreate_refreq(
|
|
args = ['LTC', '5.4321', 'XMR', '1:1'],
|
|
inputs = '3',
|
|
auto_chg_addr = '98831F3A:B:2',
|
|
auto_dest_addr = '98831F3A:M:1/1/0',
|
|
expect_pats = ['89NVJJPWPw18P7XoqiYgj4', r'to spend:.*\b0\b.*RUNE'])
|
|
|
|
swaptxsend_refreq2 = swaptxsend_refreq1
|
|
|
|
def swaptxcreate_refnum1(self):
|
|
return self._swaptxcreate_refnum(
|
|
args = ['LTC', '5.4321', 'XMR'],
|
|
inputs = '1',
|
|
auto_chg_addr = '98831F3A:B:2',
|
|
expect_pats = ['88u4waXXrPUD9EkCGMcFif', r'Spend amount:.*\b5\.43198765\b.*LTC'])
|
|
|
|
def swaptxsend_refnum1(self):
|
|
return self._swaptxsend_refnum(r'Spend amount:.*\b5\.43198765\b.*LTC')
|
|
|
|
def swaptxcreate_refnum2(self):
|
|
return self._swaptxcreate_refnum(
|
|
args = ['LTC', 'XMR'],
|
|
inputs = '2',
|
|
expect_pats = ['88u4waXXrPUD9EkCGMcFif', r'Spend amount:.*24\.99998765'])
|
|
|
|
def swaptxsend_refnum2(self):
|
|
return self._swaptxsend_refnum(r'Spend amount:.*24\.99998765')
|
|
|
|
def bob_bal2(self):
|
|
return self._bob_bal(0, '544.56799927')
|
|
|
|
def remote_get_refnum_xmr(self):
|
|
return self._remote_get_refnum(self.refreq_txid_xmr)
|
|
|
|
def swaptxcreate_refreq7_bad(self):
|
|
return self._swaptxcreate_refreq_xmr(['XMR', 'LTC'], exit_val=1)
|
|
|
|
def swaptxcreate_refreq7(self):
|
|
return self._swaptxcreate_refreq_xmr(['XMR', '0.2345', 'LTC'])
|
|
|
|
def _swaptxcreate_refreq_xmr(self, args, *, exit_val=0):
|
|
return self._swaptxcreate_refreq(
|
|
args = args,
|
|
inputs = '3',
|
|
exit_val = exit_val,
|
|
auto_dest_addr = '98831F3A:B:3',
|
|
expect_pats = ['rltc1q6rs4znw8k', r'to spend:.*\b0\b.*RUNE'])
|
|
|
|
def swaptxsend_refreq7(self):
|
|
return self._user_txsend('bob', need_rbf=True)
|
|
|
|
def swaptxcreate_refnum7_bad(self):
|
|
return self._swaptxcreate_refnum_xmr(['XMR', 'LTC'], exit_val=1)
|
|
|
|
def swaptxcreate_refnum7(self):
|
|
from mmgen.rpc import get_remote_rpc # pylint: disable=no-name-in-module
|
|
from mmgen.protocol import init_proto
|
|
rpc = get_remote_rpc(self.cfg, init_proto(self.cfg, 'RUNE', network='regtest'))
|
|
vault_addr = rpc.inbound_addrs(coin='XMR')['address']
|
|
return self._swaptxcreate_refnum_xmr(
|
|
['XMR', '0.2345', 'LTC'],
|
|
add_expect_pats = [rf'Vault:.*{vault_addr}'])
|
|
|
|
def _swaptxcreate_refnum_xmr(self, args, *, exit_val=0, add_expect_pats=[]):
|
|
return self._swaptxcreate_refnum(
|
|
add_opts = ['--priority=3'],
|
|
args = args,
|
|
inputs = '1',
|
|
tx_type = 'xmr_swap',
|
|
prompt = self.xmr_prompt,
|
|
exit_val = exit_val,
|
|
auto_dest_addr = '98831F3A:B:3',
|
|
expect_pats = [r'Amount:.*0.234499978787', r'Priority:.*high'] + add_expect_pats)
|
|
|
|
def swaptxsend_refnum7(self):
|
|
return self._swaptxsend_refnum(r'Amount:.*0.234499978787', is_xmr=True)
|
|
|
|
def swaptxstatus_refnum7a(self):
|
|
return self._swaptxsend_refnum('in mempool', add_opts=['--status'], no_wait=True)
|
|
|
|
def swaptxstatus_refnum7b(self):
|
|
return self._swaptxsend_refnum('confirmations', add_opts=['--status'], no_wait=True)
|
|
|
|
def swaptxstatus_refnum7c(self):
|
|
return self._swaptxsend_refnum(None, add_opts=['--status', '--verbose'], no_wait=True)
|
|
|
|
class CmdTestXMRSwapXMR(CmdTestXMRSwapMethods, CmdTestSwapMethods, CmdTestXMRCompat):
|
|
'Monero swap operations - Monero wallet'
|
|
networks = ('xmr',)
|
|
extra_daemons = ()
|
|
tmpdir_nums = [77]
|
|
is_helper = True
|
|
tx_relay_user = None
|
|
user = 'bob'
|
|
user_data = (
|
|
# user sid autosign kal_range add_coind_args
|
|
udata('miner', 'FE3C6545', False, '1', []),
|
|
udata('bob', '98831F3A', True, '1-2', []))
|
|
|
|
cmd_group = CmdTestXMRCompat.cmd_group + (
|
|
('user_txcreate_sweep1', 'creating a sweep transaction (address sweep)'),
|
|
('new_address_user1', 'adding an address to user’s wallet (acct 0)'),
|
|
('new_address_user2', 'adding an address to user’s wallet (acct 0)'),
|
|
('new_account_user', 'adding an account to user’s wallet'),
|
|
('swaptxview_refnum7', 'viewing the transaction'),
|
|
('user_listaddresses_refresh', 'listing the user’s wallet, with refresh'))
|
|
|
|
def user_txcreate_sweep1(self):
|
|
return self._user_txops('txcreate', menu='s', sweep_menu='1', sweep_type='sweep')
|
|
|
|
def _new_address_user(self, wallet, acct, action):
|
|
return self._new_addr_user_autosign(
|
|
f'{wallet}:{acct}',
|
|
action,
|
|
rf'Creating new address for wallet .*{wallet}.*, account .*#{acct}.* with label '
|
|
r'.*‘xmrwallet new address .*y/N\): ')
|
|
|
|
def new_address_user1(self):
|
|
return self._new_address_user(1, 0, 'start')
|
|
|
|
def new_address_user2(self):
|
|
return self._new_address_user(1, 0, 'continue')
|
|
|
|
def new_account_user(self):
|
|
return self._new_addr_user_autosign(
|
|
'1',
|
|
'stop',
|
|
r'Creating new account for wallet .*1.* with label '
|
|
r'.*‘xmrwallet new account .*y/N\): ')
|
|
|
|
def swaptxview_refnum7(self):
|
|
self.insert_device_online()
|
|
self.do_mount_online()
|
|
tdir = self.asi_online.mountpoint / 'txauto'
|
|
files = [str(p) for p in tdir.glob('*XMR*rawtx')]
|
|
t = self.spawn('mmgen-tool', ['txview'] + files)
|
|
t.read()
|
|
self.do_umount_online()
|
|
self.remove_device_online()
|
|
return t
|