Skip to content

Commit 6c42d14

Browse files
committed
trezor: restore support for external inputs
1 parent f002150 commit 6c42d14

File tree

4 files changed

+37
-33
lines changed

4 files changed

+37
-33
lines changed

docs/devices/index.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ The table below lists what devices and features are supported for each device.
5151
+------------------------------------+---------------+---------------+------------+----------------+----------+----------+---------+----------+------------------+
5252
| Arbitrary witnessScript Inputs ||||||||||
5353
+------------------------------------+---------------+---------------+------------+----------------+----------+----------+---------+----------+------------------+
54-
| Non-wallet inputs ||| \ :sup:`1` | \ :sup:`2` ||||||
54+
| Non-wallet inputs ||| \ :sup:`1` | \ :sup:`2` ||||||
5555
+------------------------------------+---------------+---------------+------------+----------------+----------+----------+---------+----------+------------------+
5656
| Mixed Segwit and Non-Segwit Inputs ||||||||||
5757
+------------------------------------+---------------+---------------+------------+----------------+----------+----------+---------+----------+------------------+
5858
| Display on device screen ||||||||||
5959
+------------------------------------+---------------+---------------+------------+----------------+----------+----------+---------+----------+------------------+
6060

61-
* 1 - Support removed for devices with firmware 1.10.6 and greater.
62-
* 2 - Support removed for devices with firmware 2.4.4 and greater.
61+
* 1 - Since firmware 1.10.6, safety checks must be disabled.
62+
* 2 - Since firmware 2.4.4, safety checks must be disabled.
6363

6464
\* There are some caveats. See the `sign_tx` for these devices.
6565

hwilib/devices/trezor.py

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,14 @@ def get_path_transport(
275275
raise BadArgumentError(f"Could not find device by path: {path}")
276276

277277

278+
def _use_external_script_type(client) -> bool:
279+
if client.features.model == "1" and client.version > (1, 10, 5):
280+
return True
281+
if client.features.model == "T" and client.version > (2, 4, 3):
282+
return True
283+
return False
284+
285+
278286
# This class extends the HardwareWalletClient for Trezor specific things
279287
class TrezorClient(HardwareWalletClient):
280288

@@ -327,15 +335,6 @@ def _check_unlocked(self) -> None:
327335
if self.client.features.pin_protection and not self.client.features.unlocked:
328336
raise DeviceNotReadyError('{} is locked. Unlock by using \'promptpin\' and then \'sendpin\'.'.format(self.type))
329337

330-
def _supports_external(self) -> bool:
331-
if self.client.features.model == "1" and self.client.version <= (1, 10, 5):
332-
return True
333-
if self.client.features.model == "T" and self.client.version <= (2, 4, 3):
334-
return True
335-
if self.client.features.model == "K1-14AM":
336-
return True
337-
return False
338-
339338
@trezor_exception
340339
def get_pubkey_at_path(self, path: str) -> ExtendedKey:
341340
self._check_unlocked()
@@ -431,9 +430,13 @@ def sign_tx(self, tx: PSBT) -> PSBT:
431430
p2wsh = True
432431

433432
def ignore_input() -> None:
434-
txinputtype.address_n = [0x80000000 | 84, 0x80000000 | (0 if self.chain == Chain.MAIN else 1), 0x80000000, 0, 0]
435433
txinputtype.multisig = None
436-
txinputtype.script_type = messages.InputScriptType.SPENDWITNESS
434+
if _use_external_script_type(self.client):
435+
txinputtype.script_type = messages.InputScriptType.EXTERNAL
436+
txinputtype.script_pubkey = utxo.scriptPubKey
437+
else:
438+
txinputtype.address_n = [0x80000000 | 84, 0x80000000 | (0 if self.chain == Chain.MAIN else 1), 0x80000000, 0, 0]
439+
txinputtype.script_type = messages.InputScriptType.SPENDWITNESS
437440
inputs.append(txinputtype)
438441
to_ignore.append(input_num)
439442

@@ -446,21 +449,12 @@ def ignore_input() -> None:
446449
if utxo.is_p2sh:
447450
txinputtype.script_type = messages.InputScriptType.SPENDMULTISIG
448451
else:
449-
# Cannot sign bare multisig, ignore it
450-
if not self._supports_external():
451-
raise BadArgumentError("Cannot sign bare multisig")
452452
ignore_input()
453453
continue
454454
elif not is_ms and not is_wit and not is_p2pkh(scriptcode):
455-
# Cannot sign unknown spk, ignore it
456-
if not self._supports_external():
457-
raise BadArgumentError("Cannot sign unknown scripts")
458455
ignore_input()
459456
continue
460457
elif not is_ms and is_wit and p2wsh:
461-
# Cannot sign unknown witness script, ignore it
462-
if not self._supports_external():
463-
raise BadArgumentError("Cannot sign unknown witness versions")
464458
ignore_input()
465459
continue
466460

@@ -497,9 +491,6 @@ def ignore_input() -> None:
497491
passes = our_keys
498492

499493
if not found and not found_in_sigs: # None of our keys were in hd_keypaths or in partial_sigs
500-
# This input is not one of ours
501-
if not self._supports_external():
502-
raise BadArgumentError("Cannot sign external inputs")
503494
ignore_input()
504495
continue
505496
elif not found and found_in_sigs:

test/test_device.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ def test_signtx(self):
573573
# Make a huge transaction which might cause some problems with different interfaces
574574
def test_big_tx(self):
575575
# make a huge transaction
576-
keypool_desc = self.do_command(self.dev_args + ["getkeypool", "--account", "10", "--addr-type", "legacy", "0", "100"])
576+
keypool_desc = self.do_command(self.dev_args + ["getkeypool", "--account", "10", "--addr-type", "legacy", "0", "200"])
577577
import_result = self.wrpc.importdescriptors(keypool_desc)
578578
self.assertTrue(import_result[0]['success'])
579579
outputs = []

test/test_trezor.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
)
2929

3030
from hwilib._cli import process_commands
31-
from hwilib.devices.trezor import TrezorClient
31+
from hwilib.devices.trezor import TrezorClient, _use_external_script_type
3232

3333
from types import MethodType
3434

@@ -63,6 +63,7 @@ def __init__(self, path, model):
6363
self.strict_bip48 = True
6464
self.include_xpubs = False
6565
self.supports_device_multiple_multisig = True
66+
self.client = None
6667

6768
def start(self):
6869
super().start()
@@ -85,12 +86,12 @@ def start(self):
8586

8687
# Setup the emulator
8788
wirelink = UdpTransport.enumerate()[0]
88-
client = TrezorClientDebugLink(wirelink)
89-
client.init_device()
90-
device.wipe(client)
91-
load_device_by_mnemonic(client=client, mnemonic='alcohol woman abuse must during monitor noble actual mixed trade anger aisle', pin='', passphrase_protection=False, label='test') # From Trezor device tests
89+
self.client = TrezorClientDebugLink(wirelink)
90+
self.client.init_device()
91+
device.wipe(self.client)
92+
load_device_by_mnemonic(client=self.client, mnemonic='alcohol woman abuse must during monitor noble actual mixed trade anger aisle', pin='', passphrase_protection=False, label='test') # From Trezor device tests
9293
atexit.register(self.stop)
93-
return client
94+
return self.client
9495

9596
def stop(self):
9697
super().stop()
@@ -407,6 +408,16 @@ def test_passphrase(self):
407408
else:
408409
self.fail("Did not enumerate device")
409410

411+
class TestSignTxTrezorExternal(TestSignTx):
412+
def setUp(self):
413+
super().setUp()
414+
if _use_external_script_type(self.emulator.client):
415+
device.apply_settings(self.emulator.client, safety_checks=messages.SafetyCheckLevel.PromptTemporarily)
416+
417+
def tearDown(self):
418+
device.apply_settings(self.emulator.client, safety_checks=messages.SafetyCheckLevel.Strict)
419+
return super().tearDown()
420+
410421
def trezor_test_suite(emulator, bitcoind, interface, model):
411422
assert model in TREZOR_MODELS
412423
# Redirect stderr to /dev/null as it's super spammy
@@ -421,13 +432,15 @@ def trezor_test_suite(emulator, bitcoind, interface, model):
421432
(["legacy", "segwit"], ["legacy", "segwit"], False, True),
422433
(["legacy", "segwit", "tap"], ["legacy", "segwit"], False, True),
423434
]
435+
signtx_cases_external = [(_addr, _multi, True, _opret) for _addr, _multi, _ext, _opret in signtx_cases]
424436

425437
# Generic Device tests
426438
suite = unittest.TestSuite()
427439
suite.addTest(DeviceTestCase.parameterize(TestDeviceConnect, bitcoind, emulator=dev_emulator, interface=interface, detect_type="trezor"))
428440
suite.addTest(DeviceTestCase.parameterize(TestGetDescriptors, bitcoind, emulator=dev_emulator, interface=interface))
429441
suite.addTest(DeviceTestCase.parameterize(TestGetKeypool, bitcoind, emulator=dev_emulator, interface=interface))
430442
suite.addTest(DeviceTestCase.parameterize(TestSignTx, bitcoind, emulator=dev_emulator, interface=interface, signtx_cases=signtx_cases))
443+
suite.addTest(DeviceTestCase.parameterize(TestSignTxTrezorExternal, bitcoind, emulator=dev_emulator, interface=interface, signtx_cases=signtx_cases_external))
431444
suite.addTest(DeviceTestCase.parameterize(TestDisplayAddress, bitcoind, emulator=dev_emulator, interface=interface))
432445
suite.addTest(DeviceTestCase.parameterize(TestSignMessage, bitcoind, emulator=dev_emulator, interface=interface))
433446
if model != 't':

0 commit comments

Comments
 (0)