Assorted fixes/improvements:
- Importing addresses with --rescan working again - Tracking and spending non-MMGen addresses now fully functional - mmgen-txcreate: improvements in unspent outputs display - mmgen-txsign: use bitcoind wallet dump as keylist fixed - Testnet support: - Practice sending transactions without risking funds (free testnet coins: https://tpfaucet.appspot.com/) - Test suite fully supported - To enable, set MMGEN_TESTNET environment variable
This commit is contained in:
parent
0be6059674
commit
73ca40ea8d
21 changed files with 282 additions and 166 deletions
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
Install required Debian/Ubuntu packages:
|
||||
|
||||
$ sudo apt-get install python-pip python-dev python-pexpect python-ecdsa python-scrypt libssl-dev git
|
||||
$ sudo apt-get install python-pip python-dev python-pexpect python-ecdsa python-scrypt libssl-dev git autoconf libtool
|
||||
|
||||
Install the Python Cryptography Toolkit:
|
||||
|
||||
$ sudo pip install pycrypto
|
||||
|
||||
Install the secp256k1 library
|
||||
Install the secp256k1 library:
|
||||
|
||||
$ git clone https://github.com/bitcoin-core/secp256k1.git
|
||||
$ cd secp256k1
|
||||
|
|
|
|||
|
|
@ -58,17 +58,17 @@ internal ECDSA library for address generation.
|
|||
def _wif2addr_python(wif):
|
||||
privhex = wif2hex(wif)
|
||||
if not privhex: return False
|
||||
return privnum2addr(int(privhex,16),wif[0] != '5')
|
||||
return privnum2addr(int(privhex,16),wif[0] != ('5','9')[g.testnet])
|
||||
|
||||
def _wif2addr_keyconv(wif):
|
||||
if wif[0] == '5':
|
||||
if wif[0] == ('5','9')[g.testnet]:
|
||||
from subprocess import check_output
|
||||
return check_output(['keyconv', wif]).split()[1]
|
||||
else:
|
||||
return _wif2addr_python(wif)
|
||||
|
||||
def _wif2addr_secp256k1(wif):
|
||||
return _privhex2addr_secp256k1(wif2hex(wif),wif[0] != '5')
|
||||
return _privhex2addr_secp256k1(wif2hex(wif),wif[0] != ('5','9')[g.testnet])
|
||||
|
||||
def _privhex2addr_python(privhex,compressed=False):
|
||||
return privnum2addr(int(privhex,16),compressed)
|
||||
|
|
|
|||
|
|
@ -51,13 +51,15 @@ b58a='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
|||
# The 'zero address':
|
||||
# 1111111111111111111114oLvT2 (use step2 = ('0' * 40) to generate)
|
||||
|
||||
import mmgen.globalvars as g
|
||||
|
||||
def pubhex2hexaddr(pubhex):
|
||||
step1 = sha256(unhexlify(pubhex)).digest()
|
||||
return hashlib_new('ripemd160',step1).hexdigest()
|
||||
|
||||
def hexaddr2addr(hexaddr, vers_num='00'):
|
||||
# See above:
|
||||
hexaddr2 = vers_num + hexaddr
|
||||
def hexaddr2addr(hexaddr,p2sh=False):
|
||||
# devdoc/ref_transactions.md:
|
||||
hexaddr2 = ('00','6f','05','c4')[g.testnet+(2*p2sh)] + hexaddr
|
||||
step1 = sha256(unhexlify(hexaddr2)).digest()
|
||||
step2 = sha256(step1).hexdigest()
|
||||
pubkey = hexaddr2 + step2[:8]
|
||||
|
|
@ -65,9 +67,8 @@ def hexaddr2addr(hexaddr, vers_num='00'):
|
|||
return ('1' * lzeroes) + _numtob58(int(pubkey,16))
|
||||
|
||||
def verify_addr(addr,verbose=False,return_hex=False):
|
||||
|
||||
for vers_num,ldigit in ('00','1'),('05','3'):
|
||||
if addr[0] != ldigit: continue
|
||||
for vers_num,ldigit in ('00','1'),('05','3'),('6f','mn'),('c4','2'):
|
||||
if addr[0] not in ldigit: continue
|
||||
num = _b58tonum(addr)
|
||||
if num == False: break
|
||||
addr_hex = '{:050x}'.format(num)
|
||||
|
|
@ -145,7 +146,7 @@ def b58decode_pad(s):
|
|||
# Compressed address support:
|
||||
|
||||
def wif2hex(wif):
|
||||
compressed = wif[0] != '5'
|
||||
compressed = wif[0] != ('5','9')[g.testnet]
|
||||
idx = (66,68)[bool(compressed)]
|
||||
num = _b58tonum(wif)
|
||||
if num == False: return False
|
||||
|
|
@ -153,19 +154,25 @@ def wif2hex(wif):
|
|||
if compressed and key[66:68] != '01': return False
|
||||
round1 = sha256(unhexlify(key[:idx])).digest()
|
||||
round2 = sha256(round1).hexdigest()
|
||||
return key[2:66] if (key[:2] == '80' and key[idx:] == round2[:8]) else False
|
||||
return key[2:66] if (key[:2] == ('80','ef')[g.testnet] and key[idx:] == round2[:8]) else False
|
||||
|
||||
def hex2wif(hexpriv,compressed=False):
|
||||
step1 = '80' + hexpriv + ('','01')[bool(compressed)]
|
||||
step1 = ('80','ef')[g.testnet] + hexpriv + ('','01')[bool(compressed)]
|
||||
step2 = sha256(unhexlify(step1)).digest()
|
||||
step3 = sha256(step2).hexdigest()
|
||||
key = step1 + step3[:8]
|
||||
return _numtob58(int(key,16))
|
||||
|
||||
# devdoc/guide_wallets.md:
|
||||
# Uncompressed public keys start with 0x04; compressed public keys begin with
|
||||
# 0x03 or 0x02 depending on whether they're greater or less than the midpoint
|
||||
# of the curve.
|
||||
def privnum2pubhex(numpriv,compressed=False):
|
||||
pko = ecdsa.SigningKey.from_secret_exponent(numpriv,_secp256k1)
|
||||
# pubkey = 32-byte X coord + 32-byte Y coord (unsigned big-endian)
|
||||
pubkey = hexlify(pko.get_verifying_key().to_string())
|
||||
if compressed:
|
||||
if compressed: # discard Y coord, replace with appropriate version byte
|
||||
# even Y: <0, odd Y: >0 -- https://bitcointalk.org/index.php?topic=129652.0
|
||||
p = ('03','02')[pubkey[-1] in '02468ace']
|
||||
return p+pubkey[:64]
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ no_license = os.getenv('MMGEN_NOLICENSE')
|
|||
bogus_wallet_data = os.getenv('MMGEN_BOGUS_WALLET_DATA')
|
||||
disable_hold_protect = os.getenv('MMGEN_DISABLE_HOLD_PROTECT')
|
||||
color = (False,True)[sys.stdout.isatty() and not os.getenv('MMGEN_DISABLE_COLOR')]
|
||||
testnet = (False,True)[bool(os.getenv('MMGEN_TESTNET'))]
|
||||
|
||||
proj_name = 'MMGen'
|
||||
prog_name = os.path.basename(sys.argv[0])
|
||||
|
|
@ -63,12 +64,14 @@ incompatible_opts = (
|
|||
('label','keep_label'),
|
||||
('tx_id', 'info'),
|
||||
('tx_id', 'terse_info'),
|
||||
('batch', 'rescan'),
|
||||
)
|
||||
|
||||
min_screen_width = 80
|
||||
minconf = 1
|
||||
|
||||
# Global value sets user opt
|
||||
dfl_vars = 'seed_len','hash_preset','usr_randchars','debug','tx_confs','tx_fee_adj','tx_fee','key_generator'
|
||||
dfl_vars = 'minconf','seed_len','hash_preset','usr_randchars','debug','tx_confs','tx_fee_adj','tx_fee','key_generator'
|
||||
|
||||
keyconv_exec = 'keyconv'
|
||||
|
||||
|
|
|
|||
|
|
@ -25,14 +25,17 @@ import time
|
|||
from mmgen.common import *
|
||||
from mmgen.addr import AddrList,KeyAddrList
|
||||
|
||||
# In batch mode, bitcoind just rescans each address separately anyway, so make
|
||||
# --batch and --rescan incompatible.
|
||||
|
||||
opts_data = {
|
||||
'desc': """Import addresses (both {pnm} and non-{pnm}) into a bitcoind
|
||||
'desc': """Import addresses (both {pnm} and non-{pnm}) into an {pnm}
|
||||
tracking wallet""".format(pnm=g.proj_name),
|
||||
'usage':'[opts] [mmgen address file]',
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-b, --batch Batch mode. Import all addresses in one RPC call
|
||||
-l, --addrlist Address source is a flat list of addresses
|
||||
-b, --batch Import all addresses in one RPC call.
|
||||
-l, --addrlist Address source is a flat list of (non-MMGen) Bitcoin addresses
|
||||
-k, --keyaddr-file Address source is a key-address file
|
||||
-q, --quiet Suppress warnings
|
||||
-r, --rescan Rescan the blockchain. Required if address to import is
|
||||
|
|
@ -42,6 +45,8 @@ opts_data = {
|
|||
'notes': """\n
|
||||
This command can also be used to update the comment fields of addresses already
|
||||
in the tracking wallet.
|
||||
|
||||
The --batch option cannot be used with the --rescan option.
|
||||
"""
|
||||
}
|
||||
|
||||
|
|
@ -116,12 +121,15 @@ for n,e in enumerate(ai.data):
|
|||
if e.idx:
|
||||
label = '%s:%s' % (ai.seed_id,e.idx)
|
||||
if e.label: label += ' ' + e.label
|
||||
else: label = 'non-{pnm}'.format(pnm=g.proj_name)
|
||||
m = label
|
||||
else:
|
||||
label = 'btc:{}'.format(e.addr)
|
||||
m = 'non-'+g.proj_name
|
||||
|
||||
if opt.batch:
|
||||
arg_list.append((e.addr,label,False))
|
||||
elif opt.rescan:
|
||||
t = threading.Thread(target=import_address, args=(e.addr,label,True))
|
||||
t = threading.Thread(target=import_address,args=[e.addr,label,True])
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
|
|
@ -131,7 +139,7 @@ for n,e in enumerate(ai.data):
|
|||
if t.is_alive():
|
||||
elapsed = int(time.time() - start)
|
||||
count = '%s/%s:' % (n+1, ai.num_addrs)
|
||||
msg_r(msg_fmt % (secs_to_hms(elapsed),count,e.addr,'(%s)'%label))
|
||||
msg_r(msg_fmt % (secs_to_hms(elapsed),count,e.addr,'(%s)' % m))
|
||||
time.sleep(1)
|
||||
else:
|
||||
if err_flag: die(2,'\nImport failed')
|
||||
|
|
@ -140,12 +148,10 @@ for n,e in enumerate(ai.data):
|
|||
else:
|
||||
import_address(e.addr,label,False)
|
||||
count = '%s/%s:' % (n+1, ai.num_addrs)
|
||||
msg_r(msg_fmt % (count, e.addr, '(%s)'%label))
|
||||
msg_r(msg_fmt % (count, e.addr, '(%s)' % m))
|
||||
if err_flag: die(2,'\nImport failed')
|
||||
msg(' - OK')
|
||||
|
||||
if opt.batch:
|
||||
if opt.rescan:
|
||||
msg('Warning: this command may take a long time to complete!')
|
||||
ret = c.importaddress(arg_list,batch=True,timeout=(False,3600)[bool(opt.rescan)])
|
||||
ret = c.importaddress(arg_list,batch=True)
|
||||
msg('OK: %s addresses imported' % len(ret))
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ opts_data = {
|
|||
-c, --comment-file= f Source the transaction's comment from file 'f'
|
||||
-C, --tx-confs= c Desired number of confirmations (default: {g.tx_confs})
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-e, --echo-passphrase Print passphrase to screen when typing it
|
||||
-e, --clear-screen Clear screen before displaying unspent outputs
|
||||
-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
|
||||
-q, --quiet Suppress warnings; overwrite files without prompting
|
||||
-v, --verbose Produce more verbose output
|
||||
|
|
@ -199,7 +200,7 @@ if not opt.info:
|
|||
|
||||
fee_estimate = get_fee_estimate()
|
||||
|
||||
tw = MMGenTrackingWallet()
|
||||
tw = MMGenTrackingWallet(minconf=opt.minconf)
|
||||
tw.view_and_sort()
|
||||
tw.display_total()
|
||||
|
||||
|
|
|
|||
|
|
@ -227,10 +227,9 @@ if opt.mmgen_keys_from_file:
|
|||
|
||||
if opt.keys_from_file:
|
||||
l = get_lines_from_file(opt.keys_from_file,'key-address data',trim_comments=True)
|
||||
kl = KeyAddrList(keylist=l)
|
||||
kl = KeyAddrList(keylist=[m.split()[0] for m in l]) # accept bitcoind wallet dumps
|
||||
if kal: kl.remove_dups(kal,key='wif')
|
||||
kl.generate_addrs()
|
||||
# pp_die(kl)
|
||||
|
||||
tx_num_str = ''
|
||||
for tx_num,tx_file in enumerate(tx_files,1):
|
||||
|
|
|
|||
18
mmgen/obj.py
18
mmgen/obj.py
|
|
@ -195,6 +195,7 @@ class AddrIdx(int,InitErrors):
|
|||
m = "'%s': addr idx cannot be less than one" % num
|
||||
else:
|
||||
return me
|
||||
|
||||
return cls.init_fail(m,on_fail)
|
||||
|
||||
class AddrIdxList(list,InitErrors):
|
||||
|
|
@ -349,7 +350,7 @@ class BTCAddr(str,Hilite,InitErrors):
|
|||
cls.arg_chk(cls,on_fail)
|
||||
me = str.__new__(cls,s)
|
||||
from mmgen.bitcoin import verify_addr
|
||||
if verify_addr(s):
|
||||
if type(s) in (str,unicode,BTCAddr) and verify_addr(s):
|
||||
return me
|
||||
else:
|
||||
m = "'%s': value is not a Bitcoin address" % s
|
||||
|
|
@ -375,13 +376,14 @@ class SeedID(str,Hilite,InitErrors):
|
|||
if seed:
|
||||
from mmgen.seed import Seed
|
||||
from mmgen.util import make_chksum_8
|
||||
assert type(seed) == Seed
|
||||
return str.__new__(cls,make_chksum_8(seed.get_data()))
|
||||
if type(seed) == Seed:
|
||||
return str.__new__(cls,make_chksum_8(seed.get_data()))
|
||||
elif sid:
|
||||
from string import hexdigits
|
||||
assert len(sid) == cls.width and set(sid) <= set(hexdigits.upper())
|
||||
return str.__new__(cls,sid)
|
||||
m = "'%s': value cannot be converted to SeedID" % s
|
||||
if len(sid) == cls.width and set(sid) <= set(hexdigits.upper()):
|
||||
return str.__new__(cls,sid)
|
||||
|
||||
m = "'%s': value cannot be converted to SeedID" % str(seed or sid)
|
||||
return cls.init_fail(m,on_fail)
|
||||
|
||||
class MMGenID(str,Hilite,InitErrors):
|
||||
|
|
@ -395,9 +397,9 @@ class MMGenID(str,Hilite,InitErrors):
|
|||
s = str(s)
|
||||
if ':' in s:
|
||||
a,b = s.split(':',1)
|
||||
sid = SeedID(sid=a,on_fail='return')
|
||||
sid = SeedID(sid=a,on_fail='silent')
|
||||
if sid:
|
||||
idx = AddrIdx(b,on_fail='return')
|
||||
idx = AddrIdx(b,on_fail='silent')
|
||||
if idx:
|
||||
return str.__new__(cls,'%s:%s' % (sid,idx))
|
||||
|
||||
|
|
|
|||
15
mmgen/rpc.py
15
mmgen/rpc.py
|
|
@ -20,9 +20,11 @@
|
|||
rpc.py: Bitcoin RPC library for the MMGen suite
|
||||
"""
|
||||
|
||||
import httplib,base64,json,decimal
|
||||
import httplib,base64,json
|
||||
|
||||
from mmgen.common import *
|
||||
from decimal import Decimal
|
||||
from mmgen.obj import BTCAmt
|
||||
|
||||
class BitcoinRPCConnection(object):
|
||||
|
||||
|
|
@ -30,7 +32,7 @@ class BitcoinRPCConnection(object):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
host='localhost',port=8332,
|
||||
host='localhost',port=(8332,18332)[g.testnet],
|
||||
user=None,passwd=None,auth_cookie=None,
|
||||
):
|
||||
|
||||
|
|
@ -64,7 +66,7 @@ class BitcoinRPCConnection(object):
|
|||
for k in cf:
|
||||
if k in kwargs and kwargs[k]: cf[k] = kwargs[k]
|
||||
|
||||
c = httplib.HTTPConnection(self.host, self.port, False, cf['timeout'])
|
||||
hc = httplib.HTTPConnection(self.host, self.port, False, cf['timeout'])
|
||||
|
||||
if cf['batch']:
|
||||
p = [{'method':cmd,'params':r,'id':n} for n,r in enumerate(args[0],1)]
|
||||
|
|
@ -80,7 +82,6 @@ class BitcoinRPCConnection(object):
|
|||
dmsg('=== rpc.py debug ===')
|
||||
dmsg(' RPC POST data ==> %s\n' % p)
|
||||
|
||||
from mmgen.obj import BTCAmt
|
||||
caller = self
|
||||
class MyJSONEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
|
|
@ -94,14 +95,14 @@ class BitcoinRPCConnection(object):
|
|||
# print(dump)
|
||||
|
||||
try:
|
||||
c.request('POST', '/', json.dumps(p,cls=MyJSONEncoder), {
|
||||
hc.request('POST', '/', json.dumps(p,cls=MyJSONEncoder), {
|
||||
'Host': self.host,
|
||||
'Authorization': 'Basic ' + base64.b64encode(self.auth_str)
|
||||
})
|
||||
except Exception as e:
|
||||
return die_maybe(None,2,'%s\nUnable to connect to bitcoind' % e)
|
||||
|
||||
r = c.getresponse() # returns HTTPResponse instance
|
||||
r = hc.getresponse() # returns HTTPResponse instance
|
||||
|
||||
if r.status != 200:
|
||||
e1 = r.read()
|
||||
|
|
@ -118,7 +119,7 @@ class BitcoinRPCConnection(object):
|
|||
if not r2:
|
||||
return die_maybe(r,2,'Error: empty reply')
|
||||
|
||||
from decimal import Decimal
|
||||
# from decimal import Decimal
|
||||
r3 = json.loads(r2.decode('utf8'), parse_float=Decimal)
|
||||
ret = []
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@ term.py: Terminal-handling routines for the MMGen suite
|
|||
import os,struct
|
||||
from mmgen.common import *
|
||||
|
||||
CUR_SHOW = '\033[?25h'
|
||||
CUR_HIDE = '\033[?25l'
|
||||
|
||||
def _kb_hold_protect_unix():
|
||||
|
||||
fd = sys.stdin.fileno()
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ def read_from_tmpfile(cfg,fn,binary=False):
|
|||
return read_from_file(os.path.join(cfg['tmpdir'],fn),binary=binary)
|
||||
|
||||
def ok():
|
||||
if opt.profile: return
|
||||
if opt.verbose or opt.exact_output:
|
||||
sys.stderr.write(green('OK\n'))
|
||||
else: msg(' OK')
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ cmd_data = OrderedDict([
|
|||
('listaddresses',["addrs [str='']",'minconf [int=1]','showempty [bool=False]','pager [bool=False]','showbtcaddrs [bool=False]']),
|
||||
('getbalance', ['minconf [int=1]']),
|
||||
('txview', ['<{} TX file> [str]'.format(pnm),'pager [bool=False]','terse [bool=False]']),
|
||||
('twview', ["sort [str='age']",'reverse [bool=False]','wide [bool=False]','pager [bool=False]']),
|
||||
('twview', ["sort [str='age']",'reverse [bool=False]','minconf [int=1]','wide [bool=False]','pager [bool=False]']),
|
||||
|
||||
('add_label', ['<{} address> [str]'.format(pnm),'<label> [str]']),
|
||||
('remove_label', ['<{} address> [str]'.format(pnm)]),
|
||||
|
|
@ -373,7 +373,7 @@ def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Fa
|
|||
for d in c.listunspent(0):
|
||||
mmaddr,comment = split2(d['account'])
|
||||
if usr_addr_list and (mmaddr not in usr_addr_list): continue
|
||||
if is_mmgen_id(mmaddr) and d['confirmations'] >= minconf:
|
||||
if (mmaddr[:4] == 'btc:' or is_mmgen_id(mmaddr)) and d['confirmations'] >= minconf:
|
||||
key = mmaddr.replace(':','_')
|
||||
if key in addrs:
|
||||
if addrs[key][2] != d['address']:
|
||||
|
|
@ -391,7 +391,7 @@ def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Fa
|
|||
for acct in accts:
|
||||
mmaddr,comment = split2(acct)
|
||||
if usr_addr_list and (mmaddr not in usr_addr_list): continue
|
||||
if is_mmgen_id(mmaddr):
|
||||
if mmaddr[:4] == 'btc:' or is_mmgen_id(mmaddr):
|
||||
key = mmaddr.replace(':','_')
|
||||
if key not in addrs:
|
||||
if showbtcaddrs: save_a.append([acct])
|
||||
|
|
@ -407,7 +407,7 @@ def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Fa
|
|||
die(0,('No addresses with balances!','No tracked addresses!')[showempty])
|
||||
|
||||
fs = ('{mid} {lbl} {amt}','{mid} {addr} {lbl} {amt}')[showbtcaddrs]
|
||||
max_mmid_len = max(len(k) for k in addrs) or 10
|
||||
max_mmid_len = max([len(k) for k in addrs if k[:4] != 'btc_'] or [10])
|
||||
max_lbl_len = max(len(addrs[k][1]) for k in addrs) or 7
|
||||
out = [fs.format(
|
||||
mid=MMGenID.fmtc('MMGenID',width=max_mmid_len),
|
||||
|
|
@ -419,10 +419,11 @@ 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
|
||||
for k in sorted(addrs,key=s_mmgen):
|
||||
if old_sid and old_sid != k[:8]: out.append('')
|
||||
old_sid = k[:8]
|
||||
if old_sid and old_sid != k.split('_')[0]: out.append('')
|
||||
old_sid = k.split('_')[0]
|
||||
m = 'non-'+g.proj_name if k[:4] == 'btc_' else k.replace('_',':')
|
||||
out.append(fs.format(
|
||||
mid=MMGenID(k.replace('_',':')).fmt(width=max_mmid_len,color=True),
|
||||
mid = MMGenID.fmtc(m,width=max_mmid_len,color=True),
|
||||
addr=(addrs[k][2].fmt(color=True) if showbtcaddrs else None),
|
||||
lbl=addrs[k][1].fmt(width=max_lbl_len,color=True,nullrepl='-'),
|
||||
amt=addrs[k][0].fmt('3.0',color=True)))
|
||||
|
|
@ -460,9 +461,9 @@ def txview(infile,pager=False,terse=False):
|
|||
tx = MMGenTX(infile)
|
||||
tx.view(pager,pause=False,terse=terse)
|
||||
|
||||
def twview(pager=False,reverse=False,wide=False,sort='age'):
|
||||
def twview(pager=False,reverse=False,wide=False,minconf=1,sort='age'):
|
||||
from mmgen.tw import MMGenTrackingWallet
|
||||
tw = MMGenTrackingWallet()
|
||||
tw = MMGenTrackingWallet(minconf=minconf)
|
||||
tw.do_sort(sort,reverse=reverse)
|
||||
out = tw.format_for_printing(color=True) if wide else tw.format_for_display()
|
||||
do_pager(out) if pager else sys.stdout.write(out)
|
||||
|
|
|
|||
61
mmgen/tw.py
61
mmgen/tw.py
|
|
@ -23,16 +23,17 @@ tw: Tracking wallet methods for the MMGen suite
|
|||
from mmgen.common import *
|
||||
from mmgen.obj import *
|
||||
from mmgen.term import get_char
|
||||
from mmgen.tx import is_mmgen_id
|
||||
|
||||
CUR_HOME,ERASE_ALL = '\033[H','\033[0J'
|
||||
|
||||
def parse_tw_acct_label(s):
|
||||
ret = s.split(None,1)
|
||||
if ret and MMGenID(ret[0],on_fail='silent'):
|
||||
if len(ret) == 2:
|
||||
return tuple(ret)
|
||||
else:
|
||||
return ret[0],None
|
||||
else:
|
||||
return None,None
|
||||
a1,a2 = None,None
|
||||
if ret:
|
||||
a1 = ret[0] if is_mmgen_id(ret[0]) else '' if ret[0][:4] == 'btc:' else None
|
||||
a2 = ret[1] if len(ret) == 2 else None
|
||||
return a1,a2
|
||||
|
||||
class MMGenTWOutput(MMGenListItem):
|
||||
attrs_reassign = 'label','skip'
|
||||
|
|
@ -48,7 +49,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
}
|
||||
sort_keys = 'addr','age','amt','txid','mmid'
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self,minconf=1):
|
||||
self.unspent = []
|
||||
self.fmt_display = ''
|
||||
self.fmt_print = ''
|
||||
|
|
@ -57,6 +58,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
self.group = False
|
||||
self.show_days = True
|
||||
self.show_mmid = True
|
||||
self.minconf = minconf
|
||||
self.get_data()
|
||||
self.sort_key = 'age'
|
||||
self.do_sort()
|
||||
|
|
@ -69,7 +71,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
if g.bogus_wallet_data: # for debugging purposes only
|
||||
us_rpc = eval(get_data_from_file(g.bogus_wallet_data))
|
||||
else:
|
||||
us_rpc = bitcoin_connection().listunspent()
|
||||
us_rpc = bitcoin_connection().listunspent(self.minconf)
|
||||
# write_data_to_file('bogus_unspent.json', repr(us), 'bogus unspent data')
|
||||
# sys.exit()
|
||||
|
||||
|
|
@ -121,6 +123,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
my_raw_input(m1+'\n'+m2.format(g.min_screen_width))
|
||||
|
||||
def display(self):
|
||||
if opt.clear_screen: msg(CUR_HOME+ERASE_ALL)
|
||||
msg(self.format_for_display())
|
||||
|
||||
def format_for_display(self):
|
||||
|
|
@ -158,18 +161,18 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
|
||||
for n,i in enumerate(unsp):
|
||||
addr_dots = '|' + '.'*33
|
||||
mmid_disp = (MMGenID.hlc('.'*mmid_w) \
|
||||
if i.skip=='addr' else i.mmid.fmt(width=mmid_w,color=True)) \
|
||||
if i.mmid else ' ' * mmid_w
|
||||
if self.show_mmid and i.mmid:
|
||||
mmid_disp = MMGenID.fmtc('.'*mmid_w if i.skip=='addr'
|
||||
else i.mmid or 'Non-{}'.format(g.proj_name),width=mmid_w,color=True)
|
||||
if self.show_mmid:
|
||||
addr_out = '%s %s' % (
|
||||
type(i.addr).fmtc(addr_dots,width=btaddr_w,color=True) if i.skip == 'addr' \
|
||||
else i.addr.fmt(width=btaddr_w,color=True),
|
||||
'{} {}'.format(mmid_disp,i.label.fmt(width=label_w,color=True) if label_w > 0 else '')
|
||||
'{} {}'.format(mmid_disp,i.label.fmt(width=label_w,color=True) \
|
||||
if label_w > 0 else '')
|
||||
)
|
||||
else:
|
||||
addr_out = type(i.addr).fmtc(addr_dots,width=addr_w,color=True) if i.skip=='addr' \
|
||||
else i.addr.fmt(width=addr_w,color=True)
|
||||
addr_out = type(i.addr).fmtc(addr_dots,width=addr_w,color=True) \
|
||||
if i.skip=='addr' else i.addr.fmt(width=addr_w,color=True)
|
||||
|
||||
tx = ' ' * (tx_w-4) + '|...' if i.skip == 'txid' \
|
||||
else i.txid[:tx_w-len(txdots)]+txdots
|
||||
|
|
@ -186,13 +189,12 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
out = [fs % ('Num','Tx ID,Vout','Address'.ljust(34),'MMGen ID'.ljust(15),
|
||||
'Amount(BTC)','Conf.','Age(d)', 'Label')]
|
||||
|
||||
max_lbl_len = max(len(i.label) for i in self.unspent if i.label) or 1
|
||||
max_lbl_len = max([len(i.label) for i in self.unspent if i.label] or [1])
|
||||
for n,i in enumerate(self.unspent):
|
||||
addr = '=' if i.skip == 'addr' and self.group else i.addr.fmt(color=color)
|
||||
tx = ' ' * 63 + '=' if i.skip == 'txid' and self.group else str(i.txid)
|
||||
s = fs % (str(n+1)+')', tx+','+str(i.vout),addr,
|
||||
(i.mmid.fmt(width=14,color=color) if i.mmid else
|
||||
MMGenID.fmtc('',width=14,nullrepl='-',color=color)),
|
||||
MMGenID.fmtc(i.mmid or 'Non-{}'.format(g.proj_name),width=14,color=color),
|
||||
i.amt.fmt(color=color),i.confs,i.days,
|
||||
i.label.hl(color=color) if i.label else
|
||||
MMGenAddrLabel.fmtc('',color=color,nullrepl='-',width=max_lbl_len))
|
||||
|
|
@ -218,9 +220,9 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
n = AddrIdx(ret,on_fail='silent') # hacky way to test and convert to integer
|
||||
if not n or n < 1 or n > len(self.unspent):
|
||||
msg('Choice must be a single number between 1 and %s' % len(self.unspent))
|
||||
elif not self.unspent[n-1].mmid:
|
||||
msg('Address #%s is not an %s address. No label can be added to it' %
|
||||
(n,g.proj_name))
|
||||
# elif not self.unspent[n-1].mmid:
|
||||
# msg('Address #%s is not an %s address. No label can be added to it' %
|
||||
# (n,g.proj_name))
|
||||
else:
|
||||
while True:
|
||||
s = my_raw_input("Enter label text (or 'q' to return to main menu): ")
|
||||
|
|
@ -290,10 +292,19 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
|
|||
|
||||
# returns on failure
|
||||
@classmethod
|
||||
def add_label(cls,mmaddr,label='',addr=None):
|
||||
mmaddr = MMGenID(mmaddr)
|
||||
def add_label(cls,arg1,label='',addr=None):
|
||||
from mmgen.tx import is_mmgen_id,is_btc_addr
|
||||
if is_mmgen_id(arg1):
|
||||
mmaddr = MMGenID(arg1)
|
||||
elif is_btc_addr(arg1): # called from 'mmgen-tool add_label'
|
||||
addr = arg1
|
||||
mmaddr = 'btc:'+arg1
|
||||
elif not arg1 and is_btc_addr(addr): # called from view_and_sort(), non-MMGen addr
|
||||
mmaddr = 'btc:'+addr
|
||||
else:
|
||||
die(3,'{}: not a BTC address or {} ID'.format(arg1,g.proj_name))
|
||||
|
||||
if addr: # called from view_and_sort()
|
||||
if addr:
|
||||
if not BTCAddr(addr,on_fail='return'): return False
|
||||
else:
|
||||
from mmgen.addr import AddrData
|
||||
|
|
|
|||
|
|
@ -662,7 +662,8 @@ def get_bitcoind_auth_cookie():
|
|||
|
||||
def bitcoin_connection():
|
||||
|
||||
host,port,user,passwd = 'localhost',8332,'rpcuser','rpcpassword'
|
||||
port = (8332,18332)[g.testnet]
|
||||
host,user,passwd = 'localhost','rpcuser','rpcpassword'
|
||||
cfg = get_bitcoind_cfg_options((user,passwd))
|
||||
auth_cookie = get_bitcoind_auth_cookie()
|
||||
|
||||
|
|
|
|||
164
test/gentest.py
164
test/gentest.py
|
|
@ -35,73 +35,139 @@ start_mscolor()
|
|||
|
||||
rounds = 100
|
||||
opts_data = {
|
||||
'desc': "Test address generation using various methods",
|
||||
'usage':'[options] a:b [rounds]',
|
||||
'desc': "Test address generation in various ways",
|
||||
'usage':'[options] [spec] [rounds | dump file]',
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-s, --system Test scripts and modules installed on system rather than
|
||||
those in the repo root
|
||||
-v, --verbose Produce more verbose output
|
||||
-q, --quiet Produce quieter output
|
||||
""",
|
||||
'notes': """
|
||||
{pnm} can generate addresses from secret keys using one of three methods,
|
||||
as specified by the user:
|
||||
Tests:
|
||||
A/B: {prog} a:b [rounds] (compare output of two key generators)
|
||||
Speed: {prog} a [rounds] (test speed of one key generator)
|
||||
Compare: {prog} a <dump file> (compare output of a key generator against wallet dump)
|
||||
where a and b are one of:
|
||||
'1' - native Python ecdsa library (very slow)
|
||||
'2' - 'keyconv' utility from the 'vanitygen' package (old default)
|
||||
'3' - bitcoincore.org's secp256k1 library (default from v0.8.6)
|
||||
|
||||
1) with the native Python ecdsa library (very slow)
|
||||
2) with the 'keyconv' utility from the 'vanitygen' package (old default)
|
||||
3) using bitcoincore.org's secp256k1 library (default from v0.8.6)
|
||||
|
||||
This test suite compares the output of these different methods against each
|
||||
other over set of randomly generated secret keys ({snum} by default).
|
||||
|
||||
EXAMPLE:
|
||||
gentest.py 2:3 1000
|
||||
(compare output of 'keyconv' with secp256k1 library, 1000 rounds)
|
||||
""".format(pnm=g.proj_name,snum=rounds)
|
||||
EXAMPLES:
|
||||
{prog} 2:3 1000
|
||||
(compare output of 'keyconv' with secp256k1 library, 1000 rounds)
|
||||
{prog} 3 1000
|
||||
(test speed of secp256k1 library address generation, 1000 rounds)
|
||||
{prog} 3 my.dump
|
||||
(compare addrs generated with secp256k1 library to bitcoind wallet dump)
|
||||
""".format(prog='gentest.py',pnm=g.proj_name,snum=rounds)
|
||||
}
|
||||
cmd_args = opts.init(opts_data,add_opts=['exact_output'])
|
||||
|
||||
if not 1 <= len(cmd_args) <= 2: opts.usage()
|
||||
|
||||
urounds,fh = None,None
|
||||
dump = []
|
||||
if len(cmd_args) == 2:
|
||||
try:
|
||||
rounds = int(cmd_args[1])
|
||||
assert rounds > 0
|
||||
urounds = int(cmd_args[1])
|
||||
assert urounds > 0
|
||||
except:
|
||||
die(1,"'rounds' must be a positive integer")
|
||||
try:
|
||||
fh = open(cmd_args[1])
|
||||
except:
|
||||
die(1,"Second argument must be filename or positive integer")
|
||||
else:
|
||||
for line in fh.readlines():
|
||||
if 'addr=' in line:
|
||||
x,addr = line.split('addr=')
|
||||
dump.append([x.split()[0],addr.split()[0]])
|
||||
|
||||
if urounds: rounds = urounds
|
||||
|
||||
a,b = None,None
|
||||
try:
|
||||
a,b = cmd_args[0].split(':')
|
||||
a,b = int(a),int(b)
|
||||
for i in a,b: assert 1 <= i <= len(g.key_generators)
|
||||
assert a != b
|
||||
except:
|
||||
die(1,"%s: incorrect 'a:b' specifier" % cmd_args[0])
|
||||
try:
|
||||
a = cmd_args[0]
|
||||
a = int(a)
|
||||
assert 1 <= a <= len(g.key_generators)
|
||||
except:
|
||||
die(1,"First argument must be one or two generator IDs, colon separated")
|
||||
else:
|
||||
try:
|
||||
a,b = int(a),int(b)
|
||||
for i in a,b: assert 1 <= i <= len(g.key_generators)
|
||||
assert a != b
|
||||
except:
|
||||
die(1,"%s: invalid generator IDs" % cmd_args[0])
|
||||
|
||||
if opt.system: sys.path.pop(0)
|
||||
def match_error(sec,wif,a_addr,b_addr,a,b):
|
||||
m = ['','py-ecdsa','keyconv','secp256k1','dump']
|
||||
msg_r(red('\nERROR: Addresses do not match!'))
|
||||
die(3,"""
|
||||
sec key : {}
|
||||
WIF key : {}
|
||||
{a:10}: {}
|
||||
{b:10}: {}
|
||||
""".format(sec,wif,a_addr,b_addr,pnm=g.proj_name,a=m[a],b=m[b]).rstrip())
|
||||
|
||||
m = "Comparing address generators '{}' and '{}'"
|
||||
msg(green(m.format(g.key_generators[a-1],g.key_generators[b-1])))
|
||||
from mmgen.addr import get_privhex2addr_f
|
||||
gen_a = get_privhex2addr_f(selector=a)
|
||||
gen_b = get_privhex2addr_f(selector=b)
|
||||
compressed = False
|
||||
for i in range(1,rounds+1):
|
||||
msg_r('\rRound %s/%s ' % (i,rounds))
|
||||
sec = hexlify(os.urandom(32))
|
||||
wif = hex2wif(sec,compressed=compressed)
|
||||
a_addr = gen_a(sec,compressed)
|
||||
b_addr = gen_b(sec,compressed)
|
||||
vmsg('\nkey: %s\naddr: %s\n' % (wif,a_addr))
|
||||
if a_addr != b_addr:
|
||||
msg_r(red('\nERROR: Addresses do not match!'))
|
||||
die(3,"""
|
||||
sec key: {}
|
||||
WIF key: {}
|
||||
{pnm}: {}
|
||||
keyconv: {}
|
||||
""".format(sec,wif,a_addr,b_addr,pnm=g.proj_name).rstrip())
|
||||
if a != 2 and b != 2:
|
||||
compressed = not compressed
|
||||
if a and b:
|
||||
m = "Comparing address generators '{}' and '{}'"
|
||||
msg(green(m.format(g.key_generators[a-1],g.key_generators[b-1])))
|
||||
from mmgen.addr import get_privhex2addr_f
|
||||
gen_a = get_privhex2addr_f(selector=a)
|
||||
gen_b = get_privhex2addr_f(selector=b)
|
||||
compressed = False
|
||||
for i in range(1,rounds+1):
|
||||
msg_r('\rRound %s/%s ' % (i,rounds))
|
||||
sec = hexlify(os.urandom(32))
|
||||
wif = hex2wif(sec,compressed=compressed)
|
||||
a_addr = gen_a(sec,compressed)
|
||||
b_addr = gen_b(sec,compressed)
|
||||
vmsg('\nkey: %s\naddr: %s\n' % (wif,a_addr))
|
||||
if a_addr != b_addr:
|
||||
match_error(sec,wif,a_addr,b_addr,a,b)
|
||||
if a != 2 and b != 2:
|
||||
compressed = not compressed
|
||||
|
||||
msg(green(('\n','')[bool(opt.verbose)] + 'OK'))
|
||||
msg(green(('\n','')[bool(opt.verbose)] + 'OK'))
|
||||
elif a and not fh:
|
||||
m = "Testing speed of address generator '{}'"
|
||||
msg(green(m.format(g.key_generators[a-1])))
|
||||
from mmgen.addr import get_privhex2addr_f
|
||||
gen_a = get_privhex2addr_f(selector=a)
|
||||
import time
|
||||
start = time.time()
|
||||
from struct import pack,unpack
|
||||
seed = os.urandom(28)
|
||||
print 'Incrementing key with each round'
|
||||
print 'Starting key:', hexlify(seed+pack('I',0))
|
||||
compressed = False
|
||||
for i in range(rounds):
|
||||
if not opt.quiet: msg_r('\rRound %s/%s ' % (i+1,rounds))
|
||||
sec = hexlify(seed+pack('I',i))
|
||||
wif = hex2wif(sec,compressed=compressed)
|
||||
a_addr = gen_a(sec,compressed)
|
||||
vmsg('\nkey: %s\naddr: %s\n' % (wif,a_addr))
|
||||
if a != 2:
|
||||
compressed = not compressed
|
||||
elapsed = int(time.time() - start)
|
||||
if not opt.quiet: msg('')
|
||||
msg('%s addresses generated in %s second%s' % (rounds,elapsed,('s','')[elapsed==1]))
|
||||
elif a and dump:
|
||||
m = "Comparing output of address generator '{}' against wallet dump '{}'"
|
||||
msg(green(m.format(g.key_generators[a-1],cmd_args[1])))
|
||||
if a == 2:
|
||||
msg("NOTE: for compressed addresses, 'python-ecdsa' generator will be used")
|
||||
from mmgen.addr import get_privhex2addr_f
|
||||
gen_a = get_privhex2addr_f(selector=a)
|
||||
from mmgen.bitcoin import wif2hex
|
||||
for n,[wif,a_addr] in enumerate(dump,1):
|
||||
msg_r('\rKey %s/%s ' % (n,len(dump)))
|
||||
sec = wif2hex(wif)
|
||||
compressed = wif[0] != ('5','9')[g.testnet]
|
||||
b_addr = gen_a(sec,compressed)
|
||||
if a_addr != b_addr:
|
||||
match_error(sec,wif,a_addr,b_addr,1 if compressed and a==2 else a,4)
|
||||
msg(green(('\n','')[bool(opt.verbose)] + 'OK'))
|
||||
|
|
|
|||
6
test/ref/98831F3A-E2687906[256,1].mmdat
Normal file
6
test/ref/98831F3A-E2687906[256,1].mmdat
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
9cc19b
|
||||
test.py ref. wallet (pw 'abc', seed len 256)
|
||||
98831f3a e2687906 256 NE 20161110_135346
|
||||
1: 12 8 1
|
||||
70413d 74ev zjeq Zw2g DspF RKpE 7H
|
||||
7c26e6 1otd mVTn 5MCR cDTF sZqY uNKA rsAm mjTw EJmS yzwX ZPJd
|
||||
19
test/ref/98831F3A[1,31-33,500-501,1010-1011].testnet.addrs
Normal file
19
test/ref/98831F3A[1,31-33,500-501,1010-1011].testnet.addrs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# MMGen address file
|
||||
#
|
||||
# This file is editable.
|
||||
# Everything following a hash symbol '#' is a comment and ignored by MMGen.
|
||||
# A text label of 32 characters or less may be added to the right of each
|
||||
# address, and it will be appended to the bitcoind wallet label upon import.
|
||||
# The label may contain any printable ASCII symbol.
|
||||
# Address data checksum for 98831F3A[1,31-33,500-501,1010-1011]: 3C2C 8558 BB54 079E
|
||||
# Record this value to a secure location.
|
||||
98831F3A {
|
||||
1 n1z4XgmpMzaZJ9Ywjefnv7yrPc2w4h6UxL
|
||||
31 mfqYRquF5Uw9YCKGoqh7tRqfYQQhdAQi5Q
|
||||
32 mp31EPM2a8evsuZDYPiPUmA5ChwCLTr9x1
|
||||
33 mmdqJpHeV1nmVpBmebGyR4Ziu2chE1gc43
|
||||
500 mu5LCgiNbbzWMod2DAFJpqfwF3vMSGPdnb
|
||||
501 mtxxVcPsLb237x4tQpiztcE2jHFVWdSs8d
|
||||
1010 mtVqD1xmEBVMijdDXpfidbF4otQsLzmNxi
|
||||
1011 msj54iM9CYCtpvcGnb8fz54hG23fQPuueN
|
||||
}
|
||||
BIN
test/ref/98831F3A[1,31-33,500-501,1010-1011].testnet.akeys.mmenc
Normal file
BIN
test/ref/98831F3A[1,31-33,500-501,1010-1011].testnet.akeys.mmenc
Normal file
Binary file not shown.
6
test/ref/FFB367[1.234].testnet.rawtx
Normal file
6
test/ref/FFB367[1.234].testnet.rawtx
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
3c0e60
|
||||
FFB367 1.234 20150405_102927 350828
|
||||
01000000013364630b6d290a82c822facc2f7c1db4452cea459b2ce22371135530485a5d010600000000ffffffff0205d7d600010000001976a914bba3993079ccdf40c9bbbe495473f0b3d2dc5eec88ac40ef5a07000000001976a914abe58e1e45f6176910a4c1ac1ee62328d5cc4fd588ac00000000
|
||||
[{'label': u'Test Wallet', 'mmid': u'98831F3A:500', 'vout': 6, 'txid': u'015d5a483055137123e22c9b45ea2c45b41d7c2fccfa22c8820a296d0b636433', 'amt': BTCAmt('44.32452045'), 'confs': 495L, 'addr': u'mu5LCgiNbbzWMod2DAFJpqfwF3vMSGPdnb', 'scriptPubKey': '76a91494b93bbe8a32f1db80b307482e83c25fa4e99b8c88ac'}]
|
||||
[{'amt': BTCAmt('43.09047045'), 'mmid': '98831F3A:3', 'addr': u'mxd6dwbbhg4g7tqGxwQ4pueajob7kjWHBG'}, {'amt': BTCAmt('1.23400000'), 'mmid': '98831F3A:2', 'addr': u'mwBrqdQGfj4yH6594qAzZVqmYfLdmB1C7W'}]
|
||||
TvwWgaAnrkQFpAxxjBa4PHvJ8NsJDsurtiv2HuzdnXWjQmY7LHyt6PZn5J7BNtB5VzHtBG7bUosCAMFon8yxUe2mYTZoH9e6dpoAz9E6JDZtUNYz9YnF1Z3jFND1X89RuKAk6YVBrfWseeyHR8vZDdaFzBPK5SPos
|
||||
55
test/test.py
55
test/test.py
|
|
@ -45,6 +45,7 @@ sys.path.__setitem__(0,os.path.abspath(os.curdir))
|
|||
# Import these _after_ local path's been added to sys.path
|
||||
from mmgen.common import *
|
||||
from mmgen.test import *
|
||||
tn_desc = ('','.testnet')[g.testnet]
|
||||
|
||||
start_mscolor()
|
||||
|
||||
|
|
@ -175,8 +176,8 @@ cfgs = {
|
|||
'seed_len': 128,
|
||||
'seed_id': 'FE3C6545',
|
||||
'ref_bw_seed_id': '33F10310',
|
||||
'addrfile_chk': 'B230 7526 638F 38CB',
|
||||
'keyaddrfile_chk': 'CF83 32FB 8A8B 08E2',
|
||||
'addrfile_chk': ('B230 7526 638F 38CB','B64D 7327 EF2A 60FE')[g.testnet],
|
||||
'keyaddrfile_chk': ('CF83 32FB 8A8B 08E2','FEBF 7878 97BB CC35')[g.testnet],
|
||||
'wpasswd': 'reference password',
|
||||
'ref_wallet': 'FE3C6545-D782B529[128,1].mmdat',
|
||||
'ic_wallet': 'FE3C6545-E29303EA-5E229E30[128,1].mmincog',
|
||||
|
|
@ -201,8 +202,8 @@ cfgs = {
|
|||
'seed_len': 192,
|
||||
'seed_id': '1378FC64',
|
||||
'ref_bw_seed_id': 'CE918388',
|
||||
'addrfile_chk': '8C17 A5FA 0470 6E89',
|
||||
'keyaddrfile_chk': '9648 5132 B98E 3AD9',
|
||||
'addrfile_chk': ('8C17 A5FA 0470 6E89','0A59 C8CD 9439 8B81')[g.testnet],
|
||||
'keyaddrfile_chk': ('9648 5132 B98E 3AD9','2F72 C83F 44C5 0FAC')[g.testnet],
|
||||
'wpasswd': 'reference password',
|
||||
'ref_wallet': '1378FC64-6F0F9BB4[192,1].mmdat',
|
||||
'ic_wallet': '1378FC64-2907DE97-F980D21F[192,1].mmincog',
|
||||
|
|
@ -227,17 +228,17 @@ cfgs = {
|
|||
'seed_len': 256,
|
||||
'seed_id': '98831F3A',
|
||||
'ref_bw_seed_id': 'B48CD7FC',
|
||||
'addrfile_chk': '6FEF 6FB9 7B13 5D91',
|
||||
'keyaddrfile_chk': '9F2D D781 1812 8BAD',
|
||||
'addrfile_chk': ('6FEF 6FB9 7B13 5D91','3C2C 8558 BB54 079E')[g.testnet],
|
||||
'keyaddrfile_chk': ('9F2D D781 1812 8BAD','7410 8F95 4B33 B4B2')[g.testnet],
|
||||
'wpasswd': 'reference password',
|
||||
'ref_wallet': '98831F3A-27F2BF93[256,1].mmdat',
|
||||
'ref_addrfile': '98831F3A[1,31-33,500-501,1010-1011].addrs',
|
||||
'ref_keyaddrfile': '98831F3A[1,31-33,500-501,1010-1011].akeys.mmenc',
|
||||
'ref_addrfile_chksum': '6FEF 6FB9 7B13 5D91',
|
||||
'ref_keyaddrfile_chksum': '9F2D D781 1812 8BAD',
|
||||
'ref_wallet': '98831F3A-{}[256,1].mmdat'.format(('27F2BF93','E2687906')[g.testnet]),
|
||||
'ref_addrfile': '98831F3A[1,31-33,500-501,1010-1011]{}.addrs'.format(tn_desc),
|
||||
'ref_keyaddrfile': '98831F3A[1,31-33,500-501,1010-1011]{}.akeys.mmenc'.format(tn_desc),
|
||||
'ref_addrfile_chksum': ('6FEF 6FB9 7B13 5D91','3C2C 8558 BB54 079E')[g.testnet],
|
||||
'ref_keyaddrfile_chksum': ('9F2D D781 1812 8BAD','7410 8F95 4B33 B4B2')[g.testnet],
|
||||
|
||||
# 'ref_fake_unspent_data':'98831F3A_unspent.json',
|
||||
'ref_tx_file': 'FFB367[1.234].rawtx',
|
||||
'ref_tx_file': 'FFB367[1.234]{}.rawtx'.format(tn_desc),
|
||||
'ic_wallet': '98831F3A-5482381C-18460FB1[256,1].mmincog',
|
||||
'ic_wallet_hex': '98831F3A-1630A9F2-870376A9[256,1].mmincox',
|
||||
|
||||
|
|
@ -483,6 +484,7 @@ opts_data = {
|
|||
-n, --names Display command names instead of descriptions.
|
||||
-I, --non-interactive Non-interactive operation (MS Windows mode)
|
||||
-p, --pause Pause between tests, resuming on keypress.
|
||||
-P, --profile Record the execution time of each script.
|
||||
-q, --quiet Produce minimal output. Suppress dependency info.
|
||||
-r, --resume=c Resume at command 'c' after interrupted run
|
||||
-s, --system Test scripts and modules installed on system rather
|
||||
|
|
@ -500,6 +502,7 @@ If no command is given, the whole suite of tests is run.
|
|||
|
||||
cmd_args = opts.init(opts_data)
|
||||
|
||||
if opt.profile: opt.names = True
|
||||
if opt.resume: opt.skip_deps = True
|
||||
if opt.log:
|
||||
log_fd = open(log_file,'a')
|
||||
|
|
@ -1040,7 +1043,10 @@ class MMGenTestSuite(object):
|
|||
else:
|
||||
return
|
||||
|
||||
if opt.profile: start = time.time()
|
||||
self.__class__.__dict__[cmd](*([self,cmd] + al))
|
||||
if opt.profile:
|
||||
msg('\r\033[50C{:.4f}'.format(time.time() - start))
|
||||
|
||||
def generate_file_deps(self,cmd):
|
||||
return [(str(n),e) for exts,n in cmd_data[cmd][2] for e in exts]
|
||||
|
|
@ -1410,6 +1416,7 @@ class MMGenTestSuite(object):
|
|||
return
|
||||
t.expect('Encrypt key list? (y/N): ','y')
|
||||
t.hash_preset('new key list','1')
|
||||
# t.passphrase_new('new key list','kafile password')
|
||||
t.passphrase_new('new key list',cfg['kapasswd'])
|
||||
t.written_to_file('Secret keys',oo=True)
|
||||
ok()
|
||||
|
|
@ -1558,27 +1565,6 @@ class MMGenTestSuite(object):
|
|||
os.unlink(f1)
|
||||
cmp_or_die(hincog_offset,int(o))
|
||||
|
||||
# def pywallet(self,name): # TODO - check output
|
||||
# pf = get_tmpfile_fn(cfg,pwfile)
|
||||
# write_data_to_file(pf,cfg['wpasswd']+'\n',silent=True)
|
||||
# args = ([],['-q','-P',pf])[ni]
|
||||
# unenc_wf = os.path.join(ref_dir,'wallet-unenc.dat')
|
||||
# enc_wf = os.path.join(ref_dir,'wallet-enc.dat')
|
||||
# for wf,enc in (unenc_wf,False),(enc_wf,True):
|
||||
# for w,o,pk in (
|
||||
# ('addresses','a',False),
|
||||
# ('private keys','k',True),
|
||||
# ('json dump','j',True)
|
||||
# ):
|
||||
# ed = '(%sencrypted wallet, %s)' % (('un','')[bool(enc)],w)
|
||||
# t = MMGenExpect(name,'mmgen-pywallet', args +
|
||||
# ['-'+o,'-d',cfg['tmpdir']] + [wf], extra_desc=ed)
|
||||
# if ni: continue
|
||||
# if pk and enc and not ni:
|
||||
# t.expect('Enter password: ',cfg['wpasswd']+'\n')
|
||||
# t.written_to_file(capfirst(w),oo=True)
|
||||
# if not ni: ok()
|
||||
|
||||
# Saved reference file tests
|
||||
def ref_wallet_conv(self,name):
|
||||
wf = os.path.join(ref_dir,cfg['ref_wallet'])
|
||||
|
|
@ -1685,6 +1671,7 @@ class MMGenTestSuite(object):
|
|||
m = grnbg("Answer 'y' at the interactive prompt if Seed ID is")
|
||||
n = cyan(cfg['seed_id'])
|
||||
msg('\n%s %s' % (m,n))
|
||||
if wtype == 'hic_wallet_old' and opt.profile: msg('')
|
||||
t = MMGenExpect(name,'mmgen-walletchk',
|
||||
add_args + slarg + hparg + of_arg + ic_arg,
|
||||
extra_desc=edesc)
|
||||
|
|
@ -1789,6 +1776,7 @@ class MMGenTestSuite(object):
|
|||
t.close()
|
||||
ok()
|
||||
# back check of result
|
||||
if opt.profile: msg('')
|
||||
self.walletchk(name,wf,pf=None,
|
||||
desc='mnemonic data',
|
||||
sid=cfg['seed_id'],
|
||||
|
|
@ -1844,6 +1832,7 @@ class MMGenTestSuite(object):
|
|||
if desc == 'hidden incognito data':
|
||||
add_args += uopts_chk
|
||||
wf = None
|
||||
if opt.profile: msg('')
|
||||
self.walletchk(name,wf,pf=pf,
|
||||
desc=desc,sid=cfg['seed_id'],pw=pw,
|
||||
add_args=add_args,
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ If no command is given, the whole suite of tests is run.
|
|||
"""
|
||||
}
|
||||
|
||||
cmd_args = opts.init(opts_data,add_opts=['exact_output'])
|
||||
cmd_args = opts.init(opts_data,add_opts=['exact_output','profile'])
|
||||
|
||||
if opt.system: sys.path.pop(0)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue