Pre-RBF changes, bugfixes

This commit is contained in:
The MMGen Project 2017-05-14 21:54:05 +03:00
commit 8556de2101
Signed by: mmgen
GPG key ID: 62DBE9E5212F05BE
12 changed files with 84 additions and 63 deletions

View file

@ -83,14 +83,13 @@ if not opt.test:
m = """
WARNING: You've chosen the '--rescan' option. Rescanning the blockchain is
necessary only if an address you're importing is already on the blockchain,
has a balance and is not already in your tracking wallet. Note that the
rescanning process is very slow (>30 min. for each imported address on a
low-powered computer).
has a balance and is not in your tracking wallet. Note that the rescanning
process is very slow (>30 min. for each imported address on a low-powered
computer).
""".strip() if opt.rescan else """
WARNING: If any of the addresses you're importing is already on the blockchain,
has a balance and is not already in your tracking wallet, you must exit the
program now and rerun it using the '--rescan' option. Otherwise you may ignore
this message and continue.
has a balance and is not in your tracking wallet, you must exit the program now
and rerun it using the '--rescan' option.
""".strip()
if not opt.quiet: confirm_or_exit(m, 'continue', expect='YES')

View file

@ -35,8 +35,8 @@ opts_data = {
-C, --tx-confs= c Desired number of confirmations (default: {g.tx_confs})
-d, --outdir= d Specify an alternate directory 'd' for output
-f, --tx-fee= f Transaction fee (default: {g.tx_fee} BTC (but see below))
-m, --minconf= n Minimum number of confirmations required to spend outputs (default: 1)
-i, --info Display unspent outputs and exit
-m, --minconf= n Minimum number of confirmations required to spend outputs (default: 1)
-q, --quiet Suppress warnings; overwrite files without prompting
-v, --verbose Produce more verbose output
""".format(g=g),

View file

@ -30,8 +30,8 @@ opts_data = {
-h, --help Print this help message
--, --longhelp Print help message for long options (common options)
-a, --tx-fee-adj= f Adjust transaction fee by factor 'f' (see below)
-b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for brainwallet
input
-b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for
brainwallet input
-B, --no-blank Don't blank screen before displaying unspent outputs
-c, --comment-file= f Source the transaction's comment from file 'f'
-C, --tx-confs= c Desired number of confirmations (default: {g.tx_confs})
@ -46,8 +46,10 @@ opts_data = {
with non-standard (< {g.seed_len}-bit) seed lengths.
-k, --keys-from-file=f Provide additional keys for non-{pnm} addresses
-K, --key-generator= m Use method 'm' for public key generation
Options: {kgs} (default: {kg})
-m, --minconf=n Minimum number of confirmations required to spend outputs (default: 1)
Options: {kgs}
(default: {kg})
-m, --minconf=n Minimum number of confirmations required to spend
outputs (default: 1)
-M, --mmgen-keys-from-file=f Provide keys for {pnm} addresses in a key-
address file (output of '{pnl}-keygen'). Permits
online signing without an {pnm} seed source. The
@ -56,7 +58,7 @@ opts_data = {
-O, --old-incog-fmt Specify old-format incognito input
-p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p'
for password hashing (default: '{g.hash_preset}')
-P, --passwd-file= f Get {pnm} wallet or bitcoind passphrase from file 'f'
-P, --passwd-file= f Get {pnm} wallet passphrase from file 'f'
-q, --quiet Suppress warnings; overwrite files without prompting
-v, --verbose Produce more verbose output
-z, --show-hash-presets Show information on available hash presets

View file

@ -20,16 +20,12 @@
mmgen/main_wallet: Entry point for MMGen wallet-related scripts
"""
import os,re
import os
from mmgen.common import *
from mmgen.seed import SeedSource,Wallet
from mmgen.filename import find_file_in_dir
from mmgen.obj import MMGenWalletLabel
bn = os.path.basename(sys.argv[0])
invoked_as = re.sub(r'^wallet','',bn.split('-')[-1])
usage = '[opts] [infile]'
nargs = 1
iaction = 'convert'
@ -37,6 +33,8 @@ oaction = 'convert'
bw_note = opts.bw_note
pw_note = opts.pw_note
invoked_as = 'passchg' if g.prog_name == 'mmgen-passchg' else g.prog_name.partition('-wallet')[2]
# full: defhHiJkKlLmoOpPqrSvz-
if invoked_as == 'gen':
desc = 'Generate an {pnm} wallet from a random seed'
@ -57,7 +55,7 @@ elif invoked_as == 'passchg':
iaction = 'input'
bw_note = ''
else:
die(1,"'%s': unrecognized invocation" % bn)
die(1,"'%s': unrecognized invocation" % g.prog_name)
opts_data = {
# Can't use: share/Opts doesn't know anything about fmt codes
@ -74,9 +72,9 @@ opts_data = {
-o, --out-fmt= f {oaction} to wallet format 'f' (see FMT CODES below)
-H, --hidden-incog-input-params=f,o Read hidden incognito data from file
'f' at offset 'o' (comma-separated)
-J, --hidden-incog-output-params=f,o Write hidden incognito data to file 'f'
at offset 'o' (comma-separated). File 'f' will be cre-
ated and filled with random data if it doesn't exist.
-J, --hidden-incog-output-params=f,o Write hidden incognito data to file
'f' at offset 'o' (comma-separated). File 'f' will be
created if necessary and filled with random data.
-O, --old-incog-fmt Specify old-format incognito input
-k, --keep-passphrase Reuse passphrase of input wallet for output wallet
-K, --keep-hash-preset Reuse hash preset of input wallet for output wallet
@ -128,7 +126,10 @@ if invoked_as in ('conv','passchg'):
ss_in = None if invoked_as == 'gen' else SeedSource(sf,passchg=(invoked_as=='passchg'))
if invoked_as == 'chk': sys.exit()
if invoked_as == 'chk':
vmsg('Wallet label: {}'.format(ss_in.ssdata.label.hl()))
# TODO: display creation date
sys.exit()
if invoked_as in ('conv','passchg'):
msg(green('Processing output wallet'))

View file

@ -243,7 +243,8 @@ class Hilite(object):
trunc_ok = True
@classmethod
def fmtc(cls,s,width=None,color=False,encl='',trunc_ok=None,center=False,nullrepl=''):
def fmtc(cls,s,width=None,color=False,encl='',trunc_ok=None,
center=False,nullrepl='',app='',appcolor=False):
if width == None: width = cls.width
if trunc_ok == None: trunc_ok = cls.trunc_ok
assert width > 0
@ -253,7 +254,11 @@ class Hilite(object):
assert type(encl) is str and len(encl) in (0,2)
a,b = list(encl) if encl else ('','')
if trunc_ok and len(s) > width: s = s[:width]
return cls.colorize((a+s+b).ljust(width),color=color)
if app:
return cls.colorize(a+s+b,color=color) + \
cls.colorize(app.ljust(width-len(a+s+b)),color=appcolor)
else:
return cls.colorize((a+s+b).ljust(width),color=color)
def fmt(self,*args,**kwargs):
assert args == () # forbid invocation w/o keywords

View file

@ -38,7 +38,7 @@ def cleandir(d):
rmtree(os.path.join(d,f))
def getrandnum(n): return int(hexlify(os.urandom(n)),16)
def getrandhex(n): return hexlify(os.urandom(n)).lstrip('0')
def getrandhex(n): return hexlify(os.urandom(n))
def getrandstr(num_chars,no_space=False):
n,m = 95,32
if no_space: n,m = 94,33

View file

@ -22,7 +22,7 @@ tool.py: Routines and data for the 'mmgen-tool' utility
import binascii as ba
import mmgen.bitcoin as bitcoin
import mmgen.bitcoin as mmb
from mmgen.common import *
from mmgen.crypto import *
from mmgen.tx import *
@ -267,17 +267,17 @@ def unhexdump(infile):
get_data_from_file(infile,dash=True,silent=True)))
def strtob58(s):
enc = bitcoin.b58encode(s)
dec = bitcoin.b58decode(enc)
enc = mmb.b58encode(s)
dec = mmb.b58decode(enc)
print_convert_results(s,enc,dec,'str')
def hextob58(s,f_enc=bitcoin.b58encode, f_dec=bitcoin.b58decode):
def hextob58(s,f_enc=mmb.b58encode, f_dec=mmb.b58decode):
s = s.strip()
enc = f_enc(ba.unhexlify(s))
dec = ba.hexlify(f_dec(enc))
print_convert_results(s,enc,dec,'hex')
def b58tohex(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
def b58tohex(s,f_enc=mmb.b58decode, f_dec=mmb.b58encode):
s = s.strip()
tmp = f_enc(s)
if tmp == False: die(1,"Unable to decode string '%s'" % s)
@ -285,7 +285,7 @@ def b58tohex(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
dec = f_dec(ba.unhexlify(enc))
print_convert_results(s,enc,dec,'b58')
def b58tostr(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
def b58tostr(s,f_enc=mmb.b58decode, f_dec=mmb.b58encode):
s = s.strip()
enc = f_enc(s)
if enc == False: die(1,"Unable to decode string '%s'" % s)
@ -294,8 +294,8 @@ def b58tostr(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
def b58randenc():
r = get_random(32)
enc = bitcoin.b58encode(r)
dec = bitcoin.b58decode(enc)
enc = mmb.b58encode(r)
dec = mmb.b58decode(enc)
print_convert_results(r,enc,dec,'str')
def randhex(nbytes='32'):
@ -303,23 +303,23 @@ def randhex(nbytes='32'):
def randwif(compressed=False):
r_hex = ba.hexlify(get_random(32))
enc = bitcoin.hex2wif(r_hex,compressed)
dec = bitcoin.wif2hex(enc)
enc = mmb.hex2wif(r_hex,compressed)
dec = mmb.wif2hex(enc)
print_convert_results(r_hex,enc,dec,'hex')
def randpair(compressed=False):
r_hex = ba.hexlify(get_random(32))
wif = bitcoin.hex2wif(r_hex,compressed)
addr = bitcoin.privnum2addr(int(r_hex,16),compressed)
wif = mmb.hex2wif(r_hex,compressed)
addr = mmb.privnum2addr(int(r_hex,16),compressed)
Vmsg('Key (hex): %s' % r_hex)
Vmsg_r('Key (WIF): '); Msg(wif)
Vmsg_r('Addr: '); Msg(addr)
def wif2addr(wif,compressed=False):
s_enc = bitcoin.wif2hex(wif)
s_enc = mmb.wif2hex(wif)
if s_enc == False:
die(1,'Invalid address')
addr = bitcoin.privnum2addr(int(s_enc,16),compressed)
addr = mmb.privnum2addr(int(s_enc,16),compressed)
Vmsg_r('Addr: '); Msg(addr)
wordlists = 'electrum','tirosh'
@ -433,7 +433,10 @@ def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Fa
)]
old_sid = ''
def s_mmgen(k): return '{:>0{w}}'.format(k,w=AddrIdx.max_digits+9) # TODO
def s_mmgen(k):
s = k.split('_')
a,b = s if len(s) == 2 else (k,'')
return '{}_{:>0{w}}'.format(a,b,w=AddrIdx.max_digits+9)
for k in sorted(addrs,key=s_mmgen):
if old_sid and old_sid != k.split('_')[0]: out.append('')
old_sid = k.split('_')[0]
@ -517,25 +520,25 @@ def sha256x2(s, file_input=False, hex_input=False):
Msg(sha256(sha256(b).digest()).hexdigest())
def hexaddr2addr(hexaddr):
Msg(bitcoin.hexaddr2addr(hexaddr))
Msg(mmb.hexaddr2addr(hexaddr))
def addr2hexaddr(addr):
Msg(bitcoin.verify_addr(addr,return_hex=True))
Msg(mmb.verify_addr(addr,return_hex=True))
def pubkey2hexaddr(pubkeyhex):
Msg(bitcoin.pubhex2hexaddr(pubkeyhex))
Msg(mmb.pubhex2hexaddr(pubkeyhex))
def pubkey2addr(pubkeyhex):
Msg(bitcoin.hexaddr2addr(bitcoin.pubhex2hexaddr(pubkeyhex)))
Msg(mmb.hexaddr2addr(mmb.pubhex2hexaddr(pubkeyhex)))
def privhex2addr(privkeyhex,compressed=False):
Msg(bitcoin.privnum2addr(int(privkeyhex,16),compressed))
Msg(mmb.privnum2addr(int(privkeyhex,16),compressed))
def wif2hex(wif,compressed=False):
Msg(bitcoin.wif2hex(wif))
Msg(mmb.wif2hex(wif))
def hex2wif(hexpriv,compressed=False):
Msg(bitcoin.hex2wif(hexpriv,compressed))
Msg(mmb.hex2wif(hexpriv,compressed))
def encrypt(infile,outfile='',hash_preset=''):
data = get_data_from_file(infile,'data for encryption',binary=True)

View file

@ -50,7 +50,7 @@ class MMGenTxInput(MMGenListItem):
label = MMGenListItemAttr('label','MMGenAddrLabel')
class MMGenTxOutput(MMGenListItem):
attrs = 'txid','vout','amt','label','mmid','addr','have_wif'
attrs = 'txid','vout','amt','label','mmid','addr','have_wif','is_chg'
label = MMGenListItemAttr('label','MMGenAddrLabel')
class MMGenTX(MMGenObject):
@ -71,7 +71,7 @@ class MMGenTX(MMGenObject):
self.size = 0 # size of raw serialized tx
self.fee = BTCAmt('0')
self.send_amt = BTCAmt('0') # total amt minus change
self.hex = '' # raw serialized hex transaction
self.hex = '' # raw serialized hex transaction
self.label = MMGenTXLabel('')
self.txid = ''
self.btc_txid = ''
@ -190,6 +190,9 @@ class MMGenTX(MMGenObject):
def get_input_sids(self):
return set([e.mmid[:8] for e in self.inputs if e.mmid])
def get_output_sids(self):
return set([e.mmid[:8] for e in self.outputs if e.mmid])
def sum_inputs(self):
return sum([e.amt for e in self.inputs])
@ -281,6 +284,8 @@ class MMGenTX(MMGenObject):
if self.btc_txid:
self.desc = 'sent transaction'
msg(m % self.btc_txid.hl())
self.add_timestamp()
self.add_blockcount(c)
return True
msg('Sending of transaction {} failed'.format(self.txid))
@ -339,7 +344,7 @@ class MMGenTX(MMGenObject):
out += 'Comment: %s\n%s' % (self.label.hl(),enl)
out += 'Inputs:\n' + enl
nonmm_str = '(non-{pnm} address)'.format(pnm=g.proj_name)
nonmm_str = '(non-{pnm} address){s}'.format(pnm=g.proj_name,s=('',' ')[terse])
# for i in self.inputs: print i #DEBUG
for n,e in enumerate(self.inputs):
if blockcount:
@ -362,15 +367,20 @@ class MMGenTX(MMGenObject):
out += 'Outputs:\n' + enl
for n,e in enumerate(self.outputs):
mmid_fmt = e.mmid.fmt(width=len(nonmm_str),encl='()',color=True) if e.mmid \
else MMGenID.hlc(nonmm_str)
if e.mmid:
app=('',' (chg)')[bool(e.is_chg and terse)]
mmid_fmt = e.mmid.fmt(width=len(nonmm_str),encl='()',color=True,
app=app,appcolor='green')
else:
mmid_fmt = MMGenID.hlc(nonmm_str)
if terse:
out += '%3s: %s %s %s BTC' % (n+1, e.addr.fmt(color=True),mmid_fmt, e.amt.hl())
else:
for d in (
(n+1, 'address:', e.addr.fmt(color=True) + ' ' + mmid_fmt),
('', 'comment:', e.label.hl() if e.label else ''),
('', 'amount:', '%s BTC' % e.amt.hl())
('', 'amount:', '%s BTC' % e.amt.hl()),
('', 'change:', green('True') if e.is_chg else '')
):
if d[2]: out += ('%3s %-8s %s\n' % d)
out += '\n'

View file

@ -109,7 +109,7 @@ def mmaddr2baddr(c,mmaddr,ad_w,ad_f):
return BTCAddr(btc_addr)
def get_fee_estimate():
def get_fee_estimate(c):
if 'tx_fee' in opt.set_by_user: # TODO
return None
else:
@ -175,7 +175,7 @@ def txcreate(opt,cmd_args,do_info=False,caller='txcreate'):
if opt.tx_fee > tx.max_fee:
die(2,'Transaction fee too large: %s > %s' % (opt.tx_fee,tx.max_fee))
fee_estimate = get_fee_estimate()
fee_estimate = get_fee_estimate(c)
tw = MMGenTrackingWallet(minconf=opt.minconf)
tw.view_and_sort()

View file

@ -172,7 +172,7 @@ def txsign(opt,c,tx,seed_files,kl,kal,tx_num_str=''):
tx.delete_attrs('inputs','have_wif')
tx.delete_attrs('outputs','have_wif')
extra_sids = set(saved_seeds) - tx.get_input_sids()
extra_sids = set(saved_seeds) - tx.get_input_sids() - tx.get_output_sids()
if extra_sids:
msg('Unused Seed ID%s: %s' %
(suf(extra_sids,'k'),' '.join(extra_sids)))

View file

@ -384,7 +384,7 @@ def get_seed_file(cmd_args,nargs,invoked_as=None):
elif len(cmd_args) > nargs:
opts.usage()
elif len(cmd_args) == nargs and wf and invoked_as != 'gen':
msg('Warning: overriding wallet in data directory with user-supplied wallet')
msg('Warning: overriding default wallet with user-supplied wallet')
if cmd_args or wf:
check_infile(cmd_args[0] if cmd_args else wf)
@ -707,6 +707,6 @@ def bitcoin_connection():
g.rpc_user or cfg['rpcuser'], # MMGen's rpcuser,rpcpassword override bitcoind's
g.rpc_password or cfg['rpcpassword'],
auth_cookie=get_bitcoind_auth_cookie())
# do an RPC call to make the function fail if we can't connect
# do an RPC call so we exit immediately if we can't connect
c.client_version = int(c.getinfo()['version'])
return c

View file

@ -80,7 +80,7 @@ ref_tx_label = ''.join([unichr(i) for i in range(65,91) +
range(1040,1072) + # cyrillic
range(913,939) + # greek
range(97,123)])[:MMGenTXLabel.max_len]
tx_fee = '0.0001'
ref_bw_hash_preset = '1'
ref_bw_file = 'wallet.mmbrain'
ref_bw_file_spc = 'wallet-spaced.mmbrain'
@ -199,8 +199,8 @@ cfgs = {
pwfile: 'walletgen',
'mmdat': 'walletgen',
'addrs': 'addrgen',
'rawtx': 'txcreate',
'sigtx': 'txsign',
'rawtx': 'txcreate',
'sigtx': 'txsign',
'mmwords': 'export_mnemonic',
'mmseed': 'export_seed',
'mmhex': 'export_hex',
@ -990,8 +990,9 @@ def create_fake_unspent_data(adata,unspent_data_file,tx_data,non_mmgen_input='')
privnum = getrandnum(32)
btcaddr = privnum2addr(privnum,compressed=True)
of = os.path.join(cfgs[non_mmgen_input]['tmpdir'],non_mmgen_fn)
write_data_to_file(of, hex2wif('{:064x}'.format(privnum),
compressed=True)+'\n','compressed bitcoin key',silent=True)
wif = hex2wif('{:064x}'.format(privnum),compressed=True)
# Msg(yellow(wif + ' ' + btcaddr))
write_data_to_file(of,wif+'\n','compressed bitcoin key',silent=True)
out.append(create_fake_unspent_entry(btcaddr,non_mmgen='Non-MMGen address'))