Example #1
0
def tt_txview():
	cmsg('Testing tx.view_with_prompt() (try each viewing option)')
	from mmgen.tx import MMGenTX
	fn = 'test/ref/0B8D5A[15.31789,14,tl=1320969600].rawtx'
	tx = MMGenTX(fn,offline=True)
	while True:
		tx.view_with_prompt('View data for transaction?',pause=False)
		if not keypress_confirm('Continue testing transaction view?',default_yes=True):
			break
Example #2
0
 def __init__(self, *args, **kwargs):
     MMGenTX.__init__(self, *args, **kwargs)
     if hasattr(opt, 'tx_gas') and opt.tx_gas:
         self.tx_gas = self.start_gas = ETHAmt(int(opt.tx_gas), 'wei')
     if hasattr(opt, 'contract_data') and opt.contract_data:
         m = "'--contract-data' option may not be used with token transaction"
         assert not 'Token' in type(self).__name__, m
         self.usr_contract_data = HexStr(
             open(opt.contract_data).read().strip())
         self.disable_fee_check = True
Example #3
0
def opt_is_tx_fee(val, desc):
    from mmgen.tx import MMGenTX
    ret = MMGenTX().process_fee_spec(val, 224, on_fail='return')
    if ret == False:
        msg("'{}': invalid {}\n(not a {} amount or {} specification)".format(
            val, desc, g.coin.upper(),
            MMGenTX().rel_fee_desc))
    elif ret != None and ret > g.proto.max_tx_fee:
        msg("'{}': invalid {}\n({} > max_tx_fee ({} {}))".format(
            val, desc, ret.fmt(fs='1.1'), g.proto.max_tx_fee, g.coin.upper()))
    else:
        return True
    return False
Example #4
0
File: opts.py Project: mmgen/mmgen
def opt_is_tx_fee(val,desc):
	from mmgen.tx import MMGenTX
	ret = MMGenTX().process_fee_spec(val,224,on_fail='return')
	# Non-standard startgas: disable fee checking
	if hasattr(opt,'contract_data') and opt.contract_data: ret = None
	if hasattr(opt,'tx_gas') and opt.tx_gas:               ret = None
	if ret == False:
		msg("'{}': invalid {}\n(not a {} amount or {} specification)".format(
				val,desc,g.coin.upper(),MMGenTX().rel_fee_desc))
	elif ret != None and ret > g.proto.max_tx_fee:
		msg("'{}': invalid {}\n({} > max_tx_fee ({} {}))".format(
				val,desc,ret.fmt(fs='1.1'),g.proto.max_tx_fee,g.coin.upper()))
	else:
		return True
	return False
Example #5
0
def opt_is_tx_fee(val,desc):
	from mmgen.tx import MMGenTX
	ret = MMGenTX().process_fee_spec(val,224,on_fail='return')
	# Non-standard startgas: disable fee checking
	if hasattr(opt,'contract_data') and opt.contract_data: ret = None
	if hasattr(opt,'tx_gas') and opt.tx_gas:               ret = None
	if ret == False:
		msg("'{}': invalid {}\n(not a {} amount or {} specification)".format(
				val,desc,g.coin.upper(),MMGenTX().rel_fee_desc))
	elif ret != None and ret > g.proto.max_tx_fee:
		msg("'{}': invalid {}\n({} > max_tx_fee ({} {}))".format(
				val,desc,ret.fmt(fs='1.1'),g.proto.max_tx_fee,g.coin.upper()))
	else:
		return True
	return False
Example #6
0
	def txview( varargs_call_sig = { # hack to allow for multiple filenames
					'args': (
						'mmgen_tx_file(s)',
						'pager',
						'terse',
						'sort',
						'filesort' ),
					'dfls': ( False, False, 'addr', 'mtime' ),
					'annots': {
						'mmgen_tx_file(s)': str,
						'sort': '(valid options: addr,raw)',
						'filesort': '(valid options: mtime,ctime,atime)'
						} },
				*infiles,**kwargs):
		"show raw/signed MMGen transaction in human-readable form"

		terse = bool(kwargs.get('terse'))
		tx_sort = kwargs.get('sort') or 'addr'
		file_sort = kwargs.get('filesort') or 'mtime'

		from mmgen.filename import MMGenFileList
		from mmgen.tx import MMGenTX
		flist = MMGenFileList(infiles,ftype=MMGenTX)
		flist.sort_by_age(key=file_sort) # in-place sort

		sep = '—'*77+'\n'
		return sep.join([MMGenTX(fn).format_view(terse=terse,sort=tx_sort) for fn in flist.names()]).rstrip()
Example #7
0
def opt_is_tx_fee(val, desc):
    from mmgen.tx import MMGenTX
    ret = MMGenTX().convert_fee_spec(val, 224, on_fail='return')
    if ret == False:
        msg("'{}': invalid {} (not a {} amount or satoshis-per-byte specification)"
            .format(val, desc, g.coin.upper()))
    elif ret != None and ret > g.proto.max_tx_fee:
        msg("'{}': invalid {} (> max_tx_fee ({} {}))".format(
            val, desc, g.proto.max_tx_fee, g.coin.upper()))
    else:
        return True
    return False
Example #8
0
		async def test_mmgen_txs():
			fns = ( ('btc',False,'test/ref/0B8D5A[15.31789,14,tl=1320969600].rawtx'),
					('btc',True,'test/ref/0C7115[15.86255,14,tl=1320969600].testnet.rawtx'),
				#	('bch',False,'test/ref/460D4D-BCH[10.19764,tl=1320969600].rawtx')
				)
			print_info('test/ref/*rawtx','MMGen reference transactions')
			g.rpc_port = None
			for n,(coin,testnet,fn) in enumerate(fns):
				g.proto = init_proto(coin,testnet=testnet)
				g.proto.daemon_data_dir = 'test/daemons/' + coin
				g.proto.rpc_port = CoinDaemon(coin + ('','_tn')[testnet],test_suite=True).rpc_port
				await rpc_init()
				await test_tx(MMGenTX(fn).hex,fn,n+1)
			Msg('OK')
Example #9
0
 async def test_mmgen_txs():
     fns = (
         ('btc', False,
          'test/ref/0B8D5A[15.31789,14,tl=1320969600].rawtx'),
         ('btc', True,
          'test/ref/0C7115[15.86255,14,tl=1320969600].testnet.rawtx'),
         #	('bch',False,'test/ref/460D4D-BCH[10.19764,tl=1320969600].rawtx')
     )
     print_info('test/ref/*rawtx', 'MMGen reference')
     for n, (coin, testnet, fn) in enumerate(fns):
         tx = MMGenTX.Unsigned(filename=fn)
         await test_tx(tx_proto=tx.proto,
                       tx_hex=tx.hex,
                       desc=fn,
                       n=n + 1)
     Msg('OK')
 def test_mmgen_txs():
     fns = (
         ('btc', False,
          'test/ref/0B8D5A[15.31789,14,tl=1320969600].rawtx'),
         ('btc', True,
          'test/ref/0C7115[15.86255,14,tl=1320969600].testnet.rawtx'),
         ('bch', False,
          'test/ref/460D4D-BCH[10.19764,tl=1320969600].rawtx'))
     from mmgen.protocol import init_coin
     from mmgen.tx import MMGenTX
     print_info('test/ref/*rawtx', 'MMGen reference transactions')
     for n, (coin, tn, fn) in enumerate(fns):
         init_coin(coin, tn)
         rpc_init(reinit=True)
         test_tx(MMGenTX(fn).hex, fn, n + 1)
     init_coin('btc', False)
     rpc_init(reinit=True)
     Msg('OK')
Example #11
0
        async def sign(
                self, tx_num_str, keys
        ):  # return TX object or False; don't exit or raise exception

            try:
                self.check_correct_chain()
            except TransactionChainMismatch:
                return False

            msg_r(f'Signing transaction{tx_num_str}...')

            try:
                await self.do_sign(keys[0].sec.wif, tx_num_str)
                msg('OK')
                return MMGenTX.Signed(data=self.__dict__)
            except Exception as e:
                msg("{e!s}: transaction signing failed!")
                if g.traceback:
                    import traceback
                    ymsg('\n' +
                         ''.join(traceback.format_exception(*sys.exc_info())))
                return False
Example #12
0
def help_notes(k):
    from mmgen.obj import SubSeedIdxRange
    from mmgen.seed import SeedSource
    from mmgen.tx import MMGenTX

    def fee_spec_letters(use_quotes=False):
        cu = g.proto.coin_amt.units
        sep, conj = ((',', ' or '), ("','", "' or '"))[use_quotes]
        return sep.join(u[0]
                        for u in cu[:-1]) + ('', conj)[len(cu) > 1] + cu[-1][0]

    def fee_spec_names():
        cu = g.proto.coin_amt.units
        return ', '.join(cu[:-1]) + ('', ' and ')[len(cu) > 1] + cu[-1] + (
            '', ',\nrespectively')[len(cu) > 1]

    return {
        'rel_fee_desc':
        MMGenTX().rel_fee_desc,
        'fee_spec_letters':
        fee_spec_letters(),
        'subwallet':
        """
SUBWALLETS:

Subwallets (subseeds) are specified by a "Subseed Index" consisting of:

  a) an integer in the range 1-{}, 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 wallet's 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 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 doesn't 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().format(SubSeedIdxRange.max_idx),
        'passwd':
        """
PASSPHRASE NOTE:

For passphrases all combinations of whitespace are equal, and leading and
trailing space are ignored.  This permits reading passphrase or brainwallet
data from a multi-line file with free spacing and indentation.
""".strip(),
        'brainwallet':
        """
BRAINWALLET NOTE:

To thwart dictionary attacks, it's 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(),
        'txcreate':
        """
The transaction's outputs are specified on the command line, while its inputs
are chosen from a list of the user's unpent outputs via an interactive menu.

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 '--tx-fee-adj',
if specified.

Ages of transactions are approximate based on an average block discovery
interval of one per {g.proto.secs_per_block} seconds.

All addresses on the command line can be either {pnu} addresses or {pnm}
addresses of the form <seed ID>:<index>.

To send the value of all inputs (minus TX fee) to a single output, specify
one address with no amount on the command line.
""".format(g=g, pnm=g.proj_name, pnu=g.proto.name.capitalize()),
        'fee':
        """
FEE SPECIFICATION: Transaction fees, both on the command line and at the
interactive prompt, may be specified as either absolute {c} amounts, using
a plain decimal number, or as {r}, using an integer followed by
'{l}', for {u}.
""".format(c=g.coin,
           r=MMGenTX().rel_fee_desc,
           l=fee_spec_letters(use_quotes=True),
           u=fee_spec_names()),
        'txsign':
        """
Transactions may contain both {pnm} or non-{pnm} input addresses.

To sign non-{pnm} inputs, a {dn} wallet dump or 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 transaction's 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 can't be found in the
default wallet.
""".format(dn=g.proto.daemon_name,
           pnm=g.proj_name,
           pnu=g.proto.name.capitalize(),
           pnl=g.proj_name.lower())
    }[k] + ('-α' if g.debug_utf8 else '')
Example #13
0
-V, --vsize-adj=   f  Adjust transaction's estimated vsize by factor 'f'
-y, --yes             Answer 'yes' to prompts, suppress non-essential output
-X, --cached-balances Use cached balances (Ethereum only)
""",
        'notes':
        '\n{}{}',
    },
    'code': {
        'options':
        lambda s: s.format(fu=help_notes('rel_fee_desc'),
                           fl=help_notes('fee_spec_letters'),
                           cu=g.coin,
                           g=g),
        'notes':
        lambda s: s.format(help_notes('txcreate'), help_notes('fee'))
    }
}

cmd_args = opts.init(opts_data)

g.use_cached_balances = opt.cached_balances

rpc_init()

from mmgen.tx import MMGenTX
tx = MMGenTX()
tx.create(cmd_args, int(opt.locktime or 0), do_info=opt.info)
tx.write_to_file(ask_write=not opt.yes,
                 ask_overwrite=not opt.yes,
                 ask_write_default_yes=False)
Example #14
0
def help_notes(k):
    from mmgen.seed import SeedSource
    from mmgen.tx import MMGenTX

    def fee_spec_letters(use_quotes=False):
        cu = g.proto.coin_amt.units
        sep, conj = ((',', ' or '), ("','", "' or '"))[use_quotes]
        return sep.join(u[0]
                        for u in cu[:-1]) + ('', conj)[len(cu) > 1] + cu[-1][0]

    def fee_spec_names():
        cu = g.proto.coin_amt.units
        return ', '.join(cu[:-1]) + ('', ' and ')[len(cu) > 1] + cu[-1] + (
            '', ',\nrespectively')[len(cu) > 1]

    return {
        'rel_fee_desc':
        MMGenTX().rel_fee_desc,
        'fee_spec_letters':
        fee_spec_letters(),
        'passwd':
        """
For passphrases all combinations of whitespace are equal and leading and
trailing space is ignored.  This permits reading passphrase or brainwallet
data from a multi-line file with free spacing and indentation.
""".strip(),
        'brainwallet':
        """
BRAINWALLET NOTE:

To thwart dictionary attacks, it's 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(),
        'txcreate':
        """
The transaction's outputs are specified on the command line, while its inputs
are chosen from a list of the user's unpent outputs via an interactive menu.

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 '--tx-fee-adj',
if specified.

Ages of transactions are approximate based on an average block discovery
interval of one per {g.proto.secs_per_block} seconds.

All addresses on the command line can be either {pnu} addresses or {pnm}
addresses of the form <seed ID>:<index>.

To send the value of all inputs (minus TX fee) to a single output, specify
one address with no amount on the command line.
""".format(g=g, pnm=g.proj_name, pnu=g.proto.name.capitalize()),
        'fee':
        """
FEE SPECIFICATION: Transaction fees, both on the command line and at the
interactive prompt, may be specified as either absolute {c} amounts, using
a plain decimal number, or as {r}, using an integer followed by
'{l}', for {u}.
""".format(c=g.coin,
           r=MMGenTX().rel_fee_desc,
           l=fee_spec_letters(use_quotes=True),
           u=fee_spec_names()),
        'txsign':
        u"""
Transactions may contain both {pnm} or non-{pnm} input addresses.

To sign non-{pnm} inputs, a {dn} wallet dump or 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 transaction's 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 can't be found in the
default wallet.

Seed source files must have the canonical extensions listed in the 'FileExt'
column below:

  {n_fmt}
""".format(dn=g.proto.daemon_name,
           n_fmt='\n  '.join(SeedSource.format_fmt_codes().splitlines()),
           pnm=g.proj_name,
           pnu=g.proto.name.capitalize(),
           pnl=g.proj_name.lower())
    }[k] + (u'-α' if g.debug_utf8 else '')
Example #15
0
-m, --minconf=     n  Minimum number of confirmations required to spend
                      outputs (default: 1)
-q, --quiet           Suppress warnings; overwrite files without prompting
-r, --rbf             Make transaction BIP 125 replaceable (replace-by-fee)
-v, --verbose         Produce more verbose output
-V, --vsize-adj=   f  Adjust transaction's estimated vsize by factor 'f'
-y, --yes             Answer 'yes' to prompts, suppress non-essential output
""",
		'notes': '\n{}{}',
	},
	'code': {
		'options': lambda s: s.format(
			fu=help_notes('rel_fee_desc'),
			fl=help_notes('fee_spec_letters'),
			cu=g.coin,
			g=g),
		'notes': lambda s: s.format(
			help_notes('txcreate'),
			help_notes('fee'))
	}
}

cmd_args = opts.init(opts_data)

rpc_init()

from mmgen.tx import MMGenTX
tx = MMGenTX()
tx.create(cmd_args,int(opt.locktime or 0),do_info=opt.info)
tx.write_to_file(ask_write=not opt.yes,ask_overwrite=not opt.yes,ask_write_default_yes=False)
Example #16
0
 async def get_exec_status(self, txid):
     from mmgen.tx import MMGenTX
     tx = MMGenTX.New(proto=self.proto)
     from mmgen.rpc import rpc_init
     tx.rpc = await rpc_init(self.proto)
     return await tx.get_exec_status(txid, True)