help: minor cleanups, move help texts to individual modules

This commit is contained in:
The MMGen Project 2025-02-24 11:27:48 +00:00
commit b3bda1b62b
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
12 changed files with 257 additions and 200 deletions

View file

@ -12,22 +12,21 @@
help: help notes functions for MMGen suite commands
"""
from ..cfg import gc
class help_notes:
def __init__(self, proto, cfg):
self.proto = proto
self.cfg = cfg
def txcreate_args(self, target):
def txcreate_args(self):
return (
'COIN1 [AMT CHG_ADDR] COIN2 [ADDR]'
if target == 'swaptx' else
'[ADDR,AMT ... | DATA_SPEC] ADDR'
if self.proto.base_proto == 'Bitcoin' else
'ADDR,AMT')
def swaptxcreate_args(self):
return 'COIN1 [AMT CHG_ADDR] COIN2 [ADDR]'
def account_info_desc(self):
return 'unspent outputs' if self.proto.base_proto == 'Bitcoin' else 'account info'
@ -40,11 +39,6 @@ class help_notes:
cu = self.proto.coin_amt.units
return ', '.join(cu[:-1]) + ('', ' and ')[len(cu)>1] + cu[-1] + ('', ',\nrespectively')[len(cu)>1]
def coind_exec(self):
from ..daemon import CoinDaemon
return (
CoinDaemon(self.cfg, self.proto.coin).exec_fn if self.proto.coin in CoinDaemon.coins else 'bitcoind')
def dfl_twname(self):
from ..proto.btc.rpc import BitcoinRPCClient
return BitcoinRPCClient.dfl_twname
@ -128,7 +122,7 @@ as {r}, using an integer followed by '{l}', for {u}.
c = self.proto.coin,
r = BaseTX(cfg=self.cfg, proto=self.proto).rel_fee_desc,
l = self.fee_spec_letters(use_quotes=True),
u = self.fee_spec_names() )
u = self.fee_spec_names())
def passwd(self):
return """
@ -146,167 +140,4 @@ BRAINWALLET NOTE:
To thwart dictionary attacks, its recommended to use a strong hash preset
with brainwallets. For a brainwallet passphrase to generate the correct
seed, the same seed length and hash preset parameters must always be used.
""".strip()
def txcreate_examples(self):
mmtype = 'B' if 'B' in self.proto.mmtypes else self.proto.mmtypes[0]
from ..tool.coin import tool_cmd
t = tool_cmd(self.cfg, mmtype=mmtype)
addr = t.privhex2addr('bead' * 16)
sample_addr = addr.views[addr.view_pref]
return f"""
EXAMPLES:
Send 0.123 {self.proto.coin} to an external {self.proto.name} address, returning the change to a
specific MMGen address in the tracking wallet:
$ {gc.prog_name} {sample_addr},0.123 01ABCDEF:{mmtype}:7
Same as above, but select the change address automatically:
$ {gc.prog_name} {sample_addr},0.123 01ABCDEF:{mmtype}
Same as above, but select the change address automatically by address type:
$ {gc.prog_name} {sample_addr},0.123 {mmtype}
Same as above, but reduce verbosity and specify fee of 20 satoshis
per byte:
$ {gc.prog_name} -q -f 20s {sample_addr},0.123 {mmtype}
Send entire balance of selected inputs minus fee to an external {self.proto.name}
address:
$ {gc.prog_name} {sample_addr}
Send entire balance of selected inputs minus fee to first unused wallet
address of specified type:
$ {gc.prog_name} {mmtype}
""" if self.proto.base_proto == 'Bitcoin' else f"""
EXAMPLES:
Send 0.123 {self.proto.coin} to an external {self.proto.name} address:
$ {gc.prog_name} {sample_addr},0.123
Send 0.123 {self.proto.coin} to another account in wallet 01ABCDEF:
$ {gc.prog_name} 01ABCDEF:{mmtype}:7,0.123
"""
def txcreate(self):
outputs_info = (
"""
Outputs are specified in the form ADDRESS,AMOUNT or ADDRESS. The first form
creates an output sending the given amount to the given address. The bare
address form designates the given address as either the change output or the
sole output of the transaction (excluding any data output). Exactly one bare
address argument is required.
For convenience, the bare address argument may be given as ADDRTYPE_CODE or
SEED_ID:ADDRTYPE_CODE (see ADDRESS TYPES below). In the first form, the first
unused address of type ADDRTYPE_CODE for each Seed ID in the tracking wallet
will be displayed in a menu, with the user prompted to select one. In the
second form, the user specifies the Seed ID as well, allowing the script to
select the transactions change output or single output without prompting.
See EXAMPLES below.
A single DATA_SPEC argument may also be given on the command line to create
an OP_RETURN data output with a zero spend amount. This is the preferred way
to embed data in the blockchain. DATA_SPEC may be of the form "data":DATA
or "hexdata":DATA. In the first form, DATA is a string in your systems native
encoding, typically UTF-8. In the second, DATA is a hexadecimal string (with
the leading 0x omitted) encoding the binary data to be embedded. In both
cases, the resulting byte string must not exceed {bl} bytes in length.
""".format(bl=self.proto.max_op_return_data_len)
if self.proto.base_proto == 'Bitcoin' else """
The transaction output is specified in the form ADDRESS,AMOUNT.
""")
return """
The transactions outputs are listed on the command line, while its inputs
are chosen from a list of the wallets unspent outputs via an interactive
menu. Alternatively, inputs may be specified using the --inputs option.
Addresses on the command line can be either native coin addresses or MMGen
IDs in the form SEED_ID:ADDRTYPE_CODE:INDEX.
{oinfo}
If the transaction fee is not specified on the command line (see FEE
SPECIFICATION below), it will be calculated dynamically using network fee
estimation for the default (or user-specified) number of confirmations.
If network fee estimation fails, the user will be prompted for a fee.
Network-estimated fees will be multiplied by the value of --fee-adjust, if
specified.
""".format(oinfo=outputs_info)
def txsign(self):
from ..proto.btc.params import mainnet
return """
Transactions may contain both {pnm} or non-{pnm} input addresses.
To sign non-{pnm} inputs, a {wd}flat key list is used
as the key source (--keys-from-file option).
To sign {pnm} inputs, key data is generated from a seed as with the
{pnl}-addrgen and {pnl}-keygen commands. Alternatively, a key-address file
may be used (--mmgen-keys-from-file option).
Multiple wallets or other seed files can be listed on the command line in
any order. If the seeds required to sign the transactions inputs are not
found in these files (or in the default wallet), the user will be prompted
for seed data interactively.
To prevent an attacker from crafting transactions with bogus {pnm}-to-{pnu}
address mappings, all outputs to {pnm} addresses are verified with a seed
source. Therefore, seed files or a key-address file for all {pnm} outputs
must also be supplied on the command line if the data cant be found in the
default wallet.
""".format(
wd = f'{self.coind_exec()} wallet dump or ' if isinstance(self.proto, mainnet) else '',
pnm = gc.proj_name,
pnu = self.proto.name,
pnl = gc.proj_name.lower())
def subwallet(self):
from ..subseed import SubSeedIdxRange
return f"""
SUBWALLETS:
Subwallets (subseeds) are specified by a Subseed Index consisting of:
a) an integer in the range 1-{SubSeedIdxRange.max_idx}, plus
b) an optional single letter, L or S
The letter designates the length of the subseed. If omitted, L is assumed.
Long (L) subseeds are the same length as their parent wallets seed
(typically 256 bits), while short (S) subseeds are always 128-bit.
The long and short subseeds for a given index are derived independently,
so both may be used.
MMGen Wallet has no notion of depth, and to an outside observer subwallets
are identical to ordinary wallets. This is a feature rather than a bug, as
it denies an attacker any way of knowing whether a given wallet has a parent.
Since subwallets are just wallets, they may be used to generate other
subwallets, leading to hierarchies of arbitrary depth. However, this is
inadvisable in practice for two reasons: Firstly, it creates accounting
complexity, requiring the user to independently keep track of a derivation
tree. More importantly, however, it leads to the danger of Seed ID
collisions between subseeds at different levels of the hierarchy, as
MMGen checks and avoids ID collisions only among sibling subseeds.
An exception to this caveat would be a multi-user setup where sibling
subwallets are distributed to different users as their default wallets.
Since the subseeds derived from these subwallets are private to each user,
Seed ID collisions among them doesnt present a problem.
A safe rule of thumb, therefore, is for *each user* to derive all of his/her
subwallets from a single parent. This leaves each user with a total of two
million subwallets, which should be enough for most practical purposes.
""".strip()

52
mmgen/help/subwallet.py Executable file
View file

@ -0,0 +1,52 @@
#!/usr/bin/env python3
#
# MMGen Wallet, a terminal-based cryptocurrency wallet
# Copyright (C)2013-2025 The MMGen Project <mmgen@tuta.io>
# Licensed under the GNU General Public License, Version 3:
# https://www.gnu.org/licenses
# Public project repositories:
# https://github.com/mmgen/mmgen-wallet
# https://gitlab.com/mmgen/mmgen-wallet
"""
help.subwallet: subwallet help notes for the MMGen Wallet suite
"""
def help(proto, cfg):
from ..subseed import SubSeedIdxRange
return f"""
SUBWALLETS:
Subwallets (subseeds) are specified by a Subseed Index consisting of:
a) an integer in the range 1-{SubSeedIdxRange.max_idx}, plus
b) an optional single letter, L or S
The letter designates the length of the subseed. If omitted, L is assumed.
Long (L) subseeds are the same length as their parent wallets seed
(typically 256 bits), while short (S) subseeds are always 128-bit.
The long and short subseeds for a given index are derived independently,
so both may be used.
MMGen Wallet has no notion of depth, and to an outside observer subwallets
are identical to ordinary wallets. This is a feature rather than a bug, as
it denies an attacker any way of knowing whether a given wallet has a parent.
Since subwallets are just wallets, they may be used to generate other
subwallets, leading to hierarchies of arbitrary depth. However, this is
inadvisable in practice for two reasons: Firstly, it creates accounting
complexity, requiring the user to independently keep track of a derivation
tree. More importantly, however, it leads to the danger of Seed ID
collisions between subseeds at different levels of the hierarchy, as
MMGen checks and avoids ID collisions only among sibling subseeds.
An exception to this caveat would be a multi-user setup where sibling
subwallets are distributed to different users as their default wallets.
Since the subseeds derived from these subwallets are private to each user,
Seed ID collisions among them doesnt present a problem.
A safe rule of thumb, therefore, is for *each user* to derive all of his/her
subwallets from a single parent. This leaves each user with a total of two
million subwallets, which should be enough for most practical purposes.
""".strip()

59
mmgen/help/txcreate.py Executable file
View file

@ -0,0 +1,59 @@
#!/usr/bin/env python3
#
# MMGen Wallet, a terminal-based cryptocurrency wallet
# Copyright (C)2013-2025 The MMGen Project <mmgen@tuta.io>
# Licensed under the GNU General Public License, Version 3:
# https://www.gnu.org/licenses
# Public project repositories:
# https://github.com/mmgen/mmgen-wallet
# https://gitlab.com/mmgen/mmgen-wallet
"""
help.txcreate: txcreate and txdo help notes for the MMGen Wallet suite
"""
def help(proto, cfg):
outputs_info = (
"""
Outputs are specified in the form ADDRESS,AMOUNT or ADDRESS. The first form
creates an output sending the given amount to the given address. The bare
address form designates the given address as either the change output or the
sole output of the transaction (excluding any data output). Exactly one bare
address argument is required.
For convenience, the bare address argument may be given as ADDRTYPE_CODE or
SEED_ID:ADDRTYPE_CODE (see ADDRESS TYPES below). In the first form, the first
unused address of type ADDRTYPE_CODE for each Seed ID in the tracking wallet
will be displayed in a menu, with the user prompted to select one. In the
second form, the user specifies the Seed ID as well, allowing the script to
select the transactions change output or single output without prompting.
See EXAMPLES below.
A single DATA_SPEC argument may also be given on the command line to create
an OP_RETURN data output with a zero spend amount. This is the preferred way
to embed data in the blockchain. DATA_SPEC may be of the form "data":DATA
or "hexdata":DATA. In the first form, DATA is a string in your systems native
encoding, typically UTF-8. In the second, DATA is a hexadecimal string (with
the leading 0x omitted) encoding the binary data to be embedded. In both
cases, the resulting byte string must not exceed {bl} bytes in length.
""".format(bl=proto.max_op_return_data_len)
if proto.base_proto == 'Bitcoin' else """
The transaction output is specified in the form ADDRESS,AMOUNT.
""")
return f"""
The transactions outputs are listed on the command line, while its inputs
are chosen from a list of the wallets unspent outputs via an interactive
menu. Alternatively, inputs may be specified using the --inputs option.
Addresses on the command line can be either native coin addresses or MMGen
IDs in the form SEED_ID:ADDRTYPE_CODE:INDEX.
{outputs_info}
If the transaction fee is not specified on the command line (see FEE
SPECIFICATION below), it will be calculated dynamically using network fee
estimation for the default (or user-specified) number of confirmations.
If network fee estimation fails, the user will be prompted for a fee.
Network-estimated fees will be multiplied by the value of --fee-adjust, if
specified.
"""

65
mmgen/help/txcreate_examples.py Executable file
View file

@ -0,0 +1,65 @@
#!/usr/bin/env python3
#
# MMGen Wallet, a terminal-based cryptocurrency wallet
# Copyright (C)2013-2025 The MMGen Project <mmgen@tuta.io>
# Licensed under the GNU General Public License, Version 3:
# https://www.gnu.org/licenses
# Public project repositories:
# https://github.com/mmgen/mmgen-wallet
# https://gitlab.com/mmgen/mmgen-wallet
"""
help.txcreate_examples: txcreate and txdo help examples for the MMGen Wallet suite
"""
from ..cfg import gc
def help(proto, cfg):
mmtype = 'B' if 'B' in proto.mmtypes else proto.mmtypes[0]
from ..tool.coin import tool_cmd
t = tool_cmd(cfg, mmtype=mmtype)
addr = t.privhex2addr('bead' * 16)
sample_addr = addr.views[addr.view_pref]
return f"""
EXAMPLES:
Send 0.123 {proto.coin} to an external {proto.name} address, returning the change to a
specific MMGen address in the tracking wallet:
$ {gc.prog_name} {sample_addr},0.123 01ABCDEF:{mmtype}:7
Same as above, but select the change address automatically:
$ {gc.prog_name} {sample_addr},0.123 01ABCDEF:{mmtype}
Same as above, but select the change address automatically by address type:
$ {gc.prog_name} {sample_addr},0.123 {mmtype}
Same as above, but reduce verbosity and specify fee of 20 satoshis
per byte:
$ {gc.prog_name} -q -f 20s {sample_addr},0.123 {mmtype}
Send entire balance of selected inputs minus fee to an external {proto.name}
address:
$ {gc.prog_name} {sample_addr}
Send entire balance of selected inputs minus fee to first unused wallet
address of specified type:
$ {gc.prog_name} {mmtype}
""" if proto.base_proto == 'Bitcoin' else f"""
EXAMPLES:
Send 0.123 {proto.coin} to an external {proto.name} address:
$ {gc.prog_name} {sample_addr},0.123
Send 0.123 {proto.coin} to another account in wallet 01ABCDEF:
$ {gc.prog_name} 01ABCDEF:{mmtype}:7,0.123
"""

48
mmgen/help/txsign.py Executable file
View file

@ -0,0 +1,48 @@
#!/usr/bin/env python3
#
# MMGen Wallet, a terminal-based cryptocurrency wallet
# Copyright (C)2013-2025 The MMGen Project <mmgen@tuta.io>
# Licensed under the GNU General Public License, Version 3:
# https://www.gnu.org/licenses
# Public project repositories:
# https://github.com/mmgen/mmgen-wallet
# https://gitlab.com/mmgen/mmgen-wallet
"""
help.txsign: txsign help notes for the MMGen Wallet suite
"""
from ..cfg import gc
from ..proto.btc.params import mainnet
from ..daemon import CoinDaemon
def help(proto, cfg):
def coind_exec():
return CoinDaemon(cfg, proto.coin).exec_fn if proto.coin in CoinDaemon.coins else 'bitcoind'
return """
Transactions may contain both {pnm} or non-{pnm} input addresses.
To sign non-{pnm} inputs, a {wd}flat key list is used
as the key source (--keys-from-file option).
To sign {pnm} inputs, key data is generated from a seed as with the
{pnl}-addrgen and {pnl}-keygen commands. Alternatively, a key-address file
may be used (--mmgen-keys-from-file option).
Multiple wallets or other seed files can be listed on the command line in
any order. If the seeds required to sign the transactions inputs are not
found in these files (or in the default wallet), the user will be prompted
for seed data interactively.
To prevent an attacker from crafting transactions with bogus {pnm}-to-{pnu}
address mappings, all outputs to {pnm} addresses are verified with a seed
source. Therefore, seed files or a key-address file for all {pnm} outputs
must also be supplied on the command line if the data cant be found in the
default wallet.
""".format(
wd = f'{coind_exec()} wallet dump or ' if isinstance(proto, mainnet) else '',
pnm = gc.proj_name,
pnu = proto.name,
pnl = gc.proj_name.lower())

View file

@ -9,7 +9,7 @@
# https://gitlab.com/mmgen/mmgen-wallet
"""
help.xmrwallet: xmrwallet help notes for MMGen suite
help.xmrwallet: xmrwallet help notes for the MMGen Wallet suite
"""
def help(proto, cfg):

View file

@ -111,9 +111,9 @@ range(s).
cfg = cfg,
gc = gc,
),
'notes': lambda help_notes, s: s.format(
'notes': lambda help_mod, help_notes, s: s.format(
n_addrkey = note_addrkey,
n_sw = help_notes('subwallet')+'\n\n',
n_sw = help_mod('subwallet')+'\n\n',
n_pw = help_notes('passwd')+'\n\n',
n_bw = help_notes('brainwallet'),
n_fmt = help_notes('fmt_codes'),

View file

@ -95,7 +95,8 @@ identical to that of ‘mmgen-txcreate’.
The user should take care to select a fee sufficient to ensure the original
transaction is replaced in the mempool.
{e}{s}
{e}
{s}
Seed source files must have the canonical extensions listed in the 'FileExt'
column below:
@ -104,7 +105,7 @@ column below:
},
'code': {
'usage': lambda cfg, proto, help_notes, s: s.format(
u_args = help_notes('txcreate_args', 'tx')),
u_args = help_notes('txcreate_args')),
'options': lambda cfg, help_notes, proto, s: s.format(
cfg = cfg,
gc = gc,
@ -116,9 +117,9 @@ column below:
coin_id = help_notes('coin_id'),
dsl = help_notes('dfl_seed_len'),
cu = proto.coin),
'notes': lambda help_notes, s: s.format(
'notes': lambda help_mod, help_notes, s: s.format(
e = help_notes('fee'),
s = help_notes('txsign'),
s = help_mod('txsign'),
f = help_notes('fmt_codes')),
}
}

View file

@ -35,7 +35,7 @@ opts_data = {
'text': {
'desc': {
'tx': f'Create a transaction with outputs to specified coin or {gc.proj_name} addresses',
'swaptx': f'Create a DEX swap transaction with {gc.proj_name} inputs and outputs',
'swaptx': f'Create a DEX swap transaction from one {gc.proj_name} tracking wallet to another',
}[target],
'usage': '[opts] {u_args} [addr file ...]',
'options': """
@ -80,7 +80,7 @@ opts_data = {
},
'code': {
'usage': lambda cfg, proto, help_notes, s: s.format(
u_args = help_notes('txcreate_args', target)),
u_args = help_notes('txcreate_args')),
'options': lambda cfg, proto, help_notes, s: s.format(
cfg = cfg,
cu = proto.coin,
@ -91,11 +91,11 @@ opts_data = {
fe_dfl = cfg._autoset_opts['fee_estimate_mode'].choices[0],
x_all = fmt_list(cfg._autoset_opts['swap_proto'].choices, fmt='no_spc'),
x_dfl = cfg._autoset_opts['swap_proto'].choices[0]),
'notes': lambda cfg, help_notes, s: s.format(
c = help_notes('txcreate'),
'notes': lambda cfg, help_mod, help_notes, s: s.format(
c = help_mod('txcreate'),
F = help_notes('fee'),
x = help_notes('txcreate_examples'),
n_at = help_notes('address_types'))
n_at = help_notes('address_types'),
x = help_mod('txcreate_examples'))
}
}

View file

@ -17,7 +17,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
mmgen-txdo: Create, sign and broadcast an online MMGen transaction
mmgen-txdo: Create, sign and send an online MMGen transaction
"""
from .cfg import gc, Config
@ -35,7 +35,7 @@ opts_data = {
'text': {
'desc': {
'tx': f'Create, sign and send an {gc.proj_name} transaction',
'swaptx': f'Create, sign and send a DEX swap transaction with {gc.proj_name} inputs and outputs',
'swaptx': f'Create, sign and send a DEX swap transaction from one {gc.proj_name} tracking wallet to another',
}[target],
'usage': '[opts] {u_args} [addr file ...] [seed source ...]',
'options': """
@ -98,22 +98,23 @@ opts_data = {
-- -z, --show-hash-presets Show information on available hash presets
""",
'notes': """
{c}\n{F}
{c}
{n_at}
{F}
SIGNING NOTES
{s}
Seed source files must have the canonical extensions listed in the 'FileExt'
column below:
{n_at}
{f}
{x}"""
},
'code': {
'usage': lambda cfg, proto, help_notes, s: s.format(
u_args = help_notes('txcreate_args', target)),
u_args = help_notes('txcreate_args')),
'options': lambda cfg, proto, help_notes, s: s.format(
gc = gc,
cfg = cfg,
@ -132,13 +133,13 @@ column below:
fe_dfl = cfg._autoset_opts['fee_estimate_mode'].choices[0],
x_all = fmt_list(cfg._autoset_opts['swap_proto'].choices, fmt='no_spc'),
x_dfl = cfg._autoset_opts['swap_proto'].choices[0]),
'notes': lambda cfg, help_notes, s: s.format(
c = help_notes('txcreate'),
'notes': lambda cfg, help_mod, help_notes, s: s.format(
c = help_mod('txcreate'),
F = help_notes('fee'),
s = help_notes('txsign'),
n_at = help_notes('address_types'),
f = help_notes('fmt_codes'),
x = help_notes('txcreate_examples')),
s = help_mod('txsign'),
x = help_mod('txcreate_examples'))
}
}

View file

@ -89,8 +89,8 @@ column below:
ss = help_notes('dfl_subseeds'),
ss_max = SubSeedIdxRange.max_idx,
cu = proto.coin),
'notes': lambda help_notes, s: s.format(
t = help_notes('txsign'),
'notes': lambda help_mod, help_notes, s: s.format(
t = help_mod('txsign'),
f = help_notes('fmt_codes')),
}
}

View file

@ -138,7 +138,7 @@ opts_data = {
'notes': lambda cfg, help_mod, help_notes, s: s.format(
f = help_notes('fmt_codes'),
n_ss = ('', help_mod('seedsplit')+'\n\n')[do_ss_note],
n_sw = ('', help_notes('subwallet')+'\n\n')[do_sw_note],
n_sw = ('', help_mod('subwallet')+'\n\n')[do_sw_note],
n_pw = help_notes('passwd'),
n_bw = ('', '\n\n'+help_notes('brainwallet'))[do_bw_note]
)