diff --git a/mmgen/proto/btc/tx/base.py b/mmgen/proto/btc/tx/base.py index f25d9675..6320ae71 100755 --- a/mmgen/proto/btc/tx/base.py +++ b/mmgen/proto/btc/tx/base.py @@ -15,7 +15,7 @@ proto.btc.tx.base: Bitcoin base transaction class from collections import namedtuple from ....addr import CoinAddr -from ....tx import base as TxBase +from ....tx.base import Base as TxBase from ....obj import MMGenList, HexStr, ListItemAttr from ....util import msg, make_chksum_6, die, pp_fmt @@ -169,16 +169,16 @@ def DeserializeTX(proto, txhex): return namedtuple('deserialized_tx', list(d.keys()))(**d) -class Base(TxBase.Base): +class Base(TxBase): rel_fee_desc = 'satoshis per byte' rel_fee_disp = 'sat/byte' _deserialized = None - class Output(TxBase.Base.Output): # output contains either addr or data, but not both + 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.Base.InputList): + class InputList(TxBase.InputList): # Lexicographical Indexing of Transaction Inputs and Outputs # https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki @@ -189,7 +189,7 @@ class Base(TxBase.Base): + int.to_bytes(a.vout, 4, 'big')) self.sort(key=sort_func) - class OutputList(TxBase.Base.OutputList): + class OutputList(TxBase.OutputList): def sort_bip69(self): def sort_func(a): diff --git a/mmgen/proto/btc/tx/new.py b/mmgen/proto/btc/tx/new.py index bccbbbcd..0ff7f6a0 100755 --- a/mmgen/proto/btc/tx/new.py +++ b/mmgen/proto/btc/tx/new.py @@ -12,13 +12,13 @@ proto.btc.tx.new: Bitcoin new transaction class """ -from ....tx import new as TxBase +from ....tx.new import New as TxNew from ....obj import MMGenTxID from ....util import msg, fmt, make_chksum_6, die, suf from ....color import pink from .base import Base -class New(Base, TxBase.New): +class New(Base, TxNew): usr_fee_prompt = 'Enter transaction fee: ' fee_fail_fs = 'Network fee estimation for {c} confirmations failed ({t})' no_chg_msg = 'Warning: Change address will be deleted as transaction produces no change' @@ -105,7 +105,7 @@ class New(Base, TxBase.New): msg(err) return False - async def get_input_addrs_from_cmdline(self): + async def get_input_addrs_from_inputs_opt(self): # Bitcoin full node, call doesn't go to the network, so just call listunspent with addrs=[] return [] diff --git a/mmgen/proto/eth/tx/new.py b/mmgen/proto/eth/tx/new.py index 35a43bf9..2b980c71 100755 --- a/mmgen/proto/eth/tx/new.py +++ b/mmgen/proto/eth/tx/new.py @@ -97,7 +97,7 @@ class New(Base, TxBase.New): amt = self.proto.coin_amt(arg.amt or '0'), is_chg = not arg.amt) - def select_unspent(self, unspent): + def get_unspent_nums_from_user(self, unspent): from ....ui import line_input while True: reply = line_input(self.cfg, 'Enter an account to spend from: ').strip() @@ -152,7 +152,7 @@ class New(Base, TxBase.New): if self.outputs and self.outputs[0].is_chg: self.update_output_amt(0, funds_left) - async def get_input_addrs_from_cmdline(self): + async def get_input_addrs_from_inputs_opt(self): ret = [] if self.cfg.inputs: data_root = (await TwCtl(self.cfg, self.proto)).data_root # must create new instance here diff --git a/mmgen/tx/new.py b/mmgen/tx/new.py index 318186de..8cae7919 100755 --- a/mmgen/tx/new.py +++ b/mmgen/tx/new.py @@ -90,7 +90,7 @@ class New(Base): o['amt'] = amt self.outputs[idx] = self.Output(self.proto, **o) - def add_mmaddrs_to_outputs(self, ad_w, ad_f): + def add_mmaddrs_to_outputs(self, ad_f, ad_w): a = [e.addr for e in self.outputs] d = ad_w.make_reverse_dict(a) if ad_f: @@ -243,8 +243,17 @@ class New(Base): if not self.outputs: die(2, 'At least one output must be specified on the command line') - async def get_outputs_from_cmdline(self, cmd_args): - from ..addrdata import AddrData, TwAddrData + self.add_mmaddrs_to_outputs(ad_f, ad_w) + self.check_dup_addrs('outputs') + + if self.chg_output is not None: + if self.chg_autoselected: + self.confirm_autoselected_addr(self.chg_output) + elif len(self.outputs) > 1: + await self.warn_chg_addr_used(self.chg_output) + + def get_addrdata_from_files(self, cmd_args): + from ..addrdata import AddrData from ..addrlist import AddrList from ..addrfile import AddrFile addrfiles = remove_dups( @@ -257,25 +266,12 @@ class New(Base): desc = 'command line', edesc = 'argument', ) - ad_f = AddrData(self.proto) from ..fileutil import check_infile for addrfile in addrfiles: check_infile(addrfile) ad_f.add(AddrList(self.cfg, self.proto, addrfile)) - - ad_w = await TwAddrData(self.cfg, self.proto, twctl=self.twctl) - - await self.process_cmd_args(cmd_args, ad_f, ad_w) - - self.add_mmaddrs_to_outputs(ad_w, ad_f) - self.check_dup_addrs('outputs') - - if self.chg_output is not None: - if self.chg_autoselected: - self.confirm_autoselected_addr(self.chg_output) - elif len(self.outputs) > 1: - await self.warn_chg_addr_used(self.chg_output) + return ad_f, cmd_args def confirm_autoselected_addr(self, chg): from ..ui import keypress_confirm @@ -304,7 +300,7 @@ class New(Base): die(1, 'Exiting at user request') # inputs methods - def select_unspent(self, unspent): + def get_unspent_nums_from_user(self, unspent): prompt = 'Enter a range or space-separated list of outputs to spend: ' from ..ui import line_input while True: @@ -317,13 +313,12 @@ class New(Base): return selected msg(f'Unspent output number must be <= {len(unspent)}') - def select_unspent_cmdline(self, unspent): + def get_unspent_nums_from_inputs_opt(self, unspent): - def idx2num(idx): + def do_add_msg(idx): uo = unspent[idx] - mmid_disp = f' ({uo.twmmid})' if uo.twmmid.type == 'mmgen' else '' - msg(f'Adding input: {idx + 1} {uo.addr}{mmid_disp}') - return idx + 1 + mm_disp = f' ({uo.twmmid})' if uo.twmmid.type == 'mmgen' else '' + msg('Adding input: {} {}{}'.format(idx + 1, uo.addr, mm_disp)) def get_uo_nums(): for addr in self.cfg.inputs.split(','): @@ -335,9 +330,10 @@ class New(Base): die(1, f'{addr!r}: not an MMGen ID or {self.coin} address') found = False - for idx, us in enumerate(unspent): - if getattr(us, attr) == addr: - yield idx2num(idx) + for idx, e in enumerate(unspent): + if getattr(e, attr) == addr: + do_add_msg(idx) + yield idx + 1 found = True if not found: @@ -364,8 +360,10 @@ class New(Base): async def get_inputs_from_user(self, outputs_sum): while True: - us_f = self.select_unspent_cmdline if self.cfg.inputs else self.select_unspent - sel_nums = us_f(self.twuo.data) + sel_nums = ( + self.get_unspent_nums_from_inputs_opt if self.cfg.inputs else + self.get_unspent_nums_from_user + )(self.twuo.data) msg(f'Selected output{suf(sel_nums)}: {{}}'.format(' '.join(str(n) for n in sel_nums))) sel_unspent = MMGenList(self.twuo.data[i-1] for i in sel_nums) @@ -405,13 +403,16 @@ class New(Base): if self.cfg.comment_file: self.add_comment(self.cfg.comment_file) - twuo_addrs = await self.get_input_addrs_from_cmdline() + twuo_addrs = await self.get_input_addrs_from_inputs_opt() self.twuo = await TwUnspentOutputs(self.cfg, self.proto, minconf=self.cfg.minconf, addrs=twuo_addrs) await self.twuo.get_data() if not do_info: - await self.get_outputs_from_cmdline(cmd_args) + ad_f, cmd_args = self.get_addrdata_from_files(cmd_args) # pops from end of cmd_args + from ..addrdata import TwAddrData + ad_w = await TwAddrData(self.cfg, self.proto, twctl=self.twctl) + await self.process_cmd_args(cmd_args, ad_f, ad_w) from ..ui import do_license_msg do_license_msg(self.cfg)