minor fixes and cleanups

This commit is contained in:
The MMGen Project 2019-07-03 17:30:51 +00:00
commit 23ae73d908
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
23 changed files with 110 additions and 93 deletions

View file

@ -556,7 +556,7 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file
pop_list.append(n)
for n in reversed(pop_list): self.data.pop(n)
if pop_list:
vmsg(self.msgs['removed_dup_keys'].format(len(pop_list),suf(removed,'s')))
vmsg(self.msgs['removed_dup_keys'].format(len(pop_list),suf(removed)))
def add_wifs(self,key_list):
if not key_list: return
@ -900,7 +900,8 @@ re-import your addresses.
def __init__(self,source=None):
self.al_ids = {}
if source == 'tw': self.add_tw_data()
if source == 'tw':
self.add_tw_data()
def seed_ids(self):
return list(self.al_ids.keys())

View file

@ -431,7 +431,6 @@ class CoinInfo(object):
line[n] = re.sub(r"'0x(....)'",r'0x\1',line[n])
line[n] = re.sub(r' ',r'',line[n]) + ('',',')[n != len(line)-1]
from mmgen.util import pmsg,pdie
if trust != -1:
if sym in tt:
src = tt[sym]

View file

@ -26,7 +26,7 @@ from . import rlp
from mmgen.globalvars import g
from mmgen.common import *
from mmgen.obj import MMGenObject,CoinAddr,TokenAddr,CoinTxID,ETHAmt
from mmgen.util import msg,pmsg
from mmgen.util import msg
try:
assert not g.use_internal_keccak_module
@ -60,17 +60,16 @@ class Token(MMGenObject): # ERC20
def do_call(self,method_sig,method_args='',toUnit=False):
data = create_method_id(method_sig) + method_args
if g.debug:
msg('{}: {}'.format(method_sig,'\n '.join(parse_abi(data))))
msg('ETH_CALL {}: {}'.format(method_sig,'\n '.join(parse_abi(data))))
ret = g.rpch.eth_call({ 'to': '0x'+self.addr, 'data': '0x'+data })
return int(ret,16) * self.base_unit if toUnit else ret
def balance(self,acct_addr):
return self.do_call('balanceOf(address)',acct_addr.rjust(64,'0'),toUnit=True)
return ETHAmt(self.do_call('balanceOf(address)',acct_addr.rjust(64,'0'),toUnit=True))
def strip(self,s):
return ''.join([chr(b) for b in s if 32 <= b <= 127]).strip()
def total_supply(self): return self.do_call('totalSupply()',toUnit=True)
def decimals(self):
ret = self.do_call('decimals()')
try:
@ -79,8 +78,15 @@ class Token(MMGenObject): # ERC20
except:
"RPC call to decimals() failed (returned '{}')".format(ret)
return int(b,16) if b else None
def name(self): return self.strip(bytes.fromhex(self.do_call('name()')[2:]))
def symbol(self): return self.strip(bytes.fromhex(self.do_call('symbol()')[2:]))
def name(self):
return self.strip(bytes.fromhex(self.do_call('name()')[2:]))
def symbol(self):
return self.strip(bytes.fromhex(self.do_call('symbol()')[2:]))
def total_supply(self):
return self.do_call('totalSupply()',toUnit=True)
def info(self):
fs = '{:15}{}\n' * 5
@ -108,8 +114,8 @@ class Token(MMGenObject): # ERC20
'startgas': start_gas.toWei(),
'gasprice': gasPrice.toWei(),
'value': 0,
'nonce': nonce,
'data': bytes.fromhex(data) }
'nonce': nonce,
'data': bytes.fromhex(data) }
def txsign(self,tx_in,key,from_addr,chain_id=None):
@ -125,8 +131,9 @@ class Token(MMGenObject): # ERC20
m = "Sender address '{}' does not match address of key '{}'!"
die(3,m.format(from_addr,tx.sender.hex()))
if g.debug:
msg('{}'.format('\n '.join(parse_abi(data))))
msg('TOKEN DATA:')
pmsg(tx.to_dict())
msg('PARSED ABI DATA:\n {}'.format('\n '.join(parse_abi(tx.data.hex()))))
return hex_tx,coin_txid
# The following are used for token deployment only:
@ -145,9 +152,3 @@ class Token(MMGenObject): # ERC20
from_addr2=from_addr2)
(hex_tx,coin_txid) = self.txsign(tx_in,key,from_addr)
return self.txsend(hex_tx)
def transfer_from(self,from_addr,to_addr,amt,key,start_gas,gasPrice):
raise NotImplementedError('method not implemented')
return self.transfer( from_addr,to_addr,amt,key,start_gas,gasPrice,
method_sig='transferFrom(address,address,uint256)',
from_addr2=from_addr)

View file

@ -233,11 +233,11 @@ class EthereumTwAddrList(TwAddrList):
rpc_init()
if g.token: self.token = Token(g.token)
tw = TrackingWallet().mmid_ordered_dict()
tw_dict = TrackingWallet().mmid_ordered_dict()
self.total = g.proto.coin_amt('0')
from mmgen.obj import CoinAddr
for mmid,d in list(tw.items()):
for mmid,d in list(tw_dict.items()):
# if d['confirmations'] < minconf: continue # cannot get confirmations for eth account
label = TwLabel(mmid+' '+d['comment'],on_fail='raise')
if usr_addr_list and (label.mmid not in usr_addr_list): continue

View file

@ -44,7 +44,7 @@ class EthereumMMGenTX(MMGenTX):
usr_rel_fee = None # not in MMGenTX
disable_fee_check = False
txobj = None # ""
data = HexStr('')
usr_contract_data = HexStr('')
def __init__(self,*args,**kwargs):
super(EthereumMMGenTX,self).__init__(*args,**kwargs)
@ -53,7 +53,7 @@ class EthereumMMGenTX(MMGenTX):
if hasattr(opt,'contract_data') and opt.contract_data:
m = "'--contract-data' option may not be used with token transaction"
assert not 'Token' in type(self).__name__, m
self.data = HexStr(open(opt.contract_data).read().strip())
self.usr_contract_data = HexStr(open(opt.contract_data).read().strip())
self.disable_fee_check = True
@classmethod
@ -120,7 +120,7 @@ class EthereumMMGenTX(MMGenTX):
'chainId': Int(d['chainId']),
'data': HexStr(d['data']) }
self.tx_gas = o['startGas'] # approximate, but better than nothing
self.data = o['data']
self.usr_contract_data = o['data']
if o['data'] and not o['to']: self.disable_fee_check = True
self.fee = self.fee_rel2abs(o['gasPrice'].toWei())
self.txobj = o
@ -139,7 +139,7 @@ class EthereumMMGenTX(MMGenTX):
'startGas': self.start_gas,
'nonce': self.get_nonce(),
'chainId': Int(g.rpch.request(chain_id_method),16),
'data': self.data,
'data': self.usr_contract_data,
}
# Instead of serializing tx data as with BTC, just create a JSON dump.
@ -147,7 +147,7 @@ class EthereumMMGenTX(MMGenTX):
# thus removing an attack vector
def create_raw(self):
assert len(self.inputs) == 1,'Transaction has more than one input!'
o_ok = (0,1) if self.data else (1,)
o_ok = (0,1) if self.usr_contract_data else (1,)
o_num = len(self.outputs)
assert o_num in o_ok,'Transaction has invalid number of outputs!'.format(o_num)
self.make_txobj()
@ -166,7 +166,7 @@ class EthereumMMGenTX(MMGenTX):
def process_cmd_args(self,cmd_args,ad_f,ad_w):
lc = len(cmd_args)
if lc == 0 and self.data and not 'Token' in type(self).__name__: return
if lc == 0 and self.usr_contract_data and not 'Token' in type(self).__name__: return
if lc != 1:
fs = '{} output{} specified, but Ethereum transactions must have exactly one'
die(1,fs.format(lc,suf(lc)))
@ -264,7 +264,7 @@ class EthereumMMGenTX(MMGenTX):
def format_view_abs_fee(self):
fee = self.fee_rel2abs(self.txobj['gasPrice'].toWei())
note = ' (max)' if self.data else ''
note = ' (max)' if self.usr_contract_data else ''
return fee.hl() + note
def format_view_rel_fee(self,terse): return ''
@ -337,7 +337,7 @@ class EthereumMMGenTX(MMGenTX):
confs = self.is_in_wallet()
if confs is not False:
if self.data:
if self.usr_contract_data:
exec_status = type(self).get_exec_status(self.coin_txid)
if exec_status == 0:
msg('Contract failed to execute!')
@ -451,12 +451,12 @@ class EthereumTokenMMGenTX(EthereumMMGenTX):
o['token_addr'] = TokenAddr(d['token_addr'])
o['decimals'] = Int(d['decimals'])
t = Token(o['token_addr'],o['decimals'])
self.data = o['data'] = t.create_data(o['to'],o['amt'])
self.usr_contract_data = o['data'] = t.create_data(o['to'],o['amt'])
def format_view_body(self,*args,**kwargs):
if self.data:
if self.usr_contract_data:
from .contract import Token
self.txobj['token_to'] = Token.transferdata2sendaddr(self.data)
self.txobj['token_to'] = Token.transferdata2sendaddr(self.usr_contract_data)
return 'Token: {d} {c}\n{r}'.format(
d=self.txobj['token_addr'].hl(),
c=blue('(' + g.dcoin + ')'),

View file

@ -24,10 +24,15 @@ mmgen.exception: Exception classes for the MMGen suite
class UserNonConfirmation(Exception): mmcode = 1
class BadAgeFormat(Exception): mmcode = 1
class BadFilename(Exception): mmcode = 1
class SocketError(Exception): mmcode = 1
class UserAddressNotInWallet(Exception): mmcode = 1
# 2: yellow hl, message only
class InvalidTokenAddress(Exception): mmcode = 2
class UnrecognizedTokenSymbol(Exception): mmcode = 2
class TokenNotInBlockchain(Exception): mmcode = 2
class TokenNotInWallet(Exception): mmcode = 2
class BadTwComment(Exception): mmcode = 2
# 3: yellow hl, 'MMGen Error' + exception + message
class RPCFailure(Exception): mmcode = 3

View file

@ -39,6 +39,7 @@ def launch(mod):
__import__('mmgen.main_' + mod)
except KeyboardInterrupt:
sys.stderr.write('\nUser interrupt\n')
sys.exit(1) # must exit normally so exit handlers will be called
except EOFError:
sys.stderr.write('\nEnd of file\n')
except Exception as e:

View file

@ -170,16 +170,14 @@ def do_umount():
def sign_tx_file(txfile,signed_txs):
try:
g.testnet = False
g.coin = 'BTC'
init_coin('BTC',testnet=False)
tmp_tx = mmgen.tx.MMGenTX(txfile,metadata_only=True)
init_coin(tmp_tx.coin)
if tmp_tx.chain != 'mainnet':
if tmp_tx.chain == 'testnet' or (
hasattr(g.proto,'chain_name') and tmp_tx.chain != g.proto.chain_name):
g.testnet = True
init_coin(tmp_tx.coin)
init_coin(tmp_tx.coin,testnet=True)
if hasattr(g.proto,'chain_name'):
m = 'Chains do not match! tx file: {}, proto: {}'

View file

@ -157,7 +157,7 @@ if invoked_as in ('conv','passchg','subgen'):
gmsg('Processing output wallet')
if invoked_as == 'subgen':
ss_out = SeedSource(seed=ss_in.seed.subseed(ss_idx,print_msg=True).data)
ss_out = SeedSource(seed_bin=ss_in.seed.subseed(ss_idx,print_msg=True).data)
else:
ss_out = SeedSource(ss=ss_in,passchg=invoked_as=='passchg')

View file

@ -491,7 +491,8 @@ class BTCAmt(Decimal,Hilite,InitErrors):
def __str__(self,color=False): # format simply, no exponential notation
return self.colorize(
str(int(self)) if int(self) == self else self.normalize().__format__('f'),
str(int(self)) if int(self) == self else
self.normalize().__format__('f'),
color=color)
def __repr__(self):

View file

@ -348,13 +348,15 @@ def init(opts_data,add_opts=[],opt_filter=None,parse_only=False):
def opt_is_tx_fee(val,desc):
from mmgen.tx import MMGenTX
ret = MMGenTX().process_fee_spec(val,224,on_fail='return')
tx = MMGenTX()
# TODO: size is just a guess; do this check after parsing tx file
ret = tx.process_fee_spec(val,224,on_fail='return')
# Non-standard startgas: disable fee checking
if hasattr(opt,'contract_data') and opt.contract_data: ret = None
if hasattr(opt,'tx_gas') and opt.tx_gas: ret = None
if ret == False:
msg("'{}': invalid {}\n(not a {} amount or {} specification)".format(
val,desc,g.coin.upper(),MMGenTX().rel_fee_desc))
val,desc,g.coin.upper(),tx.rel_fee_desc))
elif ret != None and ret > g.proto.max_tx_fee:
msg("'{}': invalid {}\n({} > max_tx_fee ({} {}))".format(
val,desc,ret.fmt(fs='1.1'),g.proto.max_tx_fee,g.coin.upper()))

View file

@ -21,7 +21,7 @@ protocol.py: Coin protocol functions, classes and methods
"""
import sys,os,hashlib
from mmgen.util import msg,pmsg,ymsg,Msg,pdie,ydie
from mmgen.util import msg,ymsg,Msg,ydie
from mmgen.obj import MMGenObject,BTCAmt,LTCAmt,BCHAmt,B2XAmt,ETHAmt
from mmgen.globalvars import g
import mmgen.bech32 as bech32
@ -520,7 +520,6 @@ def make_init_genonly_altcoins_str(data):
if proto+'Protocol' in globals(): continue
if coin.lower() in CoinProtocol.coins: continue
out += fs.format(coin.lower(),proto,('None',proto+'TestnetProtocol')[coin in tn_coins])
# print out
return out
def init_coin(coin,testnet=None):

View file

@ -333,4 +333,4 @@ def generate(blocks=1,silent=False):
if not out or len(literal_eval(out)) != blocks:
rdie(1,'Error generating blocks')
p.wait()
gmsg('Mined {} block{}'.format(blocks,suf(blocks,'s')))
gmsg('Mined {} block{}'.format(blocks,suf(blocks)))

View file

@ -28,7 +28,7 @@ from decimal import Decimal
def dmsg_rpc(s):
if g.debug_rpc: msg(s)
class CoinDaemonRPCConnection(object):
class CoinDaemonRPCConnection(MMGenObject):
auth = True
db_fs = ' host [{h}] port [{p}] user [{u}] passwd [{pw}] auth_cookie [{c}]\n'
@ -42,7 +42,7 @@ class CoinDaemonRPCConnection(object):
try:
socket.create_connection((host,port),timeout=3).close()
except:
die(1,'Unable to connect to {}:{}'.format(host,port))
raise SocketError('Unable to connect to {}:{}'.format(host,port))
if not self.auth:
pass

View file

@ -72,6 +72,10 @@ class SeedBase(MMGenObject):
def hexdata(self):
return self.data.hex()
@property
def fn_stem(self):
return self.sid
class SubSeedList(MMGenObject):
have_short = True
nonce_start = 0
@ -612,7 +616,7 @@ class SeedSourceUnenc(SeedSource):
def _filename(self):
s = self.seed
return '{}[{}]{x}.{}'.format(
s.sid,
s.fn_stem,
s.bitlen,
self.ext,
x='' if g.debug_utf8 else '')
@ -1121,7 +1125,7 @@ class Wallet (SeedSourceEnc):
s = self.seed
d = self.ssdata
return '{}-{}[{},{}]{x}.{}'.format(
s.sid,
s.fn_stem,
d.key_id,
s.bitlen,
d.hash_preset,
@ -1248,7 +1252,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
s = self.seed
d = self.ssdata
return '{}-{}-{}[{},{}]{x}.{}'.format(
s.sid,
s.fn_stem,
d.key_id,
d.iv_id,
s.bitlen,

View file

@ -111,7 +111,7 @@ def _process_args(cmd,cmd_args):
if flag != 'VAR_ARGS':
if len(cmd_args) < len(c_args):
m1 = 'Command requires exactly {} non-keyword argument{}'
msg(m1.format(len(c_args),suf(c_args,'s')))
msg(m1.format(len(c_args),suf(c_args)))
_usage(cmd)
u_args = cmd_args[:len(c_args)]
@ -459,25 +459,25 @@ class MMGenToolCmdMnemonic(MMGenToolCmdBase):
IMPORTANT NOTE: Though MMGen mnemonics use the Electrum wordlist, they're
computed using a different algorithm and are NOT Electrum-compatible!
"""
def _do_random_mn(self,nbytes:int,wordlist_id:str):
def _do_random_mn(self,nbytes:int,wordlist:str):
assert nbytes in (16,24,32), 'nbytes must be 16, 24 or 32'
hexrand = get_random(nbytes).hex()
Vmsg('Seed: {}'.format(hexrand))
return self.hex2mn(hexrand,wordlist_id=wordlist_id)
return self.hex2mn(hexrand,wordlist=wordlist)
def mn_rand128(self,wordlist=dfl_wl_id):
"generate random 128-bit mnemonic"
"generate random 128-bit mnemonic seed phrase"
return self._do_random_mn(16,wordlist)
def mn_rand192(self,wordlist=dfl_wl_id):
"generate random 192-bit mnemonic"
"generate random 192-bit mnemonic seed phrase"
return self._do_random_mn(24,wordlist)
def mn_rand256(self,wordlist=dfl_wl_id):
"generate random 256-bit mnemonic"
"generate random 256-bit mnemonic seed phrase"
return self._do_random_mn(32,wordlist)
def hex2mn(self,hexstr:'sstr',wordlist_id=dfl_wl_id):
def hex2mn(self,hexstr:'sstr',wordlist=dfl_wl_id):
"convert a 16, 24 or 32-byte hexadecimal number to a mnemonic"
opt.out_fmt = 'words'
from mmgen.seed import SeedSource
@ -553,7 +553,9 @@ class MMGenToolCmdFile(MMGenToolCmdBase):
flist.sort_by_age(key=file_sort) # in-place sort
sep = ''*77+'\n'
return sep.join([MMGenTX(fn).format_view(terse=terse,sort=tx_sort) for fn in flist.names()]).rstrip()
return sep.join(
[MMGenTX(fn).format_view(terse=terse,sort=tx_sort) for fn in flist.names()]
).rstrip()
class MMGenToolCmdFileCrypt(MMGenToolCmdBase):
"""
@ -787,11 +789,11 @@ class MMGenToolCmdRPC(MMGenToolCmdBase):
"view tracking wallet"
rpc_init()
from mmgen.tw import TwUnspentOutputs
tw = TwUnspentOutputs(minconf=minconf)
tw.do_sort(sort,reverse=reverse)
tw.age_fmt = age_fmt
tw.show_mmid = show_mmid
return tw.format_for_printing(color=True) if wide else tw.format_for_display()
twuo = TwUnspentOutputs(minconf=minconf)
twuo.do_sort(sort,reverse=reverse)
twuo.age_fmt = age_fmt
twuo.show_mmid = show_mmid
return twuo.format_for_printing(color=True) if wide else twuo.format_for_display()
def add_label(self,mmgen_or_coin_addr:str,label:str):
"add descriptive label for address in tracking wallet"

View file

@ -108,6 +108,16 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
return sum(i.amt for i in self.unspent)
def get_unspent_rpc(self):
# bitcoin-cli help listunspent:
# Arguments:
# 1. minconf (numeric, optional, default=1) The minimum confirmations to filter
# 2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter
# 3. addresses (json array, optional, default=empty array) A json array of bitcoin addresses
# 4. include_unsafe (boolean, optional, default=true) Include outputs that are not safe to spend
# 5. query_options (json object, optional) JSON with query options
# for now, self.addrs is just an empty list for Bitcoin and friends
add_args = (9999999,self.addrs) if self.addrs else ()
return g.rpch.listunspent(self.minconf)
def get_unspent_data(self):
@ -115,8 +125,6 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
us_rpc = eval(get_data_from_file(g.bogus_wallet_data)) # testing, so ok
else:
us_rpc = self.get_unspent_rpc()
# write_data_to_file('bogus_unspent.json', repr(us), 'bogus unspent data')
# sys.exit(0)
if not us_rpc: die(0,self.wmsg['no_spendable_outputs'])
confs_per_day = 60*60*24 // g.proto.secs_per_block
@ -288,7 +296,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
return self.fmt_print
def display_total(self):
fs = '\nTotal unspent: {} {} ({} outputs)'
fs = '\nTotal unspent: {} {} ({} output%s)' % suf(self.unspent)
msg(fs.format(self.total.hl(),g.dcoin,len(self.unspent)))
def get_idx_from_user(self,get_label=False):
@ -469,7 +477,7 @@ class TwAddrList(MMGenDict):
if age_fmt not in age_fmts:
raise BadAgeFormat("'{}': invalid age format (must be one of {!r})".format(age_fmt,age_fmts))
out = ['Chain: '+green(g.chain.upper())] if g.chain != 'mainnet' else []
fs = '{{mid}}{} {{cmt}} {{amt}}{}'.format(('',' {addr}')[showbtcaddrs],('',' {age}')[show_age])
fs = '{mid}' + ('',' {addr}')[showbtcaddrs] + ' {cmt} {amt}' + ('',' {age}')[show_age]
mmaddrs = [k for k in self.keys() if k.type == 'mmgen']
max_mmid_len = max(len(k) for k in mmaddrs) + 2 if mmaddrs else 10
max_cmt_width = max(max(v['lbl'].comment.screen_width for v in self.values()),7)
@ -483,7 +491,7 @@ class TwAddrList(MMGenDict):
cmt=TwComment.fmtc('COMMENT',width=max_cmt_width+1),
amt='BALANCE'.ljust(max_fp_len+4),
age=('CONFS','DAYS')[age_fmt=='days'],
)]
).rstrip()]
def sort_algo(j):
if sort and 'age' in sort:
@ -515,7 +523,7 @@ class TwAddrList(MMGenDict):
cmt=e['lbl'].comment.fmt(width=max_cmt_width,color=True,nullrepl='-'),
amt=e['amt'].fmt('4.{}'.format(max(max_fp_len,3)),color=True),
age=mmid.confs // (1,confs_per_day)[age_fmt=='days'] if hasattr(mmid,'confs') and mmid.confs != None else '-'
))
).rstrip())
return '\n'.join(out + ['\nTOTAL: {} {}'.format(self.total.hl(color=True),g.dcoin)])

View file

@ -735,7 +735,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
self.check_pubkey_scripts()
qmsg('Passing {} key{} to {}'.format(len(keys),suf(keys,'s'),g.proto.daemon_name))
qmsg('Passing {} key{} to {}'.format(len(keys),suf(keys),g.proto.daemon_name))
if self.has_segwit_inputs():
from mmgen.addr import KeyGenerator,AddrGenerator
@ -781,12 +781,12 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
msg('OK')
return True
except Exception as e:
if g.traceback:
import traceback
ymsg('\n'+''.join(traceback.format_exception(*sys.exc_info())))
try: m = '{}'.format(e.args[0])
except: m = repr(e.args[0])
msg('\n'+yellow(m))
if g.traceback:
import traceback
ymsg('\n'+''.join(traceback.format_exception(*sys.exc_info())))
return False
def mark_raw(self):
@ -1548,6 +1548,7 @@ class MMGenBumpTX(MMGenTX):
return False
return ret
# NOT MAINTAINED
class MMGenSplitTX(MMGenTX):
def get_outputs_from_cmdline(self,mmid): # TODO: check that addr is empty

View file

@ -70,7 +70,7 @@ def get_seed_for_seed_id(sid,infiles,saved_seeds):
def generate_kals_for_mmgen_addrs(need_keys,infiles,saved_seeds):
mmids = [e.mmid for e in need_keys]
sids = {i.sid for i in mmids}
vmsg('Need seed{}: {}'.format(suf(sids,'s'),' '.join(sids)))
vmsg('Need seed{}: {}'.format(suf(sids),' '.join(sids)))
d = MMGenList()
from mmgen.addr import KeyAddrList
for sid in sids:
@ -104,7 +104,7 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None):
else:
die(3,wmsg['mapping_error'].format(m1,mmid,f.addr,'tx file:',e.mmid,e.addr))
if new_keys:
vmsg('Added {} wif key{} from {}'.format(len(new_keys),suf(new_keys,'s'),desc))
vmsg('Added {} wif key{} from {}'.format(len(new_keys),suf(new_keys),desc))
return new_keys
def _pop_and_return(args,cmplist): # strips found args
@ -167,6 +167,6 @@ def txsign(tx,seed_files,kl,kal,tx_num_str=''):
extra_sids = set(saved_seeds) - tx.get_input_sids() - tx.get_output_sids()
if extra_sids:
msg('Unused Seed ID{}: {}'.format(suf(extra_sids,'s'),' '.join(extra_sids)))
msg('Unused Seed ID{}: {}'.format(suf(extra_sids),' '.join(extra_sids)))
return tx.sign(tx_num_str,keys) # returns True or False

View file

@ -184,7 +184,7 @@ def dmsg(s):
if opt.debug: msg(s)
def suf(arg,suf_type='s'):
suf_types = { 's': ('s',''), 'es': ('es',''), 'y': ('ies','y') }
suf_types = { 's': '', 'es': '', 'ies': 'y' }
assert suf_type in suf_types
t = type(arg)
if t == int:
@ -193,7 +193,7 @@ def suf(arg,suf_type='s'):
n = len(arg)
else:
die(2,'{}: invalid parameter for suf()'.format(arg))
return suf_types[suf_type][n==1]
return suf_types[suf_type] if n == 1 else suf_type
def get_extension(f):
a,b = os.path.splitext(f)

View file

@ -12,6 +12,7 @@ def traceback_run_init():
if 'TMUX' in os.environ: del os.environ['TMUX']
os.environ['MMGEN_TRACEBACK'] = '1'
os.environ['PYTHONPATH'] = '.'
of = 'my.err'
try: os.unlink(of)
@ -28,11 +29,11 @@ def traceback_run_process_exception():
exc = l.pop()
if exc[:11] == 'SystemExit:': l.pop()
if os.getenv('MMGEN_DISABLE_COLOR'):
if False: # was: if os.getenv('MMGEN_DISABLE_COLOR'):
sys.stdout.write('{}{}'.format(''.join(l),exc))
else:
red = lambda s: '\033[31;1m{}\033[0m'.format(s)
yellow = lambda s: '\033[33;1m{}\033[0m'.format(s)
def red(s): return '\033[31;1m{}\033[0m'.format(s)
def yellow(s): return '\033[33;1m{}\033[0m'.format(s)
sys.stdout.write('{}{}'.format(yellow(''.join(l)),red(exc)))
open(traceback_run_outfile,'w').write(''.join(l+[exc]))
@ -50,7 +51,8 @@ except SystemExit as e:
sys.exit(e.code)
except Exception as e:
traceback_run_process_exception()
sys.exit(e.mmcode if hasattr(e,'mmcode') else e.code if hasattr(e,'code') else 1)
retval = e.mmcode if hasattr(e,'mmcode') else e.code if hasattr(e,'code') else 1
sys.exit(retval)
blue = lambda s: s if os.getenv('MMGEN_DISABLE_COLOR') else '\033[34;1m{}\033[0m'.format(s)
sys.stdout.write(blue('Runtime: {:0.5f} secs\n'.format(time.time() - traceback_run_tstart)))

View file

@ -69,6 +69,7 @@ def create_shm_dir(data_dir,trash_dir):
import sys,os,time
os.environ['MMGEN_TEST_SUITE'] = '1'
repo_root = os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),os.pardir)))
os.chdir(repo_root)
sys.path.__setitem__(0,repo_root)
@ -386,7 +387,7 @@ def clean(usr_dirs=None):
else:
die(1,'{}: invalid directory number'.format(d))
if dirlist:
iqmsg(green('Cleaned tmp director{} {}'.format(suf(dirlist,'y'),' '.join(dirlist))))
iqmsg(green('Cleaned tmp director{} {}'.format(suf(dirlist,'ies'),' '.join(dirlist))))
cleandir(data_dir)
cleandir(trash_dir)
iqmsg(green("Cleaned directories '{}'".format("' '".join([data_dir,trash_dir]))))
@ -429,8 +430,6 @@ def set_environ_for_spawned_scripts():
os.environ['MMGEN_NO_LICENSE'] = '1'
os.environ['MMGEN_MIN_URANDCHARS'] = '3'
os.environ['MMGEN_BOGUS_SEND'] = '1'
# Tell spawned programs they're running in the test suite
os.environ['MMGEN_TEST_SUITE'] = '1'
def set_restore_term_at_exit():
import termios,atexit
@ -880,23 +879,17 @@ try:
tr = TestSuiteRunner(data_dir,trash_dir)
tr.run_tests(usr_args)
except KeyboardInterrupt:
die(1,'\nExiting at user request')
die(1,'\ntest.py exiting at user request')
except TestSuiteException as e:
ydie(1,e.args[0])
except TestSuiteFatalException as e:
rdie(1,e.args[0])
except Exception:
if opt.traceback:
msg(blue('Spawned script exited with error'))
else:
import traceback
print(''.join(traceback.format_exception(*sys.exc_info())))
try:
os.stat('my.err')
t = open('my.err').readlines()
if t:
msg_r('\n'+yellow(''.join(t[:-1]))+red(t[-1]))
except: pass
die(1,blue('Test script exited with error'))
else:
raise
msg(blue('Test script exited with error'))
except:
raise

View file

@ -155,8 +155,8 @@ class TestSuiteAutosign(TestSuiteBase):
def do_autosign(opts,mountpoint):
make_wallet(opts)
copy_files(mountpoint,include_bad_tx=True)
t = self.spawn('mmgen-autosign',opts+['--full-summary','wait'],extra_desc='(sign - full summary)')
t.expect('{} transactions signed'.format(txcount))
t.expect('2 transactions failed to sign')