New 'mmgen-walletconv' utility converts to and from all available MMGen wallet

formats, thus obsoleting much of the functionality in '-walletgen' and
'-walletchk'.  This is the first script to utilize the new OO wallet code in
'seed.py'.

New tests in the 'test.py' suite.

	new file:   mmgen-walletconv
	new file:   mmgen/main_walletconv.py
	modified:   mmgen/seed.py
	modified:   test/test.py
This commit is contained in:
philemon 2015-04-25 19:39:25 +03:00
commit 03b1b4b00e
41 changed files with 1728 additions and 1090 deletions

25
mmgen-walletconv Executable file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env python
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
mmgen-walletconv: Convert an MMGen deterministic wallet from one format
to another
"""
from mmgen.main import launch
launch("walletconv")

View file

@ -224,6 +224,13 @@ class AddrInfoList(MMGenObject):
if sid in self.data:
return self.data[sid]
def mmaddr2btcaddr(self,mmaddr):
btcaddr = ""
sid,idx = mmaddr.split(":")
if sid in self.seed_ids():
btcaddr = self.addrinfo(sid).btcaddr(int(idx))
return btcaddr
def add_wallet_data(self,c):
vmsg_r("Getting account data from wallet...")
data,accts,i = {},c.listaccounts(minconf=0,includeWatchonly=True),0
@ -291,10 +298,12 @@ class AddrInfo(MMGenObject):
self.seed_id = seed_id
self.addrdata = addrdata
self.num_addrs = len(addrdata)
if self.source in ("wallet","txsign") or \
(self.source == "addrgen" and opt.gen_what == "k"):
if self.source in ("wallet","txsign"):
self.checksum = None
self.idxs_fmt = None
elif self.source == "addrgen" and opt.gen_what == "k":
self.checksum = None
self.fmt_addr_idxs()
else: # self.source in addrfile, addrgen
self.make_addrdata_chksum()
self.fmt_addr_idxs()
@ -383,10 +392,11 @@ class AddrInfo(MMGenObject):
out = []
from mmgen.addr import addrmsgs
out.append(addrmsgs['addrfile_header'] + "\n")
w = "Key-address" if status[1] else "Address"
out.append("# {} data checksum for {}[{}]: {}".format(
w, self.seed_id, self.idxs_fmt, self.checksum))
out.append("# Record this value to a secure location\n")
if self.checksum:
w = "Key-address" if status[1] else "Address"
out.append("# {} data checksum for {}[{}]: {}".format(
w, self.seed_id, self.idxs_fmt, self.checksum))
out.append("# Record this value to a secure location\n")
out.append("%s {" % self.seed_id)
# Body
@ -409,7 +419,7 @@ class AddrInfo(MMGenObject):
out.append("}")
return "\n".join([l.rstrip() for l in out])
return "\n".join([l.rstrip() for l in out]) + "\n"
def fmt_addr_idxs(self):

View file

@ -17,7 +17,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
crypto.py: Cryptographic and related routines for the 'mmgen-tool' utility
crypto.py: Cryptographic and related routines for the MMGen suite
"""
import sys
@ -58,7 +58,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
}
def encrypt_seed(seed, key):
return encrypt_data(seed, key, iv=1, what="seed")
return encrypt_data(seed, key, iv=1, desc="seed")
def decrypt_seed(enc_seed, key, seed_id, key_id):
@ -66,21 +66,21 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
vmsg_r("Checking key...")
chk1 = make_chksum_8(key)
if key_id:
if not compare_chksums(key_id,"key id",chk1,"computed",die=False):
if not compare_chksums(key_id,"key ID",chk1,"computed"):
msg("Incorrect passphrase")
return False
dec_seed = decrypt_data(enc_seed, key, iv=1, what="seed")
dec_seed = decrypt_data(enc_seed, key, iv=1, desc="seed")
chk2 = make_chksum_8(dec_seed)
if seed_id:
if compare_chksums(seed_id,"seed id",chk2,"decrypted seed",die=False):
if compare_chksums(seed_id,"seed ID",chk2,"decrypted seed"):
qmsg("Passphrase is OK")
else:
if not opt.debug:
msg_r("Checking key ID...")
if compare_chksums(key_id,"key id",chk1,"computed",die=False):
if compare_chksums(key_id,"key ID",chk1,"computed"):
msg("Key ID is correct but decryption of seed failed")
else:
msg("Incorrect passphrase")
@ -90,29 +90,25 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
# else:
# qmsg("Generated IDs (Seed/Key): %s/%s" % (chk2,chk1))
if opt.debug: Msg("Decrypted seed: %s" % hexlify(dec_seed))
dmsg("Decrypted seed: %s" % hexlify(dec_seed))
vmsg("OK")
return dec_seed
def encrypt_data(data, key, iv=1, what="data", verify=True):
"""
Encrypt arbitrary data using AES256 in counter mode
"""
def encrypt_data(data, key, iv=1, desc="data", verify=True):
# 192-bit seed is 24 bytes -> not multiple of 16. Must use MODE_CTR
from Crypto.Cipher import AES
from Crypto.Util import Counter
vmsg("Encrypting %s" % what)
vmsg("Encrypting %s" % desc)
c = AES.new(key, AES.MODE_CTR,
counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
enc_data = c.encrypt(data)
if verify:
vmsg_r("Performing a test decryption of the %s..." % what)
vmsg_r("Performing a test decryption of the %s..." % desc)
c = AES.new(key, AES.MODE_CTR,
counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
@ -120,15 +116,15 @@ def encrypt_data(data, key, iv=1, what="data", verify=True):
if dec_data == data: vmsg("done")
else:
msg("ERROR.\nDecrypted %s doesn't match original %s" % (what,what))
msg("ERROR.\nDecrypted %s doesn't match original %s" % (desc,desc))
sys.exit(2)
return enc_data
def decrypt_data(enc_data, key, iv=1, what="data"):
def decrypt_data(enc_data, key, iv=1, desc="data"):
vmsg_r("Decrypting %s with key..." % what)
vmsg_r("Decrypting %s with key..." % desc)
from Crypto.Cipher import AES
from Crypto.Util import Counter
@ -151,18 +147,18 @@ def scrypt_hash_passphrase(passwd, salt, hash_preset, buflen=32):
def make_key(passwd,salt,hash_preset,
what="encryption key",from_what="passphrase",verbose=False):
desc="encryption key",from_what="passphrase",verbose=False):
if from_what: what += " from "
if from_what: desc += " from "
if opt.verbose or verbose:
msg_r("Generating %s%s..." % (what,from_what))
msg_r("Generating %s%s..." % (desc,from_what))
key = scrypt_hash_passphrase(passwd, salt, hash_preset)
if opt.verbose or verbose: msg("done")
if opt.debug: Msg("Key: %s" % hexlify(key))
dmsg("Key: %s" % hexlify(key))
return key
def get_random_data_from_user(uchars):
def _get_random_data_from_user(uchars):
if opt.quiet: msg("Enter %s random symbols" % uchars)
else: msg(crmsg['usr_rand_notice'] % uchars)
@ -189,8 +185,7 @@ def get_random_data_from_user(uchars):
fmt_time_data = ["{:.22f}".format(i) for i in time_data]
if opt.debug:
msg("\nUser input:\n%s\nKeystroke time intervals:\n%s\n" %
dmsg("\nUser input:\n%s\nKeystroke time intervals:\n%s\n" %
(key_data,"\n".join(fmt_time_data)))
prompt = "User random data successfully acquired. Press ENTER to continue"
@ -202,23 +197,23 @@ def get_random_data_from_user(uchars):
def get_random(length):
from Crypto import Random
os_rand = Random.new().read(length)
if g.use_urandchars:
if g.use_urandchars and opt.usr_randchars:
from_what = "OS random data"
if not g.user_entropy:
g.user_entropy = \
sha256(get_random_data_from_user(opt.usr_randchars)).digest()
sha256(_get_random_data_from_user(opt.usr_randchars)).digest()
from_what += " plus user-supplied entropy"
else:
from_what += " plus saved user-supplied entropy"
key = make_key(g.user_entropy, "", '2', from_what=from_what, verbose=True)
return encrypt_data(os_rand,key,what="random data",verify=False)
return encrypt_data(os_rand,key,desc="random data",verify=False)
else:
return os_rand
def get_seed_from_wallet(
infile,
prompt_info="{pnm} wallet".format(pnm=g.proj_name),
desc="{pnm} wallet".format(pnm=g.proj_name),
silent=False
):
@ -228,7 +223,7 @@ def get_seed_from_wallet(
if opt.debug: display_control_data(*wdata)
padd = " "+infile if opt.quiet else ""
passwd = get_mmgen_passphrase(prompt_info+padd)
passwd = get_mmgen_passphrase(desc+padd)
key = make_key(passwd, salt, hash_preset)
@ -270,17 +265,17 @@ def confirm_old_format():
def get_seed_from_incog_wallet(
infile,
prompt_info="{pnm} incognito wallet".format(pnm=g.proj_name),
desc="{pnm} incognito wallet".format(pnm=g.proj_name),
silent=False,
hex_input=False
):
what = "incognito wallet data"
desc = "incognito wallet data"
if opt.from_incog_hidden:
d = get_hidden_incog_data()
else:
d = get_data_from_file(infile,what)
d = get_data_from_file(infile,desc)
if hex_input:
try:
d = unhexlify("".join(d.split()).strip())
@ -310,10 +305,13 @@ def get_seed_from_incog_wallet(
else 'incog_iv_id'])
while True:
passwd = get_mmgen_passphrase(prompt_info+" "+incog_id)
passwd = get_mmgen_passphrase(desc+" "+incog_id)
qmsg("Configured hash presets: %s" % " ".join(sorted(g.hash_presets)))
hp = get_hash_preset_from_user(what="incog wallet")
if 'hash_preset' in opt.set_by_user:
hp = opt.hash_preset
else:
hp = get_hash_preset_from_user(desc="incog wallet")
# IV is used BOTH to initialize counter and to salt password!
key = make_key(passwd, iv, hp, "wrapper key")
@ -399,7 +397,7 @@ def _get_seed(infile,silent=False,seed_id=""):
msg("Invalid %s file '%s'" % (source,infile))
sys.exit(2)
if opt.debug: Msg("Seed: %s" % hexlify(seed))
dmsg("Seed: %s" % hexlify(seed))
return seed
@ -415,9 +413,9 @@ def get_seed_retry(infile,seed_id=""):
def _get_seed_from_brain_passphrase(words,silent=False):
bp = " ".join(words)
if opt.debug: Msg("Sanitized brain passphrase: %s" % bp)
dmsg("Sanitized brain passphrase: %s" % bp)
seed_len,hash_preset = get_from_brain_opt_params()
if opt.debug: Msg("Brainwallet l = %s, p = %s" % (seed_len,hash_preset))
dmsg("Brainwallet l = %s, p = %s" % (seed_len,hash_preset))
vmsg_r("Hashing brainwallet data. Please wait...")
# Use buflen arg of scrypt.hash() to get seed of desired length
seed = scrypt_hash_passphrase(bp, "", hash_preset, buflen=seed_len/8)
@ -432,31 +430,31 @@ def _get_seed_from_brain_passphrase(words,silent=False):
# Vars for mmgen_*crypt functions only
salt_len,sha256_len,nonce_len = 32,32,32
def mmgen_encrypt(data,what="data",hash_preset=''):
def mmgen_encrypt(data,desc="data",hash_preset=''):
salt,iv,nonce = get_random(salt_len),\
get_random(g.aesctr_iv_len), \
get_random(nonce_len)
hp = hash_preset or get_hash_preset_from_user('3',what)
hp = hash_preset or get_hash_preset_from_user('3',desc)
m = "default" if hp == '3' else "user-requested"
vmsg("Encrypting %s" % what)
vmsg("Encrypting %s" % desc)
qmsg("Using %s hash preset of '%s'" % (m,hp))
passwd = get_new_passphrase(what, {})
passwd = get_new_passphrase(desc, {})
key = make_key(passwd, salt, hp)
enc_d = encrypt_data(sha256(nonce+data).digest() + nonce + data, key,
int(hexlify(iv),16), what=what)
int(hexlify(iv),16), desc=desc)
return salt+iv+enc_d
def mmgen_decrypt(data,what="data",hash_preset=""):
def mmgen_decrypt(data,desc="data",hash_preset=""):
dstart = salt_len + g.aesctr_iv_len
salt,iv,enc_d = data[:salt_len],data[salt_len:dstart],data[dstart:]
vmsg("Preparing to decrypt %s" % what)
hp = hash_preset or get_hash_preset_from_user('3',what)
vmsg("Preparing to decrypt %s" % desc)
hp = hash_preset or get_hash_preset_from_user('3',desc)
m = "default" if hp == '3' else "user-requested"
qmsg("Using %s hash preset of '%s'" % (m,hp))
passwd = get_mmgen_passphrase(what)
passwd = get_mmgen_passphrase(desc)
key = make_key(passwd, salt, hp)
dec_d = decrypt_data(enc_d, key, int(hexlify(iv),16), what)
dec_d = decrypt_data(enc_d, key, int(hexlify(iv),16), desc)
if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
vmsg("OK")
return dec_d[sha256_len+nonce_len:]
@ -464,8 +462,8 @@ def mmgen_decrypt(data,what="data",hash_preset=""):
msg("Incorrect passphrase or hash preset")
return False
def mmgen_decrypt_retry(d,what="data"):
def mmgen_decrypt_retry(d,desc="data"):
while True:
d_dec = mmgen_decrypt(d,what)
d_dec = mmgen_decrypt(d,desc)
if d_dec: return d_dec
msg("Trying again...")

View file

@ -20,66 +20,25 @@
filename.py: Filename class and methods for the MMGen suite
"""
import sys,os
import mmgen.opt as opt
from mmgen.obj import *
import mmgen.globalvars as g
from mmgen.util import msg,fmt_code_to_sstype
from mmgen.util import die,get_extension,check_infile
class Filename(MMGenObject):
exts = {
'seed': {
"mmdat": "Wallet",
"mmseed": "SeedFile",
"mmwords": "Mnemonic",
"mmbrain": "Brainwallet",
"mmincog": "IncogWallet",
"mmincox": "IncogWalletHex",
},
'tx': {
"raw": "RawTX",
"sig": "SigTX",
},
'addr': {
"addrs": "AddrInfo",
"keys": "KeyInfo",
"akeys": "KeyAddrInfo",
"akeys.mmenc": "KeyAddrInfoEnc",
},
'other': {
"chk": "AddrInfoChecksum",
"mmenc": "MMEncInfo",
},
}
ftypes = {
'seed': {
"hincog": "IncogWalletHidden",
},
}
def __init__(self,fn,ftype=""):
def __init__(self,fn,ftype=None):
self.name = fn
self.dirname = os.path.dirname(fn)
self.basename = os.path.basename(fn)
self.ext = None
self.ftype = ftype
def mf1(k): return k == ftype
def mf2(k): return '.'+k == fn[-len('.'+k):]
# This should be done before license msg instead
# check_infile(fn)
# find file info for ftype or extension
e,attr,have_match = (self.ftypes,"ftype",mf1) if ftype else \
(self.exts,"ext",mf2)
for k in e:
for j in e[k]:
if have_match(j):
setattr(self,attr,j)
self.fclass = k
self.sstype = e[k][j]
if not hasattr(self,attr):
die(2,"Unrecognized %s for file '%s'" % (attr,fn))
if not ftype:
self.ext = get_extension(fn)
if not (self.ext):
die(2,"Unrecognized extension '.%s' for file '%s'" % (self.ext,fn))
# TODO: Check for Windows
import stat

View file

@ -53,7 +53,8 @@ version = '0.8.0'
required_opts = [
"quiet","verbose","debug","outdir","echo_passphrase","passwd_file",
"usr_randchars","stdout","show_hash_presets"
"usr_randchars","stdout","show_hash_presets","label",
"keep_passphrase","keep_hash_preset"
]
min_screen_width = 80
@ -119,16 +120,3 @@ addr_label_symbols = wallet_label_symbols = printable_nonl
max_addr_label_len = 32
max_wallet_label_len = 48
max_tx_comment_len = 72 # Comment is b58 encoded, so can permit all UTF-8
wallet_fmt_codes = (
( "Wallet", "wallet", "w" ),
( "SeedFile", "mmseed","seed", "s" ),
( "Mnemonic", "mmwords","words","mnemonic","mnem","m" ),
( "Brainwallet", "mmbrain","brainwallet","brain","bw","b" ),
( "IncogWallet", "mmincog","incog","i" ),
( "IncogWalletHex", "mmincox","incog_hex","ix" ),
( "IncogWalletHidden", "incog_hidden","ih" ),
)
#addr_label_punc = ".","_",",","-"," ","(",")"
#addr_label_symbols = tuple(ascii_letters + digits) + addr_label_punc
#wallet_label_punc = addr_label_punc

View file

@ -19,18 +19,16 @@
"""
license.py: Text of GPLv3
"""
import mmgen.globalvars as g
gpl = {
'warning': """
warning = """
{pnm} Copyright (C) {g.Cdates} by {g.author} {g.email}. This
program comes with ABSOLUTELY NO WARRANTY. This is free software, and
you are welcome to redistribute it under certain conditions.
""".format(g=g,pnm=g.proj_name),
'prompt': """
Press 'w' for conditions and warranty info, or 'c' to continue:
""",
'conditions': """
""".format(g=g,pnm=g.proj_name)
conditions = """
TERMS AND CONDITIONS
0. Definitions.
@ -581,4 +579,3 @@ an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
"""
}

View file

@ -31,6 +31,7 @@ def launch(what):
def launch_txsend(): import mmgen.main_txsend
def launch_txsign(): import mmgen.main_txsign
def launch_walletchk(): import mmgen.main_walletchk
def launch_walletconv(): import mmgen.main_walletconv
def launch_walletgen(): import mmgen.main_walletgen
try: import termios

View file

@ -161,8 +161,8 @@ if opt.stdout or not sys.stdout.isatty():
if enc_ext and sys.stdout.isatty():
msg("Cannot write encrypted data to screen. Exiting")
sys.exit(2)
write_to_stdout(addrdata_str,what,
(what=="keys"and not opt.quiet and sys.stdout.isatty()))
write_to_stdout(addrdata_str,what,ask_terminal=(what=="keys"
and not opt.quiet and sys.stdout.isatty()))
else:
outfile = "%s.%s%s" % (outfile_base, (
g.keyaddrfile_ext if "ka" in opt.gen_what else (

View file

@ -64,7 +64,7 @@ if len(cmd_args) == 1:
ai = AddrInfo(infile,has_keys=opt.keyaddr_file)
else:
msg("""
"You must specify an mmgen address file (or a list of non-{pnm} addresses
You must specify an {pnm} address file (or a list of non-{pnm} addresses
with the '--addrlist' option)
""".strip().format(pnm=g.proj_name))
sys.exit(1)

View file

@ -67,8 +67,8 @@ seed_id,key_id = metadata[:2]
# Repeat on incorrect pw entry
while True:
p = "{pnm} wallet".format(pnm=g.proj_name)
passwd = get_mmgen_passphrase(p,not opt.keep_old_passphrase)
desc = "{pnm} wallet".format(pnm=g.proj_name)
passwd = get_mmgen_passphrase(desc,not opt.keep_old_passphrase)
key = make_key(passwd, salt, hash_preset)
seed = decrypt_seed(enc_seed, key, seed_id, key_id)
if seed: break

View file

@ -180,8 +180,8 @@ class AES(object):
def rotate(self, word):
""" Rijndael's key schedule rotate operation.
Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d
Word is an char list of size 4 (32 bits overall).
Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d
Word is an char list of size 4 (32 bits overall).
"""
return word[1:] + word[:1]
@ -230,10 +230,10 @@ class AES(object):
def expandKey(self, key, size, expandedKeySize):
"""Rijndael's key expansion.
Expands an 128,192,256 key into an 176,208,240 bytes key
Expands an 128,192,256 key into an 176,208,240 bytes key
expandedKey is a char list of large enough size,
key is the non-expanded key.
expandedKey is a char list of large enough size,
key is the non-expanded key.
"""
# current expanded keySize, in bytes
currentSize = 0
@ -276,8 +276,8 @@ class AES(object):
def createRoundKey(self, expandedKey, roundKeyPointer):
"""Create a round key.
Creates a round key from the given expanded key and the
position within the expanded key.
Creates a round key from the given expanded key and the
position within the expanded key.
"""
roundKey = [0] * 16
for i in range(4):
@ -1183,8 +1183,8 @@ def parse_BlockLocator(vds):
return d
def deserialize_BlockLocator(d):
result = "Block Locator top: "+d['hashes'][0][::-1].encode('hex_codec')
return result
result = "Block Locator top: "+d['hashes'][0][::-1].encode('hex_codec')
return result
def parse_setting(setting, vds):
if setting[0] == "f": # flag (boolean) settings
@ -1404,11 +1404,11 @@ def parse_wallet(db, item_callback):
def update_wallet(db, type, data):
"""Write a single item to the wallet.
db must be open with writable=True.
type and data are the type code and data dictionary as parse_wallet would
give to item_callback.
data's __key__, __value__ and __type__ are ignored; only the primary data
fields are used.
db must be open with writable=True.
type and data are the type code and data dictionary as parse_wallet would
give to item_callback.
data's __key__, __value__ and __type__ are ignored; only the primary data
fields are used.
"""
d = data
kds = BCDataStream()
@ -1656,8 +1656,8 @@ elif opt.keysforaddrs:
msg("Warning: not all requested keys found")
len_arg = "%s" % len(wallet_addrs) \
if len(data) == len(wallet_addrs) or ext == "json" \
else "%s:%s" % (len(data),len(wallet_addrs))
if len(data) == len(wallet_addrs) or ext == "json" \
else "%s:%s" % (len(data),len(wallet_addrs))
from mmgen.util import make_chksum_8,write_to_file,write_to_stdout
wallet_id = make_chksum_8(str(sorted(wallet_addrs)))

View file

@ -26,6 +26,7 @@ from decimal import Decimal
import mmgen.globalvars as g
import mmgen.opt as opt
from mmgen.util import dmsg
from mmgen.tx import *
pnm = g.proj_name
@ -196,9 +197,9 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
i.addr = "%s%s %s" % (
i.address[:btaddr_w-len(dots)],
dots, (
("{:<{w}} ".format(i.mmid,w=mmid_w) if i.mmid else "")
+ i.comment)[:acct_w]
)
("{:<{w}} ".format(i.mmid,w=mmid_w) if i.mmid else "")
+ i.comment)[:acct_w]
)
else:
i.addr = i.address
@ -294,16 +295,13 @@ def mmaddr2btcaddr_unspent(unspent,mmaddr):
def mmaddr2btcaddr(c,mmaddr,ail_w,ail_f):
# assume mmaddr has already been checked
sid,idx = mmaddr.split(":")
btcaddr = ""
if sid in ail_w.seed_ids():
btcaddr = ail_w.addrinfo(sid).btcaddr(int(idx))
# assume mmaddr has already been checked
btcaddr = ail_w.mmaddr2btcaddr(mmaddr)
if not btcaddr:
if ail_f and sid in ail_f.seed_ids():
btcaddr = ail_f.addrinfo(sid).btcaddr(int(idx))
if ail_f:
btcaddr = ail_f.mmaddr2btcaddr(mmaddr)
if btcaddr:
msg(wmsg['addr_in_addrfile_only'].format(mmgenaddr=mmaddr))
if not keypress_confirm("Continue anyway?"):
@ -449,9 +447,7 @@ if change > 0: tx_out[change_addr] = float(change)
tx_in = [{"txid":i.txid, "vout":i.vout} for i in sel_unspent]
if opt.debug:
Msg("tx_in: " + repr(tx_in))
Msg("tx_out: " + repr(tx_out))
dmsg("tx_in: %s\ntx_out: %s" % (repr(tx_in),repr(tx_out)))
if opt.comment_file:
if keypress_confirm("Edit comment?",False):

View file

@ -68,12 +68,12 @@ if keypress_confirm("Edit transaction comment?"):
write_to_file(outfile,data,w,False,True,True)
warn = "Once this transaction is sent, there's no taking it back!"
what = "broadcast this transaction to the network"
action = "broadcast this transaction to the network"
expect = "YES, I REALLY WANT TO DO THIS"
if opt.quiet: warn,expect = "","YES"
confirm_or_exit(warn, what, expect)
confirm_or_exit(warn, action, expect)
msg("Sending transaction")

View file

@ -25,7 +25,7 @@ import sys
import mmgen.globalvars as g
import mmgen.opt as opt
from mmgen.tx import *
from mmgen.util import do_license_msg
from mmgen.util import do_license_msg,dmsg
pnm = g.proj_name
pnl = pnm.lower()
@ -150,7 +150,7 @@ def sign_transaction(c,tx_hex,tx_num_str,sig_data,keys=None):
if keys:
qmsg("Passing %s key%s to bitcoind" % (len(keys),suf(keys,"k")))
if opt.debug: Msg("Keys:\n %s" % "\n ".join(keys))
dmsg("Keys:\n %s" % "\n ".join(keys))
msg_r("Signing transaction{}...".format(tx_num_str))
from mmgen.rpc import exceptions
@ -192,11 +192,11 @@ def sign_tx_with_bitcoind_wallet(c,tx_hex,tx_num_str,sig_data,keys):
return sig_tx
def check_maps_from_seeds(maplist,what,infiles,saved_seeds,return_keys=False):
def check_maps_from_seeds(maplist,desc,infiles,saved_seeds,return_keys=False):
if not maplist: return []
qmsg("Checking {pnm} -> BTC address mappings for {w}s (from seed(s))".format(
pnm=pnm,w=what))
pnm=pnm,w=desc))
d = get_keys_for_mmgen_addrs(maplist.keys(),infiles,saved_seeds)
# 0=mmaddr 1=addr 2=wif
m = dict([(e[0],e[1]) for e in d])
@ -253,9 +253,9 @@ def parse_keylist(from_file):
# Check inputs and outputs maps against key-address file, deleting entries:
def check_maps_from_kafile(imap,what,kadata,return_keys=False):
def check_maps_from_kafile(imap,desc,kadata,return_keys=False):
if not kadata: return []
qmsg("Checking {pnm} -> BTC address mappings for {w}s (from key-address file)".format(pnm=pnm,w=what))
qmsg("Checking {pnm} -> BTC address mappings for {w}s (from key-address file)".format(pnm=pnm,w=desc))
ret = []
for k in imap.keys():
if k in kadata.keys():
@ -266,9 +266,9 @@ def check_maps_from_kafile(imap,what,kadata,return_keys=False):
kl,il = "key-address file:","tx file:"
msg(wmsg['mm2btc_mapping_error']%(kl,k,kadata[k][0],il,k,imap[k]))
sys.exit(2)
if ret: vmsg("Removed %s address%s from %ss map" % (len(ret),suf(ret,"a"),what))
if ret: vmsg("Removed %s address%s from %ss map" % (len(ret),suf(ret,"a"),desc))
if return_keys:
vmsg("Added %s wif key%s from %ss map" % (len(ret),suf(ret,"k"),what))
vmsg("Added %s wif key%s from %ss map" % (len(ret),suf(ret,"k"),desc))
return ret
@ -288,7 +288,7 @@ infiles = opt.opts.init(opts_data,add_opts=["b16"])
for l in (
('tx_id', 'info'),
('tx_id', 'terse_info'),
): opt.opts.warn_incompatible_opts(l)
): opt.opts.die_on_incompatible_opts(l)
if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True

View file

@ -95,8 +95,7 @@ def export_to_hidden_incog(incog_enc):
outfile,offset = opt.export_incog_hidden.split(",") #Already sanity-checked
if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
if opt.debug:
Msg("Incog data len %s, offset %s" % (len(incog_enc),offset))
dmsg("Incog data len %s, offset %s" % (len(incog_enc),offset))
check_data_fits_file_at_offset(outfile,int(offset),len(incog_enc),"write")
if not opt.quiet: confirm_or_exit("","alter file '%s'" % outfile)
@ -162,7 +161,7 @@ if opt.export_mnemonic:
elif opt.export_seed:
from mmgen.bitcoin import b58encode_pad
data = split_into_columns(4,b58encode_pad(seed))
data = split_into_cols(4,b58encode_pad(seed))
chk = make_chksum_6(b58encode_pad(seed))
fn = "%s.%s" % (make_chksum_8(seed).upper(), g.seed_ext)
write_to_file_or_stdout(fn, "%s %s\n" % (chk,data), "seed data")

89
mmgen/main_walletconv.py Executable file
View file

@ -0,0 +1,89 @@
#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
mmgen-walletconv: Convert an MMGen deterministic wallet from one format
to another
"""
import sys
import mmgen.globalvars as g
import mmgen.opt as opt
from mmgen.util import die,msg,green,do_license_msg,check_infile
from mmgen.seed import SeedSource
opts_data = {
'sets_disabled': (
('hidden_incog_input_params', bool, 'in_fmt', 'hi'),
('hidden_incog_output_params', bool, 'out_fmt', 'hi')
),
'desc': "Convert an {pnm} wallet from one format to another".format(
pnm=g.proj_name),
'usage':"[opts] [infile]",
'options': """
-h, --help Print this help message.
-d, --outdir= d Output files to directory 'd' instead of working dir.
-i, --in-fmt= f Convert from wallet format 'f' (see FMT CODES below).
-o, --out-fmt= f Convert to wallet format 'f' (see FMT CODES below).
-H, --hidden-incog-input-params=f,o Use filename 'f' and offset 'o' (comma
separated) for hidden incognito input.
-J, --hidden-incog-output-params=f,o Same above, but for output. If file
'f' doesn't exist, it will be created and filled with
random data.
-O, --old-incog-fmt Specify old-format incognito input.
-k, --keep-passphrase Reuse input wallet passphrase for output wallet.
-K, --keep-hash-preset Reuse input wallet hash preset for output wallet.
-l, --seed-len= l Specify wallet seed length of 'l' bits. This option
is required only for brainwallet and incognito inputs
with non-standard (< {g.seed_len}-bit) seed lengths.
-p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p'
for password hashing (default: '{g.hash_preset}').
-q, --quiet Produce quieter output; suppress some warnings.
-r, --usr-randchars=n Get 'n' characters of additional randomness from user
(min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}).
-S, --stdout Write wallet data to stdout instead of file.
-v, --verbose Produce more verbose output.
FMT CODES:
{f}
""".format(g=g,f="\n ".join(SeedSource.format_fmt_codes().split("\n")))
}
cmd_args = opt.opts.init(opts_data)
if len(cmd_args) == 0 \
and not opt.hidden_incog_input_params \
and not opt.in_fmt:
die(1,"An input file or input format must be specified")
if len(cmd_args) > 1 or (len(cmd_args) == 1 and opt.hidden_incog_input_params):
die(1,"Only one input file may be specified")
if len(cmd_args) == 1:
check_infile(cmd_args[0])
do_license_msg()
msg(green("Processing input wallet"))
ss_in = SeedSource(*cmd_args)
msg(green("Processing output wallet"))
ss_out = SeedSource(ss=ss_in)
ss_out.write_to_file()

View file

@ -20,7 +20,7 @@
obj.py: The MMGenObject class and methods
"""
import mmgen.globalvars as g
from mmgen.util import msgrepr_exit,msgrepr
from mmgen.util import mdie,mmsg
lvl = 0
@ -72,18 +72,18 @@ class MMGenObject(object):
col_w = max(len(k) for k in keys)
fs = "{}%-{}s: %s".format(indent,col_w)
methods = [k for k in keys if repr(getattr(self,k))[:14] == '<bound method ']
methods = [k for k in keys if repr(getattr(self,k))[:14] == '<bound method ']
def f(k): return repr(getattr(self,k))[:14] == '<bound method '
methods = filter(f,keys)
def f(k): return repr(getattr(self,k))[:7] == '<mmgen.'
objects = filter(f,keys)
def f(k): return repr(getattr(self,k))[:14] == '<bound method '
methods = filter(f,keys)
def f(k): return repr(getattr(self,k))[:7] == '<mmgen.'
objects = filter(f,keys)
other = list(set(keys) - set(methods) - set(objects))
for k in sorted(methods) + sorted(other) + sorted(objects):
val = getattr(self,k)
if str(type(val))[:13] == "<class 'mmgen": # recurse into sub-objects
out.append("\n%s%s (%s):" % (indent,k,repr(type(val))))
out.append("\n%s%s (%s):" % (indent,k,type(val)))
lvl += 1
out.append(str(getattr(self,k))+"\n")
lvl -= 1

View file

@ -24,7 +24,7 @@ import sys
import mmgen.globalvars as g
import mmgen.share.Opts
import opt
from mmgen.util import msg,msgrepr_exit,msgrepr,Msg,fmt_type
from mmgen.util import msg,msg_r,mdie,mmsg,Msg,die
def usage():
Msg("USAGE: %s %s" % (g.prog_name, usage_txt))
@ -32,17 +32,15 @@ def usage():
def print_version_info():
Msg("""
{progname_uc} version {g.version}
{pgnm_uc} version {g.version}
Part of the {pnm} suite, a Bitcoin cold-storage solution for the com-
mand line. Copyright (C) {g.Cdates} {g.author} {g.email}
""".format(pnm=g.proj_name,g=g,progname_uc=g.prog_name.upper()).strip())
""".format(pnm=g.proj_name, g=g, pgnm_uc=g.prog_name.upper()).strip())
def warn_incompatible_opts(incompat_list):
def die_on_incompatible_opts(incompat_list):
bad = [k for k in opt.__dict__ if opt.__dict__[k] and k in incompat_list]
if len(bad) > 1:
msg("Mutually exclusive options: %s" % " ".join(
["--"+b.replace("_","-") for b in bad]))
sys.exit(1)
die(1,"Conflicting options: %s" % ", ".join([fmt_opt(b) for b in bad]))
def _typeconvert_from_dfl(key):
@ -51,7 +49,6 @@ def _typeconvert_from_dfl(key):
gval = g.__dict__[key]
uval = opt.__dict__[key]
gtype = type(gval)
utype = type(uval)
try:
opt.__dict__[key] = gtype(opt.__dict__[key])
@ -62,16 +59,18 @@ def _typeconvert_from_dfl(key):
'float': 'a float',
'bool': 'a boolean value',
}
m = [d[k] for k in d if __builtins__[k] == gtype]
fs = "'%s': invalid parameter for '--%s' option (not %s)"
msg(fs % (opt.__dict__[key],opt.replace("_","-"),m))
sys.exit(1)
die(1, "'%s': invalid parameter for '--%s' option (not %s)" % (
opt.__dict__[key],
key.replace("_","-"),
d[gtype.__name__]
))
if g.debug:
Msg("Opt overriden by user:\n %-18s: %s" % (
key, ("%s -> %s" % (gval,uval))
))
def fmt_opt(o): return "--" + o.replace("_","-")
def _show_hash_presets():
fs = " {:<7} {:<6} {:<3} {}"
@ -116,21 +115,25 @@ def init(opts_data,add_opts=[]):
for o in [s.rstrip("=") for s in long_opts] + g.required_opts + add_opts:
opt.__dict__[o] = uopts[o] if o in uopts else None
# check user-set opts without modifying them
if not check_opts(uopts): sys.exit(1)
# A special case - do this here, before opt gets set from g.dfl_vars
if opt.usr_randchars: g.use_urandchars = True
# If user opt is set, convert its type based on value in mmgen.globalvars
# If unset, set it to default value in mmgen.globalvars (g):
opt.__dict__['set_by_user'] = []
for k in g.dfl_vars:
if k in opt.__dict__ and opt.__dict__[k] != None:
_typeconvert_from_dfl(k)
else: opt.__dict__[k] = g.__dict__[k]
opt.set_by_user.append(k)
else:
opt.__dict__[k] = g.__dict__[k]
# Check user-set opts without modifying them
if not check_opts(uopts): sys.exit(1)
if opt.show_hash_presets:
_show_hash_presets(); sys.exit(0)
if opt.debug: opt.verbose = True
if g.debug:
@ -138,7 +141,7 @@ def init(opts_data,add_opts=[]):
for k in opt.__dict__:
v = opt.__dict__[k]
if v != None and k != "opts":
Msg(" %-18s: %-6s [%s]" % (k,v,fmt_type(v)))
Msg(" %-18s: %-6s [%s]" % (k,v,type(v).__name__))
Msg("### END OPTS.PY ###\n")
for l in (
@ -146,7 +149,7 @@ def init(opts_data,add_opts=[]):
('export_incog','export_incog_hex','export_incog_hidden','export_mnemonic',
'export_seed'),
('quiet','verbose')
): warn_incompatible_opts(l)
): die_on_incompatible_opts(l)
return args
@ -160,50 +163,61 @@ def show_all_opts():
def check_opts(usr_opts): # Returns false if any check fails
def opt_splits(val,sep,n,what):
def opt_splits(val,sep,n,desc):
sepword = "comma" if sep == "," else (
"colon" if sep == ":" else ("'"+sep+"'"))
try: l = val.split(sep)
except:
msg("'%s': invalid %s (not %s-separated list)" % (val,what,sepword))
msg("'%s': invalid %s (not %s-separated list)" % (val,desc,sepword))
return False
if len(l) == n: return True
else:
msg("'%s': invalid %s (%s %s-separated items required)" %
(val,what,n,sepword))
(val,desc,n,sepword))
return False
def opt_compares(val,op,target,what):
def opt_compares(val,op,target,desc):
if not eval("%s %s %s" % (val, op, target)):
msg("%s: invalid %s (not %s %s)" % (val,what,op,target))
msg("%s: invalid %s (not %s %s)" % (val,desc,op,target))
return False
return True
def opt_is_int(val,what):
def opt_is_int(val,desc):
try: int(val)
except:
msg("'%s': invalid %s (not an integer)" % (val,what))
msg("'%s': invalid %s (not an integer)" % (val,desc))
return False
return True
def opt_is_in_list(val,lst,what):
def opt_is_in_list(val,lst,desc):
if val not in lst:
q,sep = ("'","','") if type(lst[0]) == str else ("",",")
msg("{q}{}{q}: invalid {}\nValid options: {q}{}{q}".format(
val,what,sep.join([str(i) for i in sorted(lst)]),q=q))
msg("{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}".format(
v=val,w=desc,q=q,
o=sep.join([str(i) for i in sorted(lst)])
))
return False
return True
def opt_unrecognized(key,val,desc):
msg("'%s': unrecognized %s for option '%s'"
% (val,desc,fmt_opt(key)))
return False
def opt_display(key,val='',beg="For selected",end=":\n"):
s = "%s=%s" % (fmt_opt(key),val) if val else fmt_opt(key)
msg_r("%s option '%s'%s" % (beg,s,end))
global opt
for key,val in [(k,getattr(opt,k)) for k in usr_opts]:
what = "parameter for '--%s' option" % key.replace("_","-")
desc = "parameter for '%s' option" % fmt_opt(key)
# Check for file existence and readability
from mmgen.util import check_infile
if key in ('keys_from_file','mmgen_keys_from_file',
'passwd_file','keysforaddrs','comment_file'):
from mmgen.util import check_infile
check_infile(val) # exits on error
continue
@ -220,40 +234,83 @@ def check_opts(usr_opts): # Returns false if any check fails
w = "character in label"
for ch in list(val):
if not opt_is_in_list(ch,g.wallet_label_symbols,w): return False
# NEW
elif key in ('in_fmt','out_fmt'):
from mmgen.seed import SeedSource,IncogWallet,Brainwallet,IncogWalletHidden
sstype = SeedSource.fmt_code_to_sstype(val)
if not sstype:
return opt_unrecognized(key,val,"format code")
if key == 'out_fmt':
p = 'hidden_incog_output_params'
if sstype == IncogWalletHidden and not getattr(opt,p):
die(1,"Hidden incog format output requested. You must supply"
+ " a file and offset with the '%s' option" % fmt_opt(p))
if issubclass(sstype,IncogWallet) and opt.old_incog_fmt:
opt_display(key,val,beg="Selected",end=" ")
opt_display('old_incog_fmt',beg="conflicts with",end=":\n")
die(1,"Export to old incog wallet format unsupported")
elif issubclass(sstype,Brainwallet):
die(1,"Output to brainwallet format unsupported")
elif key in ('hidden_incog_input_params','hidden_incog_output_params'):
if key == 'hidden_incog_input_params':
check_infile(val.split(",")[0])
key2 = 'in_fmt'
else:
key2 = 'out_fmt'
if hasattr(opt,key2):
val2 = getattr(opt,key2)
from mmgen.seed import IncogWalletHidden
if val2 and val2 not in IncogWalletHidden.fmt_codes:
die(1,
"Option conflict:\n %s, with\n %s=%s" % (
fmt_opt(key),fmt_opt(key2),val2
))
# begin OLD, deprecated
elif key == 'hidden_incog_params':
from mmgen.util import check_outfile
if not opt_splits(val,",",2,desc): return False
outfile,offset = val.split(",")
check_outfile(outfile)
w = "offset " + desc
if not opt_is_int(offset,w): return False
if not opt_compares(offset,">=",0,desc): return False
elif key == 'export_incog_hidden' or key == 'from_incog_hidden':
if key == 'from_incog_hidden':
if not opt_splits(val,",",3,what): return False
if not opt_splits(val,",",3,desc): return False
infile,offset,seed_len = val.split(",")
from mmgen.util import check_infile
check_infile(infile)
w = "seed length " + what
w = "seed length " + desc
if not opt_is_int(seed_len,w): return False
if not opt_is_in_list(int(seed_len),g.seed_lens,w): return False
else:
from mmgen.util import check_outfile
if not opt_splits(val,",",2,what): return False
if not opt_splits(val,",",2,desc): return False
outfile,offset = val.split(",")
check_outfile(outfile)
w = "offset " + what
w = "offset " + desc
if not opt_is_int(offset,w): return False
if not opt_compares(offset,">=",0,what): return False
if not opt_compares(offset,">=",0,desc): return False
elif key == 'from_brain':
if not opt_splits(val,",",2,what): return False
if not opt_splits(val,",",2,desc): return False
l,p = val.split(",")
w = "seed length " + what
w = "seed length " + desc
if not opt_is_int(l,w): return False
if not opt_is_in_list(int(l),g.seed_lens,w): return False
w = "hash preset " + what
w = "hash preset " + desc
if not opt_is_in_list(p,g.hash_presets.keys(),w): return False
# end OLD
elif key == 'seed_len':
if not opt_is_int(val,what): return False
if not opt_is_in_list(int(val),g.seed_lens,what): return False
if not opt_is_int(val,desc): return False
if not opt_is_in_list(int(val),g.seed_lens,desc): return False
elif key == 'hash_preset':
if not opt_is_in_list(val,g.hash_presets.keys(),what): return False
if not opt_is_in_list(val,g.hash_presets.keys(),desc): return False
elif key == 'usr_randchars':
if not opt_is_int(val,what): return False
if not opt_compares(val,">=",g.min_urandchars,what): return False
if not opt_compares(val,"<=",g.max_urandchars,what): return False
if val == 0: continue
if not opt_is_int(val,desc): return False
if not opt_compares(val,">=",g.min_urandchars,desc): return False
if not opt_compares(val,"<=",g.max_urandchars,desc): return False
else:
if g.debug: Msg("check_opts(): No test for opt '%s'" % key)

View file

@ -23,7 +23,7 @@ bitcoin-python - Easy-to-use Bitcoin API client
def connect_to_local(filename=None):
"""
"""
Connect to default bitcoin instance owned by this user, on this machine.
Returns a :class:`~mmgen.rpc.connection.BitcoinConnection` object.
@ -31,24 +31,23 @@ def connect_to_local(filename=None):
Arguments:
- `filename`: Path to a configuration file in a non-standard location (optional)
"""
from mmgen.rpc.connection import BitcoinConnection
from mmgen.rpc.config import read_default_config
"""
from mmgen.rpc.connection import BitcoinConnection
from mmgen.rpc.config import read_default_config
cfg = read_default_config(filename)
port = int(cfg.get('rpcport', '18332' if cfg.get('testnet') else '8332'))
rcpuser = cfg.get('rpcuser', '')
cfg = read_default_config(filename)
port = int(cfg.get('rpcport', '18332' if cfg.get('testnet') else '8332'))
rcpuser = cfg.get('rpcuser', '')
return BitcoinConnection(rcpuser, cfg['rpcpassword'], 'localhost', port)
return BitcoinConnection(rcpuser, cfg['rpcpassword'], 'localhost', port)
def connect_to_remote(user, password, host='localhost', port=8332,
use_https=False):
"""
def connect_to_remote(user,password,host='localhost',port=8332,use_https=False):
"""
Connect to remote or alternative local bitcoin client instance.
Returns a :class:`~mmgen.rpc.connection.BitcoinConnection` object.
"""
from mmgen.rpc.connection import BitcoinConnection
"""
from mmgen.rpc.connection import BitcoinConnection
return BitcoinConnection(user, password, host, port, use_https)
return BitcoinConnection(user, password, host, port, use_https)

View file

@ -23,53 +23,53 @@ Utilities for reading bitcoin configuration files.
def read_config_file(filename):
"""
"""
Read a simple ``'='``-delimited config file.
Raises :const:`IOError` if unable to open file, or :const:`ValueError`
if an parse error occurs.
"""
f = open(filename)
try:
cfg = {}
for line in f:
line = line.strip()
if line and not line.startswith("#"):
try:
(key, value) = line.split('=', 1)
cfg[key] = value
except ValueError:
pass # Happens when line has no '=', ignore
finally:
f.close()
return cfg
"""
f = open(filename)
try:
cfg = {}
for line in f:
line = line.strip()
if line and not line.startswith("#"):
try:
(key, value) = line.split('=', 1)
cfg[key] = value
except ValueError:
pass # Happens when line has no '=', ignore
finally:
f.close()
return cfg
def read_default_config(filename=None):
"""
"""
Read bitcoin default configuration from the current user's home directory.
Arguments:
- `filename`: Path to a configuration file in a non-standard location (optional)
"""
if filename is None:
import os
import platform
home = os.getenv("HOME")
if not home:
raise IOError("Home directory not defined, don't know where to look for config file")
"""
if filename is None:
import os
import platform
home = os.getenv("HOME")
if not home:
raise IOError("Home directory not defined, don't know where to look for config file")
if platform.system() == "Darwin":
location = 'Library/Application Support/Bitcoin/bitcoin.conf'
else:
location = '.bitcoin/bitcoin.conf'
filename = os.path.join(home, location)
if platform.system() == "Darwin":
location = 'Library/Application Support/Bitcoin/bitcoin.conf'
else:
location = '.bitcoin/bitcoin.conf'
filename = os.path.join(home, location)
elif filename.startswith("~"):
import os
filename = os.path.expanduser(filename)
elif filename.startswith("~"):
import os
filename = os.path.expanduser(filename)
try:
return read_config_file(filename)
except (IOError, ValueError):
pass # Cannot read config file, ignore
try:
return read_config_file(filename)
except (IOError, ValueError):
pass # Cannot read config file, ignore

View file

@ -32,21 +32,21 @@ from mmgen.rpc.data import (ServerInfo, AccountInfo, AddressInfo, TransactionInf
class BitcoinConnection(object):
"""
A BitcoinConnection object defines a connection to a bitcoin server.
It is a thin wrapper around a JSON-RPC API connection.
A BitcoinConnection object defines a connection to a bitcoin server.
It is a thin wrapper around a JSON-RPC API connection.
Up-to-date for SVN revision 198.
Up-to-date for SVN revision 198.
Arguments to constructor:
Arguments to constructor:
- *user* -- Authenticate as user.
- *password* -- Authentication password.
- *host* -- Bitcoin JSON-RPC host.
- *port* -- Bitcoin JSON-RPC port.
- *user* -- Authenticate as user.
- *password* -- Authentication password.
- *host* -- Bitcoin JSON-RPC host.
- *port* -- Bitcoin JSON-RPC port.
"""
def __init__(self, user, password, host='localhost', port=8332, use_https=False):
"""
Create a new bitcoin server connection.
Create a new bitcoin server connection.
"""
url = 'http{s}://{user}:{password}@{host}:{port}/'.format(
s='s' if use_https else '',
@ -59,8 +59,6 @@ class BitcoinConnection(object):
# importaddress <address> [label] [rescan=true]
def importaddress(self,address,label=None,rescan=True):
"""
"""
try:
# return self.proxy.badmethod(address,label) # DEBUG
return self.proxy.importaddress(address,label,rescan)
@ -78,8 +76,6 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
# sendrawtransaction <hex string> [allowhighfees=false]
def sendrawtransaction(self,tx):
"""
"""
try:
return self.proxy.sendrawtransaction(tx)
except JSONRPCException as e:
@ -87,7 +83,7 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def stop(self):
"""
Stop bitcoin server.
Stop bitcoin server.
"""
try:
self.proxy.stop()
@ -96,7 +92,7 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getblock(self, hash):
"""
Returns information about the given block hash.
Returns information about the given block hash.
"""
try:
return self.proxy.getblock(hash)
@ -105,7 +101,7 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getblockcount(self):
"""
Returns the number of blocks in the longest block chain.
Returns the number of blocks in the longest block chain.
"""
try:
return self.proxy.getblockcount()
@ -114,9 +110,9 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getblockhash(self, index):
"""
Returns hash of block in best-block-chain at index.
Returns hash of block in best-block-chain at index.
:param index: index ob the block
:param index: index ob the block
"""
try:
@ -126,14 +122,14 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getblocknumber(self):
"""
Returns the block number of the latest block in the longest block chain.
Deprecated. Use getblockcount instead.
Returns the block number of the latest block in the longest block chain.
Deprecated. Use getblockcount instead.
"""
return self.getblockcount()
def getconnectioncount(self):
"""
Returns the number of connections to other nodes.
Returns the number of connections to other nodes.
"""
try:
return self.proxy.getconnectioncount()
@ -142,7 +138,7 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getdifficulty(self):
"""
Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
"""
try:
return self.proxy.getdifficulty()
@ -151,8 +147,8 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getgenerate(self):
"""
Returns :const:`True` or :const:`False`, depending on whether
generation is enabled.
Returns :const:`True` or :const:`False`, depending on whether
generation is enabled.
"""
try:
return self.proxy.getgenerate()
@ -161,14 +157,14 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def setgenerate(self, generate, genproclimit=None):
"""
Enable or disable generation (mining) of coins.
Enable or disable generation (mining) of coins.
Arguments:
Arguments:
- *generate* -- is :const:`True` or :const:`False` to turn generation
on or off.
- *genproclimit* -- Number of processors that are used for generation,
-1 is unlimited.
- *generate* -- is :const:`True` or :const:`False` to turn generation
on or off.
- *genproclimit* -- Number of processors that are used for generation,
-1 is unlimited.
"""
try:
@ -181,7 +177,7 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def gethashespersec(self):
"""
Returns a recent hashes per second performance measurement while generating.
Returns a recent hashes per second performance measurement while generating.
"""
try:
return self.proxy.gethashespersec()
@ -190,8 +186,8 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getinfo(self):
"""
Returns an :class:`~mmgen.rpc.data.ServerInfo` object containing
various state info.
Returns an :class:`~mmgen.rpc.data.ServerInfo` object containing
various state info.
"""
try:
return ServerInfo(**self.proxy.getinfo())
@ -200,8 +196,8 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getmininginfo(self):
"""
Returns an :class:`~mmgen.rpc.data.MiningInfo` object containing various
mining state info.
Returns an :class:`~mmgen.rpc.data.MiningInfo` object containing various
mining state info.
"""
try:
return MiningInfo(**self.proxy.getmininginfo())
@ -210,13 +206,13 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getnewaddress(self, account=None):
"""
Returns a new bitcoin address for receiving payments.
Returns a new bitcoin address for receiving payments.
Arguments:
Arguments:
- *account* -- If account is specified (recommended), it is added to the
address book so that payments received with the address will be
credited to it.
- *account* -- If account is specified (recommended), it is added to the
address book so that payments received with the address will be
credited to it.
"""
try:
@ -229,11 +225,11 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getaccountaddress(self, account):
"""
Returns the current bitcoin address for receiving payments to an account.
Returns the current bitcoin address for receiving payments to an account.
Arguments:
Arguments:
- *account* -- Account for which the address should be returned.
- *account* -- Account for which the address should be returned.
"""
try:
@ -243,12 +239,12 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def setaccount(self, bitcoinaddress, account):
"""
Sets the account associated with the given address.
Sets the account associated with the given address.
Arguments:
Arguments:
- *bitcoinaddress* -- Bitcoin address to associate.
- *account* -- Account to associate the address to.
- *bitcoinaddress* -- Bitcoin address to associate.
- *account* -- Account to associate the address to.
"""
try:
@ -258,11 +254,11 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getaccount(self, bitcoinaddress):
"""
Returns the account associated with the given address.
Returns the account associated with the given address.
Arguments:
Arguments:
- *bitcoinaddress* -- Bitcoin address to get account for.
- *bitcoinaddress* -- Bitcoin address to get account for.
"""
try:
return self.proxy.getaccount(bitcoinaddress)
@ -271,11 +267,11 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getaddressesbyaccount(self, account):
"""
Returns the list of addresses for the given account.
Returns the list of addresses for the given account.
Arguments:
Arguments:
- *account* -- Account to get list of addresses for.
- *account* -- Account to get list of addresses for.
"""
try:
return self.proxy.getaddressesbyaccount(account)
@ -284,16 +280,16 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def sendtoaddress(self, bitcoinaddress, amount, comment=None, comment_to=None):
"""
Sends *amount* from the server's available balance to *bitcoinaddress*.
Sends *amount* from the server's available balance to *bitcoinaddress*.
Arguments:
Arguments:
- *bitcoinaddress* -- Bitcoin address to send to.
- *amount* -- Amount to send (float, rounded to the nearest 0.01).
- *minconf* -- Minimum number of confirmations required for transferred
balance.
- *comment* -- Comment for transaction.
- *comment_to* -- Comment for to-address.
- *bitcoinaddress* -- Bitcoin address to send to.
- *amount* -- Amount to send (float, rounded to the nearest 0.01).
- *minconf* -- Minimum number of confirmations required for transferred
balance.
- *comment* -- Comment for transaction.
- *comment_to* -- Comment for to-address.
"""
try:
@ -308,14 +304,14 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getreceivedbyaddress(self, bitcoinaddress, minconf=1):
"""
Returns the total amount received by a bitcoin address in transactions
with at least a certain number of confirmations.
Returns the total amount received by a bitcoin address in transactions
with at least a certain number of confirmations.
Arguments:
Arguments:
- *bitcoinaddress* -- Address to query for total amount.
- *bitcoinaddress* -- Address to query for total amount.
- *minconf* -- Number of confirmations to require, defaults to 1.
- *minconf* -- Number of confirmations to require, defaults to 1.
"""
try:
return self.proxy.getreceivedbyaddress(bitcoinaddress, minconf)
@ -324,13 +320,13 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getreceivedbyaccount(self, account, minconf=1):
"""
Returns the total amount received by addresses with an account in
transactions with at least a certain number of confirmations.
Returns the total amount received by addresses with an account in
transactions with at least a certain number of confirmations.
Arguments:
Arguments:
- *account* -- Account to query for total amount.
- *minconf* -- Number of confirmations to require, defaults to 1.
- *account* -- Account to query for total amount.
- *minconf* -- Number of confirmations to require, defaults to 1.
"""
try:
@ -340,11 +336,11 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def gettransaction(self, txid):
"""
Get detailed information about transaction
Get detailed information about transaction
Arguments:
Arguments:
- *txid* -- Transactiond id for which the info should be returned
- *txid* -- Transactiond id for which the info should be returned
"""
try:
@ -354,12 +350,12 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def getrawtransaction(self, txid, verbose=True):
"""
Get transaction raw info
Get transaction raw info
Arguments:
Arguments:
- *txid* -- Transactiond id for which the info should be returned.
- *verbose* -- If False, return only the "hex" of the transaction.
- *txid* -- Transactiond id for which the info should be returned.
- *verbose* -- If False, return only the "hex" of the transaction.
"""
try:
@ -371,24 +367,24 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def createrawtransaction(self, inputs, outputs):
"""
Creates a raw transaction spending given inputs
(a list of dictionaries, each containing a transaction id and an output
number), sending to given address(es).
Creates a raw transaction spending given inputs
(a list of dictionaries, each containing a transaction id and an output
number), sending to given address(es).
Returns hex-encoded raw transaction.
Returns hex-encoded raw transaction.
Example usage:
>>> conn.createrawtransaction(
[{"txid": "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c",
"vout": 0}],
{"mkZBYBiq6DNoQEKakpMJegyDbw2YiNQnHT":50})
Example usage:
>>> conn.createrawtransaction(
[{"txid": "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c",
"vout": 0}],
{"mkZBYBiq6DNoQEKakpMJegyDbw2YiNQnHT":50})
Arguments:
Arguments:
- *inputs* -- A list of {"txid": txid, "vout": n} dictionaries.
- *outputs* -- A dictionary mapping (public) addresses to the amount
they are to be paid.
- *inputs* -- A list of {"txid": txid, "vout": n} dictionaries.
- *outputs* -- A dictionary mapping (public) addresses to the amount
they are to be paid.
"""
try:
return self.proxy.createrawtransaction(inputs, outputs)
@ -397,22 +393,22 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def signrawtransaction(self, hexstring, previous_transactions=None, private_keys=None):
"""
Sign inputs for raw transaction (serialized, hex-encoded).
Sign inputs for raw transaction (serialized, hex-encoded).
Returns a dictionary with the keys:
"hex": raw transaction with signature(s) (hex-encoded string)
"complete": 1 if transaction has a complete set of signature(s), 0 if not
Returns a dictionary with the keys:
"hex": raw transaction with signature(s) (hex-encoded string)
"complete": 1 if transaction has a complete set of signature(s), 0 if not
Arguments:
Arguments:
- *hexstring* -- A hex string of the transaction to sign.
- *previous_transactions* -- A (possibly empty) list of dictionaries of
the form:
{"txid": txid, "vout": n, "scriptPubKey": hex, "redeemScript": hex},
representing previous transaction outputs that this transaction depends
on but may not yet be in the block chain.
- *private_keys* -- A (possibly empty) list of base58-encoded private
keys that, if given, will be the only keys used to sign the transaction.
- *hexstring* -- A hex string of the transaction to sign.
- *previous_transactions* -- A (possibly empty) list of dictionaries of
the form:
{"txid": txid, "vout": n, "scriptPubKey": hex, "redeemScript": hex},
representing previous transaction outputs that this transaction depends
on but may not yet be in the block chain.
- *private_keys* -- A (possibly empty) list of base58-encoded private
keys that, if given, will be the only keys used to sign the transaction.
"""
try:
return dict(self.proxy.signrawtransaction(hexstring,
@ -422,11 +418,11 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def decoderawtransaction(self, hexstring):
"""
Produces a human-readable JSON object for a raw transaction.
Produces a human-readable JSON object for a raw transaction.
Arguments:
Arguments:
- *hexstring* -- A hex string of the transaction to be decoded.
- *hexstring* -- A hex string of the transaction to be decoded.
"""
try:
return dict(self.proxy.decoderawtransaction(hexstring))
@ -443,16 +439,16 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def listreceivedbyaddress(self, minconf=1, includeempty=False):
"""
Returns a list of addresses.
Returns a list of addresses.
Each address is represented with a
:class:`~mmgen.rpc.data.AddressInfo` object.
Each address is represented with a
:class:`~mmgen.rpc.data.AddressInfo` object.
Arguments:
Arguments:
- *minconf* -- Minimum number of confirmations before payments are included.
- *includeempty* -- Whether to include addresses that haven't received
any payments.
- *minconf* -- Minimum number of confirmations before payments are included.
- *includeempty* -- Whether to include addresses that haven't received
any payments.
"""
try:
@ -463,12 +459,12 @@ ERROR: 'importaddress' not found. Does your bitcoind support watch-only addrs?
def listaccounts(self, minconf=1, includeWatchonly=False, as_dict=False):
"""
Returns a list of account names.
Returns a list of account names.
Arguments:
Arguments:
- *minconf* -- Minimum number of confirmations before payments are included.
- *as_dict* -- Returns a dictionary of account names, with their balance as values.
- *minconf* -- Minimum number of confirmations before payments are included.
- *as_dict* -- Returns a dictionary of account names, with their balance as values.
"""
try:
if as_dict:
@ -488,15 +484,15 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def listreceivedbyaccount(self, minconf=1, includeempty=False):
"""
Returns a list of accounts.
Returns a list of accounts.
Each account is represented with a :class:`~mmgen.rpc.data.AccountInfo` object.
Each account is represented with a :class:`~mmgen.rpc.data.AccountInfo` object.
Arguments:
Arguments:
- *minconf* -- Minimum number of confirmations before payments are included.
- *minconf* -- Minimum number of confirmations before payments are included.
- *includeempty* -- Whether to include addresses that haven't received any payments.
- *includeempty* -- Whether to include addresses that haven't received any payments.
"""
try:
return [AccountInfo(**x) for x in
@ -506,17 +502,17 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def listtransactions(self, account=None, count=10, from_=0, address=None):
"""
Returns a list of the last transactions for an account.
Returns a list of the last transactions for an account.
Each transaction is represented with a :class:`~mmgen.rpc.data.TransactionInfo` object.
Each transaction is represented with a :class:`~mmgen.rpc.data.TransactionInfo` object.
Arguments:
Arguments:
- *account* -- Account to list transactions from. Return transactions from
all accounts if None.
- *count* -- Number of transactions to return.
- *from_* -- Skip the first <from_> transactions.
- *address* -- Receive address to consider
- *account* -- Account to list transactions from. Return transactions from
all accounts if None.
- *count* -- Number of transactions to return.
- *from_* -- Skip the first <from_> transactions.
- *address* -- Receive address to consider
"""
accounts = [account] if account is not None else self.listaccounts(as_dict=True).iterkeys()
try:
@ -528,11 +524,11 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def backupwallet(self, destination):
"""
Safely copies ``wallet.dat`` to *destination*, which can be a directory or a path
with filename.
Safely copies ``wallet.dat`` to *destination*, which can be a directory or a path
with filename.
Arguments:
- *destination* -- directory or path with filename to backup wallet to.
Arguments:
- *destination* -- directory or path with filename to backup wallet to.
"""
try:
@ -542,14 +538,14 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def validateaddress(self, validateaddress):
"""
Validate a bitcoin address and return information for it.
Validate a bitcoin address and return information for it.
The information is represented by a :class:`~mmgen.rpc.data.AddressValidation` object.
The information is represented by a :class:`~mmgen.rpc.data.AddressValidation` object.
Arguments: -- Address to validate.
Arguments: -- Address to validate.
- *validateaddress*
- *validateaddress*
"""
try:
return AddressValidation(**self.proxy.validateaddress(validateaddress))
@ -558,11 +554,11 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def getbalance(self, account=None, minconf=None):
"""
Get the current balance, either for an account or the total server balance.
Get the current balance, either for an account or the total server balance.
Arguments:
- *account* -- If this parameter is specified, returns the balance in the account.
- *minconf* -- Minimum number of confirmations required for transferred balance.
Arguments:
- *account* -- If this parameter is specified, returns the balance in the account.
- *minconf* -- Minimum number of confirmations required for transferred balance.
"""
args = []
@ -577,15 +573,15 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def move(self, fromaccount, toaccount, amount, minconf=1, comment=None):
"""
Move from one account in your wallet to another.
Move from one account in your wallet to another.
Arguments:
Arguments:
- *fromaccount* -- Source account name.
- *toaccount* -- Destination account name.
- *amount* -- Amount to transfer.
- *minconf* -- Minimum number of confirmations required for transferred balance.
- *comment* -- Comment to add to transaction log.
- *fromaccount* -- Source account name.
- *toaccount* -- Destination account name.
- *amount* -- Amount to transfer.
- *minconf* -- Minimum number of confirmations required for transferred balance.
- *comment* -- Comment to add to transaction log.
"""
try:
@ -599,19 +595,19 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def sendfrom(self, fromaccount, tobitcoinaddress, amount, minconf=1, comment=None,
comment_to=None):
"""
Sends amount from account's balance to bitcoinaddress. This method will fail
if there is less than amount bitcoins with minconf confirmations in the account's
balance (unless account is the empty-string-named default account; it
behaves like the sendtoaddress method). Returns transaction ID on success.
Sends amount from account's balance to bitcoinaddress. This method will fail
if there is less than amount bitcoins with minconf confirmations in the account's
balance (unless account is the empty-string-named default account; it
behaves like the sendtoaddress method). Returns transaction ID on success.
Arguments:
Arguments:
- *fromaccount* -- Account to send from.
- *tobitcoinaddress* -- Bitcoin address to send to.
- *amount* -- Amount to send (float, rounded to the nearest 0.01).
- *minconf* -- Minimum number of confirmations required for transferred balance.
- *comment* -- Comment for transaction.
- *comment_to* -- Comment for to-address.
- *fromaccount* -- Account to send from.
- *tobitcoinaddress* -- Bitcoin address to send to.
- *amount* -- Amount to send (float, rounded to the nearest 0.01).
- *minconf* -- Minimum number of confirmations required for transferred balance.
- *comment* -- Comment for transaction.
- *comment_to* -- Comment for to-address.
"""
try:
@ -626,20 +622,20 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def sendmany(self, fromaccount, todict, minconf=1, comment=None):
"""
Sends specified amounts from account's balance to bitcoinaddresses.
This method will fail if there is less than total amount bitcoins with
minconf confirmations in the account's balance (unless account is the
empty-string-named default account; Returns transaction ID on
success.
Sends specified amounts from account's balance to bitcoinaddresses.
This method will fail if there is less than total amount bitcoins with
minconf confirmations in the account's balance (unless account is the
empty-string-named default account; Returns transaction ID on
success.
Arguments:
Arguments:
- *fromaccount* -- Account to send from.
- *todict* -- Dictionary with Bitcoin addresses as keys and amounts as
values.
- *minconf* -- Minimum number of confirmations required for transferred
balance.
- *comment* -- Comment for transaction.
- *fromaccount* -- Account to send from.
- *todict* -- Dictionary with Bitcoin addresses as keys and amounts as
values.
- *minconf* -- Minimum number of confirmations required for transferred
balance.
- *comment* -- Comment for transaction.
"""
try:
@ -652,15 +648,15 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def verifymessage(self, bitcoinaddress, signature, message):
"""
Verifies a signature given the bitcoinaddress used to sign,
the signature itself, and the message that was signed.
Returns :const:`True` if the signature is valid, and :const:`False` if it is invalid.
Verifies a signature given the bitcoinaddress used to sign,
the signature itself, and the message that was signed.
Returns :const:`True` if the signature is valid, and :const:`False` if it is invalid.
Arguments:
Arguments:
- *bitcoinaddress* -- the bitcoinaddress used to sign the message
- *signature* -- the signature to be verified
- *message* -- the message that was originally signed
- *bitcoinaddress* -- the bitcoinaddress used to sign the message
- *signature* -- the signature to be verified
- *message* -- the message that was originally signed
"""
try:
@ -670,15 +666,15 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def getwork(self, data=None):
"""
Get work for remote mining, or submit result.
If data is specified, the server tries to solve the block
using the provided data and returns :const:`True` if it was successful.
If not, the function returns formatted hash data (:class:`~mmgen.rpc.data.WorkItem`)
to work on.
Get work for remote mining, or submit result.
If data is specified, the server tries to solve the block
using the provided data and returns :const:`True` if it was successful.
If not, the function returns formatted hash data (:class:`~mmgen.rpc.data.WorkItem`)
to work on.
Arguments:
Arguments:
- *data* -- Result from remote mining.
- *data* -- Result from remote mining.
"""
try:
@ -692,13 +688,13 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def listunspent(self, minconf=1, maxconf=999999):
"""
Returns a list of unspent transaction inputs in the wallet.
Returns a list of unspent transaction inputs in the wallet.
Arguments:
Arguments:
- *minconf* -- Minimum number of confirmations required to be listed.
- *minconf* -- Minimum number of confirmations required to be listed.
- *maxconf* -- Maximal number of confirmations allowed to be listed.
- *maxconf* -- Maximal number of confirmations allowed to be listed.
"""
@ -717,15 +713,15 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def walletpassphrase(self, passphrase, timeout, dont_raise=False):
"""
Stores the wallet decryption key in memory for <timeout> seconds.
Stores the wallet decryption key in memory for <timeout> seconds.
- *passphrase* -- The wallet passphrase.
- *passphrase* -- The wallet passphrase.
- *timeout* -- Time in seconds to keep the wallet unlocked
(by keeping the passphrase in memory).
- *timeout* -- Time in seconds to keep the wallet unlocked
(by keeping the passphrase in memory).
- *dont_raise* -- instead of raising `~mmgen.rpc.exceptions.WalletPassphraseIncorrect`
return False.
- *dont_raise* -- instead of raising `~mmgen.rpc.exceptions.WalletPassphraseIncorrect`
return False.
"""
try:
self.proxy.walletpassphrase(passphrase, timeout)
@ -741,10 +737,10 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def walletlock(self):
"""
Removes the wallet encryption key from memory, locking the wallet.
After calling this method, you will need to call walletpassphrase
again before being able to call any methods which require the wallet
to be unlocked.
Removes the wallet encryption key from memory, locking the wallet.
After calling this method, you will need to call walletpassphrase
again before being able to call any methods which require the wallet
to be unlocked.
"""
try:
return self.proxy.walletlock()
@ -753,12 +749,12 @@ ERROR: 'listaccounts' failed. Does your bitcoind support watch-only addresses?
def walletpassphrasechange(self, oldpassphrase, newpassphrase, dont_raise=False):
"""
Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.
Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.
Arguments:
Arguments:
- *dont_raise* -- instead of raising
`~mmgen.rpc.exceptions.WalletPassphraseIncorrect` return False.
- *dont_raise* -- instead of raising
`~mmgen.rpc.exceptions.WalletPassphraseIncorrect` return False.
"""
try:
self.proxy.walletpassphrasechange(oldpassphrase, newpassphrase)

View file

@ -23,181 +23,181 @@ Exception definitions.
class BitcoinException(Exception):
"""
"""
Base class for exceptions received from Bitcoin server.
- *code* -- Error code from ``bitcoind``.
"""
# Standard JSON-RPC 2.0 errors
INVALID_REQUEST = -32600,
METHOD_NOT_FOUND = -32601,
INVALID_PARAMS = -32602,
INTERNAL_ERROR = -32603,
PARSE_ERROR = -32700,
"""
# Standard JSON-RPC 2.0 errors
INVALID_REQUEST = -32600,
METHOD_NOT_FOUND = -32601,
INVALID_PARAMS = -32602,
INTERNAL_ERROR = -32603,
PARSE_ERROR = -32700,
# General application defined errors
MISC_ERROR = -1 # std::exception thrown in command handling
FORBIDDEN_BY_SAFE_MODE = -2 # Server is in safe mode, and command is not allowed in safe mode
TYPE_ERROR = -3 # Unexpected type was passed as parameter
INVALID_ADDRESS_OR_KEY = -5 # Invalid address or key
OUT_OF_MEMORY = -7 # Ran out of memory during operation
INVALID_PARAMETER = -8 # Invalid, missing or duplicate parameter
DATABASE_ERROR = -20 # Database error
DESERIALIZATION_ERROR = -22 # Error parsing or validating structure in raw format
# General application defined errors
MISC_ERROR = -1 # std::exception thrown in command handling
FORBIDDEN_BY_SAFE_MODE = -2 # Server is in safe mode, and command is not allowed in safe mode
TYPE_ERROR = -3 # Unexpected type was passed as parameter
INVALID_ADDRESS_OR_KEY = -5 # Invalid address or key
OUT_OF_MEMORY = -7 # Ran out of memory during operation
INVALID_PARAMETER = -8 # Invalid, missing or duplicate parameter
DATABASE_ERROR = -20 # Database error
DESERIALIZATION_ERROR = -22 # Error parsing or validating structure in raw format
# P2P client errors
CLIENT_NOT_CONNECTED = -9 # Bitcoin is not connected
CLIENT_IN_INITIAL_DOWNLOAD = -10 # Still downloading initial blocks
# P2P client errors
CLIENT_NOT_CONNECTED = -9 # Bitcoin is not connected
CLIENT_IN_INITIAL_DOWNLOAD = -10 # Still downloading initial blocks
# Wallet errors
WALLET_ERROR = -4 # Unspecified problem with wallet (key not found etc.)
WALLET_INSUFFICIENT_FUNDS = -6 # Not enough funds in wallet or account
WALLET_INVALID_ACCOUNT_NAME = -11 # Invalid account name
WALLET_KEYPOOL_RAN_OUT = -12 # Keypool ran out, call keypoolrefill first
WALLET_UNLOCK_NEEDED = -13 # Enter the wallet passphrase with walletpassphrase first
WALLET_PASSPHRASE_INCORRECT = -14 # The wallet passphrase entered was incorrect
WALLET_WRONG_ENC_STATE = -15 # Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
WALLET_ENCRYPTION_FAILED = -16 # Failed to encrypt the wallet
WALLET_ALREADY_UNLOCKED = -17 # Wallet is already unlocked
# Wallet errors
WALLET_ERROR = -4 # Unspecified problem with wallet (key not found etc.)
WALLET_INSUFFICIENT_FUNDS = -6 # Not enough funds in wallet or account
WALLET_INVALID_ACCOUNT_NAME = -11 # Invalid account name
WALLET_KEYPOOL_RAN_OUT = -12 # Keypool ran out, call keypoolrefill first
WALLET_UNLOCK_NEEDED = -13 # Enter the wallet passphrase with walletpassphrase first
WALLET_PASSPHRASE_INCORRECT = -14 # The wallet passphrase entered was incorrect
WALLET_WRONG_ENC_STATE = -15 # Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
WALLET_ENCRYPTION_FAILED = -16 # Failed to encrypt the wallet
WALLET_ALREADY_UNLOCKED = -17 # Wallet is already unlocked
def __init__(self, error):
Exception.__init__(self, error['message'])
self.code = error['code']
def __init__(self, error):
Exception.__init__(self, error['message'])
self.code = error['code']
##### General application defined errors
class SafeMode(BitcoinException):
"""
"""
Operation denied in safe mode (run ``bitcoind`` with ``-disablesafemode``).
"""
"""
class JSONTypeError(BitcoinException):
"""
"""
Unexpected type was passed as parameter
"""
"""
InvalidAmount = JSONTypeError # Backwards compatibility
class InvalidAddressOrKey(BitcoinException):
"""
"""
Invalid address or key.
"""
"""
InvalidTransactionID = InvalidAddressOrKey # Backwards compatibility
class OutOfMemory(BitcoinException):
"""
"""
Out of memory during operation.
"""
"""
class InvalidParameter(BitcoinException):
"""
"""
Invalid parameter provided to RPC call.
"""
"""
##### Client errors
class ClientException(BitcoinException):
"""
"""
P2P network error.
This exception is never raised but functions as a superclass
for other P2P client exceptions.
"""
"""
class NotConnected(ClientException):
"""
"""
Not connected to any peers.
"""
"""
class DownloadingBlocks(ClientException):
"""
"""
Client is still downloading blocks.
"""
"""
##### Wallet errors
class WalletError(BitcoinException):
"""
"""
Unspecified problem with wallet (key not found etc.)
"""
"""
SendError = WalletError # Backwards compatibility
class InsufficientFunds(WalletError):
"""
"""
Insufficient funds to complete transaction in wallet or account
"""
"""
class InvalidAccountName(WalletError):
"""
"""
Invalid account name
"""
"""
class KeypoolRanOut(WalletError):
"""
"""
Keypool ran out, call keypoolrefill first
"""
"""
class WalletUnlockNeeded(WalletError):
"""
"""
Enter the wallet passphrase with walletpassphrase first
"""
"""
class WalletPassphraseIncorrect(WalletError):
"""
"""
The wallet passphrase entered was incorrect
"""
"""
class WalletWrongEncState(WalletError):
"""
"""
Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
"""
"""
class WalletEncryptionFailed(WalletError):
"""
"""
Failed to encrypt the wallet
"""
"""
class WalletAlreadyUnlocked(WalletError):
"""
"""
Wallet is already unlocked
"""
"""
# For convenience, we define more specific exception classes
# for the more common errors.
_exception_map = {
BitcoinException.FORBIDDEN_BY_SAFE_MODE: SafeMode,
BitcoinException.TYPE_ERROR: JSONTypeError,
BitcoinException.WALLET_ERROR: WalletError,
BitcoinException.INVALID_ADDRESS_OR_KEY: InvalidAddressOrKey,
BitcoinException.WALLET_INSUFFICIENT_FUNDS: InsufficientFunds,
BitcoinException.OUT_OF_MEMORY: OutOfMemory,
BitcoinException.INVALID_PARAMETER: InvalidParameter,
BitcoinException.CLIENT_NOT_CONNECTED: NotConnected,
BitcoinException.CLIENT_IN_INITIAL_DOWNLOAD: DownloadingBlocks,
BitcoinException.WALLET_INSUFFICIENT_FUNDS: InsufficientFunds,
BitcoinException.WALLET_INVALID_ACCOUNT_NAME: InvalidAccountName,
BitcoinException.WALLET_KEYPOOL_RAN_OUT: KeypoolRanOut,
BitcoinException.WALLET_UNLOCK_NEEDED: WalletUnlockNeeded,
BitcoinException.WALLET_PASSPHRASE_INCORRECT: WalletPassphraseIncorrect,
BitcoinException.WALLET_WRONG_ENC_STATE: WalletWrongEncState,
BitcoinException.WALLET_ENCRYPTION_FAILED: WalletEncryptionFailed,
BitcoinException.WALLET_ALREADY_UNLOCKED: WalletAlreadyUnlocked,
BitcoinException.FORBIDDEN_BY_SAFE_MODE: SafeMode,
BitcoinException.TYPE_ERROR: JSONTypeError,
BitcoinException.WALLET_ERROR: WalletError,
BitcoinException.INVALID_ADDRESS_OR_KEY: InvalidAddressOrKey,
BitcoinException.WALLET_INSUFFICIENT_FUNDS: InsufficientFunds,
BitcoinException.OUT_OF_MEMORY: OutOfMemory,
BitcoinException.INVALID_PARAMETER: InvalidParameter,
BitcoinException.CLIENT_NOT_CONNECTED: NotConnected,
BitcoinException.CLIENT_IN_INITIAL_DOWNLOAD: DownloadingBlocks,
BitcoinException.WALLET_INSUFFICIENT_FUNDS: InsufficientFunds,
BitcoinException.WALLET_INVALID_ACCOUNT_NAME: InvalidAccountName,
BitcoinException.WALLET_KEYPOOL_RAN_OUT: KeypoolRanOut,
BitcoinException.WALLET_UNLOCK_NEEDED: WalletUnlockNeeded,
BitcoinException.WALLET_PASSPHRASE_INCORRECT: WalletPassphraseIncorrect,
BitcoinException.WALLET_WRONG_ENC_STATE: WalletWrongEncState,
BitcoinException.WALLET_ENCRYPTION_FAILED: WalletEncryptionFailed,
BitcoinException.WALLET_ALREADY_UNLOCKED: WalletAlreadyUnlocked,
}
def _wrap_exception(error):
"""
"""
Convert a JSON error object to a more specific Bitcoin exception.
"""
return _exception_map.get(error['code'], BitcoinException)(error)
"""
return _exception_map.get(error['code'], BitcoinException)(error)

View file

@ -10,7 +10,7 @@
ServiceProxy class:
- HTTP connections persist for the life of the AuthServiceProxy object
(if server supports HTTP/1.1)
(if server supports HTTP/1.1)
- sends protocol 'version', per JSON-RPC 1.1
- sends proper, incrementing 'id'
- sends Basic HTTP authentication headers

View file

@ -22,28 +22,28 @@ from copy import copy
class DStruct(object):
"""
"""
Simple dynamic structure, like :const:`collections.namedtuple` but more flexible
(and less memory-efficient)
"""
# Default arguments. Defaults are *shallow copied*, to allow defaults such as [].
_fields = []
_defaults = {}
"""
# Default arguments. Defaults are *shallow copied*, to allow defaults such as [].
_fields = []
_defaults = {}
def __init__(self, *args_t, **args_d):
# order
if len(args_t) > len(self._fields):
raise TypeError("Number of arguments is larger than of predefined fields")
# Copy default values
for (k, v) in self._defaults.iteritems():
self.__dict__[k] = copy(v)
# Set pass by value arguments
self.__dict__.update(zip(self._fields, args_t))
# dict
self.__dict__.update(args_d)
def __init__(self, *args_t, **args_d):
# order
if len(args_t) > len(self._fields):
raise TypeError("Number of arguments is larger than of predefined fields")
# Copy default values
for (k, v) in self._defaults.iteritems():
self.__dict__[k] = copy(v)
# Set pass by value arguments
self.__dict__.update(zip(self._fields, args_t))
# dict
self.__dict__.update(args_d)
def __repr__(self):
return '{module}.{classname}({slots})'.format(
module=self.__class__.__module__, classname=self.__class__.__name__,
slots=", ".join('{k}={v!r}'.format(k=k, v=v) for k, v in
self.__dict__.iteritems()))
def __repr__(self):
return '{module}.{classname}({slots})'.format(
module=self.__class__.__module__, classname=self.__class__.__name__,
slots=", ".join('{k}={v!r}'.format(k=k, v=v) for k, v in
self.__dict__.iteritems()))

File diff suppressed because it is too large Load diff

View file

@ -62,6 +62,21 @@ def process_opts(argv,opts_data,short_opts,long_opts):
opt[1:]+":")][:-1].replace("-","_")] = arg
else: assert False, "Invalid option"
if 'sets' in opts_data:
for o_in,v_in,o_out,v_out in opts_data['sets']:
if o_in in opts:
v = opts[o_in]
if (v and v_in == bool) or v == v_in:
if o_out in opts and opts[o_out] != v_out:
sys.stderr.write(
"Option conflict:\n --%s=%s, with\n --%s=%s\n" % (
o_out.replace("_","-"),opts[o_out],
o_in.replace("_","-"),opts[o_in]
))
sys.exit(1)
else:
opts[o_out] = v_out
return opts,args

View file

@ -77,14 +77,14 @@ def ok_or_die(val,chk_func,s,skip_ok=False):
try: ret = chk_func(val)
except: ret = False
if ret:
if not skip_ok: ok()
if not skip_ok: ok()
else:
msg(red("Returned value '%s' is not a %s" % (val,s)))
sys.exit(3)
def cmp_or_die(s,t,skip_ok=False):
if s == t:
if not skip_ok: ok()
if not skip_ok: ok()
else:
sys.stderr.write(red(
"ERROR: recoded data:\n%s\ndiffers from original data:\n%s\n" %

View file

@ -439,19 +439,17 @@ def add_label(mmaddr,label,remove=False):
check_addr_label(label) # Exits on failure
c = connect_to_bitcoind()
from mmgen.addr import AddrInfoList
ail = AddrInfoList(bitcoind_connection=c)
btcaddr = ""
sid,idx = mmaddr.split(":")
if sid in ail.seed_ids():
btcaddr = ail.addrinfo(sid).btcaddr(int(idx))
from mmgen.addr import AddrInfoList
btcaddr = AddrInfoList(bitcoind_connection=c).mmaddr2btcaddr(mmaddr)
if not btcaddr:
die(1,"{pnm} address {a} not found in tracking wallet".format(
pnm=pnm,a=mmaddr))
try:
c.importaddress(btcaddr," ".join((mmaddr,label)),rescan=False)
l = " " + label if label else ""
c.importaddress(btcaddr,mmaddr+l,rescan=False)
except:
die(1,"Unable to add label")
@ -508,7 +506,7 @@ def encrypt(infile,outfile="",hash_preset=""):
data = get_data_from_file(infile,"data for encryption")
enc_d = mmgen_encrypt(data,"user data",hash_preset)
if outfile == '-':
write_to_stdout(enc_d,"encrypted data",confirm=True)
write_to_stdout(enc_d,"encrypted data")
else:
if not outfile:
outfile = os.path.basename(infile) + "." + g.mmenc_ext
@ -522,7 +520,7 @@ def decrypt(infile,outfile="",hash_preset=""):
if dec_d: break
msg("Trying again...")
if outfile == '-':
write_to_stdout(dec_d,"decrypted data",confirm=not opt.quiet)
write_to_stdout(dec_d,"decrypted data",ask_terminal=not opt.quiet)
else:
if not outfile:
outfile = os.path.basename(infile)

View file

@ -46,8 +46,7 @@ def normalize_btc_amt(amt):
msg("%s: Invalid amount" % amt)
return False
if opt.debug:
Msg("Decimal(amt): %s\nAs tuple: %s" % (amt,repr(ret.as_tuple())))
dmsg("Decimal(amt): %s\nAs tuple: %s" % (amt,repr(ret.as_tuple())))
if ret.as_tuple()[-1] < -8:
msg("%s: Too many decimal places in amount" % amt)

View file

@ -42,10 +42,10 @@ def msg_r(s): sys.stderr.write(s)
def Msg(s): sys.stdout.write(s + "\n")
def Msg_r(s): sys.stdout.write(s)
def msgred(s): sys.stderr.write(red(s+"\n"))
def msgrepr(*args):
def mmsg(*args):
for d in args:
sys.stdout.write(repr(d)+"\n")
def msgrepr_exit(*args):
def mdie(*args):
for d in args:
sys.stdout.write(repr(d)+"\n")
sys.exit()
@ -55,20 +55,8 @@ def die(ev,s):
def Die(ev,s):
sys.stdout.write(s+"\n"); sys.exit(ev)
def fmt_type(x): return "%s" % str(type(x)).split("'")[1]
import opt
def fmt_code_to_sstype(fmt_code):
for e in g.wallet_fmt_codes:
if fmt_code in e: return e[0]
die(2,"'%s': unrecognized format code" % fmt_code)
def format_fmt_codes():
return "".join(
["%-20s " % (e[0]+":") + ",".join(e[1:]) + "\n"
for e in g.wallet_fmt_codes])
def qmsg(s,alt=False):
if opt.quiet:
if alt != False: sys.stderr.write(alt + "\n")
@ -87,7 +75,10 @@ def Vmsg(s):
def Vmsg_r(s):
if opt.verbose: sys.stdout.write(s)
def suf(arg,what):
def dmsg(s):
if opt.debug: sys.stdout.write(s + "\n")
def suf(arg,suf_type):
t = type(arg)
if t == int:
n = arg
@ -97,9 +88,9 @@ def suf(arg,what):
msg("%s: invalid parameter" % arg)
return ""
if what in "a":
if suf_type in ("a","es"):
return "" if n == 1 else "es"
if what in "k":
if suf_type in ("k","s"):
return "" if n == 1 else "s"
def get_extension(f):
@ -125,10 +116,14 @@ def splitN(s,n,sep=None): # always return an n-element list
def split2(s,sep=None): return splitN(s,2,sep) # always return a 2-element list
def split3(s,sep=None): return splitN(s,3,sep) # always return a 3-element list
def split_into_columns(col_wid,s):
def split_into_cols(col_wid,s):
return " ".join([s[col_wid*i:col_wid*(i+1)]
for i in range(len(s)/col_wid+1)]).rstrip()
def capfirst(s):
if len(s) == 0: return s
return s[0].upper() + (s[1:] if len(s) > 1 else "")
def make_timestamp():
tv = time.gmtime(time.time())[:6]
return "{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}".format(*tv)
@ -166,6 +161,13 @@ def is_utf8(s):
def match_ext(addr,ext):
return addr.split(".")[-1] == ext
def file_exists(f):
try:
os.stat(f)
return True
except:
return False
import opt as opt
def get_from_brain_opt_params():
@ -186,7 +188,11 @@ def pretty_hexdump(data,gw=2,cols=8,line_nums=False):
def decode_pretty_hexdump(data):
from string import hexdigits
lines = [re.sub('^['+hexdigits+']+:\s+','',l) for l in data.split("\n")]
return unhexlify("".join(("".join(lines).split())))
try:
return unhexlify("".join(("".join(lines).split())))
except:
msg("Data not in hexdump format")
return False
def get_hash_params(hash_preset):
if hash_preset in g.hash_presets:
@ -195,22 +201,25 @@ def get_hash_params(hash_preset):
msg("%s: invalid 'hash_preset' value" % hash_preset)
sys.exit(3)
def compare_chksums(chk1, desc1, chk2, desc2, die=True):
def compare_chksums(chk1, desc1, chk2, desc2, hdr="", die_on_fail=False):
if not chk1 == chk2:
if die:
die(3,"Checksum error: %s checksum (%s) doesn't match %s checksum (%s)"
% (desc2,chk2,desc1,chk1))
else: return False
m = "%s ERROR: %s checksum (%s) doesn't match %s checksum (%s)"\
% ((hdr+":\n " if hdr else "CHECKSUM"),desc2,chk2,desc1,chk1)
if die_on_fail:
die(3,m)
else:
msg(m)
return False
vmsg("%s checksum OK (%s)" % (desc1.capitalize(),chk1))
vmsg("%s checksum OK (%s)" % (capfirst(desc1),chk1))
return True
def compare_or_die(val1, desc1, val2, desc2):
def compare_or_die(val1, desc1, val2, desc2, e="Error"):
if cmp(val1,val2):
die(3,"Error: %s (%s) doesn't match %s (%s)"
% (desc2,val2,desc1,val1))
vmsg("%s OK (%s)" % (desc2.capitalize(),val2))
die(3,"%s: %s (%s) doesn't match %s (%s)"
% (e,desc2,val2,desc1,val1))
dmsg("%s OK (%s)" % (capfirst(desc2),val2))
return True
def get_default_wordlist():
@ -224,8 +233,8 @@ def open_file_or_exit(filename,mode):
try:
f = open(filename, mode)
except:
what = "reading" if 'r' in mode else "writing"
msg("Unable to open file '%s' for %s" % (filename,what))
op = "reading" if 'r' in mode else "writing"
msg("Unable to open file '%s' for %s" % (filename,op))
sys.exit(2)
return f
@ -307,9 +316,9 @@ def parse_addr_idxs(arg,sep=","):
return sorted(set(ret))
def get_new_passphrase(what,passchg=False):
def get_new_passphrase(desc,passchg=False):
w = "{}passphrase for {}".format("new " if passchg else "", what)
w = "{}passphrase for {}".format("new " if passchg else "", desc)
if opt.passwd_file:
pw = " ".join(_get_words_from_file(opt.passwd_file,w))
elif opt.echo_passphrase:
@ -318,7 +327,7 @@ def get_new_passphrase(what,passchg=False):
for i in range(g.passwd_max_tries):
pw = " ".join(_get_words_from_user("Enter {}: ".format(w)))
pw2 = " ".join(_get_words_from_user("Repeat passphrase: "))
if opt.debug: Msg("Passphrases: [%s] [%s]" % (pw,pw2))
dmsg("Passphrases: [%s] [%s]" % (pw,pw2))
if pw == pw2:
vmsg("Passphrases match"); break
else: msg("Passphrases do not match. Try again.")
@ -332,27 +341,21 @@ def get_new_passphrase(what,passchg=False):
def confirm_or_exit(message, question, expect="YES"):
if not confirm_or_false(message, question, expect):
msg("Exiting at user request")
sys.exit(2)
def confirm_or_false(message, question, expect="YES"):
m = message.strip()
if m: msg(m)
conf_msg = "Type uppercase '%s' to confirm: " % expect
a = question+" " if question[0].isupper() else \
"Are you sure you want to %s?\n" % question
b = "Type uppercase '%s' to confirm: " % expect
p = question+" "+conf_msg if question[0].isupper() else \
"Are you sure you want to %s?\n%s" % (question,conf_msg)
vmsg("")
return my_raw_input(p).strip() == expect
if my_raw_input(a+b).strip() != expect:
die(2,"Exiting at user request")
def write_to_stdout(data, what, confirm=True):
if sys.stdout.isatty() and confirm:
confirm_or_exit("",'output {} to screen'.format(what))
def write_to_stdout(data, desc, ask_terminal=True):
if sys.stdout.isatty() and ask_terminal:
confirm_or_exit("",'output {} to screen'.format(desc))
elif not sys.stdout.isatty():
try:
of = os.readlink("/proc/%d/fd/1" % os.getpid())
@ -363,8 +366,83 @@ def write_to_stdout(data, what, confirm=True):
msg("Redirecting output to file")
sys.stdout.write(data)
# New function
def write_data_to_file(
outfile,
data,
desc="data",
ask_write=False,
ask_write_prompt="",
ask_write_default_yes=False,
ask_overwrite=True,
ask_tty=True,
no_tty=False,
silent=False
):
if opt.stdout or not sys.stdout.isatty():
qmsg("Output to STDOUT requested")
write_ok = False
if sys.stdout.isatty():
if no_tty:
die(2,"Printing %s to screen is not allowed" % desc)
if ask_tty:
confirm_or_exit("",'output %s to screen' % desc)
else:
try: of = os.readlink("/proc/%d/fd/1" % os.getpid()) # Linux
except: of = None # Windows
def write_to_file(outfile,data,what="data",confirm_overwrite=False,verbose=False,exit_on_error=True,silent=False):
if of:
if of[:5] == "pipe:":
if no_tty:
die(2,"Writing %s to pipe is not allowed" % desc)
if ask_tty:
confirm_or_exit("",'output %s to pipe' % desc)
msg("")
of2,pd = os.path.relpath(of),os.path.pardir
msg("Redirecting output to file '%s'" %
(of if of2[:len(pd)] == pd else of2))
else:
msg("Redirecting output to file")
sys.stdout.write(data)
else:
if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
if ask_write:
if not keypress_confirm(ask_write_prompt,
default_yes=ask_write_default_yes):
die(1,"Exiting at user request")
hush = False
if file_exists(outfile):
if ask_overwrite:
q = "File '%s' already exists\nOverwrite?" % outfile
confirm_or_exit("",q)
if not silent: msg("Overwriting file '%s'" % outfile)
hush = True
f = open_file_or_exit(outfile,'wb')
try:
f.write(data)
except:
if not silent: msg("Failed to write %s to file '%s'" % (desc,outfile))
sys.exit(2)
f.close
if not hush:
msg("%s written to file '%s'" % (capfirst(desc),outfile))
return True
def write_to_file(
outfile,
data,
desc="data",
confirm_overwrite=False,
verbose=False,
silent=False
):
if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
@ -373,12 +451,7 @@ def write_to_file(outfile,data,what="data",confirm_overwrite=False,verbose=False
else:
if confirm_overwrite:
q = "File '%s' already exists\nOverwrite?" % outfile
if exit_on_error:
confirm_or_exit("",q)
else:
if not confirm_or_false("",q):
msg("Not overwriting file at user request")
return False
confirm_or_exit("",q)
else:
if not silent: msg("Overwriting file '%s'" % outfile)
@ -386,20 +459,20 @@ def write_to_file(outfile,data,what="data",confirm_overwrite=False,verbose=False
try:
f.write(data)
except:
if not silent: msg("Failed to write %s to file '%s'" % (what,outfile))
if not silent: msg("Failed to write %s to file '%s'" % (desc,outfile))
sys.exit(2)
f.close
if verbose: msg("%s written to file '%s'" % (what.capitalize(),outfile))
if verbose: msg("%s written to file '%s'" % (capfirst(desc),outfile))
return True
def write_to_file_or_stdout(outfile, data, what="data"):
def write_to_file_or_stdout(outfile, data, desc="data"):
if opt.stdout or not sys.stdout.isatty():
write_to_stdout(data, what, confirm=True)
write_to_stdout(data, desc)
else:
write_to_file(outfile,data,what,not opt.quiet,True)
write_to_file(outfile,data,desc,not opt.quiet,True)
from mmgen.bitcoin import b58decode_pad,b58encode_pad
@ -437,7 +510,7 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed):
seed_len = str(len(seed)*8)
pw_status = "NE" if len(passwd) else "E"
hash_preset = opt.hash_preset
label = opt.label if opt.label else "No Label"
label = opt.label or "No Label"
metadata = seed_id.lower(),key_id.lower(),seed_len,\
pw_status,make_timestamp()
sf = b58encode_pad(salt)
@ -447,8 +520,8 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed):
label,
"{} {} {} {} {}".format(*metadata),
"{}: {} {} {}".format(hash_preset,*get_hash_params(hash_preset)),
"{} {}".format(make_chksum_6(sf), split_into_columns(4,sf)),
"{} {}".format(make_chksum_6(esf), split_into_columns(4,esf))
"{} {}".format(make_chksum_6(sf), split_into_cols(4,sf)),
"{} {}".format(make_chksum_6(esf), split_into_cols(4,esf))
)
chk = make_chksum_6(" ".join(lines))
@ -465,18 +538,18 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed):
def _check_mmseed_format(words):
valid = False
what = "%s data" % g.seed_ext
desc = "%s data" % g.seed_ext
try:
chklen = len(words[0])
except:
return False
if len(words) < 3 or len(words) > 12:
msg("Invalid data length (%s) in %s" % (len(words),what))
msg("Invalid data length (%s) in %s" % (len(words),desc))
elif not is_hexstring(words[0]):
msg("Invalid format of checksum '%s' in %s"%(words[0], what))
msg("Invalid format of checksum '%s' in %s"%(words[0], desc))
elif chklen != 6:
msg("Incorrect length of checksum (%s) in %s" % (chklen,what))
msg("Incorrect length of checksum (%s) in %s" % (chklen,desc))
else: valid = True
return valid
@ -484,19 +557,19 @@ def _check_mmseed_format(words):
def _check_wallet_format(infile, lines):
what = "wallet file '%s'" % infile
desc = "wallet file '%s'" % infile
valid = False
chklen = len(lines[0])
if len(lines) != 6:
vmsg("Invalid number of lines (%s) in %s" % (len(lines),what))
vmsg("Invalid number of lines (%s) in %s" % (len(lines),desc))
elif chklen != 6:
vmsg("Incorrect length of Master checksum (%s) in %s" % (chklen,what))
vmsg("Incorrect length of Master checksum (%s) in %s" % (chklen,desc))
elif not is_hexstring(lines[0]):
vmsg("Invalid format of Master checksum '%s' in %s"%(lines[0], what))
vmsg("Invalid format of Master checksum '%s' in %s"%(lines[0], desc))
else: valid = True
if valid == False:
msg("Invalid %s" % what)
msg("Invalid %s" % desc)
sys.exit(2)
@ -506,8 +579,7 @@ def _check_chksum_6(chk,val,desc,infile):
msg("%s checksum incorrect in file '%s'!" % (desc,infile))
msg("Checksum: %s. Computed value: %s" % (chk,comp_chk))
sys.exit(2)
elif opt.debug:
Msg("%s checksum passed: %s" % (desc.capitalize(),chk))
dmsg("%s checksum passed: %s" % (capfirst(desc),chk))
def get_data_from_wallet(infile,silent=False):
@ -557,23 +629,23 @@ def get_data_from_wallet(infile,silent=False):
def _get_words_from_user(prompt):
# split() also strips
words = my_raw_input(prompt, echo=opt.echo_passphrase).split()
if opt.debug: Msg("Sanitized input: [%s]" % " ".join(words))
dmsg("Sanitized input: [%s]" % " ".join(words))
return words
def _get_words_from_file(infile,what):
qmsg("Getting %s from file '%s'" % (what,infile))
def _get_words_from_file(infile,desc):
qmsg("Getting %s from file '%s'" % (desc,infile))
f = open_file_or_exit(infile, 'r')
# split() also strips
words = f.read().split()
f.close()
if opt.debug: Msg("Sanitized input: [%s]" % " ".join(words))
dmsg("Sanitized input: [%s]" % " ".join(words))
return words
def get_words(infile,what,prompt):
def get_words(infile,desc,prompt):
if infile:
return _get_words_from_file(infile,what)
return _get_words_from_file(infile,desc)
else:
return _get_words_from_user(prompt)
@ -586,24 +658,24 @@ def remove_comments(lines):
if i: ret.append(i)
return ret
def get_lines_from_file(infile,what="",trim_comments=False):
if what != "":
qmsg("Getting %s from file '%s'" % (what,infile))
def get_lines_from_file(infile,desc="",trim_comments=False):
if desc != "":
qmsg("Getting %s from file '%s'" % (desc,infile))
f = open_file_or_exit(infile,'r')
lines = f.read().splitlines()
f.close()
return remove_comments(lines) if trim_comments else lines
def get_data_from_user(what="data",silent=False):
data = my_raw_input("Enter %s: " % what, echo=opt.echo_passphrase)
if opt.debug: Msg("User input: [%s]" % data)
def get_data_from_user(desc="data",silent=False):
data = my_raw_input("Enter %s: " % desc, echo=opt.echo_passphrase)
dmsg("User input: [%s]" % data)
return data
def get_data_from_file(infile,what="data",dash=False,silent=False):
def get_data_from_file(infile,desc="data",dash=False,silent=False):
if dash and infile == "-": return sys.stdin.read()
if not silent:
qmsg("Getting %s from file '%s'" % (what,infile))
qmsg("Getting %s from file '%s'" % (desc,infile))
f = open_file_or_exit(infile,'rb')
data = f.read()
f.close()
@ -622,7 +694,7 @@ def get_seed_from_seed_data(words):
chk = make_chksum_6(seed_b58)
vmsg_r("Validating %s checksum..." % g.seed_ext)
if compare_chksums(chk, "seed", stored_chk, "input",die=False):
if compare_chksums(chk, "seed", stored_chk, "input"):
seed = b58decode_pad(seed_b58)
if seed == False:
msg("Invalid b58 number: %s" % val)
@ -645,9 +717,8 @@ def mark_passwd_file_as_used():
passwd_file_used = True
def get_mmgen_passphrase(prompt_info,passchg=False):
prompt = "Enter {}passphrase for {}: ".format(
"old " if passchg else "",prompt_info)
def get_mmgen_passphrase(desc,passchg=False):
prompt ="Enter {}passphrase for {}: ".format("old " if passchg else "",desc)
if opt.passwd_file:
mark_passwd_file_as_used()
return " ".join(_get_words_from_file(opt.passwd_file,"passphrase"))
@ -674,17 +745,18 @@ def check_data_fits_file_at_offset(fname,offset,dlen,action):
fsize = os.stat(fname).st_size
if fsize < offset + dlen:
m = "Destination" if action == "write" else "Input"
msg(
"Destination file has length %s, too short to %s %s bytes of data at offset %s"
% (fsize,action,dlen,offset))
"%s file has length %s, too short to %s %s bytes of data at offset %s"
% (m,fsize,action,dlen,offset))
sys.exit(1)
from mmgen.term import kb_hold_protect,get_char
def get_hash_preset_from_user(hp=g.hash_preset,what="data"):
def get_hash_preset_from_user(hp=g.hash_preset,desc="data"):
p = """Enter hash preset for %s,
or hit ENTER to accept the default value ('%s'): """ % (what,hp)
or hit ENTER to accept the default value ('%s'): """ % (desc,hp)
while True:
ret = my_raw_input(p)
if ret:
@ -751,17 +823,18 @@ def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
def do_license_msg(immed=False):
from mmgen.license import gpl
import mmgen.license as gpl
if opt.quiet or g.no_license: return
msg(gpl['warning'])
prompt = "%s " % gpl['prompt'].strip()
p = "Press 'w' for conditions and warranty info, or 'c' to continue:"
msg(gpl.warning)
prompt = "%s " % p.strip()
while True:
reply = get_char(prompt, immed_chars="wc" if immed else "")
if reply == 'w':
from mmgen.term import do_pager
do_pager(gpl['conditions'])
do_pager(gpl.conditions)
elif reply == 'c':
msg(""); break
else:

View file

@ -59,6 +59,7 @@ setup(
'mmgen.main_txsend',
'mmgen.main_txsign',
'mmgen.main_walletchk',
'mmgen.main_walletconv',
'mmgen.main_walletgen',
'mmgen.share.__init__',
@ -83,6 +84,7 @@ setup(
'mmgen-addrimport',
'mmgen-passchg',
'mmgen-walletchk',
'mmgen-walletconv',
'mmgen-walletgen',
'mmgen-txcreate',
'mmgen-txsign',

View file

@ -11,8 +11,8 @@ sys.path.__setitem__(0,os.path.abspath(os.curdir))
from binascii import hexlify
import mmgen.opt as opt
import mmgen.config as g
from mmgen.util import msg,msg_r,msgrepr,msgrepr_exit,red,green
import mmgen.globalvars as g
from mmgen.util import msg,msg_r,mmsg,mdie,red,green,vmsg
from mmgen.bitcoin import hextowif,privnum2addr
rounds = 100
@ -23,6 +23,7 @@ opts_data = {
-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
""",
'notes': """
@ -63,6 +64,7 @@ for i in range(1,rounds+1):
sec = hexlify(os.urandom(32))
wif = hextowif(sec)
a = privnum2addr(int(sec,16))
vmsg("\nkey: %s\naddr: %s\n" % (wif,a))
b = check_output(["keyconv", wif]).split()[1]
if a != b:
msg_r(red("\nERROR: Addresses do not match!"))
@ -74,4 +76,4 @@ for i in range(1,rounds+1):
""".format(sec,wif,a,b,pnm=g.proj_name).rstrip())
sys.exit(3)
msg(green("\nOK"))
msg(green("%sOK" % ("" if opt.verbose else "\n")))

View file

@ -0,0 +1,2 @@
H@&оRLrT.╝у%╛е7©  ычeчЬа╧:x%ё5┐НoЫ*ю!вИ д
Мё0╬sЪ│ХёR%┤Pьг

View file

@ -0,0 +1,4 @@
8e7a aa61 cf9d acba 10ec cda2 a54c a64e
ee54 970a ebdc f44d 53d2 c005 46ea 1ab3
d940 0d78 6050 16a8 4ef6 e228 661a 803a
38ea 6874 189f b022 db94 3e11 051b 0302

View file

@ -0,0 +1,5 @@
078b 15e6 42c1 8447 d5a4 f03c b3ae aab9
7d79 2640 c307 bf2a 78a6 847b 44f2 6d96
7d9b 44ac 193c a47a 9a12 6e60 9f8f fe96
720b 0682 01d8 7da4 9eea be67 60c6 cbcf
d522 5a97 8bc2 5554

View file

@ -0,0 +1 @@
▒╧и7м8╩≈vЮЛP  0,Ъ%▐║F╖║ДБNИ`зtсЛ╫$╟@≥pk%┘рL}нЯQГА▄≥p/╨=З╘-rО~GОС9

View file

@ -0,0 +1,4 @@
80f5 2531 04ab 4de4 11fd ccb8 aae4 26ed
2520 8218 e5e1 8744 d367 9e9d 3491 ff8e
f83e 523b 36f7 dbe5 3c92 aeca c935 15fa
1bc5 2c54 0e4c 0825

Binary file not shown.

BIN
test/ref/sample-text.mmenc Normal file

Binary file not shown.

View file

@ -10,7 +10,7 @@ sys.path.__setitem__(0,os.path.abspath(os.curdir))
import mmgen.globalvars as g
import mmgen.opt as opt
from mmgen.util import msgrepr,msgrepr_exit,Msg,die
from mmgen.util import mmsg,mdie,Msg,die
from mmgen.test import *
hincog_fn = "rand_data"
@ -35,6 +35,9 @@ ref_kafile_pass = "kafile password"
ref_kafile_hash_preset = "1"
ref_enc_fn = "sample-text.mmenc"
tool_enc_passwd = "Scrypt it, don't hash it!"
sample_text = \
"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks\n"
cfgs = {
'6': {
@ -46,8 +49,11 @@ cfgs = {
'keyaddrfile_chk': "CF83 32FB 8A8B 08E2 0F00 D601",
'wpasswd': "reference password",
'ref_wallet': "FE3C6545-D782B529[128,1].mmdat",
'ic_wallet': "FE3C6545-161E495F-BEB7548E[128:1].incog-offset123",
'ic_wallet_old': "FE3C6545-161E495F-9860A85B[128:1].incog-old.offset123",
'ic_wallet': "FE3C6545-E29303EA-5E229E30[128,1].mmincog",
'ic_wallet_hex': "FE3C6545-BC4BE3F2-32586837[128,1].mmincox",
'hic_wallet': "FE3C6545-161E495F-BEB7548E[128:1].incog-offset123",
'hic_wallet_old': "FE3C6545-161E495F-9860A85B[128:1].incog-old.offset123",
'tmpdir': os.path.join("test","tmp6"),
'kapasswd': "",
@ -68,8 +74,11 @@ cfgs = {
'keyaddrfile_chk': "9648 5132 B98E 3AD9 6FC3 C5AD",
'wpasswd': "reference password",
'ref_wallet': "1378FC64-6F0F9BB4[192,1].mmdat",
'ic_wallet': "1378FC64-B55E9958-77256FC1[192:1].incog.offset123",
'ic_wallet_old': "1378FC64-B55E9958-D85FF20C[192:1].incog-old.offset123",
'ic_wallet': "1378FC64-2907DE97-F980D21F[192,1].mmincog",
'ic_wallet_hex': "1378FC64-4DCB5174-872806A7[192,1].mmincox",
'hic_wallet': "1378FC64-B55E9958-77256FC1[192:1].incog.offset123",
'hic_wallet_old': "1378FC64-B55E9958-D85FF20C[192:1].incog-old.offset123",
'tmpdir': os.path.join("test","tmp7"),
'kapasswd': "",
@ -97,8 +106,11 @@ cfgs = {
# 'ref_fake_unspent_data':"98831F3A_unspent.json",
'ref_tx_file': "tx_FFB367[1.234].raw",
'ic_wallet': "98831F3A-F59B07A0-559CEF19[256:1].incog.offset123",
'ic_wallet_old': "98831F3A-F59B07A0-848535F3[256:1].incog-old.offset123",
'ic_wallet': "98831F3A-5482381C-18460FB1[256,1].mmincog",
'ic_wallet_hex': "98831F3A-1630A9F2-870376A9[256,1].mmincox",
'hic_wallet': "98831F3A-F59B07A0-559CEF19[256:1].incog.offset123",
'hic_wallet_old': "98831F3A-F59B07A0-848535F3[256:1].incog-old.offset123",
'tmpdir': os.path.join("test","tmp8"),
'kapasswd': "",
@ -108,7 +120,6 @@ cfgs = {
'addrs': "refaddrgen3",
'akeys.mmenc': "refkeyaddrgen3"
},
},
'1': {
'tmpdir': os.path.join("test","tmp1"),
@ -133,7 +144,7 @@ cfgs = {
'tmpdir': os.path.join("test","tmp2"),
'wpasswd': "Hodling away",
'addr_idx_list': "37,45,3-6,22-23", # 8 addresses
'seed_len': 128,
'seed_len': 128,
'dep_generators': {
'mmdat': "walletgen2",
'addrs': "addrgen2",
@ -157,7 +168,7 @@ cfgs = {
'tmpdir': os.path.join("test","tmp4"),
'wpasswd': "Hashrate rising",
'addr_idx_list': "63,1004,542-544,7-9", # 8 addresses
'seed_len': 192,
'seed_len': 192,
'dep_generators': {
'mmdat': "walletgen4",
'mmbrain': "walletgen4",
@ -177,9 +188,6 @@ cfgs = {
},
'9': {
'tmpdir': os.path.join("test","tmp9"),
'tool_enc_passwd': "Scrypt it, don't hash it!",
'sample_text':
"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks\n",
'tool_enc_infn': "tool_encrypt.in",
# 'tool_enc_ref_infn': "tool_encrypt_ref.in",
'dep_generators': {
@ -191,46 +199,14 @@ cfgs = {
},
}
from copy import deepcopy
for a,b in ('6','11'),('7','12'),('8','13'):
cfgs[b] = deepcopy(cfgs[a])
cfgs[b]['tmpdir'] = os.path.join("test","tmp"+b)
from collections import OrderedDict
cmd_data = OrderedDict([
# test description depends
# Check saved reference files:
['ref_wallet_chk1', (6,'saved reference wallet (128-bit)', [[[],6]])],
['ref_wallet_chk2', (7,'saved reference wallet (192-bit)', [[[],7]])],
['ref_wallet_chk3', (8,'saved reference wallet (256-bit)', [[[],8]])],
['ref_seed_chk1', (6,'saved seed file (128-bit)', [[[],6]])],
['ref_seed_chk2', (7,'saved seed file (192-bit)', [[[],7]])],
['ref_seed_chk3', (8,'saved seed file (256-bit)', [[[],8]])],
['ref_mn_chk1', (6,'saved mnemonic file (128-bit)', [[[],6]])],
['ref_mn_chk2', (7,'saved mnemonic file (192-bit)', [[[],7]])],
['ref_mn_chk3', (8,'saved mnemonic file (256-bit)', [[[],8]])],
['ref_incog_chk1', (6,'saved incog reference wallet (128-bit)', [[[],6]])],
['ref_incog_chk2', (7,'saved incog reference wallet (192-bit)', [[[],7]])],
['ref_incog_chk3', (8,'saved incog reference wallet (256-bit)', [[[],8]])],
['ref_brain_chk1', (6,'saved brainwallet (128-bit)', [[[],6]])],
['ref_brain_chk2', (7,'saved brainwallet (192-bit)', [[[],7]])],
['ref_brain_chk3', (8,'saved brainwallet (256-bit)', [[[],8]])],
['ref_brain_chk3_spc', (8,'saved brainwallet (256-bit, non-standard spacing)', [[[],8]])],
['ref_addrfile_chk', (8,'saved reference address file', [[[],8]])],
['ref_keyaddrfile_chk', (8,'saved reference key-address file', [[[],8]])],
# Create the fake inputs:
# ['txcreate8', (8,'transaction creation (8)', [[["addrs"],8]])],
['ref_tx_chk', (8,'saved reference tx file', [[[],8]])],
['ref_tool_decrypt', (9,'decryption of saved MMGen-encrypted file', [[[],9]])],
# Generate new reference ('abc' brainwallet) files:
['refwalletgen1', (6,'gen new refwallet (128-bit)', [[[],6]])],
['refwalletgen2', (7,'gen new refwallet (192-bit)', [[[],7]])],
['refwalletgen3', (8,'gen new refwallet (256-bit)', [[[],8]])],
['refaddrgen1', (6,'new refwallet addr chksum (128-bit)', [[["mmdat"],6]])],
['refaddrgen2', (7,'new refwallet addr chksum (192-bit)', [[["mmdat"],7]])],
['refaddrgen3', (8,'new refwallet addr chksum (256-bit)', [[["mmdat"],8]])],
['refkeyaddrgen1', (6,'new refwallet key-addr chksum (128-bit)', [[["mmdat"],6]])],
['refkeyaddrgen2', (7,'new refwallet key-addr chksum (192-bit)', [[["mmdat"],7]])],
['refkeyaddrgen3', (8,'new refwallet key-addr chksum (256-bit)', [[["mmdat"],8]])],
['walletgen', (1,'wallet generation', [[[],1]])],
# ['walletchk', (1,'wallet check', [[["mmdat"],1]])],
['passchg', (5,'password, label and hash preset change',[[["mmdat"],1]])],
@ -256,13 +232,11 @@ cmd_data = OrderedDict([
['keyaddrgen', (1,'key-address file generation', [[["mmdat"],1]])],
['txsign_keyaddr',(1,'transaction signing with key-address file', [[["akeys.mmenc","raw"],1]])],
# ['walletgen2',(2,'wallet generation (2)', [])],
['walletgen2',(2,'wallet generation (2), 128-bit seed', [])],
['addrgen2', (2,'address generation (2)', [[["mmdat"],2]])],
['txcreate2', (2,'transaction creation (2)', [[["addrs"],2]])],
['txsign2', (2,'transaction signing, two transactions',[[["mmdat","raw"],1],[["mmdat","raw"],2]])],
['export_mnemonic2', (2,'seed export to mmwords format (2)',[[["mmdat"],2]])],
# ['export_mnemonic2', (2,'seed export to mmwords format (2), 128-bit seed (WIP)',[[["mmdat"],2]])],
['walletgen3',(3,'wallet generation (3)', [])],
['addrgen3', (3,'address generation (3)', [[["mmdat"],3]])],
@ -270,18 +244,83 @@ cmd_data = OrderedDict([
['txsign3', (3,'tx signing with inputs and outputs from two wallets',[[["mmdat"],1],[["mmdat","raw"],3]])],
['walletgen4',(4,'wallet generation (4) (brainwallet)', [])],
# ['walletgen4',(4,'wallet generation (4) (brainwallet, 192-bit seed (WIP))', [])],
['addrgen4', (4,'address generation (4)', [[["mmdat"],4]])],
['txcreate4', (4,'tx creation with inputs and outputs from four seed sources, plus non-MMGen inputs and outputs', [[["addrs"],1],[["addrs"],2],[["addrs"],3],[["addrs"],4]])],
['txsign4', (4,'tx signing with inputs and outputs from incog file, mnemonic file, wallet and brainwallet, plus non-MMGen inputs and outputs', [[["mmincog"],1],[["mmwords"],2],[["mmdat"],3],[["mmbrain","raw"],4]])],
['tool_encrypt', (9,"'mmgen-tool encrypt' (random data)", [])],
['tool_decrypt', (9,"'mmgen-tool decrypt' (random data)",
[[[cfgs['9']['tool_enc_infn'],
cfgs['9']['tool_enc_infn']+".mmenc"],9]])],
['tool_decrypt', (9,"'mmgen-tool decrypt' (random data)", [[[cfgs['9']['tool_enc_infn'],cfgs['9']['tool_enc_infn']+".mmenc"],9]])],
# ['tool_encrypt_ref', (9,"'mmgen-tool encrypt' (reference text)", [])],
['tool_find_incog_data', (9,"'mmgen-tool find_incog_data'", [[[hincog_fn],1],[[incog_id_fn],1]])],
])
# saved reference data
cmd_data_ref = (
# reading
('ref_wallet_chk', ([],'saved reference wallet')),
('ref_seed_chk', ([],'saved seed file')),
('ref_mn_chk', ([],'saved mnemonic file')),
('ref_hincog_chk', ([],'saved hidden incog reference wallet')),
('ref_brain_chk', ([],'saved brainwallet')),
# generating new reference ('abc' brainwallet) files:
('refwalletgen', ([],'gen new refwallet')),
('refaddrgen', (["mmdat"],'new refwallet addr chksum')),
('refkeyaddrgen', (["mmdat"],'new refwallet key-addr chksum'))
)
# misc. saved reference data
cmd_data_ref_other = (
('ref_addrfile_chk', 'saved reference address file'),
('ref_keyaddrfile_chk','saved reference key-address file'),
# Create the fake inputs:
# ('txcreate8', 'transaction creation (8)'),
('ref_tx_chk', 'saved reference tx file'),
('ref_brain_chk_spc3', 'saved brainwallet (non-standard spacing)'),
('ref_tool_decrypt', 'decryption of saved MMGen-encrypted file'),
)
# mmgen-walletconv:
cmd_data_conv_in = ( # reading
('ref_wallet_conv', 'conversion of saved reference wallet'),
('ref_mn_conv', 'conversion of saved mnemonic'),
('ref_seed_conv', 'conversion of saved seed file'),
('ref_brain_conv', 'conversion of ref brainwallet'),
('ref_incog_conv', 'conversion of saved incog wallet'),
('ref_incox_conv', 'conversion of saved hex incog wallet'),
('ref_hincog_conv', 'conversion of saved hidden incog wallet'),
('ref_hincog_conv_old','conversion of saved hidden incog wallet (old format)')
)
cmd_data_conv_out = ( # writing
('ref_wallet_conv_out', 'ref seed conversion to wallet'),
('ref_mn_conv_out', 'ref seed conversion to mnemonic'),
('ref_seed_conv_out', 'ref seed conversion to seed'),
('ref_incog_conv_out', 'ref seed conversion to incog data'),
('ref_incox_conv_out', 'ref seed conversion to hex incog data'),
('ref_hincog_conv_out', 'ref seed conversion to hidden incog data')
)
cmd_groups = OrderedDict([
('main', cmd_data.keys()),
('ref', [c[0]+str(i) for c in cmd_data_ref for i in (1,2,3)]),
('ref_other', [c[0] for c in cmd_data_ref_other]),
('conv_in', [c[0]+str(i) for c in cmd_data_conv_in for i in (1,2,3)]),
('conv_out', [c[0]+str(i) for c in cmd_data_conv_out for i in (1,2,3)]),
])
for a,b in cmd_data_ref:
for i,j in (1,128),(2,192),(3,256):
cmd_data[a+str(i)] = (5+i,"%s (%s-bit)" % (b[1],j),[[b[0],5+i]])
for a,b in cmd_data_ref_other:
cmd_data[a] = (8,b,[[[],8]])
for a,b in cmd_data_conv_in:
for i,j in (1,128),(2,192),(3,256):
cmd_data[a+str(i)] = (10+i,"%s (%s-bit)" % (b,j),[[[],10+i]])
for a,b in cmd_data_conv_out:
for i,j in (1,128),(2,192),(3,256):
cmd_data[a+str(i)] = (10+i,"%s (%s-bit)" % (b,j),[[[],10+i]])
utils = {
'check_deps': 'check dependencies for specified command',
'clean': 'clean specified tmp dir(s) 1,2,3,4,5 or 6 (no arg = all dirs)',
@ -296,23 +335,34 @@ for k in cfgs.keys():
cfgs[k]['amts'][idx] = "%s.%s" % ((getrandnum(2) % mod), str(getrandnum(4))[:5])
meta_cmds = OrderedDict([
['saved_ref1', (6,("ref_wallet_chk1","ref_seed_chk1","ref_mn_chk1","ref_brain_chk1","ref_incog_chk1"))],
['saved_ref2', (7,("ref_wallet_chk2","ref_seed_chk2","ref_mn_chk2","ref_brain_chk2","ref_incog_chk2"))],
['saved_ref3', (8,("ref_wallet_chk3","ref_seed_chk3","ref_mn_chk3","ref_brain_chk3","ref_incog_chk3","ref_brain_chk3_spc"))],
['saved_ref_other', (8,("ref_addrfile_chk","ref_tx_chk","ref_tool_decrypt"))],
['ref1', (6,("refwalletgen1","refaddrgen1","refkeyaddrgen1"))],
['ref2', (7,("refwalletgen2","refaddrgen2","refkeyaddrgen2"))],
['ref3', (8,("refwalletgen3","refaddrgen3","refkeyaddrgen3"))],
['gen', (1,("walletgen","addrgen"))],
['pass', (5,("passchg","walletchk_newpass"))],
['tx', (1,("txcreate","txsign","txsend"))],
['export', (1,[k for k in cmd_data if k[:7] == "export_" and cmd_data[k][0] == 1])],
['gen_sp', (1,[k for k in cmd_data if k[:8] == "addrgen_" and cmd_data[k][0] == 1])],
['online', (1,("keyaddrgen","txsign_keyaddr"))],
['2', (2,[k for k in cmd_data if cmd_data[k][0] == 2])],
['3', (3,[k for k in cmd_data if cmd_data[k][0] == 3])],
['4', (4,[k for k in cmd_data if cmd_data[k][0] == 4])],
['tool', (9,("tool_encrypt","tool_decrypt","tool_find_incog_data"))],
['ref1', ("refwalletgen1","refaddrgen1","refkeyaddrgen1")],
['ref2', ("refwalletgen2","refaddrgen2","refkeyaddrgen2")],
['ref3', ("refwalletgen3","refaddrgen3","refkeyaddrgen3")],
['gen', ("walletgen","addrgen")],
['pass', ("passchg","walletchk_newpass")],
['tx', ("addrimport","txcreate","txsign","txsend")],
['export', [k for k in cmd_data if k[:7] == "export_" and cmd_data[k][0] == 1]],
['gen_sp', [k for k in cmd_data if k[:8] == "addrgen_" and cmd_data[k][0] == 1]],
['online', ("keyaddrgen","txsign_keyaddr")],
['2', [k for k in cmd_data if cmd_data[k][0] == 2]],
['3', [k for k in cmd_data if cmd_data[k][0] == 3]],
['4', [k for k in cmd_data if cmd_data[k][0] == 4]],
['tool', ("tool_encrypt","tool_decrypt","tool_find_incog_data")],
['saved_ref1', [c[0]+"1" for c in cmd_data_ref]],
['saved_ref2', [c[0]+"2" for c in cmd_data_ref]],
['saved_ref3', [c[0]+"3" for c in cmd_data_ref]],
['saved_ref_other', [c[0] for c in cmd_data_ref_other]],
['saved_ref_conv_in1', [c[0]+"1" for c in cmd_data_conv_in]],
['saved_ref_conv_in2', [c[0]+"2" for c in cmd_data_conv_in]],
['saved_ref_conv_in3', [c[0]+"3" for c in cmd_data_conv_in]],
['saved_ref_conv_out1', [c[0]+"1" for c in cmd_data_conv_out]],
['saved_ref_conv_out2', [c[0]+"2" for c in cmd_data_conv_out]],
['saved_ref_conv_out3', [c[0]+"3" for c in cmd_data_conv_out]],
])
opts_data = {
@ -350,8 +400,6 @@ else:
send_delay = 0
os.environ["MMGEN_DISABLE_HOLD_PROTECT"] = "1"
if opt.debug: opt.verbose = True
if opt.exact_output:
def msg(s): pass
vmsg = vmsg_r = msg_r = msg
@ -378,15 +426,22 @@ def errmsg_r(s): stderr_save.write(s)
if opt.list_cmds:
fs = " {:<{w}} - {}"
Msg("Available commands:")
Msg("AVAILABLE COMMANDS:")
w = max([len(i) for i in cmd_data])
for cmd in cmd_data:
Msg(fs.format(cmd,cmd_data[cmd][1],w=w))
Msg("\nAvailable metacommands:")
w = max([len(i) for i in meta_cmds])
Msg("\nAVAILABLE METACOMMANDS:")
for cmd in meta_cmds:
Msg(fs.format(cmd," + ".join(meta_cmds[cmd][1]),w=w))
Msg("\nAvailable utilities:")
Msg(fs.format(cmd," ".join(meta_cmds[cmd]),w=w))
w = max([len(i) for i in cmd_groups.keys()])
Msg("\nAVAILABLE COMMAND GROUPS:")
for g in cmd_groups.keys():
Msg(fs.format(g," ".join(cmd_groups[g]),w=w))
Msg("\nAVAILABLE UTILITIES:")
w = max([len(i) for i in utils])
for cmd in sorted(utils):
Msg(fs.format(cmd,utils[cmd],w=w))
@ -428,7 +483,8 @@ def my_expect(p,s,t='',delay=send_delay,regex=False,nonl=False):
else:
if t == '':
if not nonl: vmsg("")
else: ret = my_send(p,t,delay,s)
else:
my_send(p,t,delay,s)
return ret
def get_file_with_ext(ext,mydir,delete=True):
@ -502,28 +558,32 @@ class MMGenExpect(object):
vmsg("EOT")
my_expect(self.p,"ENTER to continue: ",'\n')
def passphrase_new(self,what,passphrase):
my_expect(self.p,("Enter passphrase for %s: " % what), passphrase+"\n")
def passphrase_new(self,desc,passphrase):
my_expect(self.p,("Enter passphrase for %s: " % desc), passphrase+"\n")
my_expect(self.p,"Repeat passphrase: ", passphrase+"\n")
def passphrase(self,what,passphrase,pwtype=""):
def passphrase(self,desc,passphrase,pwtype=""):
if pwtype: pwtype += " "
my_expect(self.p,("Enter %spassphrase for %s.*?: " % (pwtype,what)),
my_expect(self.p,("Enter %spassphrase for %s.*?: " % (pwtype,desc)),
passphrase+"\n",regex=True)
def hash_preset(self,what,preset=''):
my_expect(self.p,("Enter hash preset for %s," % what))
def hash_preset(self,desc,preset=''):
my_expect(self.p,("Enter hash preset for %s," % desc))
my_expect(self.p,("or hit ENTER .*?:"), str(preset)+"\n",regex=True)
def written_to_file(self,what,overwrite_unlikely=False,query="Overwrite? "):
s1 = "%s written to file " % what
def written_to_file(self,desc,overwrite_unlikely=False,query="Overwrite? ",oo=False):
s1 = "%s written to file " % desc
s2 = query + "Type uppercase 'YES' to confirm: "
ret = my_expect(self.p,s1 if overwrite_unlikely else [s1,s2])
if ret == 1:
my_send(self.p,"YES\n")
ret = my_expect(self.p,s1)
if oo:
outfile = self.expect_getend("Overwriting file '").rstrip("'")
return outfile
else:
ret = my_expect(self.p,s1)
outfile = self.p.readline().strip().strip("'")
vmsg("%s file: %s" % (what,cyan(outfile.replace("'",""))))
vmsg("%s file: %s" % (desc,cyan(outfile.replace("'",""))))
return outfile
def no_overwrite(self):
@ -679,15 +739,15 @@ def check_needs_rerun(ts,cmd,build=False,root=True,force_delete=False,dpy=False)
return rerun
def refcheck(what,chk,refchk):
vmsg("Comparing %s '%s' to stored reference" % (what,chk))
def refcheck(desc,chk,refchk):
vmsg("Comparing %s '%s' to stored reference" % (desc,chk))
if chk == refchk:
ok()
else:
if not opt.verbose: errmsg("")
errmsg(red("""
Fatal error - %s '%s' does not match reference value '%s'. Aborting test
""".strip() % (what,chk,refchk)))
""".strip() % (desc,chk,refchk)))
sys.exit(3)
def check_deps(cmds):
@ -711,7 +771,7 @@ def check_deps(cmds):
c = rebuild_list[cmd]
m = "Rebuild" if (c[0] and c[1]) else "Build" if c[0] else "OK"
msg("cmd {:<{w}} {}".format(cmd+":", m, w=w))
# msgrepr(cmd,c)
# mmsg(cmd,c)
def clean(dirs=[]):
@ -738,7 +798,7 @@ class MMGenTestSuite(object):
def get_num_exts_for_cmd(self,cmd,dpy=False): # dpy ignored here
num = str(cmd_data[cmd][0])
dgl = cfgs[num]['dep_generators']
# msgrepr(num,cmd,dgl)
# mmsg(num,cmd,dgl)
if cmd in dgl.values():
exts = [k for k in dgl if dgl[k] == cmd]
return (num,exts)
@ -801,9 +861,9 @@ class MMGenTestSuite(object):
t.usr_rand(10)
t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
seed_id = t.written_to_file("Wallet").split("-")[0].split("/")[-1]
refcheck("seed id",seed_id,cfg['seed_id'])
refcheck("seed ID",seed_id,cfg['seed_id'])
refwalletgen1 = refwalletgen2 = refwalletgen3 = refwalletgen
refwalletgen1 = refwalletgen2 = refwalletgen3 = refwalletgen
def passchg(self,name,walletfile):
@ -850,7 +910,7 @@ class MMGenTestSuite(object):
d = " (%s-bit seed)" % cfg['seed_len']
self.addrgen(name,walletfile,check_ref=True)
refaddrgen1 = refaddrgen2 = refaddrgen3 = refaddrgen
refaddrgen1 = refaddrgen2 = refaddrgen3 = refaddrgen
def addrimport(self,name,addrfile):
outfile = os.path.join(cfg['tmpdir'],"addrfile_w_comments")
@ -1014,11 +1074,11 @@ class MMGenTestSuite(object):
t.written_to_file("Data",query="")
ok()
def addrgen_seed(self,name,walletfile,foo,what="seed data",arg="-s"):
def addrgen_seed(self,name,walletfile,foo,desc="seed data",arg="-s"):
t = MMGenExpect(name,"mmgen-addrgen",
[arg,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
t.license()
t.expect_getend("Valid %s for seed ID " % what)
t.expect_getend("Valid %s for seed ID " % desc)
vmsg("Comparing generated checksum with checksum from previous address file")
chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
verify_checksum_or_exit(get_addrfile_checksum(),chk)
@ -1026,14 +1086,14 @@ class MMGenTestSuite(object):
ok()
def addrgen_mnemonic(self,name,walletfile,foo):
self.addrgen_seed(name,walletfile,foo,what="mnemonic",arg="-m")
self.addrgen_seed(name,walletfile,foo,desc="mnemonic",arg="-m")
def addrgen_incog(self,name,walletfile,foo,args=["-g"]):
t = MMGenExpect(name,"mmgen-addrgen",args+["-d",
cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
t.license()
t.expect_getend("Incog ID: ")
t.passphrase("MMGen incognito wallet \w{8}", cfg['wpasswd'])
t.passphrase("incognito wallet data \w{8}", cfg['wpasswd'])
t.hash_preset("incog wallet",'1')
vmsg("Comparing generated checksum with checksum from address file")
chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
@ -1068,7 +1128,7 @@ class MMGenTestSuite(object):
def refkeyaddrgen(self,name,walletfile):
self.keyaddrgen(name,walletfile,check_ref=True)
refkeyaddrgen1 = refkeyaddrgen2 = refkeyaddrgen3 = refkeyaddrgen
refkeyaddrgen1 = refkeyaddrgen2 = refkeyaddrgen3 = refkeyaddrgen
def txsign_keyaddr(self,name,keyaddr_file,txfile):
t = MMGenExpect(name,"mmgen-txsign", ["-d",cfg['tmpdir'],"-M",keyaddr_file,txfile])
@ -1136,9 +1196,9 @@ class MMGenTestSuite(object):
t.license()
t.tx_view()
for cnum,what,app in ('1',"incognito"," incognito"),('3',"MMGen",""):
t.expect_getend("Getting %s wallet data from file " % what)
t.passphrase("MMGen%s wallet"%app,cfgs[cnum]['wpasswd'])
for cnum,desc,app in ('1',"incognito","incognito"),('3',"MMGen","MMGen"):
t.expect_getend("Getting %s wallet data from file " % desc)
t.passphrase("%s wallet"%app,cfgs[cnum]['wpasswd'])
if cnum == '1':
t.hash_preset("incog wallet",'1')
@ -1155,7 +1215,7 @@ class MMGenTestSuite(object):
infn = get_tmpfile_fn(cfg,tmp_fn)
t = MMGenExpect(name,"mmgen-tool",["-d",cfg['tmpdir'],"encrypt",infn])
t.hash_preset("user data",'1')
t.passphrase_new("user data",cfg['tool_enc_passwd'])
t.passphrase_new("user data",tool_enc_passwd)
t.written_to_file("Encrypted data")
ok()
# Generate the reference mmenc file
@ -1168,7 +1228,7 @@ class MMGenTestSuite(object):
of = name + ".out"
t = MMGenExpect(name,"mmgen-tool",
["-d",cfg['tmpdir'],"decrypt",f2,"outfile="+of,"hash_preset=1"])
t.passphrase("user data",cfg['tool_enc_passwd'])
t.passphrase("user data",tool_enc_passwd)
t.written_to_file("Decrypted data")
d1 = read_from_file(f1)
d2 = read_from_file(get_tmpfile_fn(cfg,of))
@ -1182,7 +1242,127 @@ class MMGenTestSuite(object):
o = t.expect_getend("Incog data for ID \w{8} found at offset ",regex=True)
cmp_or_die(hincog_offset,int(o))
def walletconv_out(self,name,desc,out_fmt="w",uopts=[],uopts_chk=[],pw=False):
opts = ["-d",cfg['tmpdir'],"-r10","-p1","-o",out_fmt] + uopts
infile = os.path.join(ref_dir,cfg['seed_id']+".mmwords")
d = "(convert)"
t = MMGenExpect(name,"mmgen-walletconv",opts+[infile],extra_desc=d)
t.license()
if pw:
t.passphrase_new("new "+desc,cfg['wpasswd'])
t.usr_rand(10)
if desc == "hidden incognito data":
ret = t.expect(["Create? (Y/n): ","'YES' to confirm: "],"YES\n")
if ret == 0:
t.expect("Enter file size: ","1234\n")
wf = t.written_to_file(desc[0].upper()+desc[1:],oo=True)
ok()
d = "(check)"
if desc == "hidden incognito data":
self.keygen_chksum_chk_hincog(name,cfg['seed_id'],uopts_chk)
# elif pw:
# self.walletchk_chksum_chk(name,wf,cfg['seed_id'],uopts=uopts_chk)
else:
self.keygen_chksum_chk(name,wf,cfg['seed_id'],pw=pw)
def walletconv_in(self,name,infile,desc,uopts=[],pw=False,oo=False):
opts = ["-d",cfg['tmpdir'],"-o","words","-r10"]
if_arg = [infile] if infile else []
d = "(convert)"
t = MMGenExpect(name,"mmgen-walletconv",opts+uopts+if_arg,extra_desc=d)
t.license()
if desc == "brainwallet":
t.expect("Enter brainwallet: ",ref_wallet_brainpass+"\n")
if pw:
t.passphrase(desc,cfg['wpasswd'])
if name[:19] == "ref_hincog_conv_old":
t.expect("Is the seed ID correct? (Y/n): ","\n")
else:
t.expect(["Passphrase is OK"," are correct"])
# Output
wf = t.written_to_file("Mnemonic data",oo=oo)
t.close()
ok()
# back check of result
d = "(check)"
self.keygen_chksum_chk(name,wf,cfg['seed_id'])
# Saved reference file tests
def ref_wallet_conv(self,name):
wf = os.path.join(ref_dir,cfg['ref_wallet'])
self.walletconv_in(name,wf,"MMGen wallet",pw=True,oo=True)
def ref_mn_conv(self,name,ext="mmwords",desc="Mnemonic data"):
wf = os.path.join(ref_dir,cfg['seed_id']+"."+ext)
self.walletconv_in(name,wf,desc,oo=True)
def ref_seed_conv(self,name):
self.ref_mn_conv(name,ext="mmseed",desc="Seed data")
def ref_brain_conv(self,name):
uopts = ["-i","b","-p","1","-l",str(cfg['seed_len'])]
self.walletconv_in(name,None,"brainwallet",uopts,oo=True)
def ref_incog_conv(self,name,wfk="ic_wallet",in_fmt="i",desc="incognito data"):
uopts = ["-i",in_fmt,"-p","1","-l",str(cfg['seed_len'])]
wf = os.path.join(ref_dir,cfg[wfk])
self.walletconv_in(name,wf,desc,uopts,oo=True,pw=True)
def ref_incox_conv(self,name):
self.ref_incog_conv(name,in_fmt="xi",wfk="ic_wallet_hex",desc="hex incognito data")
def ref_hincog_conv(self,name,wfk='hic_wallet',add_uopts=[]):
ic_f = os.path.join(ref_dir,cfg[wfk])
uopts = ["-i","hi","-p","1","-l",str(cfg['seed_len'])] + add_uopts
hi_opt = ["-H","%s,%s" % (ic_f,ref_wallet_incog_offset)]
self.walletconv_in(name,None,"hidden incognito data",uopts+hi_opt,oo=True,pw=True)
def ref_hincog_conv_old(self,name):
self.ref_hincog_conv(name,wfk='hic_wallet_old',add_uopts=["-O"])
def ref_wallet_conv_out(self,name):
self.walletconv_out(name,"MMGen wallet","w",pw=True)
def ref_mn_conv_out(self,name):
self.walletconv_out(name,"mnemonic data","mn")
def ref_seed_conv_out(self,name):
self.walletconv_out(name,"seed data","seed")
def ref_incog_conv_out(self,name):
self.walletconv_out(name,"incognito data",out_fmt="i",pw=True)
def ref_incox_conv_out(self,name):
self.walletconv_out(name,"hex incognito data",out_fmt="xi",pw=True)
def ref_hincog_conv_out(self,name,extra_uopts=[]):
ic_f = os.path.join(cfg['tmpdir'],"rand.data")
hi_parms = "%s,%s" % (ic_f,ref_wallet_incog_offset)
hi_parms_legacy = "%s,%s,%s"%(ic_f,ref_wallet_incog_offset,cfg['seed_len'])
self.walletconv_out(name,
"hidden incognito data", "hi",
uopts=["-J",hi_parms] + extra_uopts,
uopts_chk=["-G",hi_parms_legacy],
pw=True
)
ref_wallet_conv1 = ref_wallet_conv2 = ref_wallet_conv3 = ref_wallet_conv
ref_mn_conv1 = ref_mn_conv2 = ref_mn_conv3 = ref_mn_conv
ref_seed_conv1 = ref_seed_conv2 = ref_seed_conv3 = ref_seed_conv
ref_brain_conv1 = ref_brain_conv2 = ref_brain_conv3 = ref_brain_conv
ref_incog_conv1 = ref_incog_conv2 = ref_incog_conv3 = ref_incog_conv
ref_incox_conv1 = ref_incox_conv2 = ref_incox_conv3 = ref_incox_conv
ref_hincog_conv1 = ref_hincog_conv2 = ref_hincog_conv3 = ref_hincog_conv
ref_hincog_conv_old1 = ref_hincog_conv_old2 = ref_hincog_conv_old3 = ref_hincog_conv_old
ref_wallet_conv_out1 = ref_wallet_conv_out2 = ref_wallet_conv_out3 = ref_wallet_conv_out
ref_mn_conv_out1 = ref_mn_conv_out2 = ref_mn_conv_out3 = ref_mn_conv_out
ref_seed_conv_out1 = ref_seed_conv_out2 = ref_seed_conv_out3 = ref_seed_conv_out
ref_incog_conv_out1 = ref_incog_conv_out2 = ref_incog_conv_out3 = ref_incog_conv_out
ref_incox_conv_out1 = ref_incox_conv_out2 = ref_incox_conv_out3 = ref_incox_conv_out
ref_hincog_conv_out1 = ref_hincog_conv_out2 = ref_hincog_conv_out3 = ref_hincog_conv_out
def ref_wallet_chk(self,name):
wf = os.path.join(ref_dir,cfg['ref_wallet'])
self.walletchk(name,wf)
@ -1191,8 +1371,8 @@ class MMGenTestSuite(object):
def ref_seed_chk(self,name,ext=g.seed_ext):
wf = os.path.join(ref_dir,"%s.%s" % (cfg['seed_id'],ext))
what = "seed data" if ext == g.seed_ext else "mnemonic"
self.keygen_chksum_chk(name,wf,cfg['seed_id'],what)
desc = "seed data" if ext == g.seed_ext else "mnemonic"
self.keygen_chksum_chk(name,wf,cfg['seed_id'])
ref_seed_chk1 = ref_seed_chk2 = ref_seed_chk3 = ref_seed_chk
@ -1203,37 +1383,60 @@ class MMGenTestSuite(object):
def ref_brain_chk(self,name,bw_file=ref_bw_file):
wf = os.path.join(ref_dir,bw_file)
arg = "-b%s,%s" % (cfg['seed_len'],ref_bw_hash_preset)
self.keygen_chksum_chk(name,wf,cfg['ref_bw_seed_id'],"brainwallet",[arg])
self.keygen_chksum_chk(name,wf,cfg['ref_bw_seed_id'],[arg])
def keygen_chksum_chk(self,name,wf,seed_id,what,args=[]):
t = MMGenExpect(name,"mmgen-keygen", ["-q","-A"]+args+[wf,"1"])
chk = t.expect_getend("Valid %s for seed ID " % what)
def keygen_chksum_chk_hincog(self,name,seed_id,hincog_parm):
t = MMGenExpect(name,"mmgen-keygen", ["-p1","-q","-S","-A"]+hincog_parm+["1"],extra_desc="(check)")
t.passphrase("",cfg['wpasswd'])
t.expect("Encrypt key list? (y/N): ","\n")
t.expect("any printable ASCII symbol.\r\n")
chk = t.readline()[:8]
vmsg("Seed ID: %s" % cyan(chk))
cmp_or_die(seed_id,chk)
def keygen_chksum_chk(self,name,wf,seed_id,args=[],pw=False):
hp_arg = ["-p1"] if pw else []
t = MMGenExpect(name,"mmgen-keygen", ["-q","-S","-A"]+args+hp_arg+[wf,"1"],extra_desc="(check)")
if pw:
t.passphrase("",cfg['wpasswd'])
t.expect("Encrypt key list? (y/N): ","\n")
t.expect("any printable ASCII symbol.\r\n")
chk = t.readline()[:8]
vmsg("Seed ID: %s" % cyan(chk))
cmp_or_die(seed_id,chk)
# Use this for encrypted wallets instead of keygen_chksum_chk()
def walletchk_chksum_chk(self,name,wf,seed_id,uopts=[]):
t = MMGenExpect(name,"mmgen-walletchk",["-v", wf]+uopts,
extra_desc="(check)")
t.passphrase("",cfg['wpasswd'])
chk = t.expect_getend("Seed ID checksum OK (")[:8]
t.close()
cmp_or_die(seed_id,chk)
ref_brain_chk1 = ref_brain_chk2 = ref_brain_chk3 = ref_brain_chk
def ref_brain_chk3_spc(self,name):
def ref_brain_chk_spc3(self,name):
self.ref_brain_chk(name,bw_file=ref_bw_file_spc)
def ref_incog_chk(self,name):
for wtype,desc,earg in ('ic_wallet','',[]), \
('ic_wallet_old','(old format)',["-o"]):
def ref_hincog_chk(self,name):
for wtype,desc,earg in ('hic_wallet','',[]), \
('hic_wallet_old','(old format)',["-o"]):
ic_arg = "%s,%s,%s" % (
os.path.join(ref_dir,cfg[wtype]),
ref_wallet_incog_offset,cfg['seed_len']
)
t = MMGenExpect(name,"mmgen-keygen",
["-q","-A"]+earg+["-G"]+[ic_arg]+['1'],extra_desc=desc)
t.passphrase("MMGen incognito wallet",cfg['wpasswd'])
t.passphrase("incognito wallet",cfg['wpasswd'])
t.hash_preset("incog wallet","1")
if wtype == 'ic_wallet_old':
if wtype == 'hic_wallet_old':
t.expect("Is the seed ID correct? (Y/n): ","\n")
chk = t.expect_getend("Valid incog data for seed ID ")
t.close()
cmp_or_die(cfg['seed_id'],chk)
ref_incog_chk1 = ref_incog_chk2 = ref_incog_chk3 = ref_incog_chk
ref_hincog_chk1 = ref_hincog_chk2 = ref_hincog_chk3 = ref_hincog_chk
def ref_addrfile_chk(self,name,ftype="addr"):
wf = os.path.join(ref_dir,cfg['ref_'+ftype+'file'])
@ -1261,11 +1464,11 @@ class MMGenTestSuite(object):
f = os.path.join(ref_dir,ref_enc_fn)
t = MMGenExpect(name,"mmgen-tool",
["-q","decrypt",f,"outfile=-","hash_preset=1"])
t.passphrase("user data",cfg['tool_enc_passwd'])
t.passphrase("user data",tool_enc_passwd)
t.readline()
import re
o = re.sub('\r\n','\n',t.read())
cmp_or_die(cfg['sample_text'],o)
cmp_or_die(sample_text,o)
# main()
if opt.pause:
@ -1288,8 +1491,11 @@ try:
globals()[arg](cmd_args[cmd_args.index(arg)+1:])
sys.exit()
elif arg in meta_cmds:
for cmd in meta_cmds[arg][1]:
check_needs_rerun(ts,cmd,build=True,force_delete=True)
for cmd in meta_cmds[arg]:
check_needs_rerun(ts,cmd,build=True)
elif arg in cmd_groups.keys():
for cmd in cmd_groups[arg]:
check_needs_rerun(ts,cmd,build=True)
elif arg in cmd_data:
check_needs_rerun(ts,arg,build=True)
else:

View file

@ -9,7 +9,7 @@ os.chdir(os.path.join(pn,os.pardir))
sys.path.__setitem__(0,os.path.abspath(os.curdir))
import mmgen.opt as opt
from mmgen.util import msg,msg_r,vmsg,vmsg_r,Msg,msgrepr, msgrepr_exit
from mmgen.util import msg,msg_r,vmsg,vmsg_r,Msg,mmsg,mdie
from collections import OrderedDict
cmd_data = OrderedDict([
@ -94,8 +94,6 @@ cmd_args = opt.opts.init(opts_data,add_opts=["exact_output"])
if opt.system: sys.path.pop(0)
if opt.debug: opt.verbose = True
if opt.list_cmds:
fs = " {:<{w}} - {}"
Msg("Available commands:")