fixes and cleanups throughout

This commit is contained in:
The MMGen Project 2025-02-15 09:54:19 +00:00
commit 92fc9fd462
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
14 changed files with 60 additions and 53 deletions

View file

@ -69,10 +69,10 @@ opts_data = {
-- -q, --quiet Suppress warnings; overwrite files without prompting
bt -R, --no-rbf Make transaction non-replaceable (non-replace-by-fee
+ according to BIP 125)
-s -s, --swap-proto Swap protocol to use (Default: {x_dfl},
+ Choices: {x_all})
-- -v, --verbose Produce more verbose output
b- -V, --vsize-adj= f Adjust transaction's estimated vsize by factor 'f'
-s -x, --swap-proto Swap protocol to use (Default: {x_dfl},
+ Choices: {x_all})
-- -y, --yes Answer 'yes' to prompts, suppress non-essential output
e- -X, --cached-balances Use cached balances
""",
@ -101,7 +101,7 @@ opts_data = {
cfg = Config(opts_data=opts_data)
if not cfg.info and len(cfg._args) < {'tx': 1, 'swaptx': 2}[target]:
if not (cfg.info or cfg.contract_data) and len(cfg._args) < {'tx': 1, 'swaptx': 2}[target]:
cfg._usage()
async def main():

View file

@ -83,16 +83,16 @@ opts_data = {
-- -p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p'
+ for password hashing (default: '{gc.dfl_hash_preset}')
-- -P, --passwd-file= f Get {pnm} wallet passphrase from file 'f'
-- -q, --quiet Suppress warnings; overwrite files without prompting
bt -R, --no-rbf Make transaction non-replaceable (non-replace-by-fee
+ according to BIP 125)
-- -q, --quiet Suppress warnings; overwrite files without prompting
-s -s, --swap-proto Swap protocol to use (Default: {x_dfl},
+ Choices: {x_all})
-- -u, --subseeds= n The number of subseed pairs to scan for (default: {ss},
+ maximum: {ss_max}). Only the default or first supplied
+ wallet is scanned for subseeds.
-- -v, --verbose Produce more verbose output
b- -V, --vsize-adj= f Adjust transaction's estimated vsize by factor 'f'
-s -x, --swap-proto Swap protocol to use (Default: {x_dfl},
+ Choices: {x_all})
e- -X, --cached-balances Use cached balances
-- -y, --yes Answer 'yes' to prompts, suppress non-essential output
-- -z, --show-hash-presets Show information on available hash presets

View file

@ -14,12 +14,12 @@ proto.btc.tx.info: Bitcoin transaction info class
from ....tx.info import TxInfo
from ....util import fmt, die
from ....color import red, green, pink, blue
from ....color import red, green, blue, pink
from ....addr import MMGenID
class TxInfo(TxInfo):
sort_orders = ('addr', 'raw')
txinfo_hdr_fs = 'TRANSACTION DATA\n\nID={i} ({a} {c}) RBF={r} Sig={s} Locktime={l}\n'
txinfo_hdr_fs = '{hdr}\n ID={i} ({a} {c}) RBF={r} Sig={s} Locktime={l}\n'
txinfo_hdr_fs_short = 'TX {i} ({a} {c}) RBF={r} Sig={s} Locktime={l}\n'
txinfo_ftr_fs = fmt("""
Input amount: {i} {d}
@ -64,7 +64,10 @@ class TxInfo(TxInfo):
append_chars=('', ' (chg)')[bool(not is_input and e.is_chg and terse)],
append_color='green')
else:
return MMGenID.fmtc(nonmm_str, width=max_mmwid, color=True)
return MMGenID.fmtc(
nonmm_str,
width = max_mmwid,
color = True)
def format_io(desc):
io = getattr(tx, desc)
@ -134,7 +137,10 @@ class TxInfo(TxInfo):
vp1 = 0
return (
'Displaying inputs and outputs in {} sort order'.format({'raw':'raw', 'addr':'address'}[sort])
'Inputs/Outputs sort order: {}'.format({
'raw': pink('UNSORTED'),
'addr': pink('ADDRESS')
}[sort])
+ ('\n\n', '\n')[terse]
+ ''.join(format_io('inputs'))
+ ''.join(format_io('outputs')))

View file

@ -125,18 +125,19 @@ class New(Base, TxNew):
def final_inputs_ok_msg(self, funds_left):
return 'Transaction produces {} {} in change'.format(funds_left.hl(), self.coin)
def check_chg_addr_is_wallet_addr(self):
if len([o for o in self.outputs if not o.data]) > 1 and not self.chg_output.mmid:
def check_chg_addr_is_wallet_addr(self, message='Change address is not an MMGen wallet address!'):
def do_err():
from ....ui import confirm_or_raise
confirm_or_raise(
cfg = self.cfg,
message = yellow('Change address is not an MMGen wallet address!'),
message = yellow(message),
action = 'Are you sure this is what you want?')
if len(self.outputs) > 1 and not self.chg_output.mmid:
do_err()
async def create_serialized(self, locktime=None, bump=None):
if not (bump or self.is_swap):
self.inputs.sort_bip69()
if not bump:
# Set all sequence numbers to the same value, in conformity with the behavior of most modern wallets:
do_rbf = self.proto.cap('rbf') and not self.cfg.no_rbf
seqnum_val = self.proto.max_int - (2 if do_rbf else 1 if locktime else 0)
@ -144,6 +145,7 @@ class New(Base, TxNew):
i.sequence = seqnum_val
if not self.is_swap:
self.inputs.sort_bip69()
self.outputs.sort_bip69()
inputs_list = [{

View file

@ -18,6 +18,6 @@ from .new import New
class NewSwap(New, TxNewSwap):
desc = 'Bitcoin swap transaction'
async def process_swap_cmdline_args(self, cmd_args):
async def process_swap_cmdline_args(self, cmd_args, addrfile_args):
import sys
sys.exit(0)

View file

@ -18,7 +18,7 @@ from ....color import pink, yellow, blue
from ....addr import MMGenID
class TxInfo(TxInfo):
txinfo_hdr_fs = 'TRANSACTION DATA\n\nID={i} ({a} {c}) Sig={s} Locktime={l}\n'
txinfo_hdr_fs = '{hdr}\n ID={i} ({a} {c}) Sig={s} Locktime={l}\n'
txinfo_hdr_fs_short = 'TX {i} ({a} {c}) Sig={s} Locktime={l}\n'
txinfo_ftr_fs = fmt("""
Total in account: {i} {d}

View file

@ -98,8 +98,8 @@ class Base(MMGenObject):
tw_copy_attrs = {'scriptPubKey', 'vout', 'amt', 'comment', 'mmid', 'addr', 'confs', 'txid'}
class Output(MMGenTxIO):
is_chg = ListItemAttr(bool, typeconv=False)
data = ListItemAttr(None, typeconv=False) # placeholder
is_chg = ListItemAttr(bool, typeconv=False)
data = ListItemAttr(None, typeconv=False) # placeholder
class InputList(MMGenTxIOList):
desc = 'transaction inputs'
@ -146,12 +146,10 @@ class Base(MMGenObject):
return self.proto.coin_amt('0')
return sum(e.amt for e in olist)
def _chg_output_ops(self, op):
is_chgs = [x.is_chg for x in self.outputs]
def _chg_output_ops(self, op, attr):
is_chgs = [getattr(x, attr) for x in self.outputs]
if is_chgs.count(True) == 1:
return (
is_chgs.index(True) if op == 'idx' else
self.outputs[is_chgs.index(True)])
return is_chgs.index(True) if op == 'idx' else self.outputs[is_chgs.index(True)]
elif is_chgs.count(True) == 0:
return None
else:
@ -159,11 +157,11 @@ class Base(MMGenObject):
@property
def chg_idx(self):
return self._chg_output_ops('idx')
return self._chg_output_ops('idx', 'is_chg')
@property
def chg_output(self):
return self._chg_output_ops('output')
return self._chg_output_ops('output', 'is_chg')
def add_timestamp(self):
self.timestamp = make_timestamp()

View file

@ -64,15 +64,13 @@ class MMGenTxFile(MMGenObject):
'send_amt': 'skip',
'timestamp': None,
'blockcount': None,
'serialized': None,
}
'serialized': None}
extra_attrs = {
'locktime': None,
'comment': MMGenTxComment,
'coin_txid': CoinTxID,
'sent_timestamp': None,
'is_swap': False,
}
'is_swap': None}
def __init__(self, tx):
self.tx = tx

View file

@ -15,7 +15,7 @@ tx.info: transaction info class
import importlib
from ..cfg import gc
from ..color import red, green, orange
from ..color import red, green, cyan, orange
from ..util import msg, msg_r, decode_timestamp, make_timestr
from ..util2 import format_elapsed_hr
@ -51,6 +51,7 @@ class TxInfo:
def gen_view():
yield (self.txinfo_hdr_fs_short if terse else self.txinfo_hdr_fs).format(
hdr = cyan('TRANSACTION DATA'),
i = tx.txid.hl(),
a = tx.send_amt.hl(),
c = tx.dcoin,
@ -63,19 +64,19 @@ class TxInfo:
for attr, label in [('timestamp', 'Created:'), ('sent_timestamp', 'Sent:')]:
if (val := getattr(tx, attr)) is not None:
_ = decode_timestamp(val)
yield f'{label:8} {make_timestr(_)} ({format_elapsed_hr(_)})\n'
yield f' {label:8} {make_timestr(_)} ({format_elapsed_hr(_)})\n'
if tx.chain != 'mainnet': # if mainnet has a coin-specific name, display it
yield green(f'Chain: {tx.chain.upper()}') + '\n'
yield green(f' Chain: {tx.chain.upper()}') + '\n'
if tx.coin_txid:
yield f'{tx.coin} TxID: {tx.coin_txid.hl()}\n'
yield f' {tx.coin} TxID: {tx.coin_txid.hl()}\n'
enl = ('\n', '')[bool(terse)]
yield enl
if tx.comment:
yield f'Comment: {tx.comment.hl()}\n{enl}'
yield f' Comment: {tx.comment.hl()}\n{enl}'
yield self.format_body(
blockcount,
@ -124,6 +125,7 @@ class TxInfo:
from ..ui import do_pager
do_pager(o)
else:
msg('')
msg_r(o)
from ..term import get_char
if pause:

View file

@ -18,5 +18,5 @@ class NewSwap(New):
desc = 'swap transaction'
is_swap = True
async def process_swap_cmdline_args(self, cmd_args):
async def process_swap_cmdline_args(self, cmd_args, addrfile_args):
raise NotImplementedError(f'Swap not implemented for protocol {self.proto.__name__}')

View file

@ -135,14 +135,14 @@ def format_elapsed_days_hr(t, now=None, cached={}):
cached[e] = f'{days} day{suf(days)} ' + ('ago' if e > 0 else 'in the future')
return cached[e]
def format_elapsed_hr(t, now=None, cached={}, rel_now=True, show_secs=False):
def format_elapsed_hr(t, now=None, cached={}, rel_now=True, show_secs=False, future_msg='in the future'):
e = int((now or time.time()) - t)
key = f'{e}:{rel_now}:{show_secs}'
if not key in cached:
def add_suffix():
return (
((' ago' if rel_now else '') if e > 0 else
(' in the future' if rel_now else ' (negative elapsed)'))
(f' {future_msg}' if rel_now else ' (negative elapsed)'))
if (abs_e if show_secs else abs_e // 60) else
('just now' if rel_now else ('0 ' + ('seconds' if show_secs else 'minutes')))
)

View file

@ -44,8 +44,9 @@ from ..include.common import (
cmp_or_die,
strip_ansi_escapes,
gr_uc,
getrandhex
)
getrandhex,
make_burn_addr)
from .common import (
ok_msg,
get_file_with_ext,
@ -53,8 +54,8 @@ from .common import (
tw_comment_lat_cyr_gr,
tw_comment_zh,
tx_comment_jp,
get_env_without_debug_vars
)
get_env_without_debug_vars)
from .ct_base import CmdTestBase
from .ct_shared import CmdTestShared
@ -161,14 +162,6 @@ rt_data = {
}
}
def make_burn_addr(proto, mmtype='compressed'):
from mmgen.tool.coin import tool_cmd
return tool_cmd(
cfg = cfg,
cmdname = 'pubhash2addr',
proto = proto,
mmtype = mmtype).pubhash2addr('00'*20)
class CmdTestRegtest(CmdTestBase, CmdTestShared):
'transacting and tracking wallet operations via regtest mode'
networks = ('btc', 'ltc', 'bch')
@ -1185,9 +1178,9 @@ class CmdTestRegtest(CmdTestBase, CmdTestShared):
txfile = self.get_file_with_ext(ext, delete=False, no_dot=True)
return self.user_txbump('bob', self.tmpdir, txfile, rtFee[2], add_args=['--send'])
def generate(self, num_blocks=1):
def generate(self, num_blocks=1, add_opts=[]):
int(num_blocks)
t = self.spawn('mmgen-regtest', ['generate', str(num_blocks)])
t = self.spawn('mmgen-regtest', add_opts + ['generate', str(num_blocks)])
t.expect(f'Mined {num_blocks} block')
return t

View file

@ -350,6 +350,14 @@ def in_nix_environment():
return True
return False
def make_burn_addr(proto, mmtype='compressed', hexdata=None):
from mmgen.tool.coin import tool_cmd
return tool_cmd(
cfg = cfg,
cmdname = 'pubhash2addr',
proto = proto,
mmtype = mmtype).pubhash2addr(hexdata or '00'*20)
def VirtBlockDevice(img_path, size):
if sys.platform == 'linux':
return VirtBlockDeviceLinux(img_path, size)

View file

@ -380,7 +380,7 @@ done
in_nix_environment && parity --help >/dev/null 2>&1 || SKIP_PARITY=1
[ "$MMGEN_DISABLE_COLOR" ] || {
[ "$MMGEN_DISABLE_COLOR" -o ! -t 1 ] || {
RED="\e[31;1m" GREEN="\e[32;1m" YELLOW="\e[33;1m" BLUE="\e[34;1m" MAGENTA="\e[35;1m" CYAN="\e[36;1m"
RESET="\e[0m"
}