cmdtest.py ethdev: new CmdTestEthdevMethods class
This commit is contained in:
parent
e6758ef1db
commit
c08fbd2cc2
4 changed files with 317 additions and 203 deletions
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue