From d558822941eb53311fee294749ace9967750b4aa Mon Sep 17 00:00:00 2001 From: MMGen Date: Wed, 24 Apr 2019 14:39:21 +0000 Subject: [PATCH] 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/main_autosign.py | 39 +++++++++++++++++++++++++++++ test/ref/25EFA3[2.34].testnet.rawtx | 5 ++++ test/test_py_d/ts_autosign.py | 21 +++++++++++++--- 3 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 test/ref/25EFA3[2.34].testnet.rawtx diff --git a/mmgen/main_autosign.py b/mmgen/main_autosign.py index 9d5bfd85..ee322705 100755 --- a/mmgen/main_autosign.py +++ b/mmgen/main_autosign.py @@ -44,6 +44,9 @@ opts_data = { -m, --mountpoint=m Specify an alternate mountpoint (default: '{mp}') -s, --stealth-led Stealth LED mode - signal busy and error only, and only 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 -v, --verbose Produce more verbose output """.format(mp=mountpoint), @@ -191,6 +194,7 @@ def sign_tx_file(txfile): if txsign(tx,wfs,None,None): tx.write_to_file(ask_write=False) + txlist.append(tx) return True else: return False @@ -241,13 +245,48 @@ def decrypt_wallets(): 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(): if not opt.stealth_led: set_led('busy') do_mount() key_ok = decrypt_wallets() if key_ok: if opt.stealth_led: set_led('busy') + global txlist + txlist = [] ret = sign() + print_summary() + txlist = [] do_umount() set_led(('standby','off','error')[(not ret)*2 or bool(opt.stealth_led)]) return ret diff --git a/test/ref/25EFA3[2.34].testnet.rawtx b/test/ref/25EFA3[2.34].testnet.rawtx new file mode 100644 index 00000000..5906d132 --- /dev/null +++ b/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'}] diff --git a/test/test_py_d/ts_autosign.py b/test/test_py_d/ts_autosign.py index 996f22b9..a7abf9fb 100755 --- a/test/test_py_d/ts_autosign.py +++ b/test/test_py_d/ts_autosign.py @@ -44,14 +44,14 @@ class TestSuiteAutosign(TestSuiteBase): return self.autosign( coins=['btc','eth'], txfiles=['btc','eth','mm1','etc'], - txcount=7, + txcount=8, live=live) # tests everything except device detection, mount/unmount def autosign( self, coins=['btc','bch','ltc','eth'], txfiles=['btc','bch','ltc','eth','mm1','etc'], - txcount=11, + txcount=12, live=False): 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] from test.test_py_d.ts_ref import TestSuiteRef 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): if fn: # use empty fn to skip file @@ -172,13 +173,25 @@ class TestSuiteAutosign(TestSuiteBase): def do_autosign(opts,mountpoint): make_wallet(opts) + 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.expect('{} transactions signed'.format(txcount)) t.expect('1 transaction failed to sign') t.expect('Waiting') t.kill(2) t.req_exit_val = 1 + imsg('') return t if live: