Browse Source

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

The MMGen Project 4 years ago
parent
commit
7412bf56ef
5 changed files with 118 additions and 24 deletions
  1. 15 14
      mmgen/tool.py
  2. 82 0
      test/misc/tool_api_test.py
  3. 12 8
      test/ref/bad-comment-unspent.json
  4. 2 2
      test/test_py_d/ts_main.py
  5. 7 0
      test/test_py_d/ts_tool.py

+ 15 - 14
mmgen/tool.py

@@ -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 - 0
test/misc/tool_api_test.py

@@ -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()

+ 12 - 8
test/ref/bad-comment-unspent.json

@@ -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
+    }
+]

+ 2 - 2
test/test_py_d/ts_main.py

@@ -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)

+ 7 - 0
test/test_py_d/ts_tool.py

@@ -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