mmgen-msg: add export command

- export signature data relevant for a third-party verifier to JSON
This commit is contained in:
The MMGen Project 2022-04-23 14:36:10 +00:00
commit 4821fc203c
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
6 changed files with 72 additions and 21 deletions

View file

@ -1 +1 @@
March 2022
April 2022

View file

@ -1 +1 @@
13.1.dev23
13.1.dev24

View file

@ -65,6 +65,17 @@ class MsgOps:
if getattr(m,'failed_sids',None):
sys.exit(1)
class export(sign):
async def __init__(self,msgfile,addr=None):
from .fileutil import write_data_to_file
write_data_to_file(
outfile = 'signatures.json',
data = SignedOnlineMsg( infile=msgfile ).get_json_for_export( addr ),
desc = 'signature data' )
opts_data = {
'text': {
'desc': 'Perform message signing operations for MMGen addresses',
@ -72,6 +83,7 @@ opts_data = {
'[opts] create MESSAGE_TEXT ADDRESS_SPEC [...]',
'[opts] sign MESSAGE_FILE [WALLET_FILE ...]',
'[opts] verify MESSAGE_FILE',
'[opts] export MESSAGE_FILE',
],
'options': """
-h, --help Print this help message
@ -88,6 +100,8 @@ create - create a raw MMGen message file with specified message text for
SPECIFIER below)
sign - perform signing operation on an unsigned MMGen message file
verify - verify and display the contents of a signed MMGen message file
export - dump signed MMGen message file to signatures.json, including only
data relevant for a third-party verifier
ADDRESS SPECIFIER
@ -149,6 +163,9 @@ $ mmgen-msg verify <signed message file>
Verify and display a single signature in the signed message file:
$ mmgen-msg verify <signed message file> DEADBEEF:B:98
Export data relevant for a third-party verifier to signatures.json:
$ mmgen-msg export <signed message file>
"""
},
'code': {
@ -174,10 +191,10 @@ async def main():
if len(cmd_args) < 1:
opts.usage()
await MsgOps.sign( cmd_args[0], cmd_args[1:] )
elif op == 'verify':
elif op in ('verify','export'):
if len(cmd_args) not in (1,2):
opts.usage()
await MsgOps.verify( cmd_args[0], cmd_args[1] if len(cmd_args) == 2 else None )
await getattr(MsgOps,op)( cmd_args[0], cmd_args[1] if len(cmd_args) == 2 else None )
else:
die(1,f'{op!r}: unrecognized operation')

View file

@ -258,7 +258,7 @@ class coin_msg:
class signed_online(signed):
async def verify(self,addr=None,summary=False):
def get_sigs(self,addr):
if addr:
mmaddr = MMGenID(self.proto,addr)
@ -266,23 +266,40 @@ class coin_msg:
else:
sigs = self.sigs
if sigs:
from .rpc import rpc_init
self.rpc = await rpc_init(self.proto)
for k,v in sigs.items():
ret = await self.do_verify(
addr = v.get('addr_p2pkh') or v['addr'],
sig = v['sig'],
message = self.data['message'] )
if not ret:
die(3,f'Invalid signature for address {k} ({v["addr"]})')
if summary:
msg('{} signature{} verified'.format( len(sigs), suf(sigs) ))
else:
if not sigs:
die(1,'No signatures')
return sigs
async def verify(self,addr=None,summary=False):
sigs = self.get_sigs(addr)
from .rpc import rpc_init
self.rpc = await rpc_init(self.proto)
for k,v in sigs.items():
ret = await self.do_verify(
addr = v.get('addr_p2pkh') or v['addr'],
sig = v['sig'],
message = self.data['message'] )
if not ret:
die(3,f'Invalid signature for address {k} ({v["addr"]})')
if summary:
msg('{} signature{} verified'.format( len(sigs), suf(sigs) ))
def get_json_for_export(self,addr=None):
return json.dumps(
{
'message': self.data['message'],
'network': self.data['network'].upper(),
'signatures': tuple( self.get_sigs(addr).values() ),
},
sort_keys = True,
indent = 4
)
def _get_obj(clsname,coin=None,network='mainnet',infile=None,data=None,*args,**kwargs):
assert not args, 'msg:_get_obj(): only keyword args allowed'

View file

@ -266,6 +266,8 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
('bob_msgverify', 'verifying the message file (all addresses)'),
('bob_msgverify_raw', 'verifying the raw message file (all addresses)'),
('bob_msgverify_single', 'verifying the message file (single address)'),
('bob_msgexport', 'exporting the message file'),
('bob_msgexport_single', 'exporting the message file (single address)'),
('stop', 'stopping regtest daemon'),
)
@ -1083,12 +1085,12 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
fn2 = get_file_with_ext(self.tmpdir,'bip39')
return self.bob_msgsign([fn2,fn1])
def bob_msgverify(self,addr=None,ext='sigmsg.json'):
def bob_msgverify(self,addr=None,ext='sigmsg.json',cmd='verify'):
return self.spawn(
'mmgen-msg', [
'--bob',
f'--outdir={self.tmpdir}',
'verify',
cmd,
get_file_with_ext(self.tmpdir,ext),
] + ([addr] if addr else []) )
@ -1101,6 +1103,15 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
sid = self._user_sid('bob')
return self.bob_msgverify(addr=f'{sid}:{self.dfl_mmtype}:1')
def bob_msgexport(self,addr=None):
t = self.bob_msgverify( addr=addr, cmd='export' )
t.written_to_file('data')
return t
def bob_msgexport_single(self):
sid = self._user_sid('bob')
return self.bob_msgexport(addr=f'{sid}:{self.dfl_mmtype}:1')
def stop(self):
if opt.no_daemon_stop:
self.spawn('',msg_only=True)

View file

@ -73,6 +73,12 @@ async def run_test(network_id):
pumsg('\nTesting single address verification:\n')
await m.verify('A091ABAA:111',summary=opt.verbose)
pumsg('\nTesting JSON dump for export:\n')
msg( m.get_json_for_export() )
pumsg('\nTesting single address JSON dump for export:\n')
msg( m.get_json_for_export('A091ABAA:111') )
stop_test_daemons(network_id)
msg('\n')