fixes and cleanups throughout
This commit is contained in:
parent
8311763e94
commit
92fc9fd462
14 changed files with 60 additions and 53 deletions
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')))
|
||||
|
|
|
|||
|
|
@ -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 = [{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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__}')
|
||||
|
|
|
|||
|
|
@ -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')))
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue