Browse Source

addr.py: move PubKey and PrivKey to key.py

The MMGen Project 3 years ago
parent
commit
545406aea4

+ 1 - 0
mmgen/addr.py

@@ -26,6 +26,7 @@ from .objmethods import MMGenObject
 from .obj import *
 from .baseconv import *
 from .protocol import hash160
+from .key import PrivKey,PubKey
 
 class AddrGenerator(MMGenObject):
 	def __new__(cls,proto,addr_type):

+ 1 - 0
mmgen/addrfile.py

@@ -33,6 +33,7 @@ from .util import (
 from .protocol import init_proto
 from .obj import *
 from .seed import SeedID,is_seed_id
+from .key import PrivKey
 from .addrlist import KeyList,AddrListData,dmsg_sc
 from .passwdlist import PasswordList
 

+ 2 - 1
mmgen/addrlist.py

@@ -23,8 +23,9 @@ addrlist.py: Address list classes for the MMGen suite
 from hashlib import sha256,sha512
 from .util import qmsg,qmsg_r,suf,make_chksum_N,Msg
 from .objmethods import MMGenObject,Hilite,InitErrors
-from .obj import MMGenListItem,ListItemAttr,MMGenDict,WalletPassword,PrivKey
+from .obj import MMGenListItem,ListItemAttr,MMGenDict,WalletPassword
 from .seed import SeedID
+from .key import PrivKey
 from .obj import MMGenID,MMGenAddrType,CoinAddr,AddrIdx,AddrListID,ViewKey
 
 def dmsg_sc(desc,data):

+ 113 - 0
mmgen/key.py

@@ -0,0 +1,113 @@
+#!/usr/bin/env python3
+#
+# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
+# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
+#
+# 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/>.
+
+"""
+key.py: MMGen public and private key objects
+"""
+
+from string import ascii_letters,digits
+from .objmethods import Hilite,InitErrors,MMGenObject
+from .obj import ImmutableAttr,get_obj,HexStr
+
+class WifKey(str,Hilite,InitErrors):
+	"""
+	Initialize a WIF key, checking its well-formedness.
+	The numeric validity of the private key it encodes is not checked.
+	"""
+	width = 53
+	color = 'blue'
+	def __new__(cls,proto,wif):
+		if type(wif) == cls:
+			return wif
+		try:
+			assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
+			proto.parse_wif(wif) # raises exception on error
+			return str.__new__(cls,wif)
+		except Exception as e:
+			return cls.init_fail(e,wif)
+
+def is_wif(proto,s):
+	return get_obj( WifKey, proto=proto, wif=s, silent=True, return_bool=True )
+
+class PubKey(HexStr,MMGenObject): # TODO: add some real checks
+
+	def __new__(cls,s,privkey):
+		try:
+			me = HexStr.__new__(cls,s,case='lower')
+			me.privkey = privkey
+			me.compressed = privkey.compressed
+			return me
+		except Exception as e:
+			return cls.init_fail(e,s)
+
+class PrivKey(str,Hilite,InitErrors,MMGenObject):
+	"""
+	Input:   a) raw, non-preprocessed bytes; or b) WIF key.
+	Output:  preprocessed hexadecimal key, plus WIF key in 'wif' attribute
+	For coins without a WIF format, 'wif' contains the preprocessed hex.
+	The numeric validity of the resulting key is always checked.
+	"""
+	color = 'red'
+	width = 64
+	trunc_ok = False
+
+	compressed = ImmutableAttr(bool,typeconv=False)
+	wif        = ImmutableAttr(WifKey,typeconv=False)
+
+	# initialize with (priv_bin,compressed), WIF or self
+	def __new__(cls,proto,s=None,compressed=None,wif=None,pubkey_type=None):
+		if type(s) == cls:
+			return s
+		if wif:
+			try:
+				assert s == None,"'wif' and key hex args are mutually exclusive"
+				assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
+				k = proto.parse_wif(wif) # raises exception on error
+				me = str.__new__(cls,k.sec.hex())
+				me.compressed = k.compressed
+				me.pubkey_type = k.pubkey_type
+				me.wif = str.__new__(WifKey,wif) # check has been done
+				me.orig_hex = None
+				if k.sec != proto.preprocess_key(k.sec,k.pubkey_type):
+					from .exception import PrivateKeyError
+					raise PrivateKeyError(
+						f'{proto.cls_name} WIF key {me.wif!r} encodes private key with invalid value {me}')
+				me.proto = proto
+				return me
+			except Exception as e:
+				return cls.init_fail(e,s,objname=f'{proto.coin} WIF key')
+		else:
+			try:
+				assert s,'private key bytes data missing'
+				assert pubkey_type is not None,"'pubkey_type' arg missing"
+				assert len(s) == cls.width // 2, f'key length must be {cls.width // 2} bytes'
+				if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
+					me = str.__new__(cls,s.hex())
+				else:
+					assert compressed is not None, "'compressed' arg missing"
+					assert type(compressed) == bool,(
+						f"'compressed' must be of type bool, not {type(compressed).__name__}" )
+					me = str.__new__(cls,proto.preprocess_key(s,pubkey_type).hex())
+					me.wif = WifKey(proto,proto.hex2wif(me,pubkey_type,compressed))
+					me.compressed = compressed
+				me.pubkey_type = pubkey_type
+				me.orig_hex = s.hex() # save the non-preprocessed key
+				me.proto = proto
+				return me
+			except Exception as e:
+				return cls.init_fail(e,s)

+ 0 - 85
mmgen/obj.py

@@ -62,8 +62,6 @@ def is_addrlist_id(s):     return get_obj(AddrListID, sid=s, silent=True,return_
 
 def is_mmgen_id(proto,s):  return get_obj(MMGenID,  proto=proto, id_str=s, silent=True,return_bool=True)
 def is_coin_addr(proto,s): return get_obj(CoinAddr, proto=proto, addr=s,   silent=True,return_bool=True)
-def is_wif(proto,s):       return get_obj(WifKey,   proto=proto, wif=s,    silent=True,return_bool=True)
-
 # dict that keeps a list of keys for efficient lookup by index
 class IndexedDict(dict):
 
@@ -435,89 +433,6 @@ class WalletPassword(HexStr): color,width,hexcase = 'blue',32,'lower'
 class MoneroViewKey(HexStr):  color,width,hexcase = 'cyan',64,'lower' # FIXME - no checking performed
 class MMGenTxID(HexStr):      color,width,hexcase = 'red',6,'upper'
 
-class WifKey(str,Hilite,InitErrors):
-	"""
-	Initialize a WIF key, checking its well-formedness.
-	The numeric validity of the private key it encodes is not checked.
-	"""
-	width = 53
-	color = 'blue'
-	def __new__(cls,proto,wif):
-		if type(wif) == cls:
-			return wif
-		try:
-			assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
-			proto.parse_wif(wif) # raises exception on error
-			return str.__new__(cls,wif)
-		except Exception as e:
-			return cls.init_fail(e,wif)
-
-class PubKey(HexStr,MMGenObject): # TODO: add some real checks
-	def __new__(cls,s,privkey):
-		try:
-			me = HexStr.__new__(cls,s,case='lower')
-			me.privkey = privkey
-			me.compressed = privkey.compressed
-			return me
-		except Exception as e:
-			return cls.init_fail(e,s)
-
-class PrivKey(str,Hilite,InitErrors,MMGenObject):
-	"""
-	Input:   a) raw, non-preprocessed bytes; or b) WIF key.
-	Output:  preprocessed hexadecimal key, plus WIF key in 'wif' attribute
-	For coins without a WIF format, 'wif' contains the preprocessed hex.
-	The numeric validity of the resulting key is always checked.
-	"""
-	color = 'red'
-	width = 64
-	trunc_ok = False
-
-	compressed = ImmutableAttr(bool,typeconv=False)
-	wif        = ImmutableAttr(WifKey,typeconv=False)
-
-	# initialize with (priv_bin,compressed), WIF or self
-	def __new__(cls,proto,s=None,compressed=None,wif=None,pubkey_type=None):
-		if type(s) == cls:
-			return s
-		if wif:
-			try:
-				assert s == None,"'wif' and key hex args are mutually exclusive"
-				assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
-				k = proto.parse_wif(wif) # raises exception on error
-				me = str.__new__(cls,k.sec.hex())
-				me.compressed = k.compressed
-				me.pubkey_type = k.pubkey_type
-				me.wif = str.__new__(WifKey,wif) # check has been done
-				me.orig_hex = None
-				if k.sec != proto.preprocess_key(k.sec,k.pubkey_type):
-					raise PrivateKeyError(
-						f'{proto.cls_name} WIF key {me.wif!r} encodes private key with invalid value {me}')
-				me.proto = proto
-				return me
-			except Exception as e:
-				return cls.init_fail(e,s,objname=f'{proto.coin} WIF key')
-		else:
-			try:
-				assert s,'private key bytes data missing'
-				assert pubkey_type is not None,"'pubkey_type' arg missing"
-				assert len(s) == cls.width // 2, f'key length must be {cls.width // 2} bytes'
-				if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
-					me = str.__new__(cls,s.hex())
-				else:
-					assert compressed is not None, "'compressed' arg missing"
-					assert type(compressed) == bool,(
-						f"'compressed' must be of type bool, not {type(compressed).__name__}" )
-					me = str.__new__(cls,proto.preprocess_key(s,pubkey_type).hex())
-					me.wif = WifKey(proto,proto.hex2wif(me,pubkey_type,compressed))
-					me.compressed = compressed
-				me.pubkey_type = pubkey_type
-				me.orig_hex = s.hex() # save the non-preprocessed key
-				me.proto = proto
-				return me
-			except Exception as e:
-				return cls.init_fail(e,s)
-
 class AddrListID(str,Hilite,InitErrors,MMGenObject):
 	width = 10
 	trunc_ok = False

+ 2 - 1
mmgen/passwdlist.py

@@ -24,8 +24,9 @@ from collections import namedtuple
 
 from .exception import InvalidPasswdFormat
 from .util import ymsg,is_hex_str,is_int,keypress_confirm
-from .obj import ImmutableAttr,ListItemAttr,MMGenPWIDString,PrivKey
+from .obj import ImmutableAttr,ListItemAttr,MMGenPWIDString
 from .baseconv import baseconv,is_b32_str,is_b58_str
+from .key import PrivKey
 from .addr import MMGenPasswordType,AddrIdx,AddrListID,is_xmrseed,is_bip39_str
 from .addrlist import (
 	AddrListChksum,

+ 1 - 4
mmgen/protocol.py

@@ -25,7 +25,7 @@ from collections import namedtuple
 
 from .util import msg,ymsg,Msg,ydie
 from .devtools import *
-from .obj import CoinAddr,MMGenAddrType,PrivKey
+from .obj import CoinAddr,MMGenAddrType
 from .globalvars import g
 from .amt import BTCAmt,LTCAmt,BCHAmt,B2XAmt,XMRAmt
 from .altcoins.eth.obj import ETHAmt
@@ -178,9 +178,6 @@ class CoinProtocol(MMGenObject):
 		def addr_type(self,id_str):
 			return MMGenAddrType( proto=self, id_str=id_str )
 
-		def priv_key(self,s):
-			return PrivKey( proto=self, s=s )
-
 	class Secp256k1(Base):
 		"""
 		Bitcoin and Ethereum protocols inherit from this class

+ 1 - 0
mmgen/tool.py

@@ -23,6 +23,7 @@ tool.py:  Routines for the 'mmgen-tool' utility
 from .protocol import hash160
 from .common import *
 from .crypto import *
+from .key import PrivKey
 from .seedsplit import MasterShareIdx
 from .addr import *
 from .addrlist import AddrList,KeyAddrList

+ 2 - 1
test/gentest.py

@@ -401,7 +401,8 @@ def parse_arg2():
 # begin execution
 from mmgen.protocol import init_proto
 from mmgen.altcoin import CoinInfo as ci
-from mmgen.obj import MMGenAddrType,PrivKey
+from mmgen.obj import MMGenAddrType
+from mmgen.key import PrivKey
 from mmgen.addr import KeyGenerator,AddrGenerator
 
 addr_type = MMGenAddrType(

+ 2 - 1
test/misc/tool_api_test.py

@@ -9,7 +9,8 @@ tool_api_test.py: test the MMGen suite tool API
 
 import sys,os
 from mmgen.common import *
-from mmgen.obj import PrivKey,CoinAddr
+from mmgen.key import PrivKey
+from mmgen.addr import CoinAddr
 
 keys = [
 	'118089d66b4a5853765e94923abdd5de4616c6e5118089d66b4a5853765e9492',

+ 1 - 0
test/objattrtest_py_d/oat_common.py

@@ -16,6 +16,7 @@ from mmgen.protocol import *
 from mmgen.addr import *
 from mmgen.tx import *
 from mmgen.tw import *
+from mmgen.key import *
 from ..include.common import getrand
 
 from collections import namedtuple

+ 1 - 0
test/objtest.py

@@ -37,6 +37,7 @@ from mmgen.addr import *
 from mmgen.addrlist import *
 from mmgen.addrdata import *
 from mmgen.amt import *
+from mmgen.key import *
 
 opts_data = {
 	'sets': [('super_silent', True, 'silent', True)],

+ 1 - 0
test/objtest_py_d/ot_btc_mainnet.py

@@ -10,6 +10,7 @@ test.objtest_py_d.ot_btc_mainnet: BTC mainnet test vectors for MMGen data object
 from mmgen.obj import *
 from mmgen.addrlist import AddrIdxList
 from mmgen.seedsplit import *
+from mmgen.key import *
 from .ot_common import *
 
 from mmgen.protocol import init_proto

+ 2 - 2
test/test_py_d/ts_main.py

@@ -374,7 +374,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 					out.append(self._create_fake_unspent_entry(coinaddr,d['al_id'],idx,lbl,segwit=d['segwit']))
 
 		if non_mmgen_input:
-			from mmgen.obj import PrivKey
+			from mmgen.key import PrivKey
 			privkey = PrivKey(
 				self.proto,
 				getrand(32),
@@ -417,7 +417,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
 		return ad,tx_data
 
 	def _make_txcreate_cmdline(self,tx_data):
-		from mmgen.obj import PrivKey
+		from mmgen.key import PrivKey
 		privkey = PrivKey(self.proto,getrand(32),compressed=True,pubkey_type='std')
 		t = ('compressed','segwit')['S' in self.proto.mmtypes]
 		from mmgen.addr import AddrGenerator,KeyGenerator

+ 2 - 1
test/tooltest.py

@@ -185,7 +185,8 @@ if opt.list_names:
 	)
 	die(0,'\n{}\n  {}'.format(yellow('Untested commands:'),'\n  '.join(uc)))
 
-from mmgen.tx import is_wif,is_coin_addr
+from mmgen.key import is_wif
+from mmgen.tx import is_coin_addr
 def is_wif_loc(s):
 	return is_wif(proto,s)
 def is_coin_addr_loc(s):

+ 2 - 1
test/tooltest2.py

@@ -44,7 +44,8 @@ NL = ('\n','\r\n')[g.platform=='win']
 def is_str(s):
 	return type(s) == str
 
-from mmgen.obj import is_wif,is_coin_addr
+from mmgen.key import is_wif
+from mmgen.obj import is_coin_addr
 def is_wif_loc(s):
 	return is_wif(proto,s)
 def is_coin_addr_loc(s):