From 1769b100b57dfe455e0b72ee740879cb304d5bf7 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 21 Mar 2025 05:46:31 +0300 Subject: [PATCH] fixes and cleanups throughout --- mmgen/proto/btc/tx/base.py | 2 - mmgen/proto/eth/tx/base.py | 8 +- mmgen/proto/eth/tx/new.py | 14 +-- mmgen/swap/proto/thorchain/thornode.py | 36 ++++-- mmgen/tx/base.py | 1 + test/cmdtest_d/ct_ethdev.py | 151 +++++++++++++------------ test/cmdtest_d/ct_swap.py | 25 +++- test/cmdtest_d/runner.py | 2 +- 8 files changed, 132 insertions(+), 107 deletions(-) diff --git a/mmgen/proto/btc/tx/base.py b/mmgen/proto/btc/tx/base.py index 64053763..9a7431ad 100755 --- a/mmgen/proto/btc/tx/base.py +++ b/mmgen/proto/btc/tx/base.py @@ -14,7 +14,6 @@ proto.btc.tx.base: Bitcoin base transaction class from collections import namedtuple -from ....addr import CoinAddr from ....tx.base import Base as TxBase from ....obj import MMGenList, HexStr, ListItemAttr from ....util import msg, make_chksum_6, die, pp_fmt @@ -175,7 +174,6 @@ class Base(TxBase): _deserialized = None class Output(TxBase.Output): # output contains either addr or data, but not both - addr = ListItemAttr(CoinAddr, include_proto=True) # ImmutableAttr in parent cls data = ListItemAttr(OpReturnData, include_proto=True) # type None in parent cls class InputList(TxBase.InputList): diff --git a/mmgen/proto/eth/tx/base.py b/mmgen/proto/eth/tx/base.py index 60f06e5b..720df5a5 100755 --- a/mmgen/proto/eth/tx/base.py +++ b/mmgen/proto/eth/tx/base.py @@ -14,10 +14,10 @@ proto.eth.tx.base: Ethereum base transaction class from collections import namedtuple -from ....tx import base as TxBase -from ....obj import HexStr, Int +from ....tx.base import Base as TxBase +from ....obj import Int -class Base(TxBase.Base): +class Base(TxBase): rel_fee_desc = 'gas price' rel_fee_disp = 'gas price in Gwei' @@ -25,7 +25,7 @@ class Base(TxBase.Base): dfl_gas = 21000 # the startGas amt used in the transaction # for simple sends with no data, startGas = 21000 contract_desc = 'contract' - usr_contract_data = HexStr('') + usr_contract_data = b'' disable_fee_check = False @property diff --git a/mmgen/proto/eth/tx/new.py b/mmgen/proto/eth/tx/new.py index 93f94f5b..8de5a2c2 100755 --- a/mmgen/proto/eth/tx/new.py +++ b/mmgen/proto/eth/tx/new.py @@ -15,7 +15,7 @@ proto.eth.tx.new: Ethereum new transaction class import json from ....tx import new as TxBase -from ....obj import Int, ETHNonce, MMGenTxID, HexStr +from ....obj import Int, ETHNonce, MMGenTxID from ....util import msg, is_int, is_hex_str, make_chksum_6, suf, die from ....tw.ctl import TwCtl from ....addr import is_mmgen_id, is_coin_addr @@ -42,7 +42,7 @@ class New(Base, TxBase.New): m = "'--contract-data' option may not be used with token transaction" assert 'Token' not in self.name, m with open(self.cfg.contract_data) as fp: - self.usr_contract_data = HexStr(fp.read().strip()) + self.usr_contract_data = bytes.fromhex(fp.read().strip()) self.disable_fee_check = True async def get_nonce(self): @@ -58,7 +58,7 @@ class New(Base, TxBase.New): 'startGas': self.gas, 'nonce': await self.get_nonce(), 'chainId': self.rpc.chainID, - 'data': self.usr_contract_data} + 'data': self.usr_contract_data.hex()} # Instead of serializing tx data as with BTC, just create a JSON dump. # This complicates things but means we avoid using the rlp library to deserialize the data, @@ -88,12 +88,12 @@ class New(Base, TxBase.New): if lc != 1: die(1, f'{lc} output{suf(lc)} specified, but Ethereum transactions must have exactly one') - arg = self.parse_cmdline_arg(self.proto, cmd_args[0], ad_f, ad_w) + a = self.parse_cmdline_arg(self.proto, cmd_args[0], ad_f, ad_w) self.add_output( - coinaddr = arg.addr, - amt = self.proto.coin_amt(arg.amt or '0'), - is_chg = not arg.amt) + coinaddr = a.addr, + amt = self.proto.coin_amt(a.amt or '0'), + is_chg = not a.amt) self.add_mmaddrs_to_outputs(ad_f, ad_w) diff --git a/mmgen/swap/proto/thorchain/thornode.py b/mmgen/swap/proto/thorchain/thornode.py index ca0eeb5d..d8b66a0a 100755 --- a/mmgen/swap/proto/thorchain/thornode.py +++ b/mmgen/swap/proto/thorchain/thornode.py @@ -13,8 +13,15 @@ swap.proto.thorchain.thornode: THORChain swap protocol network query ops """ import json +from collections import namedtuple from ....amt import UniAmt +_gd = namedtuple('gas_unit_data', ['code', 'disp']) +gas_unit_data = { + 'satsperbyte': _gd('s', 'sat/byte'), + 'gwei': _gd('G', 'Gwei'), +} + class ThornodeRPCClient: http_hdrs = {'Content-Type': 'application/json'} @@ -49,7 +56,7 @@ class Thornode: def __init__(self, tx, amt): self.tx = tx - self.in_amt = amt + self.in_amt = UniAmt(str(amt)) self.rpc = ThornodeRPCClient(tx) def get_quote(self): @@ -73,8 +80,9 @@ class Thornode: tx = self.tx in_coin = tx.send_proto.coin out_coin = tx.recv_proto.coin - in_amt = self.in_amt + in_amt = UniAmt(str(self.in_amt)) out_amt = UniAmt(int(d['expected_amount_out']), from_unit='satoshi') + gas_unit = d['gas_rate_units'] if trade_limit: from . import ExpInt4 @@ -94,19 +102,22 @@ class Thornode: trade_limit_disp = '' tx_size_adj = 0 + def get_estimated_fee(): + return tx.feespec2abs( + fee_arg = d['recommended_gas_rate'] + gas_unit_data[gas_unit].code, + tx_size = tx.estimate_size() + tx_size_adj) + _amount_in_label = 'Amount in:' if deduct_est_fee: - if d['gas_rate_units'] == 'satsperbyte': - in_amt -= tx.feespec2abs(d['recommended_gas_rate'] + 's', tx.estimate_size() + tx_size_adj) + if gas_unit in gas_unit_data: + in_amt -= UniAmt(str(get_estimated_fee())) out_amt *= (in_amt / self.in_amt) _amount_in_label = 'Amount in (estimated):' else: - ymsg('Warning: unknown gas unit ‘{}’, cannot estimate fee'.format(d['gas_rate_units'])) + ymsg(f'Warning: unknown gas unit ‘{gas_unit}’, cannot estimate fee') min_in_amt = UniAmt(int(d['recommended_min_amount_in']), from_unit='satoshi') - gas_unit = { - 'satsperbyte': 'sat/byte', - }.get(d['gas_rate_units'], d['gas_rate_units']) + gas_unit_disp = _.disp if (_ := gas_unit_data.get(gas_unit)) else gas_unit elapsed_disp = format_elapsed_hr(d['expiry'], future_msg='from now') fees = d['fees'] fees_t = UniAmt(int(fees['total']), from_unit='satoshi') @@ -118,14 +129,14 @@ class Thornode: {cyan(hdr)} Protocol: {blue(name)} Direction: {orange(f'{in_coin} => {out_coin}')} - Vault address: {cyan(d['inbound_address'])} + Vault address: {cyan(self.inbound_address)} Quote expires: {pink(elapsed_disp)} [{make_timestr(d['expiry'])}] {_amount_in_label:<22} {in_amt.hl()} {in_coin} Expected amount out: {out_amt.hl()} {out_coin}{trade_limit_disp} Rate: {(out_amt / in_amt).hl()} {out_coin}/{in_coin} Reverse rate: {(in_amt / out_amt).hl()} {in_coin}/{out_coin} Recommended minimum in amount: {min_in_amt.hl()} {in_coin} - Recommended fee: {pink(d['recommended_gas_rate'])} {pink(gas_unit)} + Recommended fee: {pink(d['recommended_gas_rate'])} {pink(gas_unit_disp)} Fees: Total: {fees_t.hl()} {out_coin} ({pink(fees_pct_disp)}) Slippage: {pink(slip_pct_disp)} @@ -137,8 +148,9 @@ class Thornode: @property def rel_fee_hint(self): - if self.data['gas_rate_units'] == 'satsperbyte': - return f'{self.data["recommended_gas_rate"]}s' + gas_unit = self.data['gas_rate_units'] + if gas_unit in gas_unit_data: + return self.data['recommended_gas_rate'] + gas_unit_data[gas_unit].code def __str__(self): from pprint import pformat diff --git a/mmgen/tx/base.py b/mmgen/tx/base.py index 09ab2d19..ca481688 100755 --- a/mmgen/tx/base.py +++ b/mmgen/tx/base.py @@ -102,6 +102,7 @@ class Base(MMGenObject): tw_copy_attrs = {'scriptPubKey', 'vout', 'amt', 'comment', 'mmid', 'addr', 'confs', 'txid'} class Output(MMGenTxIO): + addr = ListItemAttr(CoinAddr, include_proto=True) # ImmutableAttr in parent cls is_chg = ListItemAttr(bool, typeconv=False) is_vault = ListItemAttr(bool, typeconv=False) data = ListItemAttr(None, typeconv=False) # placeholder diff --git a/test/cmdtest_d/ct_ethdev.py b/test/cmdtest_d/ct_ethdev.py index 4582c16a..084d1fc5 100755 --- a/test/cmdtest_d/ct_ethdev.py +++ b/test/cmdtest_d/ct_ethdev.py @@ -118,67 +118,6 @@ def set_vbals(daemon_id): vbal7 = '1000124.91944498212345678' vbal9 = '1.226261' -bals = lambda k: { - '1': [ ('98831F3A:E:1', '123.456')], - '2': [ ('98831F3A:E:1', '123.456'), ('98831F3A:E:11', '1.234')], - '3': [ ('98831F3A:E:1', '123.456'), ('98831F3A:E:11', '1.234'), ('98831F3A:E:21', '2.345')], - '4': [ ('98831F3A:E:1', '100'), - ('98831F3A:E:2', '23.45495'), - ('98831F3A:E:11', '1.234'), - ('98831F3A:E:21', '2.345')], - '5': [ ('98831F3A:E:1', '100'), - ('98831F3A:E:2', '23.45495'), - ('98831F3A:E:11', '1.234'), - ('98831F3A:E:21', '2.345'), - (burn_addr + r'\s+non-MMGen', amt1)], - '8': [ ('98831F3A:E:1', '0'), - ('98831F3A:E:2', '23.45495'), - ('98831F3A:E:11', vbal1), - ('98831F3A:E:12', '99.99895'), - ('98831F3A:E:21', '2.345'), - (burn_addr + r'\s+non-MMGen', amt1)], - '9': [ ('98831F3A:E:1', '0'), - ('98831F3A:E:2', '23.45495'), - ('98831F3A:E:11', vbal1), - ('98831F3A:E:12', vbal2), - ('98831F3A:E:21', '2.345'), - (burn_addr + r'\s+non-MMGen', amt1)], - '10': [ ('98831F3A:E:1', '0'), - ('98831F3A:E:2', '23.0218'), - ('98831F3A:E:3', '0.4321'), - ('98831F3A:E:11', vbal1), - ('98831F3A:E:12', vbal2), - ('98831F3A:E:21', '2.345'), - (burn_addr + r'\s+non-MMGen', amt1)] -}[k] - -token_bals = lambda k: { - '1': [ ('98831F3A:E:11', '1000', '1.234')], - '2': [ ('98831F3A:E:11', '998.76544', vbal3), - ('98831F3A:E:12', '1.23456', '0')], - '3': [ ('98831F3A:E:11', '110.654317776666555545', vbal1), - ('98831F3A:E:12', '1.23456', '0')], - '4': [ ('98831F3A:E:11', '110.654317776666555545', vbal1), - ('98831F3A:E:12', '1.23456', '0'), - (burn_addr + r'\s+non-MMGen', amt2, amt1)], - '5': [ ('98831F3A:E:11', '110.654317776666555545', vbal1), - ('98831F3A:E:12', '1.23456', '99.99895'), - (burn_addr + r'\s+non-MMGen', amt2, amt1)], - '6': [ ('98831F3A:E:11', '110.654317776666555545', vbal1), - ('98831F3A:E:12', '0', vbal2), - ('98831F3A:E:13', '1.23456', '0'), - (burn_addr + r'\s+non-MMGen', amt2, amt1)], - '7': [ ('98831F3A:E:11', '67.444317776666555545', vbal9), - ('98831F3A:E:12', '43.21', vbal2), - ('98831F3A:E:13', '1.23456', '0'), - (burn_addr + r'\s+non-MMGen', amt2, amt1)] -}[k] - -token_bals_getbalance = lambda k: { - '1': (vbal4, '999777.12345689012345678'), - '2': ('111.888877776666555545', '888.111122223333444455') -}[k] - coin = cfg.coin class CmdTestEthdev(CmdTestBase, CmdTestShared): @@ -187,6 +126,70 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): passthru_opts = ('coin', 'daemon_id', 'http_timeout', 'rpc_backend') tmpdir_nums = [22] color = True + menu_prompt = 'efresh balance:\b' + input_sels_prompt = 'to spend from: ' + + bals = lambda self, k: { + '1': [ ('98831F3A:E:1', '123.456')], + '2': [ ('98831F3A:E:1', '123.456'), ('98831F3A:E:11', '1.234')], + '3': [ ('98831F3A:E:1', '123.456'), ('98831F3A:E:11', '1.234'), ('98831F3A:E:21', '2.345')], + '4': [ ('98831F3A:E:1', '100'), + ('98831F3A:E:2', '23.45495'), + ('98831F3A:E:11', '1.234'), + ('98831F3A:E:21', '2.345')], + '5': [ ('98831F3A:E:1', '100'), + ('98831F3A:E:2', '23.45495'), + ('98831F3A:E:11', '1.234'), + ('98831F3A:E:21', '2.345'), + (burn_addr + r'\s+non-MMGen', amt1)], + '8': [ ('98831F3A:E:1', '0'), + ('98831F3A:E:2', '23.45495'), + ('98831F3A:E:11', vbal1), + ('98831F3A:E:12', '99.99895'), + ('98831F3A:E:21', '2.345'), + (burn_addr + r'\s+non-MMGen', amt1)], + '9': [ ('98831F3A:E:1', '0'), + ('98831F3A:E:2', '23.45495'), + ('98831F3A:E:11', vbal1), + ('98831F3A:E:12', vbal2), + ('98831F3A:E:21', '2.345'), + (burn_addr + r'\s+non-MMGen', amt1)], + '10': [ ('98831F3A:E:1', '0'), + ('98831F3A:E:2', '23.0218'), + ('98831F3A:E:3', '0.4321'), + ('98831F3A:E:11', vbal1), + ('98831F3A:E:12', vbal2), + ('98831F3A:E:21', '2.345'), + (burn_addr + r'\s+non-MMGen', amt1)] + }[k] + + token_bals = lambda self, k: { + '1': [ ('98831F3A:E:11', '1000', '1.234')], + '2': [ ('98831F3A:E:11', '998.76544', vbal3), + ('98831F3A:E:12', '1.23456', '0')], + '3': [ ('98831F3A:E:11', '110.654317776666555545', vbal1), + ('98831F3A:E:12', '1.23456', '0')], + '4': [ ('98831F3A:E:11', '110.654317776666555545', vbal1), + ('98831F3A:E:12', '1.23456', '0'), + (burn_addr + r'\s+non-MMGen', amt2, amt1)], + '5': [ ('98831F3A:E:11', '110.654317776666555545', vbal1), + ('98831F3A:E:12', '1.23456', '99.99895'), + (burn_addr + r'\s+non-MMGen', amt2, amt1)], + '6': [ ('98831F3A:E:11', '110.654317776666555545', vbal1), + ('98831F3A:E:12', '0', vbal2), + ('98831F3A:E:13', '1.23456', '0'), + (burn_addr + r'\s+non-MMGen', amt2, amt1)], + '7': [ ('98831F3A:E:11', '67.444317776666555545', vbal9), + ('98831F3A:E:12', '43.21', vbal2), + ('98831F3A:E:13', '1.23456', '0'), + (burn_addr + r'\s+non-MMGen', amt2, amt1)] + }[k] + + token_bals_getbalance = lambda self, k: { + '1': (vbal4, '999777.12345689012345678'), + '2': ('111.888877776666555545', '888.111122223333444455') + }[k] + cmd_group_in = ( ('setup', f'dev mode tests for coin {coin} (start daemon)'), ('subgroup.misc', []), @@ -955,7 +958,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): self.mining_delay() t = self.spawn('mmgen-tool', self.eth_args + ['twview', 'wide=1']) text = t.read(strip_color=True) - for addr, amt in bals(n): + for addr, amt in self.bals(n): pat = r'\D{}\D.*\D{}\D'.format(addr, amt.replace('.', r'\.')) assert re.search(pat, text), pat ss = f'Total {self.proto.coin}:' @@ -966,7 +969,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): self.mining_delay() t = self.spawn('mmgen-tool', self.eth_args + ['--token=mm1', 'twview', 'wide=1']) text = t.read(strip_color=True) - for addr, _amt1, _amt2 in token_bals(n): + for addr, _amt1, _amt2 in self.token_bals(n): pat = fr'{addr}\b.*\D{_amt1}\D.*\b{_amt2}\D' assert re.search(pat, text), pat ss = 'Total MM1:' @@ -974,8 +977,8 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): return t def bal_getbalance(self, sid, idx, etc_adj=False, extra_args=[]): - bal1 = token_bals_getbalance(idx)[0] - bal2 = token_bals_getbalance(idx)[1] + bal1 = self.token_bals_getbalance(idx)[0] + bal2 = self.token_bals_getbalance(idx)[1] bal1 = Decimal(bal1) t = self.spawn('mmgen-tool', self.eth_args + extra_args + ['getbalance']) t.expect(rf'{sid}:.*'+str(bal1), regex=True) @@ -1056,12 +1059,12 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): omsg(yellow('Warning: all gas was used!')) return res - async def token_deploy(self, num, key, gas, mmgen_cmd='txdo', tx_fee='8G'): + 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={tx_fee}', + f'--fee={gas_price}', f'--gas={gas}', f'--contract-data={fn}', f'--inputs={dfl_devaddr}', @@ -1099,7 +1102,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): async def token_deploy1b(self): return await self.token_deploy(num=1, key='Owned', gas=1_000_000) async def token_deploy1c(self): - return await self.token_deploy(num=1, key='Token', gas=4_000_000, tx_fee='7G') + return await self.token_deploy(num=1, key='Token', gas=4_000_000, gas_price='7G') def tx_status2(self): return self.tx_status( @@ -1138,7 +1141,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): to_addr = usr_addrs[i], amt = amt, key = dfl_devkey, - gas = self.proto.coin_amt(60000, from_unit='wei'), + 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') @@ -1459,10 +1462,8 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): t = self.spawn('mmgen-txcreate', self.eth_args + args + ['-B', '-i'], pexpect_spawn=pexpect_spawn) - menu_prompt = 'efresh balance:\b' - - t.expect(menu_prompt, 'M') - t.expect(menu_prompt, action) + t.expect(self.menu_prompt, 'M') + t.expect(self.menu_prompt, action) t.expect(r'return to main menu): ', out_num+'\n') for p, r in ( @@ -1479,8 +1480,8 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): 'Label removed from account #{}') t.expect(m.format(out_num)) - t.expect(menu_prompt, 'M') - t.expect(menu_prompt, 'q') + t.expect(self.menu_prompt, 'M') + t.expect(self.menu_prompt, 'q') t.expect('Total unspent:') diff --git a/test/cmdtest_d/ct_swap.py b/test/cmdtest_d/ct_swap.py index 7c357a24..c52ecd8d 100755 --- a/test/cmdtest_d/ct_swap.py +++ b/test/cmdtest_d/ct_swap.py @@ -28,6 +28,8 @@ sample1 = gr_uc[:24] sample2 = '00010203040506' class CmdTestSwapMethods: + menu_prompt = 'abel:\b' + input_sels_prompt = 'to spend: ' def _addrgen_bob(self, proto_idx, mmtypes, subseed_idx=None): return self.addrgen('bob', subseed_idx=subseed_idx, mmtypes=mmtypes, proto=self.protos[proto_idx]) @@ -107,8 +109,8 @@ class CmdTestSwapMethods: reload_quote = False, sign_and_send = False, expect = None): - t.expect('abel:\b', 'q') - t.expect('to spend: ', f'{inputs}\n') + t.expect(self.menu_prompt, 'q') + t.expect(self.input_sels_prompt, f'{inputs}\n') if reload_quote: t.expect('to continue: ', 'r') # reload swap quote t.expect('to continue: ', '\n') # exit swap quote view @@ -136,7 +138,8 @@ class CmdTestSwapMethods: ['-q', '-d', self.tmpdir, '-B', '--bob'] + add_opts + args, - exit_val = exit_val) + exit_val = exit_val, + no_passthru_opts = ['coin']) def _swaptxcreate_bad(self, args, *, exit_val=1, expect1=None, expect2=None): t = self._swaptxcreate(args, exit_val=exit_val) @@ -159,7 +162,10 @@ class CmdTestSwapMethods: def _swaptxsend(self, *, add_opts=[], spawn_only=False): fn = self.get_file_with_ext('sigtx') - t = self.spawn('mmgen-txsend', add_opts + ['-q', '-d', self.tmpdir, '--bob', fn]) + t = self.spawn( + 'mmgen-txsend', + add_opts + ['-q', '-d', self.tmpdir, '--bob', fn], + no_passthru_opts = ['coin']) if spawn_only: return t t.expect('view: ', 'v') @@ -170,7 +176,10 @@ class CmdTestSwapMethods: def _swaptxsign(self, *, add_opts=[], expect=None): self.get_file_with_ext('sigtx', delete_all=True) fn = self.get_file_with_ext('rawtx') - t = self.spawn('mmgen-txsign', add_opts + ['-d', self.tmpdir, '--bob', fn]) + t = self.spawn( + 'mmgen-txsign', + add_opts + ['-d', self.tmpdir, '--bob', fn], + no_passthru_opts = ['coin']) t.view_tx('t') if expect: t.expect(expect) @@ -475,7 +484,11 @@ class CmdTestSwap(CmdTestRegtest, CmdTestAutosignThreaded, CmdTestSwapMethods): coin_arg = f'--coin={self.protos[proto_idx].coin}' t = self.spawn('mmgen-tool', ['--bob', coin_arg, 'listaddresses']) addr = [s for s in strip_ansi_escapes(t.read()).splitlines() if 'C:1 No' in s][0].split()[3] - t = self.spawn('mmgen-regtest', [coin_arg, 'send', addr, str(amt)], no_passthru_opts=True, no_msg=True) + t = self.spawn( + 'mmgen-regtest', + [coin_arg, 'send', addr, str(amt)], + no_passthru_opts = ['coin'], + no_msg = True) return t def bob_bal_recv(self): diff --git a/test/cmdtest_d/runner.py b/test/cmdtest_d/runner.py index 443c5c3e..9561d7b2 100755 --- a/test/cmdtest_d/runner.py +++ b/test/cmdtest_d/runner.py @@ -161,7 +161,7 @@ class CmdTestRunner: if self.logging: self.log_fd.write('[{}][{}:{}] {}\n'.format( - (self.proto.coin.lower() if 'coin' in passthru_opts else 'NONE'), + (self.proto.coin.lower() if 'coin' in self.tg.passthru_opts else 'NONE'), self.tg.group_name, self.tg.test_name, cmd_disp))