whitespace, comments throughout (excluding tests)

This commit is contained in:
The MMGen Project 2023-10-11 12:58:47 +00:00
commit 2449baed2c
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
36 changed files with 280 additions and 125 deletions

View file

@ -123,7 +123,8 @@ class AddrFile(MMGenObject):
out.append(fs.format( '', f'{p.al_id.mmtype.wif_label}: {e.sec.wif}', c ))
for k in ('viewkey','wallet_passwd'):
v = getattr(e,k)
if v: out.append(fs.format( '', f'{k}: {v}', c ))
if v:
out.append(fs.format( '', f'{k}: {v}', c ))
out.append('}')
self.fmt_data = '\n'.join([l.rstrip() for l in out]) + '\n'
@ -270,12 +271,10 @@ class AddrFile(MMGenObject):
if type(p).__name__ != 'PasswordList':
if proto.base_coin != p.proto.base_coin or proto.network != p.proto.network:
"""
Having caller supply protocol and checking address file protocol against it here
allows us to catch all mismatches in one place. This behavior differs from that of
transaction files, which determine the protocol independently, requiring the caller
to check for protocol mismatches (e.g. mmgen.tx.completed.check_correct_chain())
"""
# Having caller supply protocol and checking address file protocol against it here
# allows us to catch all mismatches in one place. This behavior differs from that of
# transaction files, which determine the protocol independently, requiring the caller
# to check for protocol mismatches (e.g. mmgen.tx.completed.check_correct_chain())
raise ValueError(
f'{p.desc} file is '
+ f'{proto.base_coin} {proto.network} but protocol is '

View file

@ -23,7 +23,8 @@ amt: MMGen CoinAmt and related classes
from decimal import Decimal
from .objmethods import Hilite,InitErrors
class DecimalNegateResult(Decimal): pass
class DecimalNegateResult(Decimal):
pass
class CoinAmt(Decimal,Hilite,InitErrors): # abstract class
"""

View file

@ -431,8 +431,10 @@ class Autosign:
return False
def wipe_existing_key(self):
try: self.keyfile.stat()
except: pass
try:
self.keyfile.stat()
except:
pass
else:
from .fileutil import shred_file
msg(f"\nShredding existing key '{self.keyfile}'")
@ -462,14 +464,20 @@ class Autosign:
def remove_wallet_dir():
msg(f"Deleting '{self.wallet_dir}'")
import shutil
try: shutil.rmtree(self.wallet_dir)
except: pass
try:
shutil.rmtree(self.wallet_dir)
except:
pass
def create_wallet_dir():
try: self.wallet_dir.mkdir(parents=True)
except: pass
try: self.wallet_dir.stat()
except: die(2,f"Unable to create wallet directory '{self.wallet_dir}'")
try:
self.wallet_dir.mkdir(parents=True)
except:
pass
try:
self.wallet_dir.stat()
except:
die(2,f"Unable to create wallet directory '{self.wallet_dir}'")
remove_wallet_dir()
create_wallet_dir()
@ -520,8 +528,10 @@ class Autosign:
async_run(m.stop_wallet_daemon())
import shutil
try: shutil.rmtree(self.xmr_outputs_dir)
except: pass
try:
shutil.rmtree(self.xmr_outputs_dir)
except:
pass
self.xmr_outputs_dir.mkdir(parents=True)
@ -574,9 +584,12 @@ class Autosign:
def get_insert_status(self):
if self.cfg.no_insert_check:
return True
try: self.dev_disk_path.stat()
except: return False
else: return True
try:
self.dev_disk_path.stat()
except:
return False
else:
return True
async def do_loop(self):
if not self.cfg.stealth_led:

View file

@ -767,7 +767,8 @@ def check_opts(cfg): # Raises exception if any check fails
check_infile(fn,blkdev_ok=True)
key2 = 'in_fmt'
else:
try: os.stat(fn)
try:
os.stat(fn)
except:
b = os.path.dirname(fn)
if b:

View file

@ -25,9 +25,11 @@
q = 2**255 - 19
def expmod(b,e,m):
if e == 0: return 1
if e == 0:
return 1
t = expmod(b,e//2,m)**2 % m
if e & 1: t = (t*b) % m
if e & 1:
t = (t*b) % m
return t
# Can probably get some extra speedup here by replacing this with
@ -81,7 +83,8 @@ def pt_unxform (pt):
return ((x*inv(z))%q, (y*inv(z))%q)
def xpt_mult (pt, n):
if n==0: return pt_xform((0,1))
if n==0:
return pt_xform((0,1))
_ = xpt_double(xpt_mult(pt, n>>1))
return xpt_add(_, pt) if n&1 else _

View file

@ -129,7 +129,8 @@ class KeccakState(object):
Formats the given state as hex, in natural byte order.
"""
rows = []
def fmt(x): return '%016x' % x
def fmt(x):
return '%016x' % x
for y in KeccakState.rangeH:
row = []
for x in rangeW:

View file

@ -126,7 +126,15 @@ class Crypto:
self.util.dmsg(f'Decrypted seed: {dec_seed.hex()}')
return dec_seed
def encrypt_data(self,data,key,iv=aesctr_dfl_iv,desc='data',verify=True,silent=False):
def encrypt_data(
self,
data,
key,
iv = aesctr_dfl_iv,
desc = 'data',
verify = True,
silent = False ):
from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
from cryptography.hazmat.backends import default_backend
if not silent:
@ -147,7 +155,13 @@ class Crypto:
return enc_data
def decrypt_data(self,enc_data,key,iv=aesctr_dfl_iv,desc='data'):
def decrypt_data(
self,
enc_data,
key,
iv = aesctr_dfl_iv,
desc = 'data' ):
from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
from cryptography.hazmat.backends import default_backend
self.util.vmsg_r(f'Decrypting {desc} with key...')
@ -155,7 +169,12 @@ class Crypto:
encryptor = c.encryptor()
return encryptor.update(enc_data) + encryptor.finalize()
def scrypt_hash_passphrase(self,passwd,salt,hash_preset,buflen=32):
def scrypt_hash_passphrase(
self,
passwd,
salt,
hash_preset,
buflen = 32 ):
# Buflen arg is for brainwallets only, which use this function to generate
# the seed directly.
@ -198,11 +217,20 @@ class Crypto:
return ret
def make_key(self,passwd,salt,hash_preset,desc='encryption key',from_what='passphrase',verbose=False):
def make_key(
self,
passwd,
salt,
hash_preset,
desc = 'encryption key',
from_what = 'passphrase',
verbose = False ):
if self.cfg.verbose or verbose:
msg_r(f"Generating {desc}{' from ' + from_what if from_what else ''}...")
key = self.scrypt_hash_passphrase(passwd,salt,hash_preset)
if self.cfg.verbose or verbose: msg('done')
if self.cfg.verbose or verbose:
msg('done')
self.util.dmsg(f'Key: {key.hex()}')
return key

View file

@ -120,10 +120,8 @@ class Daemon(Lockable):
with open(self.pidfile) as fp:
return fp.read().strip()
elif self.platform == 'win':
"""
Assumes only one running instance of given daemon. If multiple daemons are running,
the first PID in the list is returned and self.pids is set to the PID list.
"""
# Assumes only one running instance of given daemon. If multiple daemons are running,
# the first PID in the list is returned and self.pids is set to the PID list.
ss = f'{self.exec_fn}.exe'
cp = self.run_cmd(['ps','-Wl'],silent=True)
self.pids = ()

View file

@ -185,8 +185,10 @@ def write_data_to_file(
message = '',
action = f'output {desc} to screen' )
else:
try: of = os.readlink(f'/proc/{os.getpid()}/fd/1') # Linux
except: of = None # Windows
try:
of = os.readlink(f'/proc/{os.getpid()}/fd/1') # Linux
except:
of = None # Windows
if of:
if of[:5] == 'pipe:':

View file

@ -66,9 +66,12 @@ class LEDControl:
for board_id,board in self.boards.items():
if board_id == 'dummy' and not simulate:
continue
try: os.stat(board.status)
except: pass
else: break
try:
os.stat(board.status)
except:
pass
else:
break
else:
from .util import die
die( 'NoLEDSupport', 'Control files not found! LED control not supported on this system' )
@ -119,7 +122,8 @@ class LEDControl:
os.unlink(db.status)
os.unlink(db.trigger)
def noop(self,*args,**kwargs): pass
def noop(self,*args,**kwargs):
pass
def ev_sleep(self,secs):
self.ev.wait(secs)

View file

@ -28,7 +28,7 @@ opts_data = {
'sets': [('yes', True, 'quiet', True)],
'text': {
'desc': f'Create, sign and send an {gc.proj_name} transaction',
'usage': '[opts] [<addr,amt> ...] <change addr, addrlist ID or addr type> [addr file ...] ' +
'usage': '[opts] [<addr,amt> ...] <change addr, addrlist ID or addr type> [addr file ...] ' +
'[seed source ...]',
'options': """
-h, --help Print this help message

View file

@ -74,10 +74,17 @@ class IndexedDict(dict):
def key(self,idx):
return self.__keylist[idx]
def __delitem__(self,*args): self.die('item deletion')
def move_to_end(self,*args): self.die('item moving')
def clear(self,*args): self.die('clearing')
def update(self,*args): self.die('updating')
def __delitem__(self,*args):
self.die('item deletion')
def move_to_end(self,*args):
self.die('item moving')
def clear(self,*args):
self.die('clearing')
def update(self,*args):
self.die('updating')
def die(self,desc):
raise NotImplementedError(f'{desc} not implemented for type {type(self).__name__}')
@ -106,10 +113,10 @@ class ImmutableAttr: # Descriptor
assert typeconv and type(dtype) != str, 'ImmutableAttr_check3'
if dtype is None:
'use instance-defined conversion function for this attribute'
# use instance-defined conversion function for this attribute
self.conv = lambda instance,value: getattr(instance.conv_funcs,self.name)(instance,value)
elif typeconv:
"convert this attribute's type"
# convert this attribute's type
if set_none_ok:
self.conv = lambda instance,value: None if value is None else dtype(value)
elif include_proto:
@ -158,8 +165,10 @@ class ListItemAttr(ImmutableAttr):
def __get__(self,instance,owner):
"return None if attribute doesn't exist"
try: return instance.__dict__[self.name]
except: return None
try:
return instance.__dict__[self.name]
except:
return None
def setattr_condition(self,instance):
return getattr(instance,self.name) == None or self.reassign_ok

View file

@ -28,7 +28,8 @@ if 'MMGenObjectDevTools' in __builtins__: # added to builtins by devinit.init_de
else:
class MMGenObject:
'placeholder - overridden when testing'
def immutable_attr_init_check(self): pass
def immutable_attr_init_check(self):
pass
def truncate_str(s,width): # width = screen width
wide_count = 0

View file

@ -52,8 +52,10 @@ class PasswordList(AddrList):
dfl_pw_fmt = 'b58'
pwinfo = namedtuple('passwd_info',['min_len','max_len','dfl_len','valid_lens','desc','chk_func'])
pw_info = {
'b32': pwinfo(10, 42 ,24, None, 'base32 password', 'baseconv.is_b32_str'), # 32**24 < 2**128
'b58': pwinfo(8, 36 ,20, None, 'base58 password', 'baseconv.is_b58_str'), # 58**20 < 2**128
# 32**25 < 2**128 < 32**26
'b32': pwinfo(10, 42 ,24, None, 'base32 password', 'baseconv.is_b32_str'),
# 58**21 < 2**128 < 58**22
'b58': pwinfo(8, 36 ,20, None, 'base58 password', 'baseconv.is_b58_str'),
'bip39': pwinfo(12, 24 ,24, [12,18,24],'BIP39 mnemonic', 'bip39.is_bip39_mnemonic'),
'xmrseed': pwinfo(25, 25, 25, [25], 'Monero new-style mnemonic','xmrseed.is_xmrseed'),
'hex': pwinfo(32, 64 ,64, [32,48,64],'hexadecimal password', 'util.is_hex_str'),

View file

@ -26,8 +26,11 @@ class mainnet(mainnet):
max_tx_fee = '0.1'
ignore_daemon_version = False
def pubhash2redeem_script(self,pubkey): raise NotImplementedError
def pubhash2segwitaddr(self,pubkey): raise NotImplementedError
def pubhash2redeem_script(self,pubhash):
raise NotImplementedError
def pubhash2segwitaddr(self,pubhash):
raise NotImplementedError
class testnet(mainnet):
addr_ver_info = { '6f': 'p2pkh', 'c4': 'p2sh' }

View file

@ -55,9 +55,7 @@ class bitcoin_core_daemon(CoinDaemon):
def init_subclass(self):
if self.network == 'regtest':
"""
fall back on hard-coded credentials
"""
# fall back on hard-coded credentials:
from .regtest import MMGenRegtest
self.rpc_user = MMGenRegtest.rpc_user
self.rpc_password = MMGenRegtest.rpc_password

View file

@ -30,4 +30,5 @@ class coin_msg(coin_msg):
async def do_verify(self,addr,sig,message,msghash_type):
return await self.rpc.call( 'verifymessage', addr, sig, message )
class exported_sigs(coin_msg.exported_sigs,signed_online): pass
class exported_sigs(coin_msg.exported_sigs,signed_online):
pass

View file

@ -27,8 +27,10 @@ from ...rpc import rpc_init,json_encoder
from ...objmethods import MMGenObject
def create_data_dir(cfg,data_dir):
try: os.stat(os.path.join(data_dir,'regtest'))
except: pass
try:
os.stat(os.path.join(data_dir,'regtest'))
except:
pass
else:
from ...ui import keypress_confirm
if keypress_confirm(
@ -38,8 +40,10 @@ def create_data_dir(cfg,data_dir):
else:
die()
try: os.makedirs(data_dir)
except: pass
try:
os.makedirs(data_dir)
except:
pass
def cliargs_convert(args):
def gen():
@ -108,8 +112,10 @@ class MMGenRegtest(MMGenObject):
async def setup(self):
try: os.makedirs(self.d.datadir)
except: pass
try:
os.makedirs(self.d.datadir)
except:
pass
if self.d.state != 'stopped':
await self.rpc_call('stop')
@ -243,8 +249,10 @@ class MMGenRegtest(MMGenObject):
if self.d.state != 'stopped':
await self.rpc_call('stop')
try: os.makedirs(self.d.datadir)
except: pass
try:
os.makedirs(self.d.datadir)
except:
pass
create_data_dir( self.cfg, self.d.datadir )
os.rmdir(self.d.datadir)

View file

@ -91,21 +91,39 @@ class TokenCommon(MMGenObject):
async def code(self):
return (await self.rpc.call('eth_getCode','0x'+self.addr))[2:]
def create_data(self,to_addr,amt,method_sig='transfer(address,uint256)',from_addr=None):
def create_data(
self,
to_addr,
amt,
method_sig = 'transfer(address,uint256)',
from_addr = None):
from_arg = from_addr.rjust(64,'0') if from_addr else ''
to_arg = to_addr.rjust(64,'0')
amt_arg = '{:064x}'.format( int(amt / self.base_unit) )
return self.create_method_id(method_sig) + from_arg + to_arg + amt_arg
def make_tx_in( self,from_addr,to_addr,amt,start_gas,gasPrice,nonce,
method_sig='transfer(address,uint256)',from_addr2=None):
data = self.create_data(to_addr,amt,method_sig=method_sig,from_addr=from_addr2)
return {'to': bytes.fromhex(self.addr),
'startgas': start_gas.toWei(),
'gasprice': gasPrice.toWei(),
'value': 0,
'nonce': nonce,
'data': bytes.fromhex(data) }
def make_tx_in(
self,
from_addr,
to_addr,
amt,
start_gas,
gasPrice,
nonce,
method_sig = 'transfer(address,uint256)',
from_addr2 = None):
data = self.create_data(
to_addr,
amt,
method_sig = method_sig,
from_addr = from_addr2)
return {
'to': bytes.fromhex(self.addr),
'startgas': start_gas.toWei(),
'gasprice': gasPrice.toWei(),
'value': 0,
'nonce': nonce,
'data': bytes.fromhex(data)}
async def txsign(self,tx_in,key,from_addr,chain_id=None):
@ -136,17 +154,27 @@ class TokenCommon(MMGenObject):
async def txsend(self,txhex):
return (await self.rpc.call('eth_sendRawTransaction','0x'+txhex)).replace('0x','',1)
async def transfer( self,from_addr,to_addr,amt,key,start_gas,gasPrice,
method_sig='transfer(address,uint256)',
from_addr2=None,
return_data=False):
async def transfer(
self,
from_addr,
to_addr,
amt,
key,
start_gas,
gasPrice,
method_sig = 'transfer(address,uint256)',
from_addr2=None,
return_data=False):
tx_in = self.make_tx_in(
from_addr,to_addr,amt,
start_gas,gasPrice,
nonce = int(await self.rpc.call('eth_getTransactionCount','0x'+from_addr,'pending'),16),
method_sig = method_sig,
from_addr2 = from_addr2 )
(txhex,coin_txid) = await self.txsign(tx_in,key,from_addr)
from_addr,
to_addr,
amt,
start_gas,
gasPrice,
nonce = int(await self.rpc.call('eth_getTransactionCount','0x'+from_addr,'pending'),16),
method_sig = method_sig,
from_addr2 = from_addr2 )
txhex,_ = await self.txsign(tx_in,key,from_addr)
return await self.txsend(txhex)
class Token(TokenCommon):

View file

@ -36,4 +36,5 @@ class coin_msg(coin_msg):
proto = self.proto).pubhex2addr(
ec_recover_pubkey( self.cfg, message, sig, msghash_type )) == addr
class exported_sigs(coin_msg.exported_sigs,signed_online): pass
class exported_sigs(coin_msg.exported_sigs,signed_online):
pass

View file

@ -40,7 +40,7 @@ class EthereumRPCClient(RPCClient,metaclass=AsyncInit):
proto,
daemon,
backend,
ignore_wallet ):
ignore_wallet):
self.proto = proto
self.daemon = daemon

View file

@ -97,7 +97,8 @@ Actions: [q]uit menu, [D]elete addr, add [l]abel, [R]efresh balance:
)
def do_sort(self,key=None,reverse=False):
if key == 'txid': return
if key == 'txid':
return
super().do_sort(key=key,reverse=reverse)
async def get_rpc_data(self):

View file

@ -41,10 +41,10 @@ class OnlineSigned(Signed,TxBase.OnlineSigned):
try:
ret = await self.rpc.call('eth_sendRawTransaction','0x'+self.serialized)
except:
raise
ret = False
raise # TODO: raises immediately
ret = False # TODO: unreachable code
if ret == False:
if ret is False: # TODO: unreachable code
rmsg(f'Send of MMGen transaction {self.txid} failed')
if exit_on_fail:
sys.exit(1)

View file

@ -85,4 +85,5 @@ class mainnet(CoinProtocol.DummyWIF,CoinProtocol.Base):
return MoneroViewKey.__new__(MoneroViewKey,viewkey_str)
class testnet(mainnet): # use stagenet for testnet
addr_ver_info = { '18': 'monero', '24': 'monero_sub', '19': 'monero_integrated' } # testnet is {'35','3f','36'}
# testnet is {'35','3f','36'}
addr_ver_info = { '18': 'monero', '24': 'monero_sub', '19': 'monero_integrated' }

View file

@ -219,10 +219,8 @@ class RPCBackends:
for s in ('--header',f'{k}: {v}'):
yield s
if caller.auth_type:
"""
Authentication with curl is insecure, as it exposes the user's credentials
via the command line. Use for testing only.
"""
# Authentication with curl is insecure, as it exposes the user's credentials
# via the command line. Use for testing only.
for s in ('--user',f'{caller.auth.user}:{caller.auth.passwd}'):
yield s
if caller.auth_type == 'digest':
@ -237,7 +235,7 @@ class RPCBackends:
async def run(self,payload,timeout,host_path):
data = json.dumps(payload,cls=json_encoder)
if len(data) > self.arg_max:
return self.httplib(payload,timeout=timeout)
return self.httplib(payload,timeout=timeout) # TODO: check
dmsg_rpc_backend(self.host_url,host_path,payload)
exec_cmd = [
'curl',
@ -397,8 +395,10 @@ class RPCClient(MMGenObject):
try:
m = t['error']['message']
except:
try: m = t['error']
except: m = t
try:
m = t['error']
except:
m = t
die( 'RPCFailure', m )
else:
import http
@ -407,8 +407,10 @@ class RPCClient(MMGenObject):
try:
m = json.loads(text)['error']['message']
except:
try: m = text.decode()
except: m = text
try:
m = text.decode()
except:
m = text
die( 'RPCFailure', f'{s.value} {s.name}: {m}' )
async def stop_daemon(self,quiet=False,silent=False):

View file

@ -96,7 +96,8 @@ class Sha2(object):
"""
mlpack = self.pack_msglen()
padlen = self.blkSize - (len(self.M) % self.blkSize) - len(mlpack) - 1
if padlen < 0: padlen += self.blkSize
if padlen < 0:
padlen += self.blkSize
self.M = self.M + bytes([0x80] + [0] * padlen) + mlpack
def bytesToWords(self):
@ -117,8 +118,11 @@ class Sha2(object):
def processBlock(self,offset):
'Process a blkSize-byte chunk of the message'
def rrotate(a,b): return ((a << self.wordBits-b) & self.wordMask) | (a >> b)
def addm(a,b): return (a + b) & self.wordMask
def rrotate(a,b):
return ((a << self.wordBits-b) & self.wordMask) | (a >> b)
def addm(a,b):
return (a + b) & self.wordMask
# Copy chunk into first 16 words of message schedule array
for i in range(16):

View file

@ -77,7 +77,8 @@ class SubSeed(SeedBase):
# field maximums: idx: 4294967295 (1000000), nonce: 65535 (1000), short: 255 (1)
scramble_key = idx.to_bytes(4,'big') + nonce.to_bytes(2,'big') + short.to_bytes(1,'big')
from .crypto import Crypto
return Crypto(parent_list.parent_seed.cfg).scramble_seed(seed.data,scramble_key)[:16 if short else seed.byte_len]
return Crypto(parent_list.parent_seed.cfg).scramble_seed(
seed.data,scramble_key)[:16 if short else seed.byte_len]
class SubSeedList(MMGenObject):
have_short = True

View file

@ -72,7 +72,7 @@ class tool_cmd(tool_cmd_base):
async def txview(
self,
varargs_call_sig = { # hack to allow for multiple filenames
varargs_call_sig = { # hack to allow for multiple filenames - must be second argument!
'args': (
'mmgen_tx_file(s)',
'pager',

View file

@ -55,7 +55,13 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
def __new__(cls,cfg,proto,*args,**kwargs):
return MMGenObject.__new__(proto.base_proto_subclass(cls,'tw.ctl'))
async def __init__(self,cfg,proto,mode='r',token_addr=None,rpc_ignore_wallet=False):
async def __init__(
self,
cfg,
proto,
mode = 'r',
token_addr = None,
rpc_ignore_wallet = False):
assert mode in ('r','w','i'), f"{mode!r}: wallet mode must be 'r','w' or 'i'"
if mode == 'i':
@ -102,7 +108,8 @@ class TwCtl(MMGenObject,metaclass=AsyncInit):
self.orig_data = get_data_from_file( self.cfg, self.tw_fn, quiet=True )
self.data = json.loads(self.orig_data)
except:
try: os.stat(self.tw_fn)
try:
os.stat(self.tw_fn)
except:
self.orig_data = ''
self.init_empty()

View file

@ -375,7 +375,13 @@ class TwView(MMGenObject,metaclass=AsyncInit):
min(7,max(len(str(getattr(d,k).to_integral_value())) for d in data)) + 1 + self.disp_prec
for k in self.amt_keys}
async def format(self,display_type,color=True,interactive=False,line_processing=None,scroll=False):
async def format(
self,
display_type,
color = True,
interactive = False,
line_processing = None,
scroll = False):
def make_display():

View file

@ -41,7 +41,8 @@ def get_seed_for_seed_id(sid,infiles,saved_seeds):
elif subseeds_checked == False:
seed = saved_seeds[list(saved_seeds)[0]].subseed_by_seed_id(sid,print_msg=True)
subseeds_checked = True
if not seed: continue
if not seed:
continue
elif cfg.in_fmt:
cfg._util.qmsg(f'Need seed data for Seed ID {sid}')
seed = Wallet(cfg).seed

View file

@ -62,7 +62,15 @@ class Util:
else:
self.stdout_or_pager = Msg_r
def compare_chksums(self,chk1,desc1,chk2,desc2,hdr='',die_on_fail=False,verbose=False):
def compare_chksums(
self,
chk1,
desc1,
chk2,
desc2,
hdr = '',
die_on_fail = False,
verbose = False):
if not chk1 == chk2:
fs = "{} ERROR: {} checksum ({}) doesn't match {} checksum ({})"
@ -356,9 +364,12 @@ def is_hex_str_lc(s):
return set(s) <= set(hexdigits_lc)
def is_utf8(s):
try: s.decode('utf8')
except: return False
else: return True
try:
s.decode('utf8')
except:
return False
else:
return True
def remove_whitespace(s,ws='\t\r\n '):
return s.translate(dict((ord(e),None) for e in ws))

View file

@ -53,7 +53,8 @@ class wallet(wallet):
else: # Prompt, using old value as default
hp = self._get_hash_preset_from_user( old_preset=old_hp, add_desc=add_desc )
if (not self.cfg.keep_hash_preset) and self.op == 'pwchg_new':
self.cfg._util.qmsg('Hash preset {}'.format( 'unchanged' if hp == old_hp else f'changed to {hp!r}' ))
self.cfg._util.qmsg('Hash preset {}'.format(
'unchanged' if hp == old_hp else f'changed to {hp!r}'))
elif self.cfg.hash_preset:
hp = self.cfg.hash_preset
self.cfg._util.qmsg(f'Using hash preset {hp!r} requested on command line')

View file

@ -66,11 +66,11 @@ xmrwallet_uargs = namedtuple('xmrwallet_uargs',[
xmrwallet_uarg_info = (
lambda e,hp: {
'daemon': e('HOST:PORT', hp),
'tx_relay_daemon': e('HOST:PORT[:PROXY_HOST:PROXY_PORT]', rf'({hp})(?::({hp}))?'),
'newaddr_spec': e('WALLET_NUM[:ACCOUNT][,"label text"]', rf'(\d+)(?::(\d+))?(?:,(.*))?'),
'transfer_spec': e('SOURCE_WALLET_NUM:ACCOUNT:ADDRESS,AMOUNT', rf'(\d+):(\d+):([{b58a}]+),([0-9.]+)'),
'tx_relay_daemon': e('HOST:PORT[:PROXY_HOST:PROXY_PORT]', rf'({hp})(?::({hp}))?'),
'newaddr_spec': e('WALLET_NUM[:ACCOUNT][,"label text"]', r'(\d+)(?::(\d+))?(?:,(.*))?'),
'transfer_spec': e('SOURCE_WALLET_NUM:ACCOUNT:ADDRESS,AMOUNT', rf'(\d+):(\d+):([{b58a}]+),([0-9.]+)'),
'sweep_spec': e('SOURCE_WALLET_NUM:ACCOUNT[,DEST_WALLET_NUM]', r'(\d+):(\d+)(?:,(\d+))?'),
'label_spec': e('WALLET_NUM:ACCOUNT:ADDRESS,"label text"', rf'(\d+):(\d+):(\d+),(.*)'),
'label_spec': e('WALLET_NUM:ACCOUNT:ADDRESS,"label text"', r'(\d+):(\d+):(\d+),(.*)'),
})(
namedtuple('uarg_info_entry',['annot','pat']),
r'(?:[^:]+):(?:\d+)'
@ -744,9 +744,12 @@ class MoneroWalletOps:
def __init__(self,cfg,uarg_tuple):
def wallet_exists(fn):
try: fn.stat()
except: return False
else: return True
try:
fn.stat()
except:
return False
else:
return True
def check_wallets():
for d in self.addr_data:
@ -907,7 +910,7 @@ class MoneroWalletOps:
processed += await self.process_wallet(
d,
fn,
last = n == len(self.addr_data)-1 )
last = n==len(self.addr_data)-1 )
gmsg(f'\n{processed} wallet{suf(processed)} {self.stem}ed')
return processed

View file

@ -1,3 +1,11 @@
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
# Copyright (C)2013-2023 The MMGen Project <mmgen@tuta.io>
# Licensed under the GNU General Public License, Version 3:
# https://www.gnu.org/licenses
# Public project repositories:
# https://github.com/mmgen/mmgen
# https://gitlab.com/mmgen/mmgen
[metadata]
name = MMGen
version = file: mmgen/data/version

View file

@ -1,4 +1,12 @@
#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
# Copyright (C)2013-2023 The MMGen Project <mmgen@tuta.io>
# Licensed under the GNU General Public License, Version 3:
# https://www.gnu.org/licenses
# Public project repositories:
# https://github.com/mmgen/mmgen
# https://gitlab.com/mmgen/mmgen
import sys,os
from setuptools import setup,Extension