tool.py: fix tool API; test.py: add tool_api test, minor fixes

This commit is contained in:
The MMGen Project 2020-06-08 17:03:21 +00:00
commit 7412bf56ef
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
5 changed files with 118 additions and 24 deletions

View file

@ -273,7 +273,7 @@ class MMGenToolCmds(metaclass=MMGenToolCmdMeta):
def __init__(self,proto=None,mmtype=None):
from .protocol import init_proto_from_opts
self.proto = proto or init_proto_from_opts()
self.mmtype = mmtype or getattr(opt,'type',None) or self.proto.dfl_mmtype
self.mmtype = MMGenAddrType(self.proto,(mmtype or getattr(opt,'type',None) or self.proto.dfl_mmtype))
if g.token:
self.proto.tokensym = g.token.upper()
@ -470,7 +470,7 @@ class MMGenToolCmdCoin(MMGenToolCmds):
def wif2redeem_script(self,wifkey:'sstr'): # new
"convert a WIF private key to a Segwit P2SH-P2WPKH redeem script"
assert self.mmtype == 'segwit','This command is meaningful only for --type=segwit'
assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
gd = self.init_generators()
privhex = PrivKey(
self.proto,
@ -479,7 +479,7 @@ class MMGenToolCmdCoin(MMGenToolCmds):
def wif2segwit_pair(self,wifkey:'sstr'):
"generate both a Segwit P2SH-P2WPKH redeem script and address from WIF"
assert self.mmtype == 'segwit','This command is meaningful only for --type=segwit'
assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
gd = self.init_generators()
pubhex = gd.kg.to_pubhex(PrivKey(
self.proto,
@ -505,26 +505,26 @@ class MMGenToolCmdCoin(MMGenToolCmds):
def pubhex2addr(self,pubkeyhex:'sstr'):
"convert a hex pubkey to an address"
if self.mmtype == 'segwit':
if self.mmtype.name == 'segwit':
return self.proto.pubhex2segwitaddr(pubkeyhex)
else:
return self.pubhash2addr(hash160(pubkeyhex))
def pubhex2redeem_script(self,pubkeyhex:'sstr'): # new
"convert a hex pubkey to a Segwit P2SH-P2WPKH redeem script"
assert self.mmtype == 'segwit','This command is meaningful only for --type=segwit'
assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
return self.proto.pubhex2redeem_script(pubkeyhex)
def redeem_script2addr(self,redeem_scripthex:'sstr'): # new
"convert a Segwit P2SH-P2WPKH redeem script to an address"
assert self.mmtype == 'segwit','This command is meaningful only for --type=segwit'
assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
assert redeem_scripthex[:4] == '0014','{!r}: invalid redeem script'.format(redeem_scripthex)
assert len(redeem_scripthex) == 44,'{} bytes: invalid redeem script length'.format(len(redeem_scripthex)//2)
return self.pubhash2addr(hash160(redeem_scripthex))
def pubhash2addr(self,pubhashhex:'sstr'):
"convert public key hash to address"
if self.mmtype == 'bech32':
if self.mmtype.name == 'bech32':
return self.proto.pubhash2bech32addr(pubhashhex)
else:
gd = self.init_generators('addrtype_only')
@ -1222,10 +1222,11 @@ class tool_api(
"""
Initializer - takes no arguments
"""
super().__init__()
import mmgen.opts
opts.UserOpts._reset_ok += ('usr_randchars',)
if not hasattr(opt,'version'):
opts.init()
self.mmtype = self.proto.dfl_mmtype
opts.init(add_opts=['use_old_ed25519'])
super().__init__()
def init_coin(self,coinsym,network):
"""
@ -1236,7 +1237,7 @@ class tool_api(
from .protocol import init_proto,init_genonly_altcoins
altcoin_trust_level = init_genonly_altcoins(coinsym,testnet=network in ('testnet','regtest'))
warn_altcoins(coinsym,altcoin_trust_level)
self.proto = init_proto(coinsym,network=network) # FIXME
self.proto = init_proto(coinsym,network=network)
return self.proto
@property
@ -1265,14 +1266,14 @@ class tool_api(
The available address types for current coin/network pair. The
first-listed is the default
"""
return [MMGenAddrType(proto=proto,id_str=id_str).name for id_str in self.proto.mmtypes]
return [MMGenAddrType(proto=self.proto,id_str=id_str).name for id_str in self.proto.mmtypes]
def print_addrtypes(self):
"""
Print the available address types for current coin/network pair along with
a description. The first-listed is the default
"""
for t in [MMGenAddrType(proto=proto,id_str=id_str).name for id_str in self.proto.mmtypes]:
for t in [MMGenAddrType(proto=self.proto,id_str=id_str) for id_str in self.proto.mmtypes]:
print('{:<12} - {}'.format(t.name,t.desc))
@property
@ -1282,7 +1283,7 @@ class tool_api(
@addrtype.setter
def addrtype(self,val):
self.mmtype = val
self.mmtype = MMGenAddrType(self.proto,val)
@property
def usr_randchars(self):

82
test/misc/tool_api_test.py Executable file
View file

@ -0,0 +1,82 @@
#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C)2013-2020 The MMGen Project <mmgen@tuta.io>
"""
tool_api_test.py: test the MMGen suite tool API
"""
import sys,os
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(os.path.dirname(sys.argv[0])))))
sys.path[0] = os.curdir
def check_equal(a,b):
assert a == b, f'{a} != {b}'
def msg(*args,**kwargs):
print(*args,**kwargs,file=sys.stdout)
def init_coin(t,coinsym,network,addrtype,triplet):
t.init_coin(coinsym,network)
t.addrtype = addrtype
check_equal(type(t.addrtype).__name__,'MMGenAddrType')
check_equal(f'{t.coin} {t.proto.cls_name} {t.addrtype}', triplet)
msg('\ncoin/proto/type:',triplet)
def run_test():
key_bytes = bytes.fromhex('deadbeef' * 8)
from mmgen.tool import tool_api
tool = tool_api()
tool.coins
tool.print_addrtypes()
check_equal(f'{tool.coin} {tool.proto.cls_name} {tool.addrtype}', 'BTC Bitcoin L' )
tool.usr_randchars # getter
tool.usr_randchars = 0 # setter
check_equal(tool.usr_randchars,0)
init_coin(tool,'xmr','mainnet','M','XMR Monero M')
msg('\n'.join(tool.randpair()))
init_coin(tool,'etc','mainnet','E','ETC EthereumClassic E')
msg('\n'.join(tool.randpair()))
init_coin(tool,'ltc','regtest','bech32','LTC LitecoinRegtest B')
wif,addr = tool.randpair()
from mmgen.obj import PrivKey,CoinAddr
msg('wif:',PrivKey(proto=tool.proto,wif=wif).wif)
msg('addr:',CoinAddr(proto=tool.proto,addr=addr))
wif = PrivKey(proto=tool.proto,s=key_bytes,compressed=True,pubkey_type='std').wif
addr = tool.wif2addr(wif)
msg('wif:',PrivKey(proto=tool.proto,wif=wif).wif)
msg('addr:',CoinAddr(proto=tool.proto,addr=addr))
addr_chk = tool.privhex2addr(key_bytes.hex())
check_equal(addr,addr_chk)
check_equal(wif,'cV3ZRqf8PhyfiFwtJfkvGu2qmBsazE1wXoA2A16S3nixb3BTvvVx')
check_equal(addr,'rltc1qvmqas4maw7lg9clqu6kqu9zq9cluvllnz4kj9y')
init_coin(tool,'zec','mainnet','Z','ZEC Zcash Z')
wif = PrivKey(proto=tool.proto,s=key_bytes,compressed=True,pubkey_type='zcash_z').wif
addr = tool.wif2addr(wif)
msg('wif:',wif)
msg('addr:',CoinAddr(proto=tool.proto,addr=addr))
addr_chk = tool.privhex2addr(key_bytes.hex())
wif_chk = PrivKey(proto=tool.proto,wif=wif).wif
check_equal(addr,addr_chk)
check_equal(wif,wif_chk)
check_equal(
addr,
'zchFELwBxqsAubsLQ8yZgPCDDGukjXJssgCbiTPwFNmFwn9haLnDatzfhLdZzJT4PcU4o2yr92B52UFirUzEdF6ZYM2gBkM' )
run_test()

View file

@ -1,8 +1,12 @@
[{'address': '153AwzcymCgiy7dBxHkpeSn8yzbfnRvVPx',
'amount': BTCAmt('34.21677044'),
'confirmations': 7528082,
'label': '96325D83:L:99 This label has a control character (tab) here: [ ]',
'scriptPubKey': '76a9142c49a3ad8d89af713197e55190a7c3bc47e0208e88ac',
'spendable': False,
'txid': '2588cbbea1f3413ee4556c0f12f825f8670771862864a44f5ad39a71e1afdb13',
'vout': 3}]
[
{
"address": "153AwzcymCgiy7dBxHkpeSn8yzbfnRvVPx",
"amount": "34.21677044",
"confirmations": 7528082,
"label": "96325D83:L:99 This label has a control character (tab) here: [\t]",
"scriptPubKey": "76a9142c49a3ad8d89af713197e55190a7c3bc47e0208e88ac",
"spendable": false,
"txid": "2588cbbea1f3413ee4556c0f12f825f8670771862864a44f5ad39a71e1afdb13",
"vout": 3
}
]

View file

@ -223,7 +223,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
t.license()
wcls = MMGenWallet
t.passphrase(wcls.desc,self.cfgs['1']['wpasswd'])
t.expect('Generating subseed.*10S',regex=True)
t.expect(r'Generating subseed.*\D10S',regex=True)
t.passphrase_new('new '+wcls.desc,'foo')
t.usr_rand(self.usr_rand_chars)
fn = t.written_to_file(capfirst(wcls.desc))
@ -238,7 +238,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
t = self.spawn('mmgen-subwalletgen', args)
t.license()
t.passphrase(icls.desc,self.cfgs['1']['wpasswd'])
t.expect('Generating subseed.*3L',regex=True)
t.expect(r'Generating subseed.*\D3L',regex=True)
fn = t.written_to_file(capfirst(ocls.desc))
ext = get_extension(fn)
assert ext == ocls.ext,'incorrect file extension: {}'.format(ext)

View file

@ -26,6 +26,7 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase):
('tool_encrypt', (9,"'mmgen-tool encrypt' (random data)", [])),
('tool_decrypt', (9,"'mmgen-tool decrypt' (random data)", [[[enc_infn+'.mmenc'],9]])),
('tool_twview_bad_comment',(9,"'mmgen-tool twview' (with bad comment)", [])),
('tool_api', (9,'tool API (initialization, config methods)',[])),
# ('tool_encrypt_ref', (9,"'mmgen-tool encrypt' (reference text)", [])),
)
@ -80,3 +81,9 @@ class TestSuiteTool(TestSuiteMain,TestSuiteBase):
t.read()
t.req_exit_val = 2
return t
def tool_api(self):
t = self.spawn('tool_api_test.py',cmd_dir='test/misc')
t.expect('legacy.*compressed.*segwit.*bech32',regex=True)
t.read()
return t