From ac9dec3eb76b4cb7d4b3542e6a5c2f8cb49fc333 Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Tue, 29 Apr 2025 17:33:57 +0200 Subject: [PATCH 01/15] chore(core): update bundled bootloader for T3B1 to version 2.1.10 --- core/.changelog.d/noissue.added | 1 + .../T3B1/bootloaders/bootloader_T3B1.bin | Bin 96768 -> 109056 bytes .../T3B1/bootloaders/bootloader_hashes.h | 6 +++--- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 core/.changelog.d/noissue.added diff --git a/core/.changelog.d/noissue.added b/core/.changelog.d/noissue.added new file mode 100644 index 0000000000..d7f66f6938 --- /dev/null +++ b/core/.changelog.d/noissue.added @@ -0,0 +1 @@ +[T3B1] Upgrade bundled bootloader to 2.1.10. diff --git a/core/embed/models/T3B1/bootloaders/bootloader_T3B1.bin b/core/embed/models/T3B1/bootloaders/bootloader_T3B1.bin index a7bb956a54fce08e4ff966a46f475d49ede7e03f..9560d1f50a74b96ea4975c2a6cf41982c3c10e17 100644 GIT binary patch delta 55852 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 VE*I5zmh|(_tf4vJ* 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 diff --git a/core/embed/models/T3B1/bootloaders/bootloader_hashes.h b/core/embed/models/T3B1/bootloaders/bootloader_hashes.h index 2cf13e074e..c46568040b 100644 --- a/core/embed/models/T3B1/bootloaders/bootloader_hashes.h +++ b/core/embed/models/T3B1/bootloaders/bootloader_hashes.h @@ -4,9 +4,9 @@ // Auto-generated file, do not edit. // clang-format off -// bootloader_T3B1.bin version 2.1.8.0 -#define BOOTLOADER_T3B1_00 {0x23, 0x64, 0x01, 0x0c, 0x96, 0xc1, 0x04, 0x2f, 0x54, 0x12, 0x53, 0xb5, 0xd7, 0x23, 0xcf, 0xf9, 0x18, 0x59, 0xf1, 0xdd, 0x6a, 0x5e, 0x3a, 0x3f, 0xf2, 0xf1, 0x71, 0x55, 0xaf, 0x2e, 0x48, 0x92} -#define BOOTLOADER_T3B1_FF {0x8f, 0x63, 0x1a, 0x98, 0x56, 0x9c, 0xcc, 0xf1, 0xfc, 0x0b, 0x98, 0x62, 0x5d, 0xc6, 0xc7, 0x33, 0x73, 0x90, 0x2f, 0x69, 0x02, 0x57, 0xbd, 0x5e, 0x31, 0x4c, 0x0b, 0x5a, 0xdb, 0x01, 0x17, 0x24} +// bootloader_T3B1.bin version 2.1.10.0 +#define BOOTLOADER_T3B1_00 {0x64, 0x7c, 0xac, 0x56, 0x1d, 0x10, 0xcb, 0x55, 0x78, 0x01, 0xce, 0x54, 0x85, 0xa0, 0xe7, 0x39, 0x08, 0x6f, 0xb9, 0xc2, 0x87, 0x35, 0x8b, 0x75, 0xa4, 0x7d, 0xdd, 0x7a, 0xd5, 0x10, 0xa7, 0x1f} +#define BOOTLOADER_T3B1_FF {0x49, 0xdd, 0x94, 0x60, 0xb7, 0xe9, 0x57, 0x6e, 0x7d, 0xb6, 0xd7, 0x63, 0x47, 0x04, 0xb4, 0x58, 0x3d, 0x47, 0x45, 0x57, 0xf3, 0x35, 0x69, 0x05, 0x8b, 0x84, 0x24, 0xdc, 0x81, 0x88, 0x80, 0x98} // bootloader_T3B1_qa.bin version 2.1.7.0 #define BOOTLOADER_T3B1_QA_00 {0x2e, 0x90, 0x8a, 0x99, 0x25, 0x93, 0xcd, 0x9c, 0xf1, 0x23, 0x1e, 0x4e, 0x41, 0xfc, 0xc9, 0xf1, 0x4b, 0x06, 0x69, 0x57, 0x6e, 0x64, 0x84, 0x1c, 0xb1, 0xd9, 0x89, 0x0c, 0xa5, 0xb4, 0x38, 0xeb} From 46c26f331d56725931dce490c03860cbf3f07ce8 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Fri, 28 Mar 2025 09:44:41 +0100 Subject: [PATCH 02/15] feat(legacy): Implement entropy check workflow in ResetDevice. --- .../.changelog.d/+entropy_check.added | 1 + legacy/firmware/fsm.c | 1 + legacy/firmware/fsm.h | 1 + legacy/firmware/fsm_msg_common.h | 11 +- legacy/firmware/protob/Makefile | 2 +- .../protob/messages-management.options | 4 +- legacy/firmware/reset.c | 105 +++++++++++++++--- legacy/firmware/reset.h | 6 +- 8 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 legacy/firmware/.changelog.d/+entropy_check.added diff --git a/legacy/firmware/.changelog.d/+entropy_check.added b/legacy/firmware/.changelog.d/+entropy_check.added new file mode 100644 index 0000000000..c138763493 --- /dev/null +++ b/legacy/firmware/.changelog.d/+entropy_check.added @@ -0,0 +1 @@ +Entropy check workflow in ResetDevice. diff --git a/legacy/firmware/fsm.c b/legacy/firmware/fsm.c index eefed373db..639ca78c33 100644 --- a/legacy/firmware/fsm.c +++ b/legacy/firmware/fsm.c @@ -410,6 +410,7 @@ void fsm_msgRebootToBootloader(void) { } void fsm_abortWorkflows(void) { + reset_abort(); recovery_abort(); signing_abort(); authorization_type = 0; diff --git a/legacy/firmware/fsm.h b/legacy/firmware/fsm.h index 37d2183c02..abce2e9ec2 100644 --- a/legacy/firmware/fsm.h +++ b/legacy/firmware/fsm.h @@ -64,6 +64,7 @@ void fsm_msgLoadDevice(const LoadDevice *msg); #endif void fsm_msgResetDevice(const ResetDevice *msg); void fsm_msgEntropyAck(const EntropyAck *msg); +void fsm_msgEntropyCheckContinue(const EntropyCheckContinue *msg); void fsm_msgBackupDevice(const BackupDevice *msg); void fsm_msgCancel(const Cancel *msg); void fsm_msgLockDevice(const LockDevice *msg); diff --git a/legacy/firmware/fsm_msg_common.h b/legacy/firmware/fsm_msg_common.h index ab6beb5820..849d579be4 100644 --- a/legacy/firmware/fsm_msg_common.h +++ b/legacy/firmware/fsm_msg_common.h @@ -321,6 +321,8 @@ void fsm_msgResetDevice(const ResetDevice *msg) { msg->strength == 192 || msg->strength == 256, _("Invalid seed strength")); + fsm_abortWorkflows(); + reset_init(msg->has_strength ? msg->strength : 128, msg->has_passphrase_protection && msg->passphrase_protection, msg->has_pin_protection && msg->pin_protection, @@ -328,13 +330,18 @@ void fsm_msgResetDevice(const ResetDevice *msg) { msg->has_label ? msg->label : 0, msg->has_u2f_counter ? msg->u2f_counter : 0, msg->has_skip_backup ? msg->skip_backup : false, - msg->has_no_backup ? msg->no_backup : false); + msg->has_no_backup ? msg->no_backup : false, + msg->has_entropy_check ? msg->entropy_check : false); } void fsm_msgEntropyAck(const EntropyAck *msg) { reset_entropy(msg->entropy.bytes, msg->entropy.size); } +void fsm_msgEntropyCheckContinue(const EntropyCheckContinue *msg) { + reset_continue(msg->has_finish ? msg->finish : false); +} + void fsm_msgBackupDevice(const BackupDevice *msg) { (void)msg; @@ -497,6 +504,8 @@ void fsm_msgRecoveryDevice(const RecoveryDevice *msg) { msg->type == RecoveryType_DryRun, _("UnlockRepeatedBackup not supported")) + fsm_abortWorkflows(); + const bool dry_run = msg->has_type ? msg->type == RecoveryType_DryRun : false; if (!dry_run) { CHECK_NOT_INITIALIZED diff --git a/legacy/firmware/protob/Makefile b/legacy/firmware/protob/Makefile index 0b9490a3f1..92b81ada57 100644 --- a/legacy/firmware/protob/Makefile +++ b/legacy/firmware/protob/Makefile @@ -11,7 +11,7 @@ SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdPro UnlockBootloader AuthenticateDevice AuthenticityProof \ Solana StellarClaimClaimableBalanceOp \ ChangeLanguage TranslationDataRequest TranslationDataAck \ - SetBrightness DebugLinkOptigaSetSecMax EntropyCheckReady EntropyCheckContinue \ + SetBrightness DebugLinkOptigaSetSecMax \ BenchmarkListNames BenchmarkRun BenchmarkNames BenchmarkResult \ NostrGetPubkey NostrPubkey NostrSignEvent NostrEventSignature \ BleUnpair diff --git a/legacy/firmware/protob/messages-management.options b/legacy/firmware/protob/messages-management.options index e9a2aa0954..17c5c0850b 100644 --- a/legacy/firmware/protob/messages-management.options +++ b/legacy/firmware/protob/messages-management.options @@ -30,8 +30,8 @@ BackupDevice.groups type:FT_IGNORE Entropy.entropy max_size:1024 -EntropyRequest.entropy_commitment type:FT_IGNORE -EntropyRequest.prev_entropy type:FT_IGNORE +EntropyRequest.entropy_commitment max_size:32 +EntropyRequest.prev_entropy max_size:32 EntropyAck.entropy max_size:128 diff --git a/legacy/firmware/reset.c b/legacy/firmware/reset.c index 928465f53a..677d1dce95 100644 --- a/legacy/firmware/reset.c +++ b/legacy/firmware/reset.c @@ -22,6 +22,7 @@ #include "config.h" #include "fsm.h" #include "gettext.h" +#include "hmac.h" #include "layout2.h" #include "memzero.h" #include "messages.h" @@ -34,18 +35,37 @@ static uint32_t strength; static uint8_t int_entropy[32]; -static bool awaiting_entropy = false; +static uint8_t seed[64]; +static const char *reset_mnemonic; static bool skip_backup = false; static bool no_backup = false; +static bool entropy_check = false; + +static enum { + RESET_NONE = 0, + RESET_ENTROPY_REQUEST, + RESET_ENTROPY_CHECK_READY, +} reset_state = RESET_NONE; + +static void send_entropy_request(bool set_prev_entropy); +static void reset_finish(void); + +static void entropy_check_progress(uint32_t iter, uint32_t total) { + (void)iter; + (void)total; + layoutProgress(_("Entropy check"), 0); +} void reset_init(uint32_t _strength, bool passphrase_protection, bool pin_protection, const char *language, const char *label, - uint32_t u2f_counter, bool _skip_backup, bool _no_backup) { + uint32_t u2f_counter, bool _skip_backup, bool _no_backup, + bool _entropy_check) { if (_strength != 128 && _strength != 192 && _strength != 256) return; strength = _strength; skip_backup = _skip_backup; no_backup = _no_backup; + entropy_check = _entropy_check; layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("create a new wallet?"), NULL, @@ -56,8 +76,6 @@ void reset_init(uint32_t _strength, bool passphrase_protection, return; } - random_buffer(int_entropy, 32); - if (pin_protection && !protectChangePin(false)) { layoutHome(); return; @@ -67,36 +85,79 @@ void reset_init(uint32_t _strength, bool passphrase_protection, config_setLanguage(language); config_setLabel(label); config_setU2FCounter(u2f_counter); + send_entropy_request(false); +} +static void send_entropy_request(bool set_prev_entropy) { EntropyRequest resp = {0}; - memzero(&resp, sizeof(EntropyRequest)); + if (set_prev_entropy) { + memcpy(resp.prev_entropy.bytes, int_entropy, sizeof(int_entropy)); + resp.prev_entropy.size = sizeof(int_entropy); + resp.has_prev_entropy = true; + } + + random_buffer(int_entropy, sizeof(int_entropy)); + if (entropy_check) { + hmac_sha256(int_entropy, sizeof(int_entropy), NULL, 0, + resp.entropy_commitment.bytes); + resp.entropy_commitment.size = SHA256_DIGEST_LENGTH; + resp.has_entropy_commitment = true; + } msg_write(MessageType_MessageType_EntropyRequest, &resp); - awaiting_entropy = true; + reset_state = RESET_ENTROPY_REQUEST; } void reset_entropy(const uint8_t *ext_entropy, uint32_t len) { - if (!awaiting_entropy) { + if (reset_state != RESET_ENTROPY_REQUEST) { fsm_sendFailure(FailureType_Failure_UnexpectedMessage, - _("Not in Reset mode")); + _("Entropy not requested")); return; } - awaiting_entropy = false; + uint8_t secret[SHA256_DIGEST_LENGTH] = {0}; SHA256_CTX ctx = {0}; sha256_Init(&ctx); SHA256_UPDATE_BYTES(&ctx, int_entropy, 32); sha256_Update(&ctx, ext_entropy, len); - sha256_Final(&ctx, int_entropy); - const char *mnemonic = mnemonic_from_data(int_entropy, strength / 8); - memzero(int_entropy, 32); + sha256_Final(&ctx, secret); + reset_mnemonic = mnemonic_from_data(secret, strength / 8); + memzero(secret, sizeof(secret)); + if (!entropy_check) { + reset_finish(); + return; + } + reset_state = RESET_ENTROPY_CHECK_READY; + mnemonic_to_seed(reset_mnemonic, "", seed, entropy_check_progress); + EntropyCheckReady resp = {0}; + msg_write(MessageType_MessageType_EntropyCheckReady, &resp); +} + +void reset_continue(bool finish) { + if (reset_state != RESET_ENTROPY_CHECK_READY) { + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, + _("Entropy check not ready")); + return; + } + + if (finish) { + reset_finish(); + } else { + send_entropy_request(true); + } +} + +static void reset_finish(void) { + memzero(int_entropy, sizeof(int_entropy)); + memzero(seed, sizeof(seed)); + reset_state = RESET_NONE; if (skip_backup || no_backup) { if (no_backup) { config_setNoBackup(); } else { config_setNeedsBackup(true); } - if (config_setMnemonic(mnemonic)) { + if (config_setMnemonic(reset_mnemonic)) { fsm_sendSuccess(_("Device successfully initialized")); } else { fsm_sendFailure(FailureType_Failure_ProcessError, @@ -104,11 +165,18 @@ void reset_entropy(const uint8_t *ext_entropy, uint32_t len) { } layoutHome(); } else { - reset_backup(false, mnemonic); + reset_backup(false, reset_mnemonic); } mnemonic_clear(); } +const uint8_t *reset_get_seed(void) { + if (reset_state != RESET_ENTROPY_CHECK_READY) { + return NULL; + } + return seed; +} + static char current_word[10]; // separated == true if called as a separate workflow via BackupMessage @@ -170,6 +238,15 @@ void reset_backup(bool separated, const char *mnemonic) { layoutHome(); } +void reset_abort(void) { + memzero(int_entropy, sizeof(int_entropy)); + memzero(seed, sizeof(seed)); + if (reset_state != RESET_NONE) { + reset_state = RESET_NONE; + layoutHome(); + } +} + #if DEBUG_LINK uint32_t reset_get_int_entropy(uint8_t *entropy) { diff --git a/legacy/firmware/reset.h b/legacy/firmware/reset.h index cebf33cc23..5d0bee744c 100644 --- a/legacy/firmware/reset.h +++ b/legacy/firmware/reset.h @@ -25,10 +25,14 @@ void reset_init(uint32_t _strength, bool passphrase_protection, bool pin_protection, const char *language, const char *label, - uint32_t u2f_counter, bool _skip_backup, bool _no_backup); + uint32_t u2f_counter, bool _skip_backup, bool _no_backup, + bool _entropy_check); void reset_entropy(const uint8_t *ext_entropy, uint32_t len); +void reset_continue(bool finish); +const uint8_t *reset_get_seed(void); void reset_backup(bool separated, const char *mnemonic); uint32_t reset_get_int_entropy(uint8_t *entropy); const char *reset_get_word(void); +void reset_abort(void); #endif From dfb43b5c4e03f9276490511b0ab7828553baea59 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Fri, 28 Mar 2025 09:48:35 +0100 Subject: [PATCH 03/15] feat(legacy): Enable GetPublicKey in entropy check workflow. --- legacy/firmware/fsm.c | 32 ++++++++++++++++++++++++++------ legacy/firmware/fsm_msg_coin.h | 29 +++++++++++++++++++++++------ legacy/firmware/reset.c | 1 + 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/legacy/firmware/fsm.c b/legacy/firmware/fsm.c index 639ca78c33..2705e52bfd 100644 --- a/legacy/firmware/fsm.c +++ b/legacy/firmware/fsm.c @@ -226,20 +226,33 @@ static const CoinInfo *fsm_getCoin(bool has_name, const char *name) { return coin; } -static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n, - size_t address_n_count, - uint32_t *fingerprint) { +static HDNode *fsm_getDerivedNodeEx(const char *curve, + const uint32_t *address_n, + size_t address_n_count, const uint8_t *seed, + uint32_t *fingerprint) { static CONFIDENTIAL HDNode node; if (fingerprint) { *fingerprint = 0; } - if (!config_getRootNode(&node, curve)) { - layoutHome(); - return 0; + + if (seed == NULL) { + if (!config_getRootNode(&node, curve)) { + layoutHome(); + return 0; + } + } else { + if (hdnode_from_seed(seed, 64, curve, &node) != 1) { + fsm_sendFailure(FailureType_Failure_NotInitialized, + _("Unsupported curve")); + layoutHome(); + return 0; + } } + if (!address_n || address_n_count == 0) { return &node; } + if (hdnode_private_ckd_cached(&node, address_n, address_n_count, fingerprint) == 0) { fsm_sendFailure(FailureType_Failure_ProcessError, @@ -250,6 +263,13 @@ static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n, return &node; } +static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n, + size_t address_n_count, + uint32_t *fingerprint) { + return fsm_getDerivedNodeEx(curve, address_n, address_n_count, NULL, + fingerprint); +} + static bool fsm_getSlip21Key(const char *path[], size_t path_count, uint8_t key[32]) { const uint8_t *seed = config_getSeed(); diff --git a/legacy/firmware/fsm_msg_coin.h b/legacy/firmware/fsm_msg_coin.h index 66ae313f48..1ceab3b14b 100644 --- a/legacy/firmware/fsm_msg_coin.h +++ b/legacy/firmware/fsm_msg_coin.h @@ -20,10 +20,15 @@ void fsm_msgGetPublicKey(const GetPublicKey *msg) { RESP_INIT(PublicKey); - CHECK_INITIALIZED - CHECK_PIN + // Get temporary seed if running entropy check, otherwise ensure the device is + // initialized. + const uint8_t *seed = reset_get_seed(); + if (seed == NULL) { + CHECK_INITIALIZED + } + InputScriptType script_type = msg->has_script_type ? msg->script_type : InputScriptType_SPENDADDRESS; @@ -50,15 +55,23 @@ void fsm_msgGetPublicKey(const GetPublicKey *msg) { } } + // Make sure we never display the temporary XPUB to the user. + if (seed != NULL && msg->show_display) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Showing temporary XPUB is forbidden")); + layoutHome(); + return; + } + // derive m/0' to obtain root_fingerprint uint32_t root_fingerprint; uint32_t path[1] = {PATH_HARDENED | 0}; - HDNode *node = fsm_getDerivedNode(curve, path, 1, &root_fingerprint); + HDNode *node = fsm_getDerivedNodeEx(curve, path, 1, seed, &root_fingerprint); if (!node) return; uint32_t fingerprint; - node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count, - &fingerprint); + node = fsm_getDerivedNodeEx(curve, msg->address_n, msg->address_n_count, seed, + &fingerprint); if (!node) return; if (hdnode_fill_public_key(node) != 0) { @@ -132,7 +145,11 @@ void fsm_msgGetPublicKey(const GetPublicKey *msg) { resp->root_fingerprint = root_fingerprint; msg_write(MessageType_MessageType_PublicKey, resp); - layoutHome(); + + // Keep screen layout when running entropy check. + if (seed == NULL) { + layoutHome(); + } } static PathSchema fsm_getUnlockedSchema(MessageType message_type) { diff --git a/legacy/firmware/reset.c b/legacy/firmware/reset.c index 677d1dce95..dc682310a4 100644 --- a/legacy/firmware/reset.c +++ b/legacy/firmware/reset.c @@ -241,6 +241,7 @@ void reset_backup(bool separated, const char *mnemonic) { void reset_abort(void) { memzero(int_entropy, sizeof(int_entropy)); memzero(seed, sizeof(seed)); + mnemonic_clear(); if (reset_state != RESET_NONE) { reset_state = RESET_NONE; layoutHome(); From 87590b1bdaa5eedad6e46c182b839a4995d1b795 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Fri, 28 Mar 2025 09:49:02 +0100 Subject: [PATCH 04/15] feat(tests): Entropy check workflow tests for T1. --- .../reset_recovery/test_reset_bip39_t1.py | 138 +++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/tests/device_tests/reset_recovery/test_reset_bip39_t1.py b/tests/device_tests/reset_recovery/test_reset_bip39_t1.py index 0c96ee4f5c..04b66054d6 100644 --- a/tests/device_tests/reset_recovery/test_reset_bip39_t1.py +++ b/tests/device_tests/reset_recovery/test_reset_bip39_t1.py @@ -14,14 +14,23 @@ # You should have received a copy of the License along with this library. # If not, see . +from typing import Generator + import pytest from mnemonic import Mnemonic +from slip10 import SLIP10 from trezorlib import device, messages +from trezorlib.btc import get_public_node from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.tools import parse_path -from ...common import EXTERNAL_ENTROPY, generate_entropy +from ...common import ( + EXTERNAL_ENTROPY, + MOCK_GET_ENTROPY, + BRGeneratorType, + generate_entropy, +) pytestmark = pytest.mark.models("legacy") @@ -216,3 +225,130 @@ def test_already_initialized(client: Client): pin_protection=True, label="label", ) + + +class Bip39InputFlow: + def __init__(self, client: Client): + self.client = client + self.mnemonic = None + + def _get_mnemonic( + self, strength: int + ) -> Generator[None, messages.ButtonRequest, list[str]]: + mnemonic: list[str] = [] + for _ in range(strength // 32 * 3): + br = yield + assert br.code == messages.ButtonRequestType.ConfirmWord + mnemonic.append(self.client.debug.read_reset_word()) + self.client.debug.press_yes() + return mnemonic + + def input_flow_bip39_reset_backup(self, strength: int) -> BRGeneratorType: + # 1. Confirm Reset + br = yield + assert br.code == messages.ButtonRequestType.ProtectCall + self.client.debug.press_yes() + + mnemonic_write = yield from self._get_mnemonic(strength) + mnemonic_check = yield from self._get_mnemonic(strength) + assert mnemonic_write == mnemonic_check + + self.mnemonic = " ".join(mnemonic_write) + + +@pytest.mark.setup_client(uninitialized=True) +def test_reset_entropy_check(client: Client): + strength = 256 # 24 words + + with client: + IF = Bip39InputFlow(client) + client.set_input_flow(IF.input_flow_bip39_reset_backup(strength)) + # No PIN, no passphrase + path_xpubs = device.setup( + client, + strength=strength, + passphrase_protection=False, + pin_protection=False, + label="test", + entropy_check_count=2, + backup_type=messages.BackupType.Bip39, + _get_entropy=MOCK_GET_ENTROPY, + ) + + # Check that the displayed mnemonic is identical to the stored one. + assert IF.mnemonic.encode("utf-8") == client.debug.state().mnemonic_secret + + # Check that the device is properly initialized. + assert client.features.initialized is True + assert ( + client.features.backup_availability == messages.BackupAvailability.NotAvailable + ) + assert client.features.pin_protection is False + assert client.features.passphrase_protection is False + + seed = Mnemonic.to_seed(IF.mnemonic, passphrase="") + slip10 = SLIP10.from_seed(seed) + for path, xpub in path_xpubs: + # Check that the device returns the same XPUBs as those from the entropy check. + res = get_public_node(client, path) + assert res.xpub == xpub + # Check that the XPUBs derived from the displayed mnemonic are the same as those + # from the entropy check. + assert slip10.get_xpub_from_path(path) == xpub + + +@pytest.mark.setup_client(uninitialized=True) +def test_entropy_check(client: Client): + with client: + client.set_expected_responses( + [ + messages.ButtonRequest(code=messages.ButtonRequestType.ProtectCall), + messages.EntropyRequest, + messages.EntropyCheckReady, + messages.PublicKey, + messages.PublicKey, + messages.EntropyRequest, + messages.EntropyCheckReady, + messages.PublicKey, + messages.PublicKey, + messages.EntropyRequest, + messages.EntropyCheckReady, + messages.PublicKey, + messages.PublicKey, + messages.Success, + messages.Features, + ] + ) + device.setup( + client, + strength=256, + entropy_check_count=2, + backup_type=messages.BackupType.Bip39, + skip_backup=True, + pin_protection=False, + passphrase_protection=False, + _get_entropy=MOCK_GET_ENTROPY, + ) + + +@pytest.mark.setup_client(uninitialized=True) +def test_no_entropy_check(client: Client): + with client: + client.set_expected_responses( + [ + messages.ButtonRequest(code=messages.ButtonRequestType.ProtectCall), + messages.EntropyRequest, + messages.Success, + messages.Features, + ] + ) + device.setup( + client, + strength=256, + entropy_check_count=0, + backup_type=messages.BackupType.Bip39, + skip_backup=True, + pin_protection=False, + passphrase_protection=False, + _get_entropy=MOCK_GET_ENTROPY, + ) From d0741a8aed875a30482dff6136a5820310391737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan=20Biz=C4=83u?= Date: Tue, 29 Apr 2025 19:16:04 +0200 Subject: [PATCH 05/15] chore(legacy): update fixtures --- tests/ui_tests/fixtures.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 989ac7223a..1f3b4664aa 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -602,10 +602,13 @@ "T1B1_en_reset_recovery-test_reset_bip39_skipbackup.py::test_reset_device_skip_backup": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1B1_en_reset_recovery-test_reset_bip39_skipbackup.py::test_reset_device_skip_backup_break": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1B1_en_reset_recovery-test_reset_bip39_t1.py::test_already_initialized": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", +"T1B1_en_reset_recovery-test_reset_bip39_t1.py::test_entropy_check": "b63a40ea5f6d8c59a2b54db8f5f01d0aaa4b0dea7c91ddd1bd40489720ae729c", "T1B1_en_reset_recovery-test_reset_bip39_t1.py::test_failed_pin": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", +"T1B1_en_reset_recovery-test_reset_bip39_t1.py::test_no_entropy_check": "b63a40ea5f6d8c59a2b54db8f5f01d0aaa4b0dea7c91ddd1bd40489720ae729c", "T1B1_en_reset_recovery-test_reset_bip39_t1.py::test_reset_device_128": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1B1_en_reset_recovery-test_reset_bip39_t1.py::test_reset_device_192": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1B1_en_reset_recovery-test_reset_bip39_t1.py::test_reset_device_256_pin": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", +"T1B1_en_reset_recovery-test_reset_bip39_t1.py::test_reset_entropy_check": "d18e5ebf3a318a04935cbf1c1d2a9591ad209d352baed8d93aa4398b87c0bc72", "T1B1_en_stellar-test_stellar.py::test_get_address[parameters0-result0]": "b004cb1a815072ae3ba9f4aadeeb9d37d3309513e973f7ddeb2ee23a056db726", "T1B1_en_stellar-test_stellar.py::test_get_address[parameters1-result1]": "101692e06782269486e3f1093fdf03200e0cdce4b4ffc7cc5fc779b80f69d1fc", "T1B1_en_stellar-test_stellar.py::test_get_address[parameters2-result2]": "508bbd5285d2f3087fa3b872ff053a286630b80beacd0b8b30c8577aa5396c86", @@ -709,7 +712,7 @@ "T1B1_en_test_msg_loaddevice.py::test_load_device_2": "f89f8fcecf250b76dcca3e52a5a678e14ca5eeae105fa59848db41ab514d6614", "T1B1_en_test_msg_loaddevice.py::test_load_device_utf": "9523984b9cd124422558fe14ae20aab35338f86f08756d11919db7b2d3b86781", "T1B1_en_test_msg_ping.py::test_ping": "de7fc40b2f35e82fa486f1b97ee3e34a96d0a67412537e8a0fddacc0b0b1649d", -"T1B1_en_test_msg_wipedevice.py::test_autolock_not_retained": "7f5d22cc7797ce4ca6d9f46f633918aba527972c8d683493e95da45503486f61", +"T1B1_en_test_msg_wipedevice.py::test_autolock_not_retained": "9ab4e01aaf78d4ffeaadf43883258fd2ded7d4f7ebbdba9c70bd60baad314860", "T1B1_en_test_msg_wipedevice.py::test_wipe_device": "aac15a6d12d21966c77572aeebd56ebc2a47ecba3a508f5a421af2a5da2919e7", "T1B1_en_test_pin.py::test_correct_pin": "31e4ef1ef1f40b58c66bcde7631fe12699321dcdfc144381ac2b3e5668d0de5c", "T1B1_en_test_pin.py::test_exponential_backoff_t1": "427faf2049e9998229e0d40c9b54f2e359a7d2df2ba129187dafc7d2f2cc9986", From 23058e10dfa4d2d1d4109695a8b61ccf0e834254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan=20Biz=C4=83u?= Date: Wed, 30 Apr 2025 09:15:20 +0200 Subject: [PATCH 06/15] chore(build): update definitions timestamps --- common/defs/ethereum/released-definitions-timestamp.txt | 2 +- core/src/apps/common/definitions_constants.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/defs/ethereum/released-definitions-timestamp.txt b/common/defs/ethereum/released-definitions-timestamp.txt index ef8fc1c099..4366364f6a 100644 --- a/common/defs/ethereum/released-definitions-timestamp.txt +++ b/common/defs/ethereum/released-definitions-timestamp.txt @@ -1 +1 @@ -2025-02-07T18:39:08+00:00 +2025-03-04T12:16:03+00:00 diff --git a/core/src/apps/common/definitions_constants.py b/core/src/apps/common/definitions_constants.py index b220040a15..62dac5ecfc 100644 --- a/core/src/apps/common/definitions_constants.py +++ b/core/src/apps/common/definitions_constants.py @@ -9,7 +9,7 @@ PUBLIC_KEYS = ( b"\xb8\xd2\xb2\x1d\xe2\x71\x24\xf0\x51\x1f\x90\x3a\xe7\xe6\x0e\x07\x96\x18\x10\xa0\xb8\xf2\x8e\xa7\x55\xfa\x50\x36\x7a\x8a\x2b\x8b", ) -MIN_DATA_VERSION = 1738953548 +MIN_DATA_VERSION = 1741090563 FORMAT_VERSION = b"trzd1" if __debug__: From de81d59aca86a581bd4b8723bad3dfd859eb62c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan=20Biz=C4=83u?= Date: Wed, 30 Apr 2025 09:58:21 +0200 Subject: [PATCH 07/15] chore(build): changelog --- core/.changelog.d/4160.added | 1 - core/.changelog.d/4623.fixed | 1 - core/.changelog.d/4771.fixed | 1 - core/.changelog.d/4786.fixed | 1 - core/.changelog.d/4787.fixed | 1 - core/.changelog.d/4819.fixed | 1 - core/.changelog.d/4827.fixed | 1 - core/.changelog.d/4964.added | 1 - core/.changelog.d/noissue.added | 1 - core/CHANGELOG.T2B1.md | 28 ++++++++++++++++-- core/CHANGELOG.T2T1.md | 26 ++++++++++++++++- core/CHANGELOG.T3B1.md | 29 +++++++++++++++++-- core/CHANGELOG.T3T1.md | 22 +++++++++++++- core/CHANGELOG.md | 23 +++++++++++++++ .../.changelog.d/+entropy_check.added | 1 - legacy/firmware/.changelog.d/4851.added | 1 - legacy/firmware/.changelog.d/4932.fixed | 1 - legacy/firmware/CHANGELOG.md | 11 +++++++ 18 files changed, 131 insertions(+), 20 deletions(-) delete mode 100644 core/.changelog.d/4160.added delete mode 100644 core/.changelog.d/4623.fixed delete mode 100644 core/.changelog.d/4771.fixed delete mode 100644 core/.changelog.d/4786.fixed delete mode 100644 core/.changelog.d/4787.fixed delete mode 100644 core/.changelog.d/4819.fixed delete mode 100644 core/.changelog.d/4827.fixed delete mode 100644 core/.changelog.d/4964.added delete mode 100644 core/.changelog.d/noissue.added delete mode 100644 legacy/firmware/.changelog.d/+entropy_check.added delete mode 100644 legacy/firmware/.changelog.d/4851.added delete mode 100644 legacy/firmware/.changelog.d/4932.fixed diff --git a/core/.changelog.d/4160.added b/core/.changelog.d/4160.added deleted file mode 100644 index 7879e132f6..0000000000 --- a/core/.changelog.d/4160.added +++ /dev/null @@ -1 +0,0 @@ -Add Nostr support (in debug mode only!). diff --git a/core/.changelog.d/4623.fixed b/core/.changelog.d/4623.fixed deleted file mode 100644 index 83b0daa82b..0000000000 --- a/core/.changelog.d/4623.fixed +++ /dev/null @@ -1 +0,0 @@ -Replaced "next page" icon with "..." ellipsis when confirming long message. diff --git a/core/.changelog.d/4771.fixed b/core/.changelog.d/4771.fixed deleted file mode 100644 index 9e01503faa..0000000000 --- a/core/.changelog.d/4771.fixed +++ /dev/null @@ -1 +0,0 @@ -[T2T1] Fixed upgrade confirmation text overflow. diff --git a/core/.changelog.d/4786.fixed b/core/.changelog.d/4786.fixed deleted file mode 100644 index dd3d5a4196..0000000000 --- a/core/.changelog.d/4786.fixed +++ /dev/null @@ -1 +0,0 @@ -[T2T1] Fixed Solana staking dialog fonts. diff --git a/core/.changelog.d/4787.fixed b/core/.changelog.d/4787.fixed deleted file mode 100644 index 32c7a22d86..0000000000 --- a/core/.changelog.d/4787.fixed +++ /dev/null @@ -1 +0,0 @@ -[T2B1,T3B1] Fixed Solana staking dialog title. diff --git a/core/.changelog.d/4819.fixed b/core/.changelog.d/4819.fixed deleted file mode 100644 index 7597f3b214..0000000000 --- a/core/.changelog.d/4819.fixed +++ /dev/null @@ -1 +0,0 @@ -Updated EIP-1559 fee-related labels. diff --git a/core/.changelog.d/4827.fixed b/core/.changelog.d/4827.fixed deleted file mode 100644 index 168b6998ef..0000000000 --- a/core/.changelog.d/4827.fixed +++ /dev/null @@ -1 +0,0 @@ -Allow firmware upgrade even if language change failed. diff --git a/core/.changelog.d/4964.added b/core/.changelog.d/4964.added deleted file mode 100644 index 62d1556cc0..0000000000 --- a/core/.changelog.d/4964.added +++ /dev/null @@ -1 +0,0 @@ -[T3T1] Visual cues to distinguish unlocked state on Homescreen. diff --git a/core/.changelog.d/noissue.added b/core/.changelog.d/noissue.added deleted file mode 100644 index d7f66f6938..0000000000 --- a/core/.changelog.d/noissue.added +++ /dev/null @@ -1 +0,0 @@ -[T3B1] Upgrade bundled bootloader to 2.1.10. diff --git a/core/CHANGELOG.T2B1.md b/core/CHANGELOG.T2B1.md index 3885f1c296..5346297a64 100644 --- a/core/CHANGELOG.T2B1.md +++ b/core/CHANGELOG.T2B1.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.8.10] (21st May 2025) + +### Added +- Add Nostr support (in debug mode only!). [#4160] + +### Fixed +- Replaced "next page" icon with "..." ellipsis when confirming long message. [#4623] +- Fixed Solana staking dialog title. [#4787] +- Updated EIP-1559 fee-related labels. [#4819] +- Allow firmware upgrade even if language change failed. [#4827] + ## [2.8.9] (19th March 2025) ### Added @@ -11,13 +22,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - New UI for confirming long messages. [#4541] - Solana staking confirmation dialogs. [#4560] +### Changed +- Changed "swipe to continue" to "tap to continue". Screens still respond to swipe-up, but the preferred interaction method is now tapping the lower part of the screen. [#4571] + ### Fixed - Cancelling device recovery after aborting from Suite. [#3503] -## [2.8.8] (internal release) +## [2.8.8] (19th February 2025) ### Fixed -- Fix "PIN attempts exceeded" screen. [#3324] +- [T2B1, T3B1] Fix "PIN attempts exceeded" screen. [#3324] ## [2.8.7] (22th January 2025) @@ -1033,6 +1047,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4142]: https://github.com/trezor/trezor-firmware/pull/4142 [#4151]: https://github.com/trezor/trezor-firmware/pull/4151 [#4155]: https://github.com/trezor/trezor-firmware/pull/4155 +[#4160]: https://github.com/trezor/trezor-firmware/pull/4160 [#4161]: https://github.com/trezor/trezor-firmware/pull/4161 [#4165]: https://github.com/trezor/trezor-firmware/pull/4165 [#4167]: https://github.com/trezor/trezor-firmware/pull/4167 @@ -1057,4 +1072,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4541]: https://github.com/trezor/trezor-firmware/pull/4541 [#4560]: https://github.com/trezor/trezor-firmware/pull/4560 [#4571]: https://github.com/trezor/trezor-firmware/pull/4571 -[#1658739]: https://github.com/trezor/trezor-firmware/pull/1658739 +[#4623]: https://github.com/trezor/trezor-firmware/pull/4623 +[#4665]: https://github.com/trezor/trezor-firmware/pull/4665 +[#4771]: https://github.com/trezor/trezor-firmware/pull/4771 +[#4786]: https://github.com/trezor/trezor-firmware/pull/4786 +[#4787]: https://github.com/trezor/trezor-firmware/pull/4787 +[#4819]: https://github.com/trezor/trezor-firmware/pull/4819 +[#4827]: https://github.com/trezor/trezor-firmware/pull/4827 +[#4964]: https://github.com/trezor/trezor-firmware/pull/4964 diff --git a/core/CHANGELOG.T2T1.md b/core/CHANGELOG.T2T1.md index 448a03cf16..58a8d32b2e 100644 --- a/core/CHANGELOG.T2T1.md +++ b/core/CHANGELOG.T2T1.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.8.10] (21st May 2025) + +### Added +- Add Nostr support (in debug mode only!). [#4160] + +### Fixed +- Replaced "next page" icon with "..." ellipsis when confirming long message. [#4623] +- Fixed upgrade confirmation text overflow. [#4771] +- Fixed Solana staking dialog fonts. [#4786] +- Updated EIP-1559 fee-related labels. [#4819] +- Allow firmware upgrade even if language change failed. [#4827] + ## [2.8.9] (19th March 2025) ### Added @@ -12,12 +24,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - New UI for confirming long messages. [#4541] - Solana staking confirmation dialogs. [#4560] +### Changed +- Changed "swipe to continue" to "tap to continue". Screens still respond to swipe-up, but the preferred interaction method is now tapping the lower part of the screen. [#4571] + ### Fixed - Cancelling device recovery after aborting from Suite. [#3503] ## [2.8.8] (19th February 2025) ### Fixed +- [T2B1, T3B1] Fix "PIN attempts exceeded" screen. [#3324] - Fix wrong RSOD color on some older Model T devices. [#4491] - Fixed a bug resulting in restarting the recovery flow when inputting 33-word mnemonic. [#4537] @@ -1031,6 +1047,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4142]: https://github.com/trezor/trezor-firmware/pull/4142 [#4151]: https://github.com/trezor/trezor-firmware/pull/4151 [#4155]: https://github.com/trezor/trezor-firmware/pull/4155 +[#4160]: https://github.com/trezor/trezor-firmware/pull/4160 [#4161]: https://github.com/trezor/trezor-firmware/pull/4161 [#4165]: https://github.com/trezor/trezor-firmware/pull/4165 [#4167]: https://github.com/trezor/trezor-firmware/pull/4167 @@ -1055,4 +1072,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4541]: https://github.com/trezor/trezor-firmware/pull/4541 [#4560]: https://github.com/trezor/trezor-firmware/pull/4560 [#4571]: https://github.com/trezor/trezor-firmware/pull/4571 -[#1658739]: https://github.com/trezor/trezor-firmware/pull/1658739 +[#4623]: https://github.com/trezor/trezor-firmware/pull/4623 +[#4665]: https://github.com/trezor/trezor-firmware/pull/4665 +[#4771]: https://github.com/trezor/trezor-firmware/pull/4771 +[#4786]: https://github.com/trezor/trezor-firmware/pull/4786 +[#4787]: https://github.com/trezor/trezor-firmware/pull/4787 +[#4819]: https://github.com/trezor/trezor-firmware/pull/4819 +[#4827]: https://github.com/trezor/trezor-firmware/pull/4827 +[#4964]: https://github.com/trezor/trezor-firmware/pull/4964 diff --git a/core/CHANGELOG.T3B1.md b/core/CHANGELOG.T3B1.md index c51d08a610..209e41edd0 100644 --- a/core/CHANGELOG.T3B1.md +++ b/core/CHANGELOG.T3B1.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.8.10] (21st May 2025) + +### Added +- Upgrade bundled bootloader to 2.1.10. [#noissue] +- Add Nostr support (in debug mode only!). [#4160] + +### Fixed +- Replaced "next page" icon with "..." ellipsis when confirming long message. [#4623] +- Fixed Solana staking dialog title. [#4787] +- Updated EIP-1559 fee-related labels. [#4819] +- Allow firmware upgrade even if language change failed. [#4827] + ## [2.8.9] (19th March 2025) ### Added @@ -11,13 +23,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - New UI for confirming long messages. [#4541] - Solana staking confirmation dialogs. [#4560] +### Changed +- Changed "swipe to continue" to "tap to continue". Screens still respond to swipe-up, but the preferred interaction method is now tapping the lower part of the screen. [#4571] + ### Fixed - Cancelling device recovery after aborting from Suite. [#3503] -## [2.8.8] (internal release) +## [2.8.8] (19th February 2025) ### Fixed -- Fix "PIN attempts exceeded" screen. [#3324] +- [T2B1, T3B1] Fix "PIN attempts exceeded" screen. [#3324] - Fix behavior of a button press during "hold to confirm". [#3772] - Fix backup failing if middle button is pressed during confirmation. [#4500] @@ -1024,6 +1039,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4142]: https://github.com/trezor/trezor-firmware/pull/4142 [#4151]: https://github.com/trezor/trezor-firmware/pull/4151 [#4155]: https://github.com/trezor/trezor-firmware/pull/4155 +[#4160]: https://github.com/trezor/trezor-firmware/pull/4160 [#4161]: https://github.com/trezor/trezor-firmware/pull/4161 [#4165]: https://github.com/trezor/trezor-firmware/pull/4165 [#4167]: https://github.com/trezor/trezor-firmware/pull/4167 @@ -1048,4 +1064,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4541]: https://github.com/trezor/trezor-firmware/pull/4541 [#4560]: https://github.com/trezor/trezor-firmware/pull/4560 [#4571]: https://github.com/trezor/trezor-firmware/pull/4571 -[#1658739]: https://github.com/trezor/trezor-firmware/pull/1658739 +[#4623]: https://github.com/trezor/trezor-firmware/pull/4623 +[#4665]: https://github.com/trezor/trezor-firmware/pull/4665 +[#4771]: https://github.com/trezor/trezor-firmware/pull/4771 +[#4786]: https://github.com/trezor/trezor-firmware/pull/4786 +[#4787]: https://github.com/trezor/trezor-firmware/pull/4787 +[#4819]: https://github.com/trezor/trezor-firmware/pull/4819 +[#4827]: https://github.com/trezor/trezor-firmware/pull/4827 +[#4964]: https://github.com/trezor/trezor-firmware/pull/4964 diff --git a/core/CHANGELOG.T3T1.md b/core/CHANGELOG.T3T1.md index a9321a01b9..a9eee0eeda 100644 --- a/core/CHANGELOG.T3T1.md +++ b/core/CHANGELOG.T3T1.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.8.10] (21st May 2025) + +### Added +- Add Nostr support (in debug mode only!). [#4160] +- Visual cues to distinguish unlocked state on Homescreen. [#4964] + +### Fixed +- Replaced "next page" icon with "..." ellipsis when confirming long message. [#4623] +- Updated EIP-1559 fee-related labels. [#4819] +- Allow firmware upgrade even if language change failed. [#4827] + ## [2.8.9] (19th March 2025) ### Added @@ -18,9 +29,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Cancelling device recovery after aborting from Suite. [#3503] -## [2.8.8] (internal release) +## [2.8.8] (19th February 2025) ### Fixed +- [T2B1, T3B1] Fix "PIN attempts exceeded" screen. [#3324] - Fixed flashing old content when fading. [#4492] ## [2.8.7] (22th January 2025) @@ -1068,6 +1080,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4142]: https://github.com/trezor/trezor-firmware/pull/4142 [#4151]: https://github.com/trezor/trezor-firmware/pull/4151 [#4155]: https://github.com/trezor/trezor-firmware/pull/4155 +[#4160]: https://github.com/trezor/trezor-firmware/pull/4160 [#4161]: https://github.com/trezor/trezor-firmware/pull/4161 [#4165]: https://github.com/trezor/trezor-firmware/pull/4165 [#4167]: https://github.com/trezor/trezor-firmware/pull/4167 @@ -1092,4 +1105,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4541]: https://github.com/trezor/trezor-firmware/pull/4541 [#4560]: https://github.com/trezor/trezor-firmware/pull/4560 [#4571]: https://github.com/trezor/trezor-firmware/pull/4571 +[#4623]: https://github.com/trezor/trezor-firmware/pull/4623 [#4665]: https://github.com/trezor/trezor-firmware/pull/4665 +[#4771]: https://github.com/trezor/trezor-firmware/pull/4771 +[#4786]: https://github.com/trezor/trezor-firmware/pull/4786 +[#4787]: https://github.com/trezor/trezor-firmware/pull/4787 +[#4819]: https://github.com/trezor/trezor-firmware/pull/4819 +[#4827]: https://github.com/trezor/trezor-firmware/pull/4827 +[#4964]: https://github.com/trezor/trezor-firmware/pull/4964 diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index bbd90cf286..3b7d25b4c2 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.8.10] (21st May 2025) + +### Added +- [T3B1] Upgrade bundled bootloader to 2.1.10. [#noissue] +- Add Nostr support (in debug mode only!). [#4160] +- [T3T1] Visual cues to distinguish unlocked state on Homescreen. [#4964] + +### Fixed +- Replaced "next page" icon with "..." ellipsis when confirming long message. [#4623] +- [T2T1] Fixed upgrade confirmation text overflow. [#4771] +- [T2T1] Fixed Solana staking dialog fonts. [#4786] +- [T2B1,T3B1] Fixed Solana staking dialog title. [#4787] +- Updated EIP-1559 fee-related labels. [#4819] +- Allow firmware upgrade even if language change failed. [#4827] + ## [2.8.9] (19th March 2025) ### Added @@ -1097,6 +1112,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4142]: https://github.com/trezor/trezor-firmware/pull/4142 [#4151]: https://github.com/trezor/trezor-firmware/pull/4151 [#4155]: https://github.com/trezor/trezor-firmware/pull/4155 +[#4160]: https://github.com/trezor/trezor-firmware/pull/4160 [#4161]: https://github.com/trezor/trezor-firmware/pull/4161 [#4165]: https://github.com/trezor/trezor-firmware/pull/4165 [#4167]: https://github.com/trezor/trezor-firmware/pull/4167 @@ -1121,4 +1137,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4541]: https://github.com/trezor/trezor-firmware/pull/4541 [#4560]: https://github.com/trezor/trezor-firmware/pull/4560 [#4571]: https://github.com/trezor/trezor-firmware/pull/4571 +[#4623]: https://github.com/trezor/trezor-firmware/pull/4623 [#4665]: https://github.com/trezor/trezor-firmware/pull/4665 +[#4771]: https://github.com/trezor/trezor-firmware/pull/4771 +[#4786]: https://github.com/trezor/trezor-firmware/pull/4786 +[#4787]: https://github.com/trezor/trezor-firmware/pull/4787 +[#4819]: https://github.com/trezor/trezor-firmware/pull/4819 +[#4827]: https://github.com/trezor/trezor-firmware/pull/4827 +[#4964]: https://github.com/trezor/trezor-firmware/pull/4964 diff --git a/legacy/firmware/.changelog.d/+entropy_check.added b/legacy/firmware/.changelog.d/+entropy_check.added deleted file mode 100644 index c138763493..0000000000 --- a/legacy/firmware/.changelog.d/+entropy_check.added +++ /dev/null @@ -1 +0,0 @@ -Entropy check workflow in ResetDevice. diff --git a/legacy/firmware/.changelog.d/4851.added b/legacy/firmware/.changelog.d/4851.added deleted file mode 100644 index c5ae5d78c1..0000000000 --- a/legacy/firmware/.changelog.d/4851.added +++ /dev/null @@ -1 +0,0 @@ -Clear sign ETH staking transactions on Everstake pool. diff --git a/legacy/firmware/.changelog.d/4932.fixed b/legacy/firmware/.changelog.d/4932.fixed deleted file mode 100644 index 7710c9923e..0000000000 --- a/legacy/firmware/.changelog.d/4932.fixed +++ /dev/null @@ -1 +0,0 @@ -Use `GWei` when formatting large ETH amounts. diff --git a/legacy/firmware/CHANGELOG.md b/legacy/firmware/CHANGELOG.md index 54b5b23c11..007472e173 100644 --- a/legacy/firmware/CHANGELOG.md +++ b/legacy/firmware/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## 2.8.10 [21st May 2025] + +### Added +- Clear sign ETH staking transactions on Everstake pool. [#4851] +- Entropy check workflow in ResetDevice. + +### Fixed +- Use `GWei` when formatting large ETH amounts. [#4932] + ## 1.13.0 [19th February 2025] ### Added @@ -640,3 +649,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#4119]: https://github.com/trezor/trezor-firmware/pull/4119 [#4324]: https://github.com/trezor/trezor-firmware/pull/4324 [#4396]: https://github.com/trezor/trezor-firmware/pull/4396 +[#4851]: https://github.com/trezor/trezor-firmware/pull/4851 +[#4932]: https://github.com/trezor/trezor-firmware/pull/4932 From 4551af24889639230ef59a057501f02a1e1d8c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan=20Biz=C4=83u?= Date: Wed, 30 Apr 2025 10:10:41 +0200 Subject: [PATCH 08/15] chore(release): fix T1B1 version --- legacy/firmware/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/legacy/firmware/CHANGELOG.md b/legacy/firmware/CHANGELOG.md index 007472e173..5aec73c64c 100644 --- a/legacy/firmware/CHANGELOG.md +++ b/legacy/firmware/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## 2.8.10 [21st May 2025] +## 1.13.1 [21st May 2025] ### Added - Clear sign ETH staking transactions on Everstake pool. [#4851] From 41b786353e09390b64b107e64e1cfe3020514ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan=20Biz=C4=83u?= Date: Wed, 30 Apr 2025 10:11:02 +0200 Subject: [PATCH 09/15] chore: update releases --- common/releases.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/releases.json b/common/releases.json index 2f55bdf725..3d7f117d44 100644 --- a/common/releases.json +++ b/common/releases.json @@ -43,6 +43,7 @@ "1.11.2": ["T1B1"], "1.12.1": ["T1B1"], "1.13.0": ["T1B1"], + "1.13.1": ["T1B1"], "2.0.5": ["T2T1"], "2.0.6": ["T2T1"], "2.0.7": ["T2T1"], @@ -80,6 +81,7 @@ "2.8.3": ["T3B1", "T3T1"], "2.8.7": ["T2B1", "T2T1", "T3B1", "T3T1"], "2.8.8": ["T2T1"], - "2.8.9": ["T2B1", "T2T1", "T3B1", "T3T1"] + "2.8.9": ["T2B1", "T2T1", "T3B1", "T3T1"], + "2.8.10": ["T2B1", "T2T1", "T3B1", "T3T1"] } } From e21c80bfcbe2463348a61d0b7d69e23b74a2a103 Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Wed, 30 Apr 2025 14:12:14 +0200 Subject: [PATCH 10/15] docs: fix changelogs --- core/CHANGELOG.T2B1.md | 7 ++----- core/CHANGELOG.T2T1.md | 4 ---- core/CHANGELOG.T3B1.md | 9 +++------ core/CHANGELOG.T3T1.md | 3 +-- core/CHANGELOG.md | 2 +- 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/core/CHANGELOG.T2B1.md b/core/CHANGELOG.T2B1.md index 5346297a64..19502b7c01 100644 --- a/core/CHANGELOG.T2B1.md +++ b/core/CHANGELOG.T2B1.md @@ -22,16 +22,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - New UI for confirming long messages. [#4541] - Solana staking confirmation dialogs. [#4560] -### Changed -- Changed "swipe to continue" to "tap to continue". Screens still respond to swipe-up, but the preferred interaction method is now tapping the lower part of the screen. [#4571] - ### Fixed - Cancelling device recovery after aborting from Suite. [#3503] -## [2.8.8] (19th February 2025) +## [2.8.8] (internal release) ### Fixed -- [T2B1, T3B1] Fix "PIN attempts exceeded" screen. [#3324] +- Fix "PIN attempts exceeded" screen. [#3324] ## [2.8.7] (22th January 2025) diff --git a/core/CHANGELOG.T2T1.md b/core/CHANGELOG.T2T1.md index 58a8d32b2e..1bb1a6d95a 100644 --- a/core/CHANGELOG.T2T1.md +++ b/core/CHANGELOG.T2T1.md @@ -24,16 +24,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - New UI for confirming long messages. [#4541] - Solana staking confirmation dialogs. [#4560] -### Changed -- Changed "swipe to continue" to "tap to continue". Screens still respond to swipe-up, but the preferred interaction method is now tapping the lower part of the screen. [#4571] - ### Fixed - Cancelling device recovery after aborting from Suite. [#3503] ## [2.8.8] (19th February 2025) ### Fixed -- [T2B1, T3B1] Fix "PIN attempts exceeded" screen. [#3324] - Fix wrong RSOD color on some older Model T devices. [#4491] - Fixed a bug resulting in restarting the recovery flow when inputting 33-word mnemonic. [#4537] diff --git a/core/CHANGELOG.T3B1.md b/core/CHANGELOG.T3B1.md index 209e41edd0..4e38397ef7 100644 --- a/core/CHANGELOG.T3B1.md +++ b/core/CHANGELOG.T3B1.md @@ -7,7 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [2.8.10] (21st May 2025) ### Added -- Upgrade bundled bootloader to 2.1.10. [#noissue] +- Upgrade bundled bootloader to 2.1.10. - Add Nostr support (in debug mode only!). [#4160] ### Fixed @@ -23,16 +23,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - New UI for confirming long messages. [#4541] - Solana staking confirmation dialogs. [#4560] -### Changed -- Changed "swipe to continue" to "tap to continue". Screens still respond to swipe-up, but the preferred interaction method is now tapping the lower part of the screen. [#4571] - ### Fixed - Cancelling device recovery after aborting from Suite. [#3503] -## [2.8.8] (19th February 2025) +## [2.8.8] (internal release) ### Fixed -- [T2B1, T3B1] Fix "PIN attempts exceeded" screen. [#3324] +- Fix "PIN attempts exceeded" screen. [#3324] - Fix behavior of a button press during "hold to confirm". [#3772] - Fix backup failing if middle button is pressed during confirmation. [#4500] diff --git a/core/CHANGELOG.T3T1.md b/core/CHANGELOG.T3T1.md index a9eee0eeda..c23aa4174d 100644 --- a/core/CHANGELOG.T3T1.md +++ b/core/CHANGELOG.T3T1.md @@ -29,10 +29,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Cancelling device recovery after aborting from Suite. [#3503] -## [2.8.8] (19th February 2025) +## [2.8.8] (internal release) ### Fixed -- [T2B1, T3B1] Fix "PIN attempts exceeded" screen. [#3324] - Fixed flashing old content when fading. [#4492] ## [2.8.7] (22th January 2025) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 3b7d25b4c2..977d4d7922 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -7,7 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [2.8.10] (21st May 2025) ### Added -- [T3B1] Upgrade bundled bootloader to 2.1.10. [#noissue] +- [T3B1] Upgrade bundled bootloader to 2.1.10. - Add Nostr support (in debug mode only!). [#4160] - [T3T1] Visual cues to distinguish unlocked state on Homescreen. [#4964] From 1dc9942acfba2bda1186322461bd289542fe6c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan=20Biz=C4=83u?= Date: Wed, 30 Apr 2025 15:47:54 +0200 Subject: [PATCH 11/15] docs: add Solana to changelogs --- core/CHANGELOG.T2B1.md | 3 +++ core/CHANGELOG.T2T1.md | 3 +++ core/CHANGELOG.T3B1.md | 3 +++ core/CHANGELOG.T3T1.md | 3 +++ core/CHANGELOG.md | 3 +++ 5 files changed, 15 insertions(+) diff --git a/core/CHANGELOG.T2B1.md b/core/CHANGELOG.T2B1.md index 19502b7c01..0e2271e91c 100644 --- a/core/CHANGELOG.T2B1.md +++ b/core/CHANGELOG.T2B1.md @@ -8,12 +8,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Add Nostr support (in debug mode only!). [#4160] +- Solana: rent fee calculation [#4933] +- Solana: loadable token definitions [#3541] ### Fixed - Replaced "next page" icon with "..." ellipsis when confirming long message. [#4623] - Fixed Solana staking dialog title. [#4787] - Updated EIP-1559 fee-related labels. [#4819] - Allow firmware upgrade even if language change failed. [#4827] +- Solana: fees calculation is now exact [#4965] ## [2.8.9] (19th March 2025) diff --git a/core/CHANGELOG.T2T1.md b/core/CHANGELOG.T2T1.md index 1bb1a6d95a..d4d41243e9 100644 --- a/core/CHANGELOG.T2T1.md +++ b/core/CHANGELOG.T2T1.md @@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Add Nostr support (in debug mode only!). [#4160] +- Solana: rent fee calculation [#4933] +- Solana: loadable token definitions [#3541] ### Fixed - Replaced "next page" icon with "..." ellipsis when confirming long message. [#4623] @@ -15,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fixed Solana staking dialog fonts. [#4786] - Updated EIP-1559 fee-related labels. [#4819] - Allow firmware upgrade even if language change failed. [#4827] +- Solana: fees calculation is now exact [#4965] ## [2.8.9] (19th March 2025) diff --git a/core/CHANGELOG.T3B1.md b/core/CHANGELOG.T3B1.md index 4e38397ef7..50b64943b0 100644 --- a/core/CHANGELOG.T3B1.md +++ b/core/CHANGELOG.T3B1.md @@ -9,12 +9,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Upgrade bundled bootloader to 2.1.10. - Add Nostr support (in debug mode only!). [#4160] +- Solana: rent fee calculation [#4933] +- Solana: loadable token definitions [#3541] ### Fixed - Replaced "next page" icon with "..." ellipsis when confirming long message. [#4623] - Fixed Solana staking dialog title. [#4787] - Updated EIP-1559 fee-related labels. [#4819] - Allow firmware upgrade even if language change failed. [#4827] +- Solana: fees calculation is now exact [#4965] ## [2.8.9] (19th March 2025) diff --git a/core/CHANGELOG.T3T1.md b/core/CHANGELOG.T3T1.md index c23aa4174d..0a13467c30 100644 --- a/core/CHANGELOG.T3T1.md +++ b/core/CHANGELOG.T3T1.md @@ -9,11 +9,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Add Nostr support (in debug mode only!). [#4160] - Visual cues to distinguish unlocked state on Homescreen. [#4964] +- Solana: rent fee calculation [#4933] +- Solana: loadable token definitions [#3541] ### Fixed - Replaced "next page" icon with "..." ellipsis when confirming long message. [#4623] - Updated EIP-1559 fee-related labels. [#4819] - Allow firmware upgrade even if language change failed. [#4827] +- Solana: fees calculation is now exact [#4965] ## [2.8.9] (19th March 2025) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 977d4d7922..e3f36ddb5b 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - [T3B1] Upgrade bundled bootloader to 2.1.10. - Add Nostr support (in debug mode only!). [#4160] - [T3T1] Visual cues to distinguish unlocked state on Homescreen. [#4964] +- Solana: rent fee calculation [#4933] +- Solana: loadable token definitions [#3541] ### Fixed - Replaced "next page" icon with "..." ellipsis when confirming long message. [#4623] @@ -18,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - [T2B1,T3B1] Fixed Solana staking dialog title. [#4787] - Updated EIP-1559 fee-related labels. [#4819] - Allow firmware upgrade even if language change failed. [#4827] +- Solana: fees calculation is now exact [#4965] ## [2.8.9] (19th March 2025) From ed89a99fa608e4661092d966b41f1c8ca8738fe9 Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Mon, 5 May 2025 14:57:12 +0300 Subject: [PATCH 12/15] fix(core): handle empty 'info_items' in Caesar 'confirm_value' [no changelog] (cherry picked from commit bf2a588a25937b39d1170a689760ce17fa311d18) --- core/src/trezor/ui/layouts/caesar/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py index 1a8d7c3d77..a0ef604ad1 100644 --- a/core/src/trezor/ui/layouts/caesar/__init__.py +++ b/core/src/trezor/ui/layouts/caesar/__init__.py @@ -770,7 +770,7 @@ async def confirm_value( if description and value: description += ":" - if info_items is None: + if not info_items: return await raise_if_not_confirmed( trezorui_api.confirm_value( title=title, From 5f2cf6b24e1c526f6d7446de148847fc9557ba5d Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Mon, 5 May 2025 16:09:52 +0300 Subject: [PATCH 13/15] fix(core): update UI fixtures Following #5003. [no changelog] (cherry picked from commit 2882e20e6be6ad9e41376060a6b5cb4f35109d23) --- tests/ui_tests/fixtures.json | 48 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 1f3b4664aa..235224575a 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -11371,17 +11371,17 @@ "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[sync_native]": "34dec40603326e86160b3ecbaa413885a45a90090465941c08bc1b23cbd865db", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account]": "aac921f86c036204b0546148bc126b9a3078d597669dbdc44145106b52daba25", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account_-_multisig]": "c6171b194d05ad97d94f1738b747d003ef61c0f3c471b7436679b2fe76277f1c", -"T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "a9273baa0134ba06ffab58f5c5f4938b54cebd262469592b76269502cfe6f242", +"T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "f10f8134aa469c66a36b2af41fd529349446b98041c0277b1ebe21f0dfa48626", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer1]": "b29b927258e34eb0444dbd31ae7521b19f868e1b83fee9e9bee7237d60467e68", -"T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "985886978465bcb68b401a8c22736049e9a9038d7642e1b3876dc2e6466f126e", +"T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "3ebe33534201244eca082591907fe8edb3f0ed55b10c88f5692c31f33c23e177", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_-_multisig]": "6169f6f813515d4cef4543c57ddb46584a5f50e64e0214aa87b22595d6d49bd6", -"T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "cf4eacda09304170c586669b857468e6419a583f8ca75afb809f4501012128bd", +"T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "4b5d03cc157fa70bae762e84a6eca80e39a2b53d878803f96cf607fac4f15c92", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined]": "29064112b69cf409d43b5edc0efaf7121fff3d11391617fb7bbb9da953f6994d", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined_-_with_definitions]": "e9a574ef47cc98cf7f9aa6b3515d9598b06f37af0cbe97f296b1a820f37a69a4", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget]": "cf156b9b6872039858a6debc50868f675739c50c071ef9a453df623ac6ecccee", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget_without_heap-9ae0985e": "e4fe81b085b954114d8bb90386a102994147c9d3f3c19bec13f3d1aed157c07f", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget]": "188b26022c0aef67138a93b440694f702e6bd3f0486e77a143a4f773d8858d6a", -"T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "7159d3681b6ca83e85146b1ce20eb317444bee2a097b702a69af5b9c7ff91d58", +"T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "08dd5f4dede7e66bc1e4eee21b030d416f6cec0a494643cee908096e52d254a7", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_seed]": "612140b80417b26485b80f1f4d85f258d6b2a5b7686d759128860184b3a95edb", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[unknown_instruction]": "dc0c3f67882a750cd9521704918a7c4e18cf32c0069ae6230c2d81dff1028f93", "T3B1_cs_solana-test_sign_tx.py::test_solana_sign_tx[unstake_instructions]": "f3b206e3a442590461ab0d96abd1cd6ec09ca06f35ab3af566eb69079bbbbcf1", @@ -12761,17 +12761,17 @@ "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[sync_native]": "fd0f0b102518501941b4cd9281d5af9fa211756fd1fa5fe51c57ab79407a64f0", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account]": "220be40fc6b77955ee1761798dd0b1f2675c1fca518a3032a3e8b6ac8f75d68a", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account_-_multisig]": "89f87ba1ef19e361cbabac090e49db61675ece20bfcbdf43f068445b1e7c81fd", -"T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "bb10afb9b2daaebd6d52401ba33b31f07c4a6ad20550326b456a3b1f6e5ea3da", +"T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "bde1d6bb96368134704ee39eeefe418146c2bb52c0c35ff73b7ed98c23584adc", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer1]": "dfb88b0901dc91a0e10227f19ac773efa30dc98752dc5c98d5b8485c6bf62b0f", -"T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "c667a0eef454b799d765d36538267445427b92c0879d40757763fde080389175", +"T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "2611578ccf795067f0ac0d80bb0e0c122b76a35bd431781b52803ad2e946775f", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_-_multisig]": "4f08179d98da62a0aec77899dcf04c366c3b7e37c689755bad5541f6959350c9", -"T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "7705792c2a496cd50a40fbab8304e50b041f76816c4e043ad449eb35e5c6271c", +"T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "45b95928a8dcc6cb0c6f5906d0dcc5fa6cb82cd2527c09ffc8a26f8468ee14a4", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined]": "5ffc7f57b2faf227a5473d2509f3f70767725dd65dd3610bed562558bd4f88b9", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined_-_with_definitions]": "5689e5cfe5b978e18d27dc1b941d115721a2058c1f8d51dcc1185501c55930b7", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget]": "dbc86277cf91d80b601ad2c64ed4bd1fade1653b35c4681ae5e84b8eb96d3b8f", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget_without_heap-9ae0985e": "59194ce37f3253636a94cf999398c9d7184b035975ae8ea78a8f7883e0b0bb63", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget]": "3a26bf6be0a09c1b42d1706fe293611a513e4d017bf826aa5101abb4201ec261", -"T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "00fc341f7fcab651a0003fe5033c871a574e65c87e40b66edaa795a1deb2c839", +"T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "2a5247cc91494cd553368716e53af002d81b447ca108e960b0f50ee5b85d6f30", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_seed]": "e82ea0728a30d69802efb1e5a8b9214c72bf68cd4e52b3f4859b1879dea0b986", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[unknown_instruction]": "1508ed495e85e925ca7f1a085e76191ac910a7b9b18ac518cd05e3c9ce0859cd", "T3B1_de_solana-test_sign_tx.py::test_solana_sign_tx[unstake_instructions]": "14a20627592de91cab4b01e8c24df1f5871b34f94c8247827199424e38c286e6", @@ -14151,17 +14151,17 @@ "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[sync_native]": "4845cd997da8bd468d0e052f6843fe3f4dd583058f14df69fc6a0f1b4747b816", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account]": "029ebafa244245aed0ef450bb6c7fa88a8c8a4d3dbf4e8965fde3588b540c05d", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account_-_multisig]": "fbf1f00162205c1f8f6526d5b7394c7acf1fcdee0bc4b4a019f38e308ab47cac", -"T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "31fe5f6369d6aa15da18f38186608bfa2e5f933da4f890c747293c9b3ebd7053", +"T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "c3adf32268b6a6f6531b976786305f1dc67155784a882d989946e1ab67db5007", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer1]": "6cbade16b3cf613a536ce3514b38ffecdda7b6b172099b826bf1bf9579ca9173", -"T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "d392a9a0bdfbf24265079ad9c5249efb34ad11571fce62a31ee61a51cdda53ef", +"T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "decfcd86405f57664d3a154f467aa4bb7dc5078b5f9ffac8585e3afc20621d13", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_-_multisig]": "c719ed3a0f12a30cb7ac102eaf64ba754134436f4c08ac6b101ff19978c37674", -"T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "24cad77c8ae9c3c466cf23c41e13c22e77d5ed807df0724d93cfab822862480d", +"T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "d3742508577b3e88629cedfc9d80a6f3611f3d71d1fbdd06e0c5ce57ed63b194", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined]": "665c376811d30d3f7eab5b6e95786dbac725686fac1e76adb11e96dbff60ece3", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined_-_with_definitions]": "e940237c00a7ae23a67fcea09bd25d9677fcd873d9a3e3100e55d1164b4565e5", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget]": "cc4690b0f6e9abe37326a1c9acdfb3c8dc592c2fc5326e2f5074ad4f5f3a4ff4", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget_without_heap-9ae0985e": "e0c6ffa6a546506807a3336981a1e09f99d5d9bc095da6392cd181f0a47b8e9f", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget]": "10ca73fa7364d5b4caf6a84558c61db09b6f4cd88fbedc298e4f08720dbfd5b0", -"T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "cb69057adc6e51c838bb73a899cd70b670ebbdf1cfe7494600a67b0c8e0ab65c", +"T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "a690a597b4f9534baebd1903484261ec6fba8067dfb6378afc5bddf21889c392", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_seed]": "cb1f52ed67d964dc641dbe7b061716b7b02ffd9ad8c726e35c9a48c68e4421a8", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[unknown_instruction]": "fca2fb5fed9824868ca80b7c02bbdb8c4e85ea8bcd4ff186f0d3c17a42fae3e5", "T3B1_en_solana-test_sign_tx.py::test_solana_sign_tx[unstake_instructions]": "23e56ba69e737ba8a005140b2fe6f85f2c9bd31ef912f8034d115a7e715c2a3f", @@ -15541,17 +15541,17 @@ "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[sync_native]": "30140c031b03d808c1deb1e9bcd8b78a6f47969e2623cf080425bd4a5482ef83", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account]": "01b4ec0462d035b3b7c9ad9871c97f8f67a7edc6feeb6856e82d472e62f9d795", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account_-_multisig]": "45cfa4569953b39aa3bd4091d3131d6fcf1a55659d739a7647729f646f0b2cd6", -"T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "aa4e510b44c5b4b11a8bc07c84e69847f5073d042273a1d75203171d204baa9a", +"T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "ccdba9f83387c84b1fe133e8125acf94c4ba575743e4e500a13491fdc61c823f", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer1]": "8dbe55c75708171c954856a91aa46776ea9db1daa8074920cc599e19186163ed", -"T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "160a4e0f06dc1afb9639e8b27d9ed82ae500be2039b51a6d4ada3a8a7198bed2", +"T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "b61383f17034bc20232fae56ef52d33caa077a45d3d86f6979e3a40836039e62", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_-_multisig]": "3375e539c8cac1b0d8b1dde32d4a48e7c013fb69379ce0fb297a3e0839056756", -"T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "bb0c5dedc95450d75911d6b58c6741ff1812c459a46d817ae790fe9ff70602df", +"T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "d5ac1ccc5dc2781a310108f2e9248dfcb59aa1d75de3264f7480e3d1c8979194", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined]": "ff635c1ad07c7c49a4b80d8fbab2d3ccc650a9cfe89a9ca0c977cbd47de3e659", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined_-_with_definitions]": "5cfee01e13bb32f2e73dcacd3015675f7645a1bc802f37ae3e69d9cb4136aef1", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget]": "6d7ed79ca26a3cf74b4fb9bcf6fcb2f06ecb42264a32d184937917831998ee0d", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget_without_heap-9ae0985e": "0f21f354b2ef99f3ed915b02f0e4b9dcd4fbcbf84fb43df01894c507e57d7072", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget]": "f1bd34897b18cb78a9c6d07e10a4b2de41914cef3d8476fd334d43e3048e56e4", -"T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "31469abf069a46601fa470220ae71d766aab84024cb601c2f4d48ebdd0cd486c", +"T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "6fe9df5f4a6f596cda4a197c5b24ef15c173b3a8058054ac9d8c0260acf1def1", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_seed]": "af975a350c78b6a8acab63246ad16d09561a1fce1fc6576d38e1b1f88ff98430", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[unknown_instruction]": "74d252fde57f7ba89380b21c1302f6e226eae5c319d1a9583a3a4e5d4554a809", "T3B1_es_solana-test_sign_tx.py::test_solana_sign_tx[unstake_instructions]": "2855dbbf681f23161f0165b5b0329070b1769de91517cf015f8e2aac645f3834", @@ -16931,17 +16931,17 @@ "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[sync_native]": "a4b014f06b18f5e9c88551c1b3ee92a4be00e46e5300b9e8c9ee5593c00153f0", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account]": "206764f3c618fa89063d6076fc8b3b396390d2fe0283a8546da3cc25b76a3128", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account_-_multisig]": "ac8b5774fe46314bfea997fc3a6615ad578f194adf23165a44bd46fefe78aae2", -"T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "fe1a198d858ca925a13947b267ab0d52fdd916f5c81df65e85f9811d200960c2", +"T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "1a5f99aeb220b3a361f3a896818a44f29015bf35d3173d2b01aff86f4bd4e112", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer1]": "d58673ad1804e06d4578324e2273b4f35f9e440b288781892e25c046ccca5945", -"T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "8daa4a82d7e9df976300eb1105fdf5aa18ca495ecd4d631ddb8ba78b3329ced7", +"T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "cc66d6b378e602f9648e0b2e7427076e7b6d5f567552724c2e5a2b559a4262a1", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_-_multisig]": "9fac431f56daaf691322f951389961ac25e9c2cd6b761d73a2cc6ed0b44403a0", -"T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "da353f7a67a0acbde8ae86b6aec282eefe3267493dca812892498f548b4d86da", +"T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "c8d0cfcc507f7c41308a39a49ad8fc79efc78899355dde07bc87d9c3cd5290a9", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined]": "8fa8c775dd0c83d1d61236a589d0f9b2b7ccb9e0a3af684f5203e6c2bcdceb4e", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined_-_with_definitions]": "51c9b844496e23ca8b4f99ca5556dac1b236b82dd802c782909e6c2ce4a2a503", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget]": "f76af1eccbde503fe076f68debc27bedb00ebd53a71101ed2c8b1ccd25b0357e", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget_without_heap-9ae0985e": "db94d6fa46d4851af5070dd2220169eed8696a3bb5946def394d6a235f261480", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget]": "d36214bd578a206bfb4b30cf9522eb63a2352942181d562a594037b18c40bb08", -"T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "7c6c2339125e992f475160027452b71a0a3427d03372a52def413366bf0dfc30", +"T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "747350387fc123108c90cdd97ec301bd024fdc5c5608fa755a4820ba8a5fa87d", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_seed]": "e324ded27bd6cd953f5776e865b7382d2053a6962cabd9bb224c9d219e5f0957", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[unknown_instruction]": "80ba405edfabba1038d6d23eb60d92cf3b2f563759458628323e15cf5d8b7a23", "T3B1_fr_solana-test_sign_tx.py::test_solana_sign_tx[unstake_instructions]": "78274f5998cea2fffd3453180b75e19a51cf81e7ce4072509079206fe74ef631", @@ -18321,17 +18321,17 @@ "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[sync_native]": "d996b75023f668d351634235c2575a339ff9da691d74e91dab36266e3be7e232", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account]": "8abf042ba056a25de428b76ee5896c716ccf68719a8d0e3c1c6f7165fa4c3d09", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[thaw_account_-_multisig]": "5013412d3ee37e91763e37d3b438c70bc456ea97b91872bd9b0f13da690d8029", -"T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "3fe7eea87fb78553808bbc19c57aaf6d4153c5d532306d4bf24239fc78cc3587", +"T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer0]": "0b9759b66b768278e4dcf369182a2f4c48069faf209f6c5e781c7e45e998045e", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer1]": "d6c7f97edd543c603897c411fa9d2591fda0612a92e1e09327d85294b74e1704", -"T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "81e78ac2e49bea482978642df4b8409ca3f1e1823b03a70c3400673b634eb746", +"T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked]": "421758bba4e2a00a11ba5b641d213cbbbece80eed64afa6fe2a77bb9e8265385", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_-_multisig]": "6ba7b0821a8c308a894d692d035134cd900ea6ef8a9b4c72acc3d48ded66e554", -"T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "c8663d3a854d8d8fcde0aa9871dfde4f3457c5be2eca24e03c3082b40a325e54", +"T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_checked_with_token_definition]": "8d304dc2f69be9bccaa1f8afe3e4245503f7caf6ef37d2382ff02fcc3550fb7e", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined]": "f23801584518e07e2636701c89bad884fcc7b5d993f783225de6bc1e129eef89", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_-_predefined_-_with_definitions]": "d016af26755500ed4f3f57d2446ba250b1e997634d3809d5fec9df8293bff916", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget]": "248e5cd96b47dabaf99dc6cd710beb73c16963a03db8f3ab31c9b7f508c49f5f", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_token_with_compute_budget_without_heap-9ae0985e": "12f9587741f9d5f9b52232fff2cfbb9ed7cb4b740c01609232cb08ed5ecaf8b0", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget]": "1caf9af54a865fad30c49497cd9fa25920307219b76c46e74de30574b14afc3b", -"T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "1053ee1402eeec662f28adce2cbe795294ea9174ca60bc9b57874e393d4288f3", +"T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_compute_budget_without_heap_confirmation]": "b4b247020ef3681ed54e8262490793b273563015790979bb0c39eb3a667ee349", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[transfer_with_seed]": "5aaf925bb9951a2c1c3da8a780084a24e8ba0d9862ef1bf6139ff18daf8f04b0", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[unknown_instruction]": "f03d4d97f96e7d5793a45ae7246e12524528fbac6a67b1ce01fa5318506afda9", "T3B1_pt_solana-test_sign_tx.py::test_solana_sign_tx[unstake_instructions]": "aff9bdafc49f54c0e5d7d1d67e23968e601c8f2aa117e8c108f07f34b77b886a", From 2a65d18200580005dc419b9569ed97fae440806a Mon Sep 17 00:00:00 2001 From: obrusvit Date: Mon, 10 Mar 2025 15:08:13 +0100 Subject: [PATCH 14/15] fix(core): fix Delizia request_number dialog - NumberInputDialog now reacts to tap to continue - change the types there to u16 to be consistent with NumberInputSlider (cherry picked from commit 8ba5b4b6b2c3dd825bc371cde712eb09041f5417) --- core/.changelog.d/4751.fixed | 1 + .../layout_delizia/component/number_input.rs | 61 +++++++++---------- .../component/number_input_slider.rs | 12 +--- .../ui/layout_delizia/flow/request_number.rs | 17 +++--- .../ui/layout_delizia/flow/set_brightness.rs | 3 +- tests/click_tests/test_reset_slip39_basic.py | 3 +- 6 files changed, 44 insertions(+), 53 deletions(-) create mode 100644 core/.changelog.d/4751.fixed diff --git a/core/.changelog.d/4751.fixed b/core/.changelog.d/4751.fixed new file mode 100644 index 0000000000..f8c242c9da --- /dev/null +++ b/core/.changelog.d/4751.fixed @@ -0,0 +1 @@ +[T3T1] Not being able to tap to continue in request number dialog. diff --git a/core/embed/rust/src/ui/layout_delizia/component/number_input.rs b/core/embed/rust/src/ui/layout_delizia/component/number_input.rs index c6140679de..c9f4c20a3c 100644 --- a/core/embed/rust/src/ui/layout_delizia/component/number_input.rs +++ b/core/embed/rust/src/ui/layout_delizia/component/number_input.rs @@ -5,10 +5,10 @@ use crate::{ component::{ paginated::SinglePage, text::paragraphs::{Paragraph, Paragraphs}, - Component, Event, EventCtx, Pad, + Component, Event, EventCtx, Never, Pad, }, - event::SwipeEvent, - geometry::{Alignment, Direction, Grid, Insets, Offset, Rect}, + event::TouchEvent, + geometry::{Alignment, Grid, Insets, Offset, Rect}, shape::{self, Renderer}, }, }; @@ -16,8 +16,7 @@ use crate::{ use super::{super::fonts::FONT_DEMIBOLD, theme, Button, ButtonMsg}; pub enum NumberInputDialogMsg { - Confirmed(u32), - Changed(u32), + Changed(u16), } pub struct NumberInputDialog { @@ -28,7 +27,7 @@ pub struct NumberInputDialog { } impl NumberInputDialog { - pub fn new(min: u32, max: u32, init_value: u32, text: TString<'static>) -> Result { + pub fn new(min: u16, max: u16, init_value: u16, text: TString<'static>) -> Result { Ok(Self { area: Rect::zero(), input: NumberInput::new(min, max, init_value), @@ -37,7 +36,7 @@ impl NumberInputDialog { }) } - pub fn value(&self) -> u32 { + pub fn value(&self) -> u16 { self.input.value } } @@ -62,14 +61,16 @@ impl Component for NumberInputDialog { } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - if let Some(NumberInputMsg::Changed(i)) = self.input.event(ctx, event) { - return Some(NumberInputDialogMsg::Changed(i)); - } - - if let Event::Swipe(SwipeEvent::End(Direction::Up)) = event { - return Some(NumberInputDialogMsg::Confirmed(self.input.value)); - } + self.input.event(ctx, event); self.paragraphs.event(ctx, event); + + // Consume all touch events to prevent dialog confirmation if not clicking + // directly on the Footer + if let Event::Touch(TouchEvent::TouchStart(point)) = event { + if self.area.contains(point) { + return Some(NumberInputDialogMsg::Changed(self.value())); + } + } None } @@ -91,21 +92,17 @@ impl crate::trace::Trace for NumberInputDialog { } } -pub enum NumberInputMsg { - Changed(u32), -} - pub struct NumberInput { area: Rect, dec: Button, inc: Button, - min: u32, - max: u32, - value: u32, + min: u16, + max: u16, + value: u16, } impl NumberInput { - pub fn new(min: u32, max: u32, value: u32) -> Self { + pub fn new(min: u16, max: u16, value: u16) -> Self { let dec = Button::with_icon(theme::ICON_MINUS).styled(theme::button_counter()); let inc = Button::with_icon(theme::ICON_PLUS).styled(theme::button_counter()); let value = value.clamp(min, max); @@ -118,10 +115,16 @@ impl NumberInput { value, } } + + fn update_button_states(&mut self, ctx: &mut EventCtx) { + self.dec.enable_if(ctx, self.value > self.min); + self.inc.enable_if(ctx, self.value < self.max); + ctx.request_paint(); + } } impl Component for NumberInput { - type Msg = NumberInputMsg; + type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { let grid = Grid::new(bounds, 1, 3).with_spacing(theme::KEYBOARD_SPACING); @@ -132,21 +135,15 @@ impl Component for NumberInput { } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - let mut changed = false; if let Some(ButtonMsg::Clicked) = self.dec.event(ctx, event) { self.value = self.min.max(self.value.saturating_sub(1)); - changed = true; + self.update_button_states(ctx); }; if let Some(ButtonMsg::Clicked) = self.inc.event(ctx, event) { self.value = self.max.min(self.value.saturating_add(1)); - changed = true; + self.update_button_states(ctx); }; - if changed { - self.dec.enable_if(ctx, self.value > self.min); - self.inc.enable_if(ctx, self.value < self.max); - ctx.request_paint(); - return Some(NumberInputMsg::Changed(self.value)); - } + None } diff --git a/core/embed/rust/src/ui/layout_delizia/component/number_input_slider.rs b/core/embed/rust/src/ui/layout_delizia/component/number_input_slider.rs index c520ff7f73..5b4e30b666 100644 --- a/core/embed/rust/src/ui/layout_delizia/component/number_input_slider.rs +++ b/core/embed/rust/src/ui/layout_delizia/component/number_input_slider.rs @@ -17,9 +17,6 @@ pub enum NumberInputSliderDialogMsg { pub struct NumberInputSliderDialog { area: Rect, input: NumberInputSlider, - min: u16, - max: u16, - val: u16, init_val: u16, } @@ -28,9 +25,6 @@ impl NumberInputSliderDialog { Self { area: Rect::zero(), input: NumberInputSlider::new(min, max, init_value), - min, - max, - val: init_value, init_val: init_value, } } @@ -73,11 +67,7 @@ impl Component for NumberInputSliderDialog { fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { let msg_opt = self.input.event(ctx, event); - - msg_opt.map(|value| { - self.val = value; - Self::Msg::Changed(value) - }) + msg_opt.map(Self::Msg::Changed) } fn render<'s>(&'s self, target: &mut impl Renderer<'s>) { diff --git a/core/embed/rust/src/ui/layout_delizia/flow/request_number.rs b/core/embed/rust/src/ui/layout_delizia/flow/request_number.rs index d455c33b38..4ca05a4e8d 100644 --- a/core/embed/rust/src/ui/layout_delizia/flow/request_number.rs +++ b/core/embed/rust/src/ui/layout_delizia/flow/request_number.rs @@ -38,6 +38,9 @@ impl FlowController for RequestNumber { fn handle_swipe(&'static self, direction: Direction) -> Decision { match (self, direction) { (Self::Number, Direction::Left) => Self::Menu.swipe(direction), + (Self::Number, Direction::Up) => self.return_msg(FlowMsg::Choice( + NUM_DISPLAYED.load(Ordering::Relaxed).into(), + )), (Self::Menu, Direction::Right) => Self::Number.swipe(direction), (Self::Info, Direction::Right) => Self::Menu.swipe(direction), _ => self.do_nothing(), @@ -50,7 +53,6 @@ impl FlowController for RequestNumber { (Self::Menu, FlowMsg::Choice(0)) => Self::Info.swipe_left(), (Self::Menu, FlowMsg::Cancelled) => Self::Number.swipe_right(), (Self::Info, FlowMsg::Cancelled) => Self::Menu.goto(), - (Self::Number, FlowMsg::Choice(n)) => self.return_msg(FlowMsg::Choice(n)), _ => self.do_nothing(), } } @@ -75,20 +77,21 @@ pub fn new_request_number( info_closure(curr_number as u32) }; - let number_input_dialog = NumberInputDialog::new(min_count, max_count, count, description)?; + let number_input_dialog = NumberInputDialog::new( + min_count as u16, + max_count as u16, + count as u16, + description, + )?; let content_number_input = Frame::left_aligned(title, SwipeContent::new(number_input_dialog)) .with_menu_button() .with_swipeup_footer(None) .with_swipe(Direction::Left, SwipeSettings::default()) .map(|msg| match msg { NumberInputDialogMsg::Changed(n) => { - NUM_DISPLAYED.store(n as u16, Ordering::Relaxed); + NUM_DISPLAYED.store(n, Ordering::Relaxed); None } - NumberInputDialogMsg::Confirmed(n) => { - NUM_DISPLAYED.store(n as u16, Ordering::Relaxed); - Some(FlowMsg::Choice(n as usize)) - } }); let content_menu = Frame::left_aligned( diff --git a/core/embed/rust/src/ui/layout_delizia/flow/set_brightness.rs b/core/embed/rust/src/ui/layout_delizia/flow/set_brightness.rs index 6afde2cbbd..5e3257c520 100644 --- a/core/embed/rust/src/ui/layout_delizia/flow/set_brightness.rs +++ b/core/embed/rust/src/ui/layout_delizia/flow/set_brightness.rs @@ -12,14 +12,13 @@ use crate::{ FlowController, SwipeFlow, }, geometry::Direction, - layout_delizia::component::Footer, }, }; use super::super::{ component::{ number_input_slider::{NumberInputSliderDialog, NumberInputSliderDialogMsg}, - Frame, PromptMsg, PromptScreen, StatusScreen, SwipeContent, VerticalMenu, + Footer, Frame, PromptMsg, PromptScreen, StatusScreen, SwipeContent, VerticalMenu, }, theme, }; diff --git a/tests/click_tests/test_reset_slip39_basic.py b/tests/click_tests/test_reset_slip39_basic.py index 4ddfdd7e12..57de47bf77 100644 --- a/tests/click_tests/test_reset_slip39_basic.py +++ b/tests/click_tests/test_reset_slip39_basic.py @@ -91,7 +91,8 @@ def test_reset_slip39_basic( if num_of_shares == 1 and threshold == 1: reset.set_selection(debug, 0) elif num_of_shares == 16 and threshold == 16: - reset.set_selection(debug, 11) + # set threshold - dialog starts at 9 + reset.set_selection(debug, 7) else: raise RuntimeError("not a supported combination") From e5f38baf4eb0c5d39fef4d4ee31487e0fd5a1c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan=20Biz=C4=83u?= Date: Mon, 12 May 2025 09:38:11 +0200 Subject: [PATCH 15/15] release: sign translations --- core/translations/signatures.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/translations/signatures.json b/core/translations/signatures.json index b042ba044b..c42bb31575 100644 --- a/core/translations/signatures.json +++ b/core/translations/signatures.json @@ -5,6 +5,13 @@ "commit": "af2c36a22ffa4d6326a6161b2ce3037560f5eb7d" }, "history": [ + { + "signature": "0309f4f95fa152310e8a984e3b105cd899d12e37a7510057f37f20a72955635e0ff1605da8c44a847c86f8b0aa0afada995f0c628234fa910d0aa275fbaef8f504", + "version": "2.8.10.0", + "merkle_root": "cf835159e227117eb1c2cd8a784c259b523c5b9489e408a4cefa169601437af6", + "datetime": "2025-05-12T07:36:20.997218", + "commit": "2a65d18200580005dc419b9569ed97fae440806a" + }, { "signature": "03a0f2df5f468356b0444a24050a60789bff459a90328533b0aa9693666ca3145c8df72adc23f5b7a52d0814ef58c44ebec0b4f4482d12fa137b6c9a8858374209", "version": "2.8.9.0",