From ef640d10a961ae3d00e3044b198254a1e5e072d5 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sun, 16 Mar 2025 10:46:32 +0000 Subject: [PATCH] proto.eth.tx: minor cleanups, fixes --- mmgen/proto/eth/contract.py | 24 +++++++++++++----------- mmgen/proto/eth/tx/info.py | 11 ++++------- mmgen/proto/eth/tx/unsigned.py | 20 +++++++++++--------- test/cmdtest_d/ct_ethdev.py | 14 +++++++++----- 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/mmgen/proto/eth/contract.py b/mmgen/proto/eth/contract.py index f350d08d..42ea34a9 100755 --- a/mmgen/proto/eth/contract.py +++ b/mmgen/proto/eth/contract.py @@ -21,6 +21,8 @@ proto.eth.contract: Ethereum ERC20 token classes """ from decimal import Decimal +from collections import namedtuple + from . import rlp from . import erigon_sleep @@ -135,21 +137,21 @@ class TokenCommon(MMGenObject): res = await self.rpc.call('eth_chainId') chain_id = None if res is None else int(res, 16) - tx = Transaction(**tx_in).sign(key, chain_id) + etx = Transaction(**tx_in).sign(key, chain_id) - if tx.sender.hex() != from_addr: - die(3, f'Sender address {from_addr!r} does not match address of key {tx.sender.hex()!r}!') + if etx.sender.hex() != from_addr: + die(3, f'Sender address {from_addr!r} does not match address of key {etx.sender.hex()!r}!') if self.cfg.debug: msg('TOKEN DATA:') - pp_msg(tx.to_dict()) + pp_msg(etx.to_dict()) msg('PARSED ABI DATA:\n {}'.format( - '\n '.join(parse_abi(tx.data.hex())))) + '\n '.join(parse_abi(etx.data.hex())))) - return ( - rlp.encode(tx).hex(), - CoinTxID(tx.hash.hex()) - ) + return namedtuple('signed_contract_transaction', ['etx', 'txhex', 'txid'])( + etx, + rlp.encode(etx).hex(), + CoinTxID(etx.hash.hex())) # The following are used for token deployment only: @@ -173,8 +175,8 @@ class TokenCommon(MMGenObject): gasPrice, nonce = int(await self.rpc.call('eth_getTransactionCount', '0x'+from_addr, 'pending'), 16), method_sig = method_sig) - txhex, _ = await self.txsign(tx_in, key, from_addr) - return await self.txsend(txhex) + res = await self.txsign(tx_in, key, from_addr) + return await self.txsend(res.txhex) class Token(TokenCommon): diff --git a/mmgen/proto/eth/tx/info.py b/mmgen/proto/eth/tx/info.py index 9f7be4a6..2a5e726c 100755 --- a/mmgen/proto/eth/tx/info.py +++ b/mmgen/proto/eth/tx/info.py @@ -30,11 +30,8 @@ class TxInfo(TxInfo): def format_body(self, blockcount, nonmm_str, max_mmwid, enl, *, terse, sort): tx = self.tx - m = {} - for k in ('inputs', 'outputs'): - if len(getattr(tx, k)): - m[k] = getattr(tx, k)[0].mmid if len(getattr(tx, k)) else '' - m[k] = ' ' + m[k].hl() if m[k] else ' ' + MMGenID.hlc(nonmm_str) + def mmid_disp(io): + return ' ' + (io.mmid.hl() if io.mmid else MMGenID.hlc(nonmm_str)) fs = """ From: {f}{f_mmid} To: {t}{t_mmid} @@ -56,8 +53,8 @@ class TxInfo(TxInfo): c = tx.proto.dcoin if len(tx.outputs) else '', g = yellow(tx.pretty_fmt_fee(t['gasPrice'].to_unit('Gwei'))), G = yellow(tx.pretty_fmt_fee(t['startGas'].to_unit('Kwei'))), - t_mmid = m['outputs'] if len(tx.outputs) else '', - f_mmid = m['inputs']) + '\n\n' + f_mmid = mmid_disp(tx.inputs[0]), + t_mmid = mmid_disp(tx.outputs[0]) if tx.outputs else '') + '\n\n' def format_abs_fee(self, iwidth, /, *, color=None): return self.tx.fee.fmt(iwidth, color=color) + (' (max)' if self.tx.txobj['data'] else '') diff --git a/mmgen/proto/eth/tx/unsigned.py b/mmgen/proto/eth/tx/unsigned.py index 71ccc3f8..f9e976dd 100755 --- a/mmgen/proto/eth/tx/unsigned.py +++ b/mmgen/proto/eth/tx/unsigned.py @@ -40,8 +40,7 @@ class Unsigned(Completed, TxBase.Unsigned): self.txobj = o return d # 'token_addr', 'decimals' required by Token subclass - async def do_sign(self, wif): - o = self.txobj + async def do_sign(self, o, wif): o_conv = { 'to': bytes.fromhex(o['to'] or ''), 'startgas': o['startGas'].toWei(), @@ -59,10 +58,10 @@ class Unsigned(Completed, TxBase.Unsigned): self.serialized = rlp.encode(etx).hex() self.coin_txid = CoinTxID(etx.hash.hex()) - if o['data']: + if o['data']: # contract-creating transaction if o['to']: - assert self.txobj['token_addr'] == TokenAddr(self.proto, etx.creates.hex()), 'Token address mismatch' - else: # token- or contract-creating transaction + raise ValueError('contract-creating transaction cannot have to-address') + else: self.txobj['token_addr'] = TokenAddr(self.proto, etx.creates.hex()) async def sign(self, tx_num_str, keys): # return TX object or False; don't exit or raise exception @@ -73,10 +72,12 @@ class Unsigned(Completed, TxBase.Unsigned): except TransactionChainMismatch: return False + o = self.txobj + msg_r(f'Signing transaction{tx_num_str}...') try: - await self.do_sign(keys[0].sec.wif) + await self.do_sign(o, keys[0].sec.wif) msg('OK') from ....tx import SignedTX return await SignedTX(cfg=self.cfg, data=self.__dict__, automount=self.automount) @@ -96,8 +97,7 @@ class TokenUnsigned(TokenCompleted, Unsigned): o['data'] = t.create_data(o['to'], o['amt']) o['token_to'] = t.transferdata2sendaddr(o['data']) - async def do_sign(self, wif): - o = self.txobj + async def do_sign(self, o, wif): t = Token(self.cfg, self.proto, o['token_addr'], o['decimals']) tx_in = t.make_tx_in( to_addr = o['to'], @@ -105,7 +105,9 @@ class TokenUnsigned(TokenCompleted, Unsigned): start_gas = self.start_gas, gasPrice = o['gasPrice'], nonce = o['nonce']) - (self.serialized, self.coin_txid) = await t.txsign(tx_in, wif, o['from'], chain_id=o['chainId']) + res = await t.txsign(tx_in, wif, o['from'], chain_id=o['chainId']) + self.serialized = res.txhex + self.coin_txid = res.txid class AutomountUnsigned(TxBase.AutomountUnsigned, Unsigned): pass diff --git a/test/cmdtest_d/ct_ethdev.py b/test/cmdtest_d/ct_ethdev.py index 431ba5f4..3d116882 100755 --- a/test/cmdtest_d/ct_ethdev.py +++ b/test/cmdtest_d/ct_ethdev.py @@ -721,9 +721,9 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): self.eth_args + [f'--coin={self.proto.coin}'] + ['--rpc-host=bad_host'] # ETH signing must work without RPC - + add_args + ([], ['--yes'])[ni] + ([f'--keys-from-file={keyfile}'] if dev_send else []) + + add_args + [txfile, dfl_words_file]) return self.txsign_ui_common(t, ni=ni, has_label=True) @@ -924,7 +924,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): return t def txsign4(self): - return self.txsign(ni=True, ext='.45495,50000]{}.regtest.rawtx', dev_send=True) + return self.txsign(ext='.45495,50000]{}.regtest.rawtx', add_args=['--no-quiet', '--no-yes']) def txsend4(self): return self.txsend(ext='.45495,50000]{}.regtest.sigtx') def bal4(self): @@ -1222,8 +1222,8 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): input_sels_prompt = 'to spend from', add_comment = tx_comment_lat_cyr_gr, file_desc = file_desc) - def token_txsign(self, ext='', token=''): - return self.txsign(ni=True, ext=ext, add_args=['--token='+token]) + def token_txsign(self, ext='', token='', add_args=[], ni=True): + return self.txsign(ni=ni, ext=ext, add_args=[f'--token={token}'] + add_args) def token_txsend(self, ext='', token=''): return self.txsend(ext=ext, add_args=['--token='+token]) @@ -1232,7 +1232,11 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared): def token_txview1_raw(self): return self.txview(ext_fs='1.23456,50000]{}.regtest.rawtx') def token_txsign1(self): - return self.token_txsign(ext='1.23456,50000]{}.regtest.rawtx', token='mm1') + return self.token_txsign( + ext = '1.23456,50000]{}.regtest.rawtx', + token = 'mm1', + ni = False, + add_args = ['--no-quiet', '--no-yes']) def token_txsend1(self): return self.token_txsend(ext='1.23456,50000]{}.regtest.sigtx', token='mm1') def token_txview1_sig(self):