diff --git a/mmgen/proto/eth/misc.py b/mmgen/proto/eth/misc.py index 130a84cd..8d47c986 100755 --- a/mmgen/proto/eth/misc.py +++ b/mmgen/proto/eth/misc.py @@ -71,3 +71,8 @@ def ec_recover_pubkey(cfg, message, sig, msghash_type): *ecdsa_raw_recover( hash_message(cfg, message, msghash_type), tuple(int(hexstr, 16) for hexstr in (v, r, s))) ) + +def compute_contract_addr(cfg, deployer_addr, nonce): + from . import rlp + encoded = rlp.encode([bytes.fromhex(deployer_addr), nonce]) + return get_keccak(cfg)(encoded).hexdigest()[-40:] diff --git a/test/cmdtest_d/automount_eth.py b/test/cmdtest_d/automount_eth.py index 4bab78f5..771ac1c3 100755 --- a/test/cmdtest_d/automount_eth.py +++ b/test/cmdtest_d/automount_eth.py @@ -14,11 +14,11 @@ test.cmdtest_d.automount_eth: Ethereum automount autosigning tests for the cmdte import os, re from .autosign import CmdTestAutosignThreaded -from .ethdev import CmdTestEthdev, parity_devkey_fn +from .ethdev import CmdTestEthdev, CmdTestEthdevMethods from .include.common import dfl_words_file -from ..include.common import cfg +from ..include.common import cfg, joinpath -class CmdTestAutosignETH(CmdTestAutosignThreaded, CmdTestEthdev): +class CmdTestAutosignETH(CmdTestAutosignThreaded, CmdTestEthdev, CmdTestEthdevMethods): 'automounted transacting operations for Ethereum via ethdev' networks = ('eth', 'etc') @@ -41,8 +41,8 @@ class CmdTestAutosignETH(CmdTestAutosignThreaded, CmdTestEthdev): ('token_deploy1c', 'deploying ERC20 token #1 (Token)'), ('tx_status2', 'getting the transaction status'), ('token_fund_user', 'transferring token funds from dev to user'), - ('token_addrgen_addr1', 'generating token addresses'), - ('token_addrimport_addr1', 'importing token addresses using token address (MM1)'), + ('token_addrgen', 'generating token addresses'), + ('token_addrimport', 'importing token addresses using token address (MM1)'), ('token_bal1', f'the {cfg.coin} balance and token balance'), ('create_token_tx', 'creating a token transaction'), ('send_token_tx', 'sending a token transaction'), @@ -62,33 +62,11 @@ class CmdTestAutosignETH(CmdTestAutosignThreaded, CmdTestEthdev): self.txop_opts = ['--autosign', '--regtest=1', '--quiet'] def fund_mmgen_address(self): - keyfile = os.path.join(self.tmpdir, parity_devkey_fn) - t = self.spawn( - 'mmgen-txdo', - self.eth_args - + [f'--keys-from-file={keyfile}'] - + ['--fee=40G', '98831F3A:E:1,123.456', dfl_words_file], - ) - t.expect('efresh balance:\b', 'q') - t.expect('from: ', '10') - t.expect('(Y/n): ', 'y') - t.expect('(Y/n): ', 'y') - t.expect('(y/N): ', 'n') - t.expect('view: ', 'n') - t.expect('confirm: ', 'YES') - return t + return self._fund_mmgen_address(arg='98831F3A:E:1,123.456') def create_tx(self): self.insert_device_online() - t = self.spawn('mmgen-txcreate', self.txop_opts + ['-B', '98831F3A:E:11,54.321']) - t = self.txcreate_ui_common( - t, - caller = 'txcreate', - input_sels_prompt = 'to spend from', - inputs = '1', - file_desc = 'transaction', - interactive_fee = '50G', - fee_desc = 'transaction fee or gas price') + t = self._create_tx(fee='50G', args=['98831F3A:E:11,54.321'], add_opts=self.txop_opts) t.read() self.remove_device_online() return t @@ -96,49 +74,38 @@ class CmdTestAutosignETH(CmdTestAutosignThreaded, CmdTestEthdev): def run_autosign_setup(self): return self.run_setup(mn_type='bip39', mn_file='test/ref/98831F3A.bip39', use_dfl_wallet=None) - def send_tx(self, add_args=[]): + def send_tx(self): self._wait_signed('transaction') self.insert_device_online() - t = self.spawn('mmgen-txsend', self.txop_opts + add_args, no_passthru_opts=['coin']) - t.view_tx('t') - t.expect('(y/N): ', 'n') - self._do_confirm_send(t, quiet=True) - t.written_to_file('Sent automount transaction') + t = self._send_tx(desc='automount transaction', add_opts=self.txop_opts) t.read() self.remove_device_online() return t - def token_fund_user(self): - return self.token_transfer_ops(op='do_transfer', num_tokens=1) + def token_addrgen(self): + return self._token_addrgen(mm_idxs=[11], naddrs=3) - def token_addrgen_addr1(self): - return self.token_addrgen(num_tokens=1) + def token_addrimport(self): + return self._token_addrimport('token_addr1', '11-13', expect='3/3') + + def token_fund_user(self): + return self._token_transfer_ops(op='fund_user', mm_idxs=[11]) def token_bal1(self): - return self.token_bal(pat=r':E:11\s+1000\s+54\.321\s+') + return self._bal_check(pat=r':E:11\s+1000\s+54\.321\s+') def token_bal2(self): - return self.token_bal(pat=r':E:11\s+998.76544\s+54.318\d+\s+.*:E:12\s+1\.23456\s+') - - def token_bal(self, pat): - self.mining_delay() - t = self.spawn('mmgen-tool', ['--regtest=1', '--token=mm1', 'twview', 'wide=1']) - text = t.read(strip_color=True) - assert re.search(pat, text, re.DOTALL), f'output failed to match regex {pat}' - return t + return self._bal_check(pat=r':E:11\s+998.76544\s+54.318\d+\s+.*:E:12\s+1\.23456\s+') def create_token_tx(self): self.insert_device_online() - t = self.txcreate_ui_common( - self.spawn( - 'mmgen-txcreate', - self.txop_opts + ['--token=MM1', '-B', '--fee=50G', '98831F3A:E:12,1.23456']), - inputs = '1', - input_sels_prompt = 'to spend from', - file_desc = 'Unsigned automount transaction') + t = self._create_token_tx( + cmd = 'txcreate', + fee = '50G', + args = ['98831F3A:E:12,1.23456'], + add_opts = self.txop_opts) t.read() self.remove_device_online() return t - def send_token_tx(self): - return self.send_tx() + send_token_tx = send_tx diff --git a/test/cmdtest_d/ethdev.py b/test/cmdtest_d/ethdev.py index 035e792a..63377ea6 100755 --- a/test/cmdtest_d/ethdev.py +++ b/test/cmdtest_d/ethdev.py @@ -26,8 +26,9 @@ from collections import namedtuple from subprocess import run, PIPE, DEVNULL from pathlib import Path -from mmgen.color import yellow, blue, cyan, set_vt100 +from mmgen.color import red, yellow, blue, cyan, set_vt100 from mmgen.util import msg, rmsg, die +from mmgen.proto.eth.misc import compute_contract_addr from ..include.common import ( cfg, @@ -43,8 +44,8 @@ from ..include.common import ( silence, end_silence, gr_uc, - stop_test_daemons -) + stop_test_daemons) + from .include.common import ( ref_dir, dfl_words_file, @@ -54,8 +55,9 @@ from .include.common import ( tw_comment_lat_cyr_gr, get_file_with_ext, ok_msg, - Ctrl_U -) + Ctrl_U, + cleanup_env) + from .base import CmdTestBase from .shared import CmdTestShared from .httpd.etherscan import EtherscanServer @@ -120,7 +122,227 @@ def set_vbals(daemon_id): coin = cfg.coin -class CmdTestEthdev(CmdTestBase, CmdTestShared): +class CmdTestEthdevMethods: # mixin class + + def _addrgen(self, addrs='1-3,11-13,21-23', no_msg=False): + t = self.spawn( + 'mmgen-addrgen', + [f'--coin={self.proto.coin}'] + self.eth_args + [dfl_words_file, addrs], + no_msg = no_msg, + no_passthru_opts = True) + t.written_to_file('Addresses') + return t + + def _create_tx(self, *, fee, args, add_opts=[]): + return self.txcreate_ui_common( + self.spawn('mmgen-txcreate', add_opts + ['-B'] + args), + caller = 'txcreate', + input_sels_prompt = 'to spend from', + inputs = '1', + file_desc = 'transaction', + interactive_fee = fee, + fee_desc = 'transaction fee or gas price') + + def _send_tx(self, *, desc='transaction', add_opts=[]): + t = self.spawn('mmgen-txsend', add_opts, no_passthru_opts=['coin']) + t.view_tx('t') + t.expect('(y/N): ', 'n') + self._do_confirm_send(t, quiet=True) + t.written_to_file(f'Sent {desc}') + return t + + def _txdo(self, *, args, acct, fee='50G'): + t = self.txcreate( + args, + acct = acct, + caller = 'txdo', + no_read = True, + bad_input_sels = False, + interactive_fee = fee, + print_listing = False) + t.written_to_file('Signed transaction') + t.expect('confirm: ', 'YES\n') + return t + + def _txbump(self, *, fee, ext, add_opts=[], add_args=[]): + ext = ext.format('-α' if self.cfg.debug_utf8 else '') + txfile = self.get_file_with_ext(ext, no_dot=True) + t = self.spawn('mmgen-txbump', self.eth_args + add_opts + ['--yes', txfile] + add_args) + t.expect('or gas price: ', fee+'\n') + return t + + def _tx_receipt(self, ext='{}.regtest.sigtx'): + self.mining_delay() + return self.txsend( + ext = ext, + add_args = ['--receipt'], + return_early = True, + env = cleanup_env(cfg=self.cfg)) + + def _fund_mmgen_address(self, arg): + return self._txdo( + args = [f'--keys-from-file={joinpath(self.tmpdir, parity_devkey_fn)}', arg, dfl_words_file], + acct = '10') + + def _token_addrgen(self, *, mm_idxs, naddrs): + self.spawn(msg_only=True) + for idx in mm_idxs: + t = self._addrgen(addrs=f'{idx}-{idx+naddrs-1}', no_msg=True) + return t + + def _token_addrimport(self, addr_file, addr_range, expect, extra_args=[]): + token_addr = self.read_from_tmpfile(addr_file).strip() + return self.addrimport( + ext = f'[{addr_range}]{{}}.regtest.addrs', + expect = expect, + add_args = ['--token-addr='+token_addr]+extra_args) + + def _get_contract_address(self, deployer_addr): + t = self.spawn( + 'mmgen-cli', + ['--regtest=1', 'eth_getTransactionCount', '0x'+deployer_addr, 'pending'], + env = cleanup_env(cfg=self.cfg), + silent = True, + no_msg = True) + nonce = t.read().strip() + imsg(f'Nonce: {red(nonce)}') + ret = compute_contract_addr(self.cfg, deployer_addr, int(nonce, 16)) + imsg(f'Computed contract address: {blue(ret)}') + return ret + + async def _token_deploy(self, num, key, gas, mmgen_cmd='txdo', gas_price='8G', get_receipt=True): + keyfile = joinpath(self.tmpdir, parity_devkey_fn) + fn = joinpath(self.tmpdir, 'mm'+str(num), key+'.bin') + args = [ + '-B', + f'--fee={gas_price}', + f'--gas={gas}', + f'--contract-data={fn}', + f'--inputs={dfl_devaddr}', + '--yes', + ] + + contract_addr = self._get_contract_address(dfl_devaddr) + if key == 'Token': + self.write_to_tmpfile(f'token_addr{num}', contract_addr+'\n') + + if mmgen_cmd == 'txdo': + args += ['-k', keyfile] + t = self.spawn('mmgen-'+mmgen_cmd, self.eth_args + args) + if mmgen_cmd == 'txcreate': + t.written_to_file('transaction') + ext = '[0,8000]{}.regtest.rawtx'.format('-α' if self.cfg.debug_utf8 else '') + txfile = self.get_file_with_ext(ext, no_dot=True) + t = self.spawn( + 'mmgen-txsign', + self.eth_args + ['--yes', '-k', keyfile, txfile], no_msg=True, no_passthru_opts=['coin']) + self.txsign_ui_common(t, ni=True) + + txfile = txfile.replace('.rawtx', '.sigtx') + t = self.spawn('mmgen-txsend', + self.eth_args + [txfile], no_msg=True, no_passthru_opts=['coin']) + + txid = self.txsend_ui_common(t, + caller = mmgen_cmd, + quiet = mmgen_cmd == 'txdo' or not self.cfg.debug, + bogus_send = False) + + _ = strip_ansi_escapes(t.expect_getend('Contract address: ')) + assert _ == contract_addr, f'Contract address mismatch: {_} != {contract_addr}' + + if get_receipt: + if (await self.get_tx_receipt(txid)).status == 0: + die(2, f'Contract {num}:{key} failed to execute. Aborting') + + if key == 'Token': + imsg(f'\nToken MM{num} deployed!') + + return t + + async def _token_deploy_math(self, *, num, get_receipt=True, mmgen_cmd='txdo'): + return await self._token_deploy( + num=num, key='SafeMath', gas=500_000, get_receipt=get_receipt, mmgen_cmd=mmgen_cmd) + + async def _token_deploy_owned(self, *, num, get_receipt=True): + return await self._token_deploy( + num=num, key='Owned', gas=1_000_000, get_receipt=get_receipt) + + async def _token_deploy_token(self, *, num, get_receipt=True): + return await self._token_deploy( + num=num, key='Token', gas=4_000_000, gas_price='7G', get_receipt=get_receipt) + + def _bal_check(self, *, pat): + self.mining_delay() + t = self.spawn('mmgen-tool', ['--regtest=1', '--token=mm1', 'twview', 'wide=1']) + text = t.read(strip_color=True) + assert re.search(pat, text, re.DOTALL), f'output failed to match regex {pat}' + return t + + def _create_token_tx(self, *, cmd, fee, args, add_opts=[]): + return self.txcreate_ui_common( + self.spawn( + f'mmgen-{cmd}', + ['--token=MM1', '-B', f'--fee={fee}'] + add_opts + args), + inputs = '1', + input_sels_prompt = 'to spend from', + caller = cmd, + file_desc = 'Unsigned automount transaction') + + async def _token_transfer_ops(self, *, op, mm_idxs, amt=1000, get_receipt=True, sid=dfl_sid): + self.spawn(msg_only=True) + from mmgen.tool.wallet import tool_cmd + usr_mmaddrs = [f'{sid}:E:{i}' for i in mm_idxs] + + from mmgen.proto.eth.contract import ResolvedToken + async def fund_user(rpc): + for i in range(len(usr_mmaddrs)): + tk = await ResolvedToken( + self.cfg, + self.proto, + rpc, + self.read_from_tmpfile(f'token_addr{i+1}').strip()) + imsg_r('\n' + await tk.info()) + imsg('dev token balance (pre-send): {}'.format(await tk.get_balance(dfl_devaddr))) + imsg(f'Sending {amt} {self.proto.dcoin} to address {usr_addrs[i]} ({usr_mmaddrs[i]})') + txid = await tk.transfer( + from_addr = dfl_devaddr, + to_addr = usr_addrs[i], + amt = amt, + key = dfl_devkey, + gas = self.proto.coin_amt(120000, from_unit='wei'), + gasPrice = self.proto.coin_amt(8, from_unit='Gwei')) + + if get_receipt and (await self.get_tx_receipt(txid)).status == 0: + die(2, 'Transfer of token funds failed. Aborting') + + async def show_bals(rpc): + for i in range(len(usr_mmaddrs)): + tk = await ResolvedToken( + self.cfg, + self.proto, + rpc, + self.read_from_tmpfile(f'token_addr{i+1}').strip()) + imsg('Token: {}'.format(await tk.get_symbol())) + imsg(f'dev token balance: {await tk.get_balance(dfl_devaddr)}') + imsg('usr token balance: {} ({} {})'.format( + await tk.get_balance(usr_addrs[i]), + usr_mmaddrs[i], + usr_addrs[i])) + + def gen_addr(addr): + return tool_cmd( + self.cfg, cmdname='gen_addr', proto=self.proto).gen_addr(addr, wallet=dfl_words_file) + + silence() + usr_addrs = list(map(gen_addr, usr_mmaddrs)) + if op == 'show_bals': + await show_bals(await self.rpc) + elif op == 'fund_user': + await fund_user(await self.rpc) + end_silence() + return 'ok' + +class CmdTestEthdev(CmdTestBase, CmdTestShared, CmdTestEthdevMethods): 'Ethereum transacting, token deployment and tracking wallet operations' networks = ('eth', 'etc') passthru_opts = ('coin', 'daemon_id', 'eth_daemon_id', 'http_timeout', 'rpc_backend') @@ -595,7 +817,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): make_key() signer_addr = self.keystore_data['address'] - self.write_to_tmpfile( 'signer_addr', signer_addr + '\n') + self.write_to_tmpfile('signer_addr', signer_addr + '\n') imsg(f' Keystore: {self.keystore_dir}') imsg(f' Signer key: {self.keystore_data["key"]}') @@ -647,13 +869,8 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): async def wallet_upgrade2(self): return await self._wallet_upgrade('tracking-wallet-v2.json', 'token params field', 'network field') - def addrgen(self, addrs='1-3,11-13,21-23'): - t = self.spawn( - 'mmgen-addrgen', - [f'--coin={self.proto.coin}'] + self.eth_args + [dfl_words_file, addrs], - no_passthru_opts = True) - t.written_to_file('Addresses') - return t + def addrgen(self): + return self._addrgen() def addrimport( self, @@ -716,7 +933,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): t.read() return t - def txsign(self, ni=False, ext='{}.regtest.rawtx', add_args=[], dev_send=False): + def txsign(self, ni=False, ext='{}.regtest.rawtx', add_args=[], dev_send=False, has_label=True): ext = ext.format('-α' if self.cfg.debug_utf8 else '') keyfile = joinpath(self.tmpdir, parity_devkey_fn) txfile = self.get_file_with_ext(ext, no_dot=True) @@ -729,18 +946,31 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): + add_args + [txfile, dfl_words_file], no_passthru_opts = ['coin']) - return self.txsign_ui_common(t, ni=ni, has_label=True) + return self.txsign_ui_common(t, ni=ni, has_label=has_label) - def txsend(self, ext='{}.regtest.sigtx', add_args=[], test=False): + def txsend( + self, + ext = '{}.regtest.sigtx', + add_args = [], + test = False, + return_early = False, + has_label = True, + env = {}): ext = ext.format('-α' if self.cfg.debug_utf8 else '') txfile = self.get_file_with_ext(ext, no_dot=True) - t = self.spawn('mmgen-txsend', self.eth_args + add_args + [txfile], no_passthru_opts=['coin']) + t = self.spawn( + 'mmgen-txsend', + self.eth_args + add_args + [txfile], + no_passthru_opts = ['coin'], + env = env) + if return_early: + return t self.txsend_ui_common( t, quiet = not self.cfg.debug, bogus_send = False, test = test, - has_label = True) + has_label = has_label) return t def txview(self, ext_fs): @@ -931,12 +1161,8 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): interactive_fee = '40G', fee_info_data = ('0.00084', '40')) - def txbump(self, ext=',40000]{}.regtest.rawtx', fee='50G', add_args=[]): - ext = ext.format('-α' if self.cfg.debug_utf8 else '') - txfile = self.get_file_with_ext(ext, no_dot=True) - t = self.spawn('mmgen-txbump', self.eth_args + add_args + ['--yes', txfile]) - t.expect('or gas price: ', fee+'\n') - return t + def txbump(self): + return self._txbump(fee='50G', ext=',40000]{}.regtest.rawtx') def txsign4(self): return self.txsign(ext='.45495,50000]{}.regtest.rawtx', add_args=['--no-quiet', '--no-yes']) @@ -1053,6 +1279,8 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): tx = await NewTX(cfg=self.cfg, proto=self.proto, target='tx') tx.rpc = await self.rpc res = await tx.get_receipt(txid) + if not res: + die(1, f'Error getting receipt for transaction {txid}') imsg(f'Gas sent: {res.gas_sent.hl():<9} {(res.gas_sent*res.gas_price).hl2(encl="()")}') imsg(f'Gas used: {res.gas_used.hl():<9} {(res.gas_used*res.gas_price).hl2(encl="()")}') imsg(f'Gas price: {res.gas_price.hl()}') @@ -1060,50 +1288,23 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): omsg(yellow('Warning: all gas was used!')) return res - async def token_deploy(self, num, key, gas, mmgen_cmd='txdo', gas_price='8G'): - keyfile = joinpath(self.tmpdir, parity_devkey_fn) - fn = joinpath(self.tmpdir, 'mm'+str(num), key+'.bin') - args = [ - '-B', - f'--fee={gas_price}', - f'--gas={gas}', - f'--contract-data={fn}', - f'--inputs={dfl_devaddr}', - '--yes', - ] - if mmgen_cmd == 'txdo': - args += ['-k', keyfile] - t = self.spawn('mmgen-'+mmgen_cmd, self.eth_args + args) - if mmgen_cmd == 'txcreate': - t.written_to_file('transaction') - ext = '[0,8000]{}.regtest.rawtx'.format('-α' if self.cfg.debug_utf8 else '') - txfile = self.get_file_with_ext(ext, no_dot=True) - t = self.spawn( - 'mmgen-txsign', - self.eth_args + ['--yes', '-k', keyfile, txfile], no_msg=True, no_passthru_opts=['coin']) - self.txsign_ui_common(t, ni=True) - txfile = txfile.replace('.rawtx', '.sigtx') - t = self.spawn('mmgen-txsend', - self.eth_args + [txfile], no_msg=True, no_passthru_opts=['coin']) - - txid = self.txsend_ui_common(t, - caller = mmgen_cmd, - quiet = mmgen_cmd == 'txdo' or not self.cfg.debug, - bogus_send = False) - addr = strip_ansi_escapes(t.expect_getend('Contract address: ')) - if (await self.get_tx_receipt(txid)).status == 0: - die(2, f'Contract {num}:{key} failed to execute. Aborting') - if key == 'Token': - self.write_to_tmpfile(f'token_addr{num}', addr+'\n') - imsg(f'\nToken MM{num} deployed!') - return t - async def token_deploy1a(self): - return await self.token_deploy(num=1, key='SafeMath', gas=500_000) + return await self._token_deploy_math(num=1) + async def token_deploy1b(self): - return await self.token_deploy(num=1, key='Owned', gas=1_000_000) + return await self._token_deploy_owned(num=1) + async def token_deploy1c(self): - return await self.token_deploy(num=1, key='Token', gas=4_000_000, gas_price='7G') + return await self._token_deploy_token(num=1) + + async def token_deploy2a(self): # test create, sign, send: + return await self._token_deploy_math(num=2, mmgen_cmd='txcreate') + + async def token_deploy2b(self): + return await self._token_deploy_owned(num=2) + + async def token_deploy2c(self): + return await self._token_deploy_token(num=2) def tx_status2(self): return self.tx_status( @@ -1113,79 +1314,14 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): def bal6(self): return self.bal5() - async def token_deploy2a(self): # test create, sign, send: - return await self.token_deploy(num=2, key='SafeMath', gas=500_000, mmgen_cmd='txcreate') - async def token_deploy2b(self): - return await self.token_deploy(num=2, key='Owned', gas=1_000_000) - async def token_deploy2c(self): - return await self.token_deploy(num=2, key='Token', gas=4_000_000) - - async def token_transfer_ops(self, op, amt=1000, num_tokens=2): - self.spawn(msg_only=True) - sid = dfl_sid - from mmgen.tool.wallet import tool_cmd - usr_mmaddrs = [f'{sid}:E:{i}' for i in (11, 21)][:num_tokens] - - from mmgen.proto.eth.contract import ResolvedToken - async def do_transfer(rpc): - for i in range(num_tokens): - tk = await ResolvedToken( - self.cfg, - self.proto, - rpc, - self.read_from_tmpfile(f'token_addr{i+1}').strip()) - imsg_r('\n' + await tk.info()) - imsg('dev token balance (pre-send): {}'.format(await tk.get_balance(dfl_devaddr))) - imsg(f'Sending {amt} {self.proto.dcoin} to address {usr_addrs[i]} ({usr_mmaddrs[i]})') - txid = await tk.transfer( - from_addr = dfl_devaddr, - to_addr = usr_addrs[i], - amt = amt, - key = dfl_devkey, - gas = self.proto.coin_amt(120000, from_unit='wei'), - gasPrice = self.proto.coin_amt(8, from_unit='Gwei')) - if (await self.get_tx_receipt(txid)).status == 0: - die(2, 'Transfer of token funds failed. Aborting') - - async def show_bals(rpc): - for i in range(num_tokens): - tk = await ResolvedToken( - self.cfg, - self.proto, - rpc, - self.read_from_tmpfile(f'token_addr{i+1}').strip()) - imsg('Token: {}'.format(await tk.get_symbol())) - imsg(f'dev token balance: {await tk.get_balance(dfl_devaddr)}') - imsg('usr token balance: {} ({} {})'.format( - await tk.get_balance(usr_addrs[i]), - usr_mmaddrs[i], - usr_addrs[i])) - - def gen_addr(addr): - return tool_cmd( - self.cfg, cmdname='gen_addr', proto=self.proto).gen_addr(addr, wallet=dfl_words_file) - - silence() - usr_addrs = list(map(gen_addr, usr_mmaddrs)) - if op == 'show_bals': - await show_bals(await self.rpc) - elif op == 'do_transfer': - await do_transfer(await self.rpc) - end_silence() - return 'ok' - def token_fund_users(self): - return self.token_transfer_ops(op='do_transfer') + return self._token_transfer_ops(op='fund_user', mm_idxs=[11, 21]) def token_user_bals(self): - return self.token_transfer_ops(op='show_bals') + return self._token_transfer_ops(op='show_bals', mm_idxs=[11, 21]) - def token_addrgen(self, num_tokens=2): - t = self.addrgen(addrs='11-13') - if num_tokens == 1: - return t - ok_msg() - return self.addrgen(addrs='21-23') + def token_addrgen(self): + return self._token_addrgen(mm_idxs=[11, 21], naddrs=3) def token_addrimport_badaddr1(self): t = self.addrimport( @@ -1205,21 +1341,14 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): t.expect('could not be resolved') return t - def token_addrimport(self, addr_file, addr_range, expect, extra_args=[]): - token_addr = self.read_from_tmpfile(addr_file).strip() - return self.addrimport( - ext = f'[{addr_range}]{{}}.regtest.addrs', - expect = expect, - add_args = ['--token-addr='+token_addr]+extra_args) - def token_addrimport_addr1(self): - return self.token_addrimport('token_addr1', '11-13', expect='3/3') + return self._token_addrimport('token_addr1', '11-13', expect='3/3') def token_addrimport_addr2(self): - return self.token_addrimport('token_addr2', '21-23', expect='3/3') + return self._token_addrimport('token_addr2', '21-23', expect='3/3') def token_addrimport_batch(self): - return self.token_addrimport('token_addr1', '11-13', expect='3 addresses', extra_args=['--batch']) + return self._token_addrimport('token_addr1', '11-13', expect='3 addresses', extra_args=['--batch']) def token_addrimport_sym(self): return self.addrimport( @@ -1285,7 +1414,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): def token_txcreate2(self): return self.token_txcreate(args=[burn_addr+', '+amt2], token='mm1') def token_txbump(self): - return self.txbump(ext=amt2+',50000]{}.regtest.rawtx', fee='56G', add_args=['--token=mm1']) + return self._txbump(ext=amt2+',50000]{}.regtest.rawtx', fee='56G', add_opts=['--token=MM1']) def token_txsign2(self): return self.token_txsign(ext=amt2+',50000]{}.regtest.rawtx', token='mm1') def token_txsend2(self): diff --git a/test/cmdtest_d/shared.py b/test/cmdtest_d/shared.py index 8475bbf0..a34a16cf 100755 --- a/test/cmdtest_d/shared.py +++ b/test/cmdtest_d/shared.py @@ -118,6 +118,7 @@ class CmdTestShared: return t t.view_tx(view) + if not txdo: t.expect('(y/N): ', ('n', 'y')[save]) t.written_to_file(file_desc) @@ -192,6 +193,18 @@ class CmdTestShared: return txid + def txbump_ui_common(self, t, *, fee, fee_desc='transaction fee', bad_fee=None): + t.expect('(Y/n): ', 'n') # network-estimated fee OK? + if bad_fee: + t.expect(f'{fee_desc}: ', f'{bad_fee}\n') + t.expect(f'{fee_desc}: ', f'{fee}\n') + t.expect('(Y/n): ', 'y') # fee OK? + t.expect('(Y/n): ', 'y') # signoff + t.expect('(y/N): ', 'n') # edit comment + t.expect('(y/N): ', 'y') # save TX? + t.written_to_file('Fee-bumped transaction') + return t + def txsign_end(self, t, tnum=None, has_label=False): t.expect('Signing transaction') t.do_comment(False, has_label=has_label)