tx.new + subclasses: method & import renames, refactor, cleanups

This commit is contained in:
The MMGen Project 2025-02-15 09:54:18 +00:00
commit 8b6c24cc07
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
4 changed files with 41 additions and 40 deletions

View file

@ -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):

View file

@ -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 []

View file

@ -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

View file

@ -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)