message signing: cleanups, default to base cls if proto cls missing

This commit is contained in:
The MMGen Project 2022-05-03 21:01:04 +00:00
commit b84e68d486
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
8 changed files with 56 additions and 57 deletions

View file

@ -16,20 +16,16 @@ from ...msg import coin_msg
class coin_msg(coin_msg):
class base(coin_msg.base): pass
include_pubhash = True
sigdata_pfx = None
msghash_types = ('raw',) # first-listed is the default
class new(base,coin_msg.new): pass
class completed(base,coin_msg.completed): pass
class unsigned(completed,coin_msg.unsigned):
class unsigned(coin_msg.unsigned):
async def do_sign(self,wif,message,msghash_type):
return await self.rpc.call( 'signmessagewithprivkey', wif, message )
class signed(completed,coin_msg.signed): pass
class signed_online(signed,coin_msg.signed_online):
class signed_online(coin_msg.signed_online):
async def do_verify(self,addr,sig,message,msghash_type):
return await self.rpc.call( 'verifymessage', addr, sig, message )

View file

@ -16,21 +16,17 @@ from ...msg import coin_msg
class coin_msg(coin_msg):
class base(coin_msg.base): pass
include_pubhash = False
sigdata_pfx = '0x'
msghash_types = ('eth_sign','raw') # first-listed is the default
class new(base,coin_msg.new): pass
class completed(base,coin_msg.completed): pass
class unsigned(completed,coin_msg.unsigned):
class unsigned(coin_msg.unsigned):
async def do_sign(self,wif,message,msghash_type):
from .misc import ec_sign_message_with_privkey
return ec_sign_message_with_privkey( message, bytes.fromhex(wif), msghash_type )
class signed(completed,coin_msg.signed): pass
class signed_online(signed,coin_msg.signed_online):
class signed_online(coin_msg.signed_online):
async def do_verify(self,addr,sig,message,msghash_type):
from ...tool.coin import tool_cmd

View file

@ -29,7 +29,7 @@ class MsgOps:
network = proto.network,
message = msg,
addrlists = addr_specs,
msghash_type = opt.msghash_type or proto.msghash_types[0]
msghash_type = opt.msghash_type
).write_to_file( ask_overwrite=False )
class sign(metaclass=AsyncInit):

View file

@ -49,12 +49,11 @@ class MMGenIDRange(str,Hilite,InitErrors,MMGenObject):
class coin_msg:
supported_base_protos = ('Bitcoin','Ethereum')
class base(MMGenObject):
ext = 'rawmsg.json'
signed = False
chksum_keys = ('addrlists','message','msghash_type','network')
@property
def desc(self):
@ -64,7 +63,7 @@ class coin_msg:
def chksum(self):
return make_chksum_6(
json.dumps(
{k:v for k,v in self.data.items() if k in ('addrlists','message','network','msghash_type')},
{k:self.data[k] for k in self.chksum_keys},
sort_keys = True,
separators = (',', ':')
))
@ -107,7 +106,8 @@ class coin_msg:
class new(base):
def __init__(self,message,addrlists,msghash_type,*args,**kwargs):
if msghash_type not in self.proto.msghash_types:
msghash_type = msghash_type or self.msg_cls.msghash_types[0]
if msghash_type not in self.msg_cls.msghash_types:
die(2,f'msghash_type {msghash_type!r} not supported for {self.proto.base_proto} protocol')
self.data = {
'network': '{}_{}'.format( self.proto.coin.lower(), self.proto.network ),
@ -181,7 +181,7 @@ class coin_msg:
'failed_sids': ('Failed Seed IDs:', lambda v: red(fmt_list(v,fmt='bare')) ),
}
if len(self.proto.msghash_types) == 1:
if len(self.msg_cls.msghash_types) == 1:
del hdr_data['msghash_type']
if req_addr or type(self).__name__ == 'exported_sigs':
@ -193,7 +193,7 @@ class coin_msg:
fs1 = '{:%s} {}' % max(len(v[0]) for v in hdr_data.values())
fs2 = '{:%s} %s{}' % (
max(len(labels[k]) for v in self.sigs.values() for k in v.keys()),
'0x' if self.proto.base_proto == 'Ethereum' else ''
self.msg_cls.sigdata_pfx or ''
)
if req_addr:
@ -229,7 +229,7 @@ class coin_msg:
'addr': e.addr,
'sig': sig,
}
if self.proto.base_proto != 'Ethereum':
if self.msg_cls.include_pubhash:
data.update({ 'pubhash': self.proto.parse_addr(e.addr_p2pkh or e.addr).bytes.hex() })
if e.addr_p2pkh:
@ -319,8 +319,9 @@ class coin_msg:
def get_json_for_export(self,addr=None):
sigs = list( self.get_sigs(addr).values() )
if self.proto.base_proto == 'Ethereum':
sigs = [{k:'0x'+v for k,v in e.items()} for e in sigs]
pfx = self.msg_cls.sigdata_pfx
if pfx:
sigs = [{k:pfx+v for k,v in e.items()} for e in sigs]
return json.dumps(
{
'message': self.data['message'],
@ -342,9 +343,10 @@ class coin_msg:
desc = self.desc )
)
pfx = self.msg_cls.sigdata_pfx
self.sigs = {sig_data['addr']:sig_data for sig_data in (
[{k:v[2:] for k,v in e.items()} for e in self.data['signatures']] # remove '0x' prefix
if self.proto.base_proto == 'Ethereum' else
[{k:v[len(pfx):] for k,v in e.items()} for e in self.data['signatures']]
if pfx else
self.data['signatures']
)}
@ -362,14 +364,15 @@ def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**k
init_proto( coin=coin, network=network ) if coin else
coin_msg.base().get_proto_from_file(infile) )
if proto.base_proto not in coin_msg.supported_base_protos:
die(f'Message signing operations not supported for {proto.base_proto} protocol')
try:
msg_cls = getattr(
importlib.import_module(f'mmgen.base_proto.{proto.base_proto.lower()}.msg'),
'coin_msg' )
except:
die(1,f'Message signing operations not supported for {proto.base_proto} protocol')
cls = getattr(
getattr(importlib.import_module(f'mmgen.base_proto.{proto.base_proto.lower()}.msg'),'coin_msg'),
clsname )
me = MMGenObject.__new__(cls)
me = MMGenObject.__new__(getattr( msg_cls, clsname, getattr(coin_msg,clsname) ))
me.msg_cls = msg_cls
me.proto = proto
me.__init__(infile=infile,data=data,*args,**kwargs)

View file

@ -43,7 +43,6 @@ class mainnet(CoinProtocol.Secp256k1): # chainparams.cpp
witness_vernum = int(witness_vernum_hex,16)
bech32_hrp = 'bc'
sign_mode = 'daemon'
msghash_types = ('raw',) # first-listed is the default
avg_bdi = int(9.7 * 60) # average block discovery interval (historical)
halving_interval = 210000
max_halvings = 64

View file

@ -30,7 +30,6 @@ class mainnet(CoinProtocol.DummyWIF,CoinProtocol.Secp256k1):
max_tx_fee = '0.005'
chain_names = ['ethereum','foundation']
sign_mode = 'standalone'
msghash_types = ('eth_sign','raw') # first-listed is the default
caps = ('token',)
mmcaps = ('key','addr','rpc','tx')
base_proto = 'Ethereum'

View file

@ -712,9 +712,10 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
t.written_to_file('Signed message data')
return t
def msgverify(self,fn=None):
def msgverify(self,fn=None,msghash_type='eth_sign'):
fn = fn or get_file_with_ext(self.tmpdir,'sigmsg.json')
t = self.spawn('mmgen-msg', self.eth_args_noquiet + [ 'verify', fn ])
t.expect(msghash_type)
t.expect('1 signature verified')
return t
@ -725,25 +726,28 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
return t
def msgverify_export(self):
return self.msgverify( fn=os.path.join(self.tmpdir,'signatures.json') )
return self.msgverify(
fn = os.path.join(self.tmpdir,'signatures.json') )
def msgcreate_raw(self):
get_file_with_ext(self.tmpdir,'rawmsg.json',delete_all=True)
return self.msgcreate(add_args=['--msghash-type=raw'])
def msgsign_raw(self,*args,**kwargs):
def msgsign_raw(self):
get_file_with_ext(self.tmpdir,'sigmsg.json',delete_all=True)
return self.msgsign(*args,**kwargs)
return self.msgsign()
def msgverify_raw(self,*args,**kwargs):
return self.msgverify(*args,**kwargs)
def msgverify_raw(self):
return self.msgverify(msghash_type='raw')
def msgexport_raw(self,*args,**kwargs):
def msgexport_raw(self):
get_file_with_ext(self.tmpdir,'signatures.json',no_dot=True,delete_all=True)
return self.msgexport(*args,**kwargs)
return self.msgexport()
def msgverify_export_raw(self,*args,**kwargs):
return self.msgverify_export(*args,**kwargs)
def msgverify_export_raw(self):
return self.msgverify(
fn = os.path.join(self.tmpdir,'signatures.json'),
msghash_type = 'raw' )
def txcreate4(self):
return self.txcreate(

View file

@ -29,7 +29,7 @@ def get_obj(coin,network,msghash_type):
addrlists = addrlists,
msghash_type = msghash_type )
async def run_test(network_id,msghash_type='raw'):
async def run_test(network_id,chksum,msghash_type='raw'):
coin,network = CoinProtocol.Base.parse_network_id(network_id)
@ -48,6 +48,8 @@ async def run_test(network_id,msghash_type='raw'):
os.makedirs(tmpdir,exist_ok=True)
assert m.chksum.upper() == chksum, f'{m.chksum.upper()} != {chksum}'
m.write_to_file(
outdir = tmpdir,
ask_overwrite = False )
@ -122,22 +124,22 @@ class unit_tests:
altcoin_deps = ('ltc','bch','eth','eth_raw')
def btc(self,name,ut):
return run_test('btc')
return run_test('btc','AA0DB5')
def btc_tn(self,name,ut):
return run_test('btc_tn')
return run_test('btc_tn','A88E1D')
def btc_rt(self,name,ut):
return run_test('btc_rt')
return run_test('btc_rt','578018')
def ltc(self,name,ut):
return run_test('ltc')
return run_test('ltc','BA7549')
def bch(self,name,ut):
return run_test('bch')
return run_test('bch','1B8065')
def eth(self,name,ut):
return run_test('eth',msghash_type='eth_sign')
return run_test('eth','35BAD9',msghash_type='eth_sign')
def eth_raw(self,name,ut):
return run_test('eth')
return run_test('eth','9D900C')