chore(python): stop sending `DebugLinkWatchLayout`
What changed, and why it matters
This commit removes calls to an old, already-deprecated debug-only feature named DebugLinkWatchLayout from the Python test library and test scripts. It is a cleanup change affecting only testing and developer tooling, not the firmware that runs on the Trezor device or any user-facing security behavior.
No security action needed. This is a test-infrastructure cleanup. Reviewers may verify that the deprecated DebugLinkWatchLayout message is no longer required by supported firmware versions, as implied by the commit message.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change deletes the watch_layout() helper methods in trezorlib/debuglink.py and TrezorTestContext, and removes all invocations from test files. It also updates one firmware-upgrade test to send DebugLinkWatchLayout directly only for firmware versions 2.3.2 through 2.8.5, instead of relying on the now-removed helper. The commit message explicitly states the feature was deprecated earlier and that there is no changelog entry, indicating routine maintenance.
Changed components
python/src/trezorlib/debuglink.pytests/click_tests/record_layout.pytests/device_handler.pytests/device_tests/bitcoin/test_getaddress_show.pytests/device_tests/cardano/test_sign_tx.pytests/device_tests/ethereum/test_sign_typed_data.pytests/device_tests/ethereum/test_signtx.pytests/device_tests/reset_recovery/test_recovery_bip39_dryrun.pytests/device_tests/reset_recovery/test_reset_recovery_bip39.pytests/device_tests/test_language.pytests/device_tests/test_sdcard.pytests/device_tests/test_session_id_and_passphrase.pytests/input_flows.pytests/upgrade_tests/test_firmware_upgrades.pyInspect captured patch +3 / −44
diff --git a/python/src/trezorlib/debuglink.py b/python/src/trezorlib/debuglink.py
index 24522a15..230fde68 100644
--- a/python/src/trezorlib/debuglink.py
+++ b/python/src/trezorlib/debuglink.py
@@ -748,15 +748,6 @@ class DebugLink:
raise RuntimeError("Timeout waiting for layout")
time.sleep(0.1)
- def watch_layout(self, watch: bool) -> None:
- """Enable or disable watching layouts.
- If disabled, wait_layout will not work.
-
- The message is missing on T1. Use `TrezorClientDebugLink.watch_layout` for
- cross-version compatibility.
- """
- self._call(messages.DebugLinkWatchLayout(watch=watch), expect=messages.Success)
-
def encode_pin(self, pin: str, matrix: str | None = None) -> str:
"""Transform correct PIN according to the displayed matrix."""
if matrix is None:
@@ -1531,19 +1522,6 @@ class TrezorTestContext:
def ping(self, message: str) -> str:
return self.client.ping(message)
- def watch_layout(self, watch: bool = True) -> None:
- """Enable or disable watching layout changes.
-
- Since trezor-core v2.3.2, it is necessary to call `watch_layout()` before
- using `debug.wait_layout()`, otherwise layout changes are not reported.
- """
- if self.version >= (2, 3, 2):
- # version check is necessary because otherwise we cannot reliably detect
- # whether and where to wait for reply:
- # - T1 reports unknown debuglink messages on the wirelink
- # - TT < 2.3.0 does not reply to unknown debuglink messages due to a bug
- self.debug.watch_layout(watch)
-
def use_pin_sequence(self, pins: t.Iterable[str]) -> None:
"""Respond to PIN prompts from device with the provided PINs.
The sequence must be at least as long as the expected number of PIN prompts.
diff --git a/tests/click_tests/record_layout.py b/tests/click_tests/record_layout.py
index 98c53513..fc625b2c 100644
--- a/tests/click_tests/record_layout.py
+++ b/tests/click_tests/record_layout.py
@@ -64,7 +64,6 @@ def get_client(conn):
client = get_client_orig(conn)
DEBUGLINK = debuglink.DebugLink(client.transport.find_debug())
DEBUGLINK.open()
- DEBUGLINK.watch_layout(True)
return client
@@ -278,7 +277,6 @@ for best results.
main()
finally:
if DEBUGLINK is not None:
- DEBUGLINK.watch_layout(False)
DEBUGLINK.close()
echo("\n# ========== test cases ==========\n")
for call in CALLS_DONE:
diff --git a/tests/device_handler.py b/tests/device_handler.py
index afec20af..3f25f9af 100644
--- a/tests/device_handler.py
+++ b/tests/device_handler.py
@@ -47,7 +47,6 @@ class BackgroundDeviceHandler:
self.client = client
self.client.ui = NullUI # type: ignore [NullUI is OK UI]
self.client.app.button_callback = self.client.ui.button_request
- self.client.watch_layout(True)
self.client.debug.input_wait_type = DebugWaitType.CURRENT_LAYOUT
def get_session(self, *args, **kwargs):
diff --git a/tests/device_tests/bitcoin/test_getaddress_show.py b/tests/device_tests/bitcoin/test_getaddress_show.py
index a000913c..5662d728 100644
--- a/tests/device_tests/bitcoin/test_getaddress_show.py
+++ b/tests/device_tests/bitcoin/test_getaddress_show.py
@@ -277,7 +277,6 @@ def test_show_multisig_xpubs(
IF = InputFlowShowMultisigXPUBs(session, address, xpubs, i)
client.set_input_flow(IF.get())
session.debug.synchronize_at("Homescreen")
- client.watch_layout()
btc.get_address(
session,
"Bitcoin",
diff --git a/tests/device_tests/cardano/test_sign_tx.py b/tests/device_tests/cardano/test_sign_tx.py
index 058f109e..c92e214d 100644
--- a/tests/device_tests/cardano/test_sign_tx.py
+++ b/tests/device_tests/cardano/test_sign_tx.py
@@ -162,7 +162,6 @@ def call_sign_tx(session: Session, parameters, input_flow=None, chunkify: bool =
with session.test_ctx as client:
if input_flow is not None:
- client.watch_layout()
client.set_input_flow(input_flow(client))
return cardano.sign_tx(
diff --git a/tests/device_tests/ethereum/test_sign_typed_data.py b/tests/device_tests/ethereum/test_sign_typed_data.py
index 66201abc..c55431b1 100644
--- a/tests/device_tests/ethereum/test_sign_typed_data.py
+++ b/tests/device_tests/ethereum/test_sign_typed_data.py
@@ -103,7 +103,6 @@ DATA = {
@pytest.mark.models("core")
def test_ethereum_sign_typed_data_show_more_button(session: Session):
with session.test_ctx as client:
- client.watch_layout()
IF = InputFlowEIP712ShowMore(client)
client.set_input_flow(IF.get())
ethereum.sign_typed_data(
@@ -117,7 +116,6 @@ def test_ethereum_sign_typed_data_show_more_button(session: Session):
@pytest.mark.models("core")
def test_ethereum_sign_typed_data_cancel(session: Session):
with session.test_ctx as client, pytest.raises(exceptions.Cancelled):
- client.watch_layout()
IF = InputFlowEIP712Cancel(client)
client.set_input_flow(IF.get())
ethereum.sign_typed_data(
diff --git a/tests/device_tests/ethereum/test_signtx.py b/tests/device_tests/ethereum/test_signtx.py
index 695b183c..968568d7 100644
--- a/tests/device_tests/ethereum/test_signtx.py
+++ b/tests/device_tests/ethereum/test_signtx.py
@@ -94,7 +94,6 @@ def _do_test_signtx(
):
with session.test_ctx as client:
if input_flow:
- client.watch_layout()
client.set_input_flow(input_flow)
sig_v, sig_r, sig_s = ethereum.sign_tx(
session,
@@ -531,14 +530,12 @@ def test_signtx_data_pagination(session: Session, scroll: bool, size: int):
# test pagination
flow = InputFlowEthereumSignTxData(session, scroll=scroll, cancel=False)
with session.test_ctx as client:
- client.watch_layout()
client.set_input_flow(flow.get())
_sign_tx_call()
# test cancellation
flow = InputFlowEthereumSignTxData(session, scroll=scroll, cancel=True)
with client, pytest.raises(exceptions.Cancelled):
- client.watch_layout()
client.set_input_flow(flow.get())
_sign_tx_call()
diff --git a/tests/device_tests/reset_recovery/test_recovery_bip39_dryrun.py b/tests/device_tests/reset_recovery/test_recovery_bip39_dryrun.py
index 030ebd28..847bf3a8 100644
--- a/tests/device_tests/reset_recovery/test_recovery_bip39_dryrun.py
+++ b/tests/device_tests/reset_recovery/test_recovery_bip39_dryrun.py
@@ -60,7 +60,6 @@ def do_recover_core(
session: Session, mnemonic: list[str], mismatch: bool = False
) -> None:
with session.test_ctx as client:
- client.watch_layout()
IF = InputFlowBip39RecoveryDryRun(session, mnemonic, mismatch=mismatch)
client.set_input_flow(IF.get())
return device.recover(
@@ -99,7 +98,6 @@ def test_invalid_seed_t1(session: Session):
@pytest.mark.models("core")
def test_invalid_seed_core(session: Session):
with session.test_ctx as client:
- client.watch_layout()
IF = InputFlowBip39RecoveryDryRunInvalid(session)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
diff --git a/tests/device_tests/reset_recovery/test_reset_recovery_bip39.py b/tests/device_tests/reset_recovery/test_reset_recovery_bip39.py
index e9b9d649..8dd1125d 100644
--- a/tests/device_tests/reset_recovery/test_reset_recovery_bip39.py
+++ b/tests/device_tests/reset_recovery/test_reset_recovery_bip39.py
@@ -79,7 +79,6 @@ def recover(session: Session, mnemonic: str):
with session.test_ctx as client:
IF = InputFlowBip39Recovery(session, words)
client.set_input_flow(IF.get())
- client.watch_layout()
device.recover(
session,
pin_protection=False,
diff --git a/tests/device_tests/test_language.py b/tests/device_tests/test_language.py
index a2dafe1f..f3d463b1 100644
--- a/tests/device_tests/test_language.py
+++ b/tests/device_tests/test_language.py
@@ -97,7 +97,6 @@ def _check_ping_screen_texts(session: Session, title: str, right_button: str) ->
right_button = "-"
with session.test_ctx as client:
- client.watch_layout(True)
client.set_input_flow(ping_input_flow(session, title, right_button))
ping = session.call(messages.Ping(message="ahoj!", button_protection=True))
assert ping == messages.Success(message="ahoj!")
diff --git a/tests/device_tests/test_sdcard.py b/tests/device_tests/test_sdcard.py
index e9244e7e..112785dd 100644
--- a/tests/device_tests/test_sdcard.py
+++ b/tests/device_tests/test_sdcard.py
@@ -77,7 +77,6 @@ def test_sd_protect_unlock(session: Session):
debug.press_yes()
with session.test_ctx as client:
- client.watch_layout()
client.set_input_flow(input_flow_enable_sd_protect)
device.sd_protect(session, Op.ENABLE)
@@ -103,7 +102,6 @@ def test_sd_protect_unlock(session: Session):
debug.press_yes()
with session.test_ctx as client:
- client.watch_layout()
client.set_input_flow(input_flow_change_pin)
device.change_pin(session)
@@ -126,7 +124,6 @@ def test_sd_protect_unlock(session: Session):
debug.press_no() # close
with session.test_ctx as client, pytest.raises(TrezorFailure) as e:
- client.watch_layout()
client.set_input_flow(input_flow_change_pin_format)
device.change_pin(session)
diff --git a/tests/device_tests/test_session_id_and_passphrase.py b/tests/device_tests/test_session_id_and_passphrase.py
index 689839b0..77caace4 100644
--- a/tests/device_tests/test_session_id_and_passphrase.py
+++ b/tests/device_tests/test_session_id_and_passphrase.py
@@ -445,7 +445,6 @@ def test_hide_passphrase_from_host(test_ctx: TrezorTestContext):
else:
raise KeyError
- test_ctx.watch_layout()
test_ctx.set_input_flow(input_flow)
test_ctx.set_expected_responses(
[
@@ -490,7 +489,6 @@ def test_hide_passphrase_from_host(test_ctx: TrezorTestContext):
assert passphrase in test_ctx.debug.read_layout().text_content()
test_ctx.debug.press_yes()
- test_ctx.watch_layout()
test_ctx.set_input_flow(input_flow)
test_ctx.set_expected_responses(
[
diff --git a/tests/input_flows.py b/tests/input_flows.py
index 822a0595..164b593e 100644
--- a/tests/input_flows.py
+++ b/tests/input_flows.py
@@ -60,7 +60,6 @@ class InputFlowBase:
self.layout_type = client.layout_type
def get(self) -> Callable[[], BRGeneratorType]:
- self.client.watch_layout(True)
# There could be one common input flow for all models
if hasattr(self, "input_flow_common"):
diff --git a/tests/upgrade_tests/test_firmware_upgrades.py b/tests/upgrade_tests/test_firmware_upgrades.py
index 69b098df..d7bd3174 100644
--- a/tests/upgrade_tests/test_firmware_upgrades.py
+++ b/tests/upgrade_tests/test_firmware_upgrades.py
@@ -29,6 +29,7 @@ from trezorlib.messages import (
BackupAvailability,
BackupType,
CardanoDerivationType,
+ DebugLinkWatchLayout,
RecoveryStatus,
Success,
)
@@ -451,8 +452,9 @@ def test_upgrade_shamir_recovery(
BackgroundDeviceHandler(emu.client) as device_handler,
):
assert emu.client.features.recovery_status == RecoveryStatus.Nothing
- emu.client.watch_layout(True)
debug = device_handler.debuglink()
+ if (2, 3, 2) <= debug.version <= (2, 8, 5):
+ debug._call(DebugLinkWatchLayout(watch=True), expect=Success)
device_handler.run_with_session(
device.recover, seedless=True, pin_protection=False
@@ -480,7 +482,6 @@ def test_upgrade_shamir_recovery(
assert device_id == emu.client.features.device_id
assert emu.client.features.recovery_status == RecoveryStatus.Recovery
debug = emu.client.debug
- emu.client.watch_layout(True)
layout = debug.read_layout()
if (
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.