From 33773ecfd6d5b94479b7e328d5a67edd1fe20cd2 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 9 Sep 2026 18:33:59 +0000 Subject: [PATCH] new `allow_legacy_tx_files` and `forbid_version_mismatched_tx_files` options These options may be set in the cfg file only. `allow_legacy_tx_files` - allow non-signing and non-sending operations with legacy non-JSON transaction files (disabled by default) `forbid_version_mismatched_tx_files` - forbid all operations with transaction files with missing or mismatched version number (disabled by default) Refer to the cfg file sample for more details. --- mmgen/cfg.py | 6 +++++ mmgen/data/mmgen.cfg | 14 ++++++++++ mmgen/data/version | 2 +- mmgen/tx/completed.py | 5 ++++ test/cmdtest_d/ref.py | 37 +++++++++++++++++++++++++++ test/ref/3B13CB[30.65968918,90].rawtx | 1 + test/ref/721A2C[33.55555877,90].rawtx | 1 + 7 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 test/ref/3B13CB[30.65968918,90].rawtx create mode 100644 test/ref/721A2C[33.55555877,90].rawtx diff --git a/mmgen/cfg.py b/mmgen/cfg.py index 03c8bd3c..8851ee11 100755 --- a/mmgen/cfg.py +++ b/mmgen/cfg.py @@ -194,6 +194,10 @@ class Config(Lockable): max_urandchars = 80 macos_autosign_ramdisk_size = 10 # see MacOSRamDisk + # security + allow_legacy_tx_files = False + forbid_version_mismatched_tx_files = False + # debug debug = False debug_daemon = False @@ -306,6 +310,7 @@ class Config(Lockable): # proto-specific only: eth_mainnet_chain_names eth_testnet_chain_names # coin-specific only: bch_cashaddr (alias of cashaddr) _cfg_file_opts = ( + 'allow_legacy_tx_files', 'autochg_ignore_labels', 'autosign', 'color', @@ -313,6 +318,7 @@ class Config(Lockable): 'daemon_id', # also coin-specific 'debug', 'fee_adjust', + 'forbid_version_mismatched_tx_files', 'force_256_color', 'hash_preset', 'http_timeout', diff --git a/mmgen/data/mmgen.cfg b/mmgen/data/mmgen.cfg index 51fe71b7..c52fb0a2 100644 --- a/mmgen/data/mmgen.cfg +++ b/mmgen/data/mmgen.cfg @@ -50,6 +50,20 @@ # A value of 0 disables user entropy, but this is not recommended: # usr_randchars 30 +# Uncomment to allow non-signing and non-sending operations with legacy +# non-JSON transaction files (e.g. viewing historical transaction files on +# the removable device). By default, all operations with legacy TX files are +# forbidden. This option is intended for the online device only: enabling it +# on the offline device is strongly discouraged. +# allow_legacy_tx_files true + +# Uncomment to forbid all operations with transaction files with a missing +# version number or version number mismatched with the running software. By +# default, only signing and sending operations with these files are forbidden, +# while other operations are allowed. For extra security, it’s recommended to +# enable this option on the offline device. +# forbid_version_mismatched_tx_files true + # Set the maximum transaction fee for BTC: # btc_max_tx_fee 0.003 diff --git a/mmgen/data/version b/mmgen/data/version index 813e4250..b0711b80 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -16.3.0dev2 +16.3.0dev3 diff --git a/mmgen/tx/completed.py b/mmgen/tx/completed.py index 38949260..88060d07 100755 --- a/mmgen/tx/completed.py +++ b/mmgen/tx/completed.py @@ -112,6 +112,8 @@ class Completed(Base): a, b = ('sign', 'Has your online installation been compromised?') elif 'send' in gc.prog_name: a, b = ('send', 'Is your offline installation out of date?') + elif not self.cfg.allow_legacy_tx_files: + a, b = ('operate on', 'Operation forbidden by ‘allow_legacy_tx_files false’') else: return fs = f'Request to {{}} legacy-format transaction ({desc}). {{}}' @@ -124,6 +126,9 @@ class Completed(Base): elif 'send' in gc.prog_name: a = 'send' b = 'Is your {} installation out of date?'.format('offline' if f_ver < s_ver else 'online') + elif self.cfg.forbid_version_mismatched_tx_files: + a = 'operate on' + b = 'Operation forbidden by ‘forbid_version_mismatched_tx_files true’' else: return fs = ( diff --git a/test/cmdtest_d/ref.py b/test/cmdtest_d/ref.py index ea3e367c..f6894a4b 100755 --- a/test/cmdtest_d/ref.py +++ b/test/cmdtest_d/ref.py @@ -27,6 +27,7 @@ from ..include.common import ( joinpath, cmp_or_die, ref_kafile_pass, + write_to_cfgfile, ) from .include.common import ( dfl_words_file, @@ -296,6 +297,12 @@ class CmdTestRefTX(CmdTestRef): cmd_group = ( ('ref_txfile_sign', 'signing saved reference tx file'), + ('ref_txfile_legacy_forbidden', 'viewing a legacy tx file (forbidden)'), + ('ref_txfile_legacy_allowed', 'viewing a legacy tx file (allowed)'), + ('ref_txfile_mismatched_forbidden', 'viewing an out-of-date tx file (forbidden)'), + ('ref_txfile_mismatched_allowed', 'viewing an out-of-date tx file (allowed)'), + ('ref_txfile_mismatched_forbidden_nover', 'viewing an out-of-date tx file (forbidden, no version)'), + ('ref_txfile_mismatched_allowed_nover', 'viewing an out-of-date tx file (allowed, no version)'), ) sources = { @@ -303,6 +310,8 @@ class CmdTestRefTX(CmdTestRef): 'btc': ( '0B8D5A[15.31789,14,tl=1320969600].rawtx', '0C7115[15.86255,14,tl=1320969600].testnet.rawtx', + '721A2C[33.55555877,90].rawtx', # JSON, no version (no key) + '3B13CB[30.65968918,90].rawtx' # JSON, version 1 (no key) ), 'ltc': ( 'AF3CDF-LTC[620.76194,1453,tl=1320969600].rawtx', @@ -334,3 +343,31 @@ class CmdTestRefTX(CmdTestRef): self.write_to_tmpfile(pwfile, wpasswd) idx = 1 if self.tn_ext else 0 return self.txsign(dfl_words_file, self._get_txfile(idx), save=False, has_label=True, view='y') + + def _ref_txfile_chk(self, cfgfile_lines=[], *, idx, ver=None, allowed=False): + expect_str = 'legacy-format' if ver is None else f'with version {ver}' + write_to_cfgfile(cfgfile_lines) + t = self.spawn('mmgen-tool', ['txview', self._get_txfile(idx)]) + if allowed: + assert not expect_str in t.read() + else: + t.expect(expect_str) + return t + + def ref_txfile_legacy_forbidden(self): + return self._ref_txfile_chk(idx=0) + + def ref_txfile_legacy_allowed(self): + return self._ref_txfile_chk(['allow_legacy_tx_files true'], idx=0, allowed=True) + + def ref_txfile_mismatched_forbidden(self): + return self._ref_txfile_chk(['forbid_version_mismatched_tx_files true'], idx=3, ver=1) + + def ref_txfile_mismatched_allowed(self): + return self._ref_txfile_chk(idx=3, ver=1, allowed=True) + + def ref_txfile_mismatched_forbidden_nover(self): + return self._ref_txfile_chk(['forbid_version_mismatched_tx_files true'], idx=2, ver=0) + + def ref_txfile_mismatched_allowed_nover(self): + return self._ref_txfile_chk(idx=2, ver=0, allowed=True) diff --git a/test/ref/3B13CB[30.65968918,90].rawtx b/test/ref/3B13CB[30.65968918,90].rawtx new file mode 100644 index 00000000..7723e55c --- /dev/null +++ b/test/ref/3B13CB[30.65968918,90].rawtx @@ -0,0 +1 @@ +{"MMGenTransaction":{"coin_id":"BTC","version":1,"chain":"mainnet","txid":"3B13CB","send_amt":"30.65968918","timestamp":"20260909_102135","blockcount":0,"serialized":"02000000014c70bb0b81e5345a8c1c7bca55cabe0990c709814b0ec4bcd319d1125276a7690500000000fdffffff0116f9beb6000000001976a914e5092862dbd816c54250400c460623c3e523028c88ac00000000","inputs":[{"vout":5,"txid":"69a7765212d119d3bcc40e4b8109c79009beca55ca7b1c8c5a34e5810bbb704c","scriptPubKey":"76a91458b65c1131e40898f5d09ddebd3a86cf2c99eff288ac","amt":"30.65988898","comment":"Ian\u2019s inheritance","addr":"1964vT9XkvH7uS5L9zBk2yPU5Y9mfXdyYW","confs":750745,"mmid":"05C9011E:L:3","sequence":4294967293}],"outputs":[{"addr":"1Mt2iDqijqbrizKb5XQd1psxCuCWomgeWi","amt":"30.65968918","is_chg":true,"mmid":"05C9011E:L:45"}]},"chksum":"9b79be"} \ No newline at end of file diff --git a/test/ref/721A2C[33.55555877,90].rawtx b/test/ref/721A2C[33.55555877,90].rawtx new file mode 100644 index 00000000..834f1de2 --- /dev/null +++ b/test/ref/721A2C[33.55555877,90].rawtx @@ -0,0 +1 @@ +{"MMGenTransaction":{"coin_id":"BTC","chain":"mainnet","txid":"721A2C","send_amt":"33.55555877","timestamp":"20260909_102022","blockcount":0,"serialized":"0200000001fec8c7cc5c19d174140a4fc9ee23ac6545ac26953b81d2fb27f0322a7e78de9a0200000000fdffffff0125b801c8000000001976a9146497030410c814c5f092d02fd63006635897db1588ac00000000","inputs":[{"vout":2,"txid":"9ade787e2a32f027fbd2813b9526ac4565ac23eec94f0a1474d1195cccc7c8fe","scriptPubKey":"76a914d665dcb873126ef4cf59122eed30cd0ad6354f9588ac","amt":"33.55575857","comment":"Carl\u2019s capital","addr":"1LYddZXEaHuZmJVQpbShmCB9GzrRLtmEiB","confs":782493,"mmid":"F30828B2:L:3","sequence":4294967293}],"outputs":[{"addr":"1AAsUfboFuUcmVd5VebnKdKNd55orfFcaE","amt":"33.55555877","is_chg":true,"mmid":"F30828B2:L:45"}]},"chksum":"3ed48b"} \ No newline at end of file