Browse Source

autosign: print summary of non-MMGen output addresses and amounts

To allow the user to verify that transaction outputs have not been altered
by a compromised online MMGen installation, print a list of non-MMGen output
addresses and amounts for all signed transactions after every autosign run.

With --full-summary, print a full view of each signed transaction instead.
MMGen 5 years ago
parent
commit
d558822941
3 changed files with 61 additions and 4 deletions
  1. 39 0
      mmgen/main_autosign.py
  2. 5 0
      test/ref/25EFA3[2.34].testnet.rawtx
  3. 17 4
      test/test_py_d/ts_autosign.py

+ 39 - 0
mmgen/main_autosign.py

@@ -44,6 +44,9 @@ opts_data = {
 -m, --mountpoint=m  Specify an alternate mountpoint (default: '{mp}')
 -m, --mountpoint=m  Specify an alternate mountpoint (default: '{mp}')
 -s, --stealth-led   Stealth LED mode - signal busy and error only, and only
 -s, --stealth-led   Stealth LED mode - signal busy and error only, and only
                     after successful authorization.
                     after successful authorization.
+-S, --full-summary  Print a full summary of each signed transaction after
+                    each autosign run. The default list of non-MMGen outputs
+                    will not be printed.
 -q, --quiet         Produce quieter output
 -q, --quiet         Produce quieter output
 -v, --verbose       Produce more verbose output
 -v, --verbose       Produce more verbose output
 """.format(mp=mountpoint),
 """.format(mp=mountpoint),
@@ -191,6 +194,7 @@ def sign_tx_file(txfile):
 
 
 		if txsign(tx,wfs,None,None):
 		if txsign(tx,wfs,None,None):
 			tx.write_to_file(ask_write=False)
 			tx.write_to_file(ask_write=False)
+			txlist.append(tx)
 			return True
 			return True
 		else:
 		else:
 			return False
 			return False
@@ -241,13 +245,48 @@ def decrypt_wallets():
 
 
 	return False if fails else True
 	return False if fails else True
 
 
+
+def print_summary():
+
+	if opt.full_summary:
+		bmsg('\nAutosign summary:')
+		for tx in txlist:
+			init_coin(tx.coin,tx.chain == 'testnet')
+			msg_r(tx.format_view(terse=True))
+		return
+
+	body = []
+	for tx in txlist:
+		non_mmgen = [o for o in tx.outputs if not o.mmid]
+		if non_mmgen:
+			body.append((tx,non_mmgen))
+
+	if body:
+		bmsg('\nAutosign summary:')
+		fs = '{}  {} {}'
+		t_wid,a_wid = 6,44
+		msg(fs.format('TX ID ','Non-MMGen outputs'+' '*(a_wid-17),'Amount'))
+		msg(fs.format('-'*t_wid, '-'*a_wid, '-'*7))
+		for tx,non_mmgen in body:
+			for nm in non_mmgen:
+				msg(fs.format(
+					tx.txid.fmt(width=t_wid,color=True) if nm is non_mmgen[0] else ' '*t_wid,
+					nm.addr.fmt(width=a_wid,color=True),
+					nm._amt.hl() + ' ' + yellow(tx.coin)))
+	else:
+		msg('No non-MMGen outputs')
+
 def do_sign():
 def do_sign():
 	if not opt.stealth_led: set_led('busy')
 	if not opt.stealth_led: set_led('busy')
 	do_mount()
 	do_mount()
 	key_ok = decrypt_wallets()
 	key_ok = decrypt_wallets()
 	if key_ok:
 	if key_ok:
 		if opt.stealth_led: set_led('busy')
 		if opt.stealth_led: set_led('busy')
+		global txlist
+		txlist = []
 		ret = sign()
 		ret = sign()
+		print_summary()
+		txlist = []
 		do_umount()
 		do_umount()
 		set_led(('standby','off','error')[(not ret)*2 or bool(opt.stealth_led)])
 		set_led(('standby','off','error')[(not ret)*2 or bool(opt.stealth_led)])
 		return ret
 		return ret

+ 5 - 0
test/ref/25EFA3[2.34].testnet.rawtx

@@ -0,0 +1,5 @@
+3df942
+TESTNET 25EFA3 2.34 20190424_101833 433
+0200000001db9d489e2b43e30b96931364d1a4ba97bd2f5515790c82703e7b7f11e549f51a0100000000ffffffff03c0b99d06000000001976a91496235113a5c6608bdb9bc5f5e08c69458a6b17dd88acc0d45407000000001976a9148970c0a71207de041f707febfcbdac46cbd7aaf588ac702e19460200000017a914daaf772bc9d05b41f6811fa200a34fcd4d58ed2c8700000000
+[{'txid': '1af549e5117f7b3e70820c7915552fbd97baa4d1641393960be3432b9e489ddb', 'vout': 1, 'label': '', 'scriptPubKey': 'a914f199a1b4f46bd0f659287517435f4d84fda854c887', 'amt': '100', 'addr': '2NFGgt2iky94KDocUV6nE2GsGg6uvetUWzH', 'confs': 1, 'mmid': '98831F3A:S:31'}]
+[{'addr': 'muCoxgBTXA1CFb9phJqMZgqP2rm1mPrbKE', 'amt': '1.11', 'is_chg': False}, {'addr': 'mt3fwX52huqVB1aU6Y2cLts3WVG4bBeqS1', 'amt': '1.23', 'is_chg': False}, {'addr': '2NDBXVfG7uUDwrZPyEZPc8vRMyZN4dfPoY7', 'amt': '97.6599', 'is_chg': True, 'mmid': '98831F3A:S:1'}]

+ 17 - 4
test/test_py_d/ts_autosign.py

@@ -44,14 +44,14 @@ class TestSuiteAutosign(TestSuiteBase):
 		return self.autosign(
 		return self.autosign(
 					coins=['btc','eth'],
 					coins=['btc','eth'],
 					txfiles=['btc','eth','mm1','etc'],
 					txfiles=['btc','eth','mm1','etc'],
-					txcount=7,
+					txcount=8,
 					live=live)
 					live=live)
 
 
 	# tests everything except device detection, mount/unmount
 	# tests everything except device detection, mount/unmount
 	def autosign(   self,
 	def autosign(   self,
 					coins=['btc','bch','ltc','eth'],
 					coins=['btc','bch','ltc','eth'],
 					txfiles=['btc','bch','ltc','eth','mm1','etc'],
 					txfiles=['btc','bch','ltc','eth','mm1','etc'],
-					txcount=11,
+					txcount=12,
 					live=False):
 					live=False):
 
 
 		if self.skip_for_win(): return
 		if self.skip_for_win(): return
@@ -103,8 +103,9 @@ class TestSuiteAutosign(TestSuiteBase):
 			fdata = [e for e in fdata_in if e[0] in txfiles]
 			fdata = [e for e in fdata_in if e[0] in txfiles]
 			from test.test_py_d.ts_ref import TestSuiteRef
 			from test.test_py_d.ts_ref import TestSuiteRef
 			tfns  = [TestSuiteRef.sources['ref_tx_file'][c][1] for c,d in fdata] + \
 			tfns  = [TestSuiteRef.sources['ref_tx_file'][c][1] for c,d in fdata] + \
-					[TestSuiteRef.sources['ref_tx_file'][c][0] for c,d in fdata]
-			tfs = [joinpath(ref_dir,d[1],fn) for d,fn in zip(fdata+fdata,tfns)]
+					[TestSuiteRef.sources['ref_tx_file'][c][0] for c,d in fdata] + \
+					['25EFA3[2.34].testnet.rawtx'] # TX with 2 non-MMGen outputs
+			tfs = [joinpath(ref_dir,d[1],fn) for d,fn in zip(fdata+fdata+[('btc','')],tfns)]
 
 
 			for f,fn in zip(tfs,tfns):
 			for f,fn in zip(tfs,tfns):
 				if fn: # use empty fn to skip file
 				if fn: # use empty fn to skip file
@@ -172,13 +173,25 @@ class TestSuiteAutosign(TestSuiteBase):
 
 
 		def do_autosign(opts,mountpoint):
 		def do_autosign(opts,mountpoint):
 			make_wallet(opts)
 			make_wallet(opts)
+
 			copy_files(mountpoint,include_bad_tx=True)
 			copy_files(mountpoint,include_bad_tx=True)
+			t = self.spawn('mmgen-autosign',opts+['--full-summary','wait'],extra_desc='(sign - full summary)')
+			t.expect('{} transactions signed'.format(txcount))
+			t.expect('1 transaction failed to sign')
+			t.expect('Waiting')
+			t.kill(2)
+			t.req_exit_val = 1
+			imsg('')
+			t.ok()
+
+			copy_files(mountpoint,remove_signed_only=True)
 			t = self.spawn('mmgen-autosign',opts+['wait'],extra_desc='(sign)')
 			t = self.spawn('mmgen-autosign',opts+['wait'],extra_desc='(sign)')
 			t.expect('{} transactions signed'.format(txcount))
 			t.expect('{} transactions signed'.format(txcount))
 			t.expect('1 transaction failed to sign')
 			t.expect('1 transaction failed to sign')
 			t.expect('Waiting')
 			t.expect('Waiting')
 			t.kill(2)
 			t.kill(2)
 			t.req_exit_val = 1
 			t.req_exit_val = 1
+			imsg('')
 			return t
 			return t
 
 
 		if live:
 		if live: