diff --git a/.github/workflows/build-pro.yml b/.github/workflows/build-pro.yml index 49030151f1..8637f0f7cf 100644 --- a/.github/workflows/build-pro.yml +++ b/.github/workflows/build-pro.yml @@ -27,8 +27,8 @@ jobs: bitcoin_only: 1 secret_key_1_name: "SECRET_KEY_1" secret_key_2_name: "SECRET_KEY_2" - output_dir: prod-bc-only - artifact_suffix: "prod-bc-only" + output_dir: prod-btc-only + artifact_suffix: "prod-btc-only" - production: 0 bitcoin_only: 0 secret_key_1_name: "SECRET_QA_KEY_1" @@ -39,8 +39,8 @@ jobs: bitcoin_only: 1 secret_key_1_name: "SECRET_QA_KEY_1" secret_key_2_name: "SECRET_QA_KEY_2" - output_dir: qa-bc-only - artifact_suffix: "qa-bc-only" + output_dir: qa-btc-only + artifact_suffix: "qa-btc-only" steps: - name: "Checkout" diff --git a/common/protob/messages-polkadot.proto b/common/protob/messages-polkadot.proto index ba3ff6bba7..41252b6769 100644 --- a/common/protob/messages-polkadot.proto +++ b/common/protob/messages-polkadot.proto @@ -36,6 +36,7 @@ message PolkadotSignTx { repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node required bytes raw_tx = 2; // serialized raw transaction required string network = 3; // Network name + optional uint32 prefix = 4; // SS58 address-type } /** diff --git a/core/embed/firmware/version.h b/core/embed/firmware/version.h index 01475a74d5..5d346aa2c5 100644 --- a/core/embed/firmware/version.h +++ b/core/embed/firmware/version.h @@ -9,8 +9,8 @@ #define FIX_VERSION_BUILD VERSION_BUILD #define ONEKEY_VERSION_MAJOR 4 -#define ONEKEY_VERSION_MINOR 17 -#define ONEKEY_VERSION_PATCH 1 +#define ONEKEY_VERSION_MINOR 18 +#define ONEKEY_VERSION_PATCH 0 #define ONEKEY_VERSION_BUILD 0 // Minimum SE version required for firmware upgrade diff --git a/core/src/apps/polkadot/__init__.py b/core/src/apps/polkadot/__init__.py index 06c6d0afb3..39b8abb744 100644 --- a/core/src/apps/polkadot/__init__.py +++ b/core/src/apps/polkadot/__init__.py @@ -3,15 +3,21 @@ CURVE = "ed25519-ledger" SLIP44_ID = 354 PATTERN = PATTERN_BIP44 -PRIMARY_COLOR = 0xE6007A -PRIMARY_COLOR_KSM = 0xFFFFFF +PRIMARY_COLOR = 0x00FF33 +PRIMARY_COLOR_KSM = 0x00FF33 PRIMARY_COLOR_WESTEND = 0x969696 PRIMARY_COLOR_ASTAR = 0x04E2FE PRIMARY_COLOR_JOY = 0x4038FF PRIMARY_COLOR_MAMTA = 0x26C8BF +PRIMARY_COLOR_HYDRATION = 0x00FF33 +PRIMARY_COLOR_BIFROST = 0x00FF33 +PRIMARY_COLOR_BIFROST_KSMC = 0x00FF33 ICON = "A:/res/chain-dot.png" ICON_KSM = "A:/res/chain-kusama.png" ICON_WESTEND = "A:/res/chain-westend.png" ICON_ASTAR = "A:/res/chain-astar.png" ICON_JOY = "A:/res/chain-joystream.png" ICON_MANTA = "A:/res/chain-manta.png" +ICON_HYDRATION = "A:/res/chain-hydration.png" +ICON_BIFROST = "A:/res/chain-bifrost.png" +ICON_BIFROST_KSM = "A:/res/chain-bifrost-ksm.png" diff --git a/core/src/apps/polkadot/helper.py b/core/src/apps/polkadot/helper.py index 727b5ebf6d..e56f080a8d 100644 --- a/core/src/apps/polkadot/helper.py +++ b/core/src/apps/polkadot/helper.py @@ -7,12 +7,18 @@ from . import ( ICON, ICON_ASTAR, + ICON_BIFROST, + ICON_BIFROST_KSM, + ICON_HYDRATION, ICON_JOY, ICON_KSM, ICON_MANTA, ICON_WESTEND, PRIMARY_COLOR, PRIMARY_COLOR_ASTAR, + PRIMARY_COLOR_BIFROST, + PRIMARY_COLOR_BIFROST_KSMC, + PRIMARY_COLOR_HYDRATION, PRIMARY_COLOR_JOY, PRIMARY_COLOR_KSM, PRIMARY_COLOR_MAMTA, @@ -30,6 +36,9 @@ ["astar", 5], ["joystream", 126], ["manta", 77], + ["hydration", 0], + ["bifrost", 0], + ["bifrost-ksm", 0], ] COIN_AMOUNT_DECIMAL_PLACES = 10 @@ -41,6 +50,8 @@ ASTAR_COIN_TICKER = "ASTR" JOY_COIN_TICKER = "JOY" MANTA_COIN_TICKER = "MANTA" +HYDRATION_COIN_TICKER = "HDX" +BIFROST_COIN_TICKER = "BNC" def ss58_encode(address: bytes, ss58_format: int = 42) -> str: @@ -118,6 +129,30 @@ def retrieval_chain_res(ctx: Context, network: str) -> tuple[str, str, int]: chain_name = "Manta" symbol = MANTA_COIN_TICKER decimal = COIN_AMOUNT_DECIMAL_PLACES_18 + elif network == "hydration": + ctx.primary_color, ctx.icon_path = ( + lv.color_hex(PRIMARY_COLOR_HYDRATION), + ICON_HYDRATION, + ) + chain_name = "Hydration" + symbol = HYDRATION_COIN_TICKER + decimal = COIN_AMOUNT_DECIMAL_PLACES_12 + elif network == "bifrost": + ctx.primary_color, ctx.icon_path = ( + lv.color_hex(PRIMARY_COLOR_BIFROST), + ICON_BIFROST, + ) + chain_name = "Bifrost" + symbol = BIFROST_COIN_TICKER + decimal = COIN_AMOUNT_DECIMAL_PLACES_12 + elif network == "bifrost-ksm": + ctx.primary_color, ctx.icon_path = ( + lv.color_hex(PRIMARY_COLOR_BIFROST_KSMC), + ICON_BIFROST_KSM, + ) + chain_name = "Bifrost-KSM" + symbol = BIFROST_COIN_TICKER + decimal = COIN_AMOUNT_DECIMAL_PLACES_12 else: ctx.primary_color, ctx.icon_path = lv.color_hex(PRIMARY_COLOR), ICON chain_name = "UNKN Chain" @@ -126,10 +161,10 @@ def retrieval_chain_res(ctx: Context, network: str) -> tuple[str, str, int]: return chain_name, symbol, decimal -def get_address_type(network: str) -> int: - address_type = 42 +def get_address_type(network: str, preset_prefix: int | None = None) -> int: + address_type = preset_prefix if preset_prefix is not None else 42 for element in POLKADOT_ADDRESS_TYPES: if network == element[0]: address_type = element[1] - + break return address_type diff --git a/core/src/apps/polkadot/sign_tx.py b/core/src/apps/polkadot/sign_tx.py index 872f04491f..ed34e940b0 100644 --- a/core/src/apps/polkadot/sign_tx.py +++ b/core/src/apps/polkadot/sign_tx.py @@ -19,7 +19,7 @@ async def sign_tx( public_key = node.public_key()[1:] else: public_key = ed25519.publickey(node.private_key()) - address_type = helper.get_address_type(msg.network) + address_type = helper.get_address_type(msg.network, msg.prefix) address = helper.ss58_encode(public_key, address_type) chain_name, symbol, decimal = helper.retrieval_chain_res(ctx, msg.network) tx = transaction.Transaction.deserialize(msg.raw_tx, msg.network) diff --git a/core/src/apps/polkadot/transaction.py b/core/src/apps/polkadot/transaction.py index 4e887b7c4c..6d54e953da 100644 --- a/core/src/apps/polkadot/transaction.py +++ b/core/src/apps/polkadot/transaction.py @@ -23,21 +23,26 @@ async def layout( return None @staticmethod - def _readAccountIdLookupOfT_V15(rawtx: codec.base.ScaleBytes, address_type) -> str: - value = rawtx.get_next_bytes(1)[0] - if value == 0: # Id + def _readAccountIdLookupOfT_V15( + rawtx: codec.base.ScaleBytes, address_type, skip_type_lookup: bool = False + ) -> str: + if not skip_type_lookup: + value = rawtx.get_next_bytes(1)[0] + else: + value = 0 + if value == 0: accountid = helper.ss58_encode(rawtx.get_next_bytes(32), address_type) - elif value == 1: # Index + elif value == 1: obj = codec.types.Compact(rawtx) accountid = str(obj.decode(check_remaining=False)) - elif value == 2: # Raw + elif value == 2: obj = codec.types.Compact(rawtx) value = obj.decode(check_remaining=False) clen = int(0 if value is None else value) accountid = hexlify(rawtx.get_next_bytes(clen)).decode() - elif value == 3: # Address32 + elif value == 3: accountid = hexlify(rawtx.get_next_bytes(32)).decode() - elif value == 4: # Address20 + elif value == 4: accountid = hexlify(rawtx.get_next_bytes(20)).decode() else: raise Exception("Unexpected value") @@ -49,23 +54,23 @@ def deserialize_polkadot( rawtx: codec.base.ScaleBytes, callPrivIdx: int ) -> "Transaction": tx = TransactionUnknown(rawtx) - if callPrivIdx in (1287, 1280): + if callPrivIdx in (1287, 1280, 2560): desc = Transaction._readAccountIdLookupOfT_V15(rawtx, 0) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesTransfer(desc, balance) - elif callPrivIdx == 1282: + elif callPrivIdx in (1282, 2562): source = Transaction._readAccountIdLookupOfT_V15(rawtx, 0) dest = Transaction._readAccountIdLookupOfT_V15(rawtx, 0) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesForceTransfer(source, dest, balance) - elif callPrivIdx == 1283: + elif callPrivIdx in (1283, 2563): desc = Transaction._readAccountIdLookupOfT_V15(rawtx, 0) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesTransferKeepAlive(desc, balance) - elif callPrivIdx == 1284: + elif callPrivIdx in (1284, 2564): desc = Transaction._readAccountIdLookupOfT_V15(rawtx, 0) keep_alive = rawtx.get_next_bytes(1)[0] tx = BalancesTransferAll(desc, keep_alive) @@ -77,23 +82,23 @@ def deserialize_kusama( rawtx: codec.base.ScaleBytes, callPrivIdx: int ) -> "Transaction": tx = TransactionUnknown(rawtx) - if callPrivIdx in (1031, 1024): + if callPrivIdx in (1031, 1024, 2560): desc = Transaction._readAccountIdLookupOfT_V15(rawtx, 2) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesTransfer(desc, balance) - elif callPrivIdx == 1026: + elif callPrivIdx in (1026, 2562): source = Transaction._readAccountIdLookupOfT_V15(rawtx, 2) dest = Transaction._readAccountIdLookupOfT_V15(rawtx, 2) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesForceTransfer(source, dest, balance) - elif callPrivIdx == 1027: + elif callPrivIdx in (1027, 2563): desc = Transaction._readAccountIdLookupOfT_V15(rawtx, 2) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesTransferKeepAlive(desc, balance) - elif callPrivIdx == 1028: + elif callPrivIdx in (1028, 2564): desc = Transaction._readAccountIdLookupOfT_V15(rawtx, 2) keep_alive = rawtx.get_next_bytes(1)[0] tx = BalancesTransferAll(desc, keep_alive) @@ -162,17 +167,33 @@ def deserialize_joy( ) -> "Transaction": tx = TransactionUnknown(rawtx) if callPrivIdx == 1280: - dest = helper.ss58_encode(rawtx.get_next_bytes(32), 126) + dest = Transaction._readAccountIdLookupOfT_V15( + rawtx, 126, skip_type_lookup=True + ) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesTransfer(dest, balance) + elif callPrivIdx == 1282: + source = Transaction._readAccountIdLookupOfT_V15( + rawtx, 126, skip_type_lookup=True + ) + dest = Transaction._readAccountIdLookupOfT_V15( + rawtx, 126, skip_type_lookup=True + ) + obj = codec.types.Compact(rawtx) + balance = obj.decode(check_remaining=False) + tx = BalancesForceTransfer(source, dest, balance) elif callPrivIdx == 1283: - dest = helper.ss58_encode(rawtx.get_next_bytes(32), 126) + dest = Transaction._readAccountIdLookupOfT_V15( + rawtx, 126, skip_type_lookup=True + ) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesTransferKeepAlive(dest, balance) elif callPrivIdx == 1284: - dest = helper.ss58_encode(rawtx.get_next_bytes(32), 126) + dest = Transaction._readAccountIdLookupOfT_V15( + rawtx, 126, skip_type_lookup=True + ) keep_alive = rawtx.get_next_bytes(1)[0] tx = BalancesTransferAll(dest, keep_alive) @@ -180,29 +201,67 @@ def deserialize_joy( @staticmethod def deserialize_manta( - rawtx: codec.base.ScaleBytes, callPrivIdx: int + rawtx: codec.base.ScaleBytes, callPrivIdx: int, address_type: int ) -> "Transaction": tx = TransactionUnknown(rawtx) if callPrivIdx == 2560: - desc = Transaction._readAccountIdLookupOfT_V15(rawtx, 77) + desc = Transaction._readAccountIdLookupOfT_V15(rawtx, address_type) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesTransfer(desc, balance) + elif callPrivIdx == 2562: + source = Transaction._readAccountIdLookupOfT_V15(rawtx, address_type) + dest = Transaction._readAccountIdLookupOfT_V15(rawtx, address_type) + obj = codec.types.Compact(rawtx) + balance = obj.decode(check_remaining=False) + tx = BalancesForceTransfer(source, dest, balance) elif callPrivIdx == 2563: - desc = Transaction._readAccountIdLookupOfT_V15(rawtx, 77) + desc = Transaction._readAccountIdLookupOfT_V15(rawtx, address_type) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesTransferKeepAlive(desc, balance) elif callPrivIdx == 2564: - desc = Transaction._readAccountIdLookupOfT_V15(rawtx, 77) + desc = Transaction._readAccountIdLookupOfT_V15(rawtx, address_type) keep_alive = rawtx.get_next_bytes(1)[0] tx = BalancesTransferAll(desc, keep_alive) - elif callPrivIdx == 2562: - source = Transaction._readAccountIdLookupOfT_V15(rawtx, 77) - dest = Transaction._readAccountIdLookupOfT_V15(rawtx, 77) + + return tx + + @staticmethod + def deserialize_hydration( + rawtx: codec.base.ScaleBytes, callPrivIdx: int + ) -> "Transaction": + tx = TransactionUnknown(rawtx) + if callPrivIdx == 1792: + desc = Transaction._readAccountIdLookupOfT_V15( + rawtx, 0, skip_type_lookup=True + ) + obj = codec.types.Compact(rawtx) + balance = obj.decode(check_remaining=False) + tx = BalancesTransfer(desc, balance) + elif callPrivIdx == 1794: + source = Transaction._readAccountIdLookupOfT_V15( + rawtx, 0, skip_type_lookup=True + ) + dest = Transaction._readAccountIdLookupOfT_V15( + rawtx, 0, skip_type_lookup=True + ) obj = codec.types.Compact(rawtx) balance = obj.decode(check_remaining=False) tx = BalancesForceTransfer(source, dest, balance) + elif callPrivIdx == 1795: + desc = Transaction._readAccountIdLookupOfT_V15( + rawtx, 0, skip_type_lookup=True + ) + obj = codec.types.Compact(rawtx) + balance = obj.decode(check_remaining=False) + tx = BalancesTransferKeepAlive(desc, balance) + elif callPrivIdx == 1796: + desc = Transaction._readAccountIdLookupOfT_V15( + rawtx, 0, skip_type_lookup=True + ) + keep_alive = rawtx.get_next_bytes(1)[0] + tx = BalancesTransferAll(desc, keep_alive) return tx @@ -224,8 +283,14 @@ def deserialize(raw_tx: bytes, network: str) -> "Transaction": tx = Transaction.deserialize_astar(rawtx, callPrivIdx) elif network == "joystream": tx = Transaction.deserialize_joy(rawtx, callPrivIdx) - elif network == "manta": - tx = Transaction.deserialize_manta(rawtx, callPrivIdx) + elif network in ("manta", "bifrost", "bifrost-ksm"): + if network == "manta": + address_type = 77 + else: + address_type = 0 + tx = Transaction.deserialize_manta(rawtx, callPrivIdx, address_type) + elif network == "hydration": + tx = Transaction.deserialize_hydration(rawtx, callPrivIdx) else: tx = TransactionUnknown(rawtx) @@ -248,6 +313,9 @@ def deserialize(raw_tx: bytes, network: str) -> "Transaction": tip = obj.decode(check_remaining=False) tx.tip = tip if tip is not None else 0 + # optional: assetId(asset location if assetId is not equal to 0) + # optional: mode + return tx diff --git a/core/src/apps/ur_registry/chains/base_sign_request.py b/core/src/apps/ur_registry/chains/base_sign_request.py index 6e59da41c9..f24acfcbd1 100644 --- a/core/src/apps/ur_registry/chains/base_sign_request.py +++ b/core/src/apps/ur_registry/chains/base_sign_request.py @@ -140,7 +140,7 @@ def from_cbor(cls, cbor): @classmethod def decode(cls, decoder): - sol_sign_req = cls() + sign_req = cls() size, _ = decoder.decodeMapSize() for _ in range(size): key, _ = decoder.decodeInteger() @@ -148,21 +148,21 @@ def decode(cls, decoder): tag, _ = decoder.decodeTag() if tag != UUID.get_tag(): raise Exception(f"Expected Tag {tag}") - sol_sign_req.request_id, _ = decoder.decodeBytes() + sign_req.request_id, _ = decoder.decodeBytes() if key == SIGN_DATA: - sol_sign_req.sign_data, _ = decoder.decodeBytes() + sign_req.sign_data, _ = decoder.decodeBytes() if key == DERIVATION_PATH: tag, _ = decoder.decodeTag() if tag != CryptoKeyPath.get_tag(): raise Exception(f"Expected Tag {tag}") - sol_sign_req.derivation_path = CryptoKeyPath.decode(decoder) + sign_req.derivation_path = CryptoKeyPath.decode(decoder) if key == ADDRESS: - sol_sign_req.address, _ = decoder.decodeBytes() + sign_req.address, _ = decoder.decodeBytes() if key == ORIGIN: - sol_sign_req.origin, _ = decoder.decodeText() + sign_req.origin, _ = decoder.decodeText() if key == REQUEST_TYPE: - sol_sign_req.request_type, _ = decoder.decodeInteger() - return sol_sign_req + sign_req.request_type, _ = decoder.decodeInteger() + return sign_req def get_address_n(self) -> list[int]: path = self.derivation_path.get_path() if self.derivation_path else "" diff --git a/core/src/apps/ur_registry/chains/ethereum/typed_data_transacion.py b/core/src/apps/ur_registry/chains/ethereum/typed_data_transacion.py index 96aa5c6669..659fa59a09 100644 --- a/core/src/apps/ur_registry/chains/ethereum/typed_data_transacion.py +++ b/core/src/apps/ur_registry/chains/ethereum/typed_data_transacion.py @@ -258,7 +258,9 @@ async def interact(self): raise ValueError(f"Invalid operation: {operation}") chain_id_origin = data["domain"]["chainId"] if isinstance(chain_id_origin, str): - if chain_id_origin.startswith(("0x", "0X")): + if chain_id_origin.startswith( + "0x" + ) or chain_id_origin.startswith("0X"): chain_id = int.from_bytes( EthereumTypedDataTransacion.decode_hex(chain_id_origin), "big", @@ -292,7 +294,9 @@ async def interact(self): print(f"Message error: {response}.") except Exception as e: if __debug__: - print(f"Data error: {e}") + import sys + + sys.print_exception(e) # type: ignore["print_exception" is not a known member of module] response = messages.Failure( code=FailureType.DataError, message=f"Error: {e}" ) diff --git a/core/src/apps/ur_registry/chains/hardware_requests/get_multi_accounts.py b/core/src/apps/ur_registry/chains/hardware_requests/get_multi_accounts.py index a813320b23..79090f040d 100644 --- a/core/src/apps/ur_registry/chains/hardware_requests/get_multi_accounts.py +++ b/core/src/apps/ur_registry/chains/hardware_requests/get_multi_accounts.py @@ -28,11 +28,15 @@ async def run(self): root_fingerprint = None if any(key not in param for key in ("paths", "chain") for param in params): raise ValueError("Invalid param") + if __debug__: + print(f"params: {params}") for param in params: chain = param["chain"] paths = param["paths"] if utils.BITCOIN_ONLY: if chain.lower() not in ("btc", "tbtc", "sbtc"): + if __debug__: + print(f"unsupported chain: {chain} with params: {param}") raise ValueError( "Only Bitcoin chains are supported in BITCOIN_ONLY mode" ) diff --git a/core/src/apps/ur_registry/crypto_multi_accounts.py b/core/src/apps/ur_registry/crypto_multi_accounts.py index 61c8bab6c3..e76bf38bb5 100644 --- a/core/src/apps/ur_registry/crypto_multi_accounts.py +++ b/core/src/apps/ur_registry/crypto_multi_accounts.py @@ -149,9 +149,6 @@ async def generate_crypto_multi_accounts(ctx: wire.Context) -> UREncoder: from storage import device from trezor import utils - eth_pub = await bitcoin_get_public_key.get_public_key( - ctx, GetPublicKey(address_n=paths.parse_path(helpers.ETH_STANDARD_PREFIX)) - ) btc_legacy_pub = await bitcoin_get_public_key.get_public_key( ctx, GetPublicKey(address_n=paths.parse_path(helpers.BTC_LEGACY_PREFIX)) ) @@ -164,35 +161,47 @@ async def generate_crypto_multi_accounts(ctx: wire.Context) -> UREncoder: btc_taproot_pub = await bitcoin_get_public_key.get_public_key( ctx, GetPublicKey(address_n=paths.parse_path(helpers.BTC_TAPROOT_PREFIX)) ) - tron_pub = await bitcoin_get_public_key.get_public_key( - ctx, - GetPublicKey(address_n=paths.parse_path(helpers.TRON_STANDARD_PATH)), - ) - sol_pubs = await misc_batch_get_pubkeys.batch_get_pubkeys( - ctx, - BatchGetPublickeys( - ecdsa_curve_name="ed25519", - paths=[ - Path(address_n=paths.parse_path(helpers.SOL_STANDARD_PATH)), - Path(address_n=paths.parse_path(helpers.SOL_LEDGER_LIVE_PATH)), - ], - ), - ) + keys = [ + helpers.generate_hdkey_BTCLegacy(btc_legacy_pub), + helpers.generate_hdkey_BTCSegWit(btc_segwit_pub), + helpers.generate_hdkey_BTCNativeSegWit(btc_native_segwit_pub), + helpers.generate_hdkey_BTCTaproot(btc_taproot_pub), + ] + if not utils.BITCOIN_ONLY: + eth_pub = await bitcoin_get_public_key.get_public_key( + ctx, GetPublicKey(address_n=paths.parse_path(helpers.ETH_STANDARD_PREFIX)) + ) + tron_pub = await bitcoin_get_public_key.get_public_key( + ctx, + GetPublicKey(address_n=paths.parse_path(helpers.TRON_STANDARD_PATH)), + ) + sol_pubs = await misc_batch_get_pubkeys.batch_get_pubkeys( + ctx, + BatchGetPublickeys( + ecdsa_curve_name="ed25519", + paths=[ + Path(address_n=paths.parse_path(helpers.SOL_STANDARD_PATH)), + Path(address_n=paths.parse_path(helpers.SOL_LEDGER_LIVE_PATH)), + ], + ), + ) + keys.extend( + [ + helpers.generate_hdkey_ETHStandard(ctx, eth_pub, False), + helpers.generate_hdkey_TRONStandard(tron_pub), + helpers.generate_hdkey_SOLStandard(sol_pubs.public_keys[0]), + helpers.generate_hdkey_SOLLedgerLive(sol_pubs.public_keys[1]), + ] + ) + + assert ( + btc_legacy_pub.root_fingerprint is not None + ), "Root fingerprint should not be None" + name = helpers.reveal_name(ctx, btc_legacy_pub.root_fingerprint) - assert eth_pub.root_fingerprint is not None, "Root fingerprint should not be None" - name = helpers.reveal_name(ctx, eth_pub.root_fingerprint) cma = CryptoMultiAccounts( - eth_pub.root_fingerprint, - [ - helpers.generate_hdkey_ETHStandard(ctx, eth_pub, False), - helpers.generate_hdkey_BTCLegacy(btc_legacy_pub), - helpers.generate_hdkey_BTCSegWit(btc_segwit_pub), - helpers.generate_hdkey_BTCNativeSegWit(btc_native_segwit_pub), - helpers.generate_hdkey_BTCTaproot(btc_taproot_pub), - helpers.generate_hdkey_SOLStandard(sol_pubs.public_keys[0]), - helpers.generate_hdkey_TRONStandard(tron_pub), - helpers.generate_hdkey_SOLLedgerLive(sol_pubs.public_keys[1]), - ], + btc_legacy_pub.root_fingerprint, + keys, device=name, device_id=device.get_device_id(), device_version=utils.ONEKEY_VERSION, diff --git a/core/src/apps/ur_registry/helpers.py b/core/src/apps/ur_registry/helpers.py index c0cd73f70a..1e71119bc6 100644 --- a/core/src/apps/ur_registry/helpers.py +++ b/core/src/apps/ur_registry/helpers.py @@ -62,6 +62,7 @@ def generate_HDKey_ED25519( def reveal_name(ctx, root_fingerprint: int, eth_only: bool = False) -> str: from apps.common import passphrase import storage + from trezor import utils device_name = "OneKey Pro" @@ -71,6 +72,8 @@ def reveal_name(ctx, root_fingerprint: int, eth_only: bool = False) -> str: if serial_no: name_components.append(serial_no) + if utils.BITCOIN_ONLY: + name_components.append("btc") name = ":".join(name_components) if (passphrase.is_enabled() and ctx.passphrase) or ( diff --git a/core/src/storage/device.py b/core/src/storage/device.py index 3776ef1667..6bd84d308c 100644 --- a/core/src/storage/device.py +++ b/core/src/storage/device.py @@ -93,6 +93,8 @@ _FIDO_ENABLED_VALUE: bool | None = None _TURBOMODE_VALUE: bool | None = None _DEVICE_NAME_DISPLAY_ENABLED_VALUE: bool | None = None +_USB_ENABLED_VALUE: bool | None = None +_BLE_ENABLED_BACKUP_VALUE: bool | None = None if utils.USE_THD89: import uctypes @@ -264,6 +266,10 @@ offset += uctypes.sizeof(struct_bool, uctypes.LITTLE_ENDIAN) struct_public["lockscreen"] = (offset, struct_lockscreen) offset += uctypes.sizeof(struct_lockscreen, uctypes.LITTLE_ENDIAN) + struct_public["usb_enabled"] = (offset, struct_bool) + offset += uctypes.sizeof(struct_bool, uctypes.LITTLE_ENDIAN) + struct_public["ble_enabled_bak"] = (offset, struct_bool) + offset += uctypes.sizeof(struct_bool, uctypes.LITTLE_ENDIAN) # public_field = uctypes.struct(0, struct_public, uctypes.LITTLE_ENDIAN) assert ( @@ -329,6 +335,8 @@ _FIDO_ENABLED = struct_public["fido_enabled"][0] _TURBOMODE = struct_public["turbomode"][0] _DEVICE_NAME_DISPLAY_ENABLED = struct_public["device_name_display_enabled"][0] + _USB_ENABLED = struct_public["usb_enabled"][0] + _BLE_ENABLED_BACKUP = struct_public["ble_enabled_bak"][0] U2F_COUNTER = 0x00 # u2f counter # recovery key @@ -399,6 +407,8 @@ _FIDO_ENABLED = (0x91) # bool _TURBOMODE = (0x92) # bool _DEVICE_NAME_DISPLAY_ENABLED = (0x93) # bool + _USB_ENABLED = (0x94) # bool + _BLE_ENABLED_BACKUP = (0x95) # bool # fmt: on SAFETY_CHECK_LEVEL_STRICT: Literal[0] = const(0) SAFETY_CHECK_LEVEL_PROMPT: Literal[1] = const(1) @@ -498,6 +508,30 @@ def set_ble_status(enable: bool) -> None: _BLE_ENABLED_VALUE = enable +def ble_enabled_backup() -> bool: + global _BLE_ENABLED_BACKUP_VALUE + if _BLE_ENABLED_BACKUP_VALUE is None: + ble_enabled = common.get(_NAMESPACE, _BLE_ENABLED_BACKUP, public=True) + if ble_enabled == common._FALSE_BYTE: + _BLE_ENABLED_BACKUP_VALUE = False + else: + _BLE_ENABLED_BACKUP_VALUE = True + return _BLE_ENABLED_BACKUP_VALUE + + +def set_ble_status_backup(enable: bool) -> None: + global _BLE_ENABLED_BACKUP_VALUE + if _BLE_ENABLED_BACKUP_VALUE == enable: + return + common.set_bool( + _NAMESPACE, + _BLE_ENABLED_BACKUP, + enable, + public=True, + ) + _BLE_ENABLED_BACKUP_VALUE = enable + + def set_ble_version(version: str) -> None: """Set ble firmware version.""" if len(version.encode("utf-8")) > BLE_VERSION_MAXLENGTH: @@ -587,6 +621,30 @@ def set_usb_lock_enable(enable: bool) -> None: _USE_USB_PROTECT_VALUE = enable +def is_usb_enabled() -> bool: + global _USB_ENABLED_VALUE + if _USB_ENABLED_VALUE is None: + usb_enabled = common.get(_NAMESPACE, _USB_ENABLED, public=True) + if usb_enabled == common._FALSE_BYTE: + _USB_ENABLED_VALUE = False + else: + _USB_ENABLED_VALUE = True + return _USB_ENABLED_VALUE + + +def set_usb_status(enable: bool) -> None: + global _USB_ENABLED_VALUE + if _USB_ENABLED_VALUE == enable: + return + common.set_bool( + _NAMESPACE, + _USB_ENABLED, + enable, + public=True, + ) + _USB_ENABLED_VALUE = enable + + def enable_fingerprint_unlock(enable: bool) -> None: global _USE_FINGERPRINT_UNLOCK_VALUE diff --git a/core/src/trezor/lvglui/res/chain-bifrost-ksm.png b/core/src/trezor/lvglui/res/chain-bifrost-ksm.png new file mode 100644 index 0000000000..c9be792295 Binary files /dev/null and b/core/src/trezor/lvglui/res/chain-bifrost-ksm.png differ diff --git a/core/src/trezor/lvglui/res/chain-bifrost.png b/core/src/trezor/lvglui/res/chain-bifrost.png new file mode 100644 index 0000000000..3259c31b14 Binary files /dev/null and b/core/src/trezor/lvglui/res/chain-bifrost.png differ diff --git a/core/src/trezor/lvglui/res/chain-dot.png b/core/src/trezor/lvglui/res/chain-dot.png index 4cbb7832ea..50ece63f39 100644 Binary files a/core/src/trezor/lvglui/res/chain-dot.png and b/core/src/trezor/lvglui/res/chain-dot.png differ diff --git a/core/src/trezor/lvglui/res/chain-hydration.png b/core/src/trezor/lvglui/res/chain-hydration.png new file mode 100644 index 0000000000..dbf7e5afac Binary files /dev/null and b/core/src/trezor/lvglui/res/chain-hydration.png differ diff --git a/core/src/trezor/lvglui/res/chain-kusama.png b/core/src/trezor/lvglui/res/chain-kusama.png old mode 100755 new mode 100644 index af0fa509d1..cd867f3c8c Binary files a/core/src/trezor/lvglui/res/chain-kusama.png and b/core/src/trezor/lvglui/res/chain-kusama.png differ diff --git a/core/src/trezor/lvglui/scrs/components/button.py b/core/src/trezor/lvglui/scrs/components/button.py index 542bfe89a6..6f4c97bac0 100644 --- a/core/src/trezor/lvglui/scrs/components/button.py +++ b/core/src/trezor/lvglui/scrs/components/button.py @@ -291,3 +291,41 @@ def clear_state(self) -> None: def add_state(self) -> None: self.switch.add_state(lv.STATE.CHECKED) + + def disable(self) -> None: + self.add_style( + StyleWrapper() + .bg_color(lv_colors.ONEKEY_GRAY_3) + .text_color(lv_colors.WHITE_2), + 0, + ) + self.switch.add_style(StyleWrapper().bg_opa(lv.OPA._40), 0) + self.switch.add_style( + StyleWrapper().bg_opa(lv.OPA._40), + lv.PART.INDICATOR | lv.STATE.CHECKED, + ) + self.switch.add_style( + StyleWrapper().bg_opa(lv.OPA._40), + lv.PART.KNOB | lv.STATE.DEFAULT, + ) + self.clear_flag(lv.obj.FLAG.CLICKABLE) + self.switch.clear_flag(lv.obj.FLAG.CLICKABLE) + + def enable(self) -> None: + self.add_style( + StyleWrapper() + .bg_color(lv_colors.ONEKEY_BLACK_3) + .text_color(lv_colors.WHITE), + 0, + ) + self.switch.add_style(StyleWrapper().bg_opa(lv.OPA.COVER), 0) + self.switch.add_style( + StyleWrapper().bg_opa(lv.OPA.COVER), + lv.PART.INDICATOR | lv.STATE.CHECKED, + ) + self.switch.add_style( + StyleWrapper().bg_opa(lv.OPA.COVER), + lv.PART.KNOB | lv.STATE.DEFAULT, + ) + self.add_flag(lv.obj.FLAG.CLICKABLE) + self.switch.add_flag(lv.obj.FLAG.CLICKABLE) diff --git a/core/src/trezor/lvglui/scrs/homescreen.py b/core/src/trezor/lvglui/scrs/homescreen.py index 270bd0b71b..eb2e1b6738 100644 --- a/core/src/trezor/lvglui/scrs/homescreen.py +++ b/core/src/trezor/lvglui/scrs/homescreen.py @@ -1024,27 +1024,17 @@ def init_ui(self): self.show_page(0) def init_items(self): - if utils.BITCOIN_ONLY: - items = [ - ("connect", "app-connect", i18n_keys.APP__CONNECT_WALLET), - ("scan", "app-scan", i18n_keys.APP__SCAN), - ("my_address", "app-address", i18n_keys.APP__ADDRESS), - ("settings", "app-settings", i18n_keys.APP__SETTINGS), - ("backup", "app-backup", i18n_keys.APP__BACK_UP), - ("nft", "app-nft", i18n_keys.APP__NFT_GALLERY), - ("guide", "app-tips", i18n_keys.APP__TIPS), - ] - else: - items = [ - ("connect", "app-connect", i18n_keys.APP__CONNECT_WALLET), - ("scan", "app-scan", i18n_keys.APP__SCAN), - ("my_address", "app-address", i18n_keys.APP__ADDRESS), - ("settings", "app-settings", i18n_keys.APP__SETTINGS), - ("passkey", "app-keys", i18n_keys.FIDO_FIDO_KEYS_LABEL), - ("backup", "app-backup", i18n_keys.APP__BACK_UP), - ("nft", "app-nft", i18n_keys.APP__NFT_GALLERY), - ("guide", "app-tips", i18n_keys.APP__TIPS), - ] + items = [ + ("connect", "app-connect", i18n_keys.APP__CONNECT_WALLET), + ("scan", "app-scan", i18n_keys.APP__SCAN), + ("my_address", "app-address", i18n_keys.APP__ADDRESS), + ("settings", "app-settings", i18n_keys.APP__SETTINGS), + ("backup", "app-backup", i18n_keys.APP__BACK_UP), + ("guide", "app-tips", i18n_keys.APP__TIPS), + ] + if not utils.BITCOIN_ONLY: + items.insert(4, ("passkey", "app-keys", i18n_keys.FIDO_FIDO_KEYS_LABEL)) + items.insert(6, ("nft", "app-nft", i18n_keys.APP__NFT_GALLERY)) items_per_page = 4 cols = 2 @@ -2455,6 +2445,12 @@ def __init__(self, prev_scr=None): self._load_scr(self) return airgap_enabled = storage_device.is_airgap_mode() + usb_enabled = False + ble_enabled = False + if not airgap_enabled: + usb_enabled = storage_device.is_usb_enabled() + ble_enabled = uart.is_ble_opened() + if airgap_enabled: self.waring_bar = Banner( self.content_area, @@ -2478,11 +2474,12 @@ def __init__(self, prev_scr=None): _(i18n_keys.ITEM__USB), left_img_src="A:/res/connect-way-usb-on.png", ) - if airgap_enabled: - self.by_ble.disable() - self.by_ble.img_left.set_src("A:/res/connect-way-ble-off.png") + if not usb_enabled: self.by_usb.disable() self.by_usb.img_left.set_src("A:/res/connect-way-usb-off.png") + if not ble_enabled: + self.by_ble.disable() + self.by_ble.img_left.set_src("A:/res/connect-way-ble-off.png") # self.by_qrcode = ListItemBtn( # self.container, @@ -2491,14 +2488,26 @@ def __init__(self, prev_scr=None): # ) self.add_event_cb(self.on_event, lv.EVENT.CLICKED, None) + if ( + storage_device.is_passphrase_enabled() + or passphrase.is_passphrase_pin_enabled() + ): + retrieval_encoder() + + def show_connect_wallet_guide(self, connect_type): + if not utils.BITCOIN_ONLY: + ConnectWalletGuide(connect_type, self) + else: + ConnectWalletGuide.show_wallet_tutorial("onekey", connect_type) + def on_event(self, event_obj): code = event_obj.code target = event_obj.get_target() if code == lv.EVENT.CLICKED: if target == self.by_ble: - ConnectWalletGuide("ble", self) + self.show_connect_wallet_guide("ble") elif target == self.by_usb: - ConnectWalletGuide("usb", self) + self.show_connect_wallet_guide("usb") # elif target == self.by_qrcode: # gc.collect() # WalletList(self) @@ -2507,7 +2516,10 @@ def on_event(self, event_obj): def on_click_ext(self, target): if target == self.rti_btn: - QRWalletTips(self) + if not utils.BITCOIN_ONLY: + QRWalletTips(self) + else: + WalletList.connect_onekey(target, bitcoin_only=True) def _load_scr(self, scr: "Screen", back: bool = False) -> None: lv.scr_load(scr) @@ -2587,108 +2599,127 @@ def __init__(self, c_type, prev_scr=None): self.add_event_cb(self.on_click, lv.EVENT.CLICKED, None) - def on_click(self, event_obj): - code = event_obj.code - target = event_obj.get_target() - if code == lv.EVENT.CLICKED: - if target not in [self.onekey, self.mm, self.okx]: - return - from trezor.lvglui.scrs.template import ConnectWalletTutorial - - if target == self.onekey: - title = _(i18n_keys.ITEM__ONEKEY_WALLET) - subtitle = ( - _(i18n_keys.CONTENT__IOS_ANDROID) - if self.connect_type == "ble" - else _(i18n_keys.CONTENT__DESKTOP_BROWSER_EXTENSION) - ) - steps = [ + @staticmethod + def _get_wallet_tutorial_config(wallet_type, connect_type): + is_ble = connect_type == "ble" + configs = { + "onekey": { + "title": i18n_keys.ITEM__ONEKEY_WALLET, + "subtitle": i18n_keys.CONTENT__IOS_ANDROID + if is_ble + else i18n_keys.CONTENT__DESKTOP_BROWSER_EXTENSION, + "steps": [ ( - _(i18n_keys.FORM__DOWNLOAD_ONEKEY_APP), - _(i18n_keys.FORM__DOWNLOAD_ONEKEY_APP_MOBILE) - if self.connect_type == "ble" - else _(i18n_keys.FORM__DOWNLOAD_ONEKEY_APP_DESKTOP), + i18n_keys.FORM__DOWNLOAD_ONEKEY_APP, + i18n_keys.FORM__DOWNLOAD_ONEKEY_APP_MOBILE + if is_ble + else i18n_keys.FORM__DOWNLOAD_ONEKEY_APP_DESKTOP, ), ( - _(i18n_keys.FORM__CONNECT_VIA_BLUETOOTH) - if self.connect_type == "ble" - else _(i18n_keys.FORM__CONNECT_YOUR_DEVICE), - _(i18n_keys.FORM__CONNECT_VIA_BLUETOOTH_DESC) - if self.connect_type == "ble" - else _(i18n_keys.FORM__CONNECT_YOUR_DEVICE_DESC), + i18n_keys.FORM__CONNECT_VIA_BLUETOOTH + if is_ble + else i18n_keys.FORM__CONNECT_YOUR_DEVICE, + i18n_keys.FORM__CONNECT_VIA_BLUETOOTH_DESC + if is_ble + else i18n_keys.FORM__CONNECT_YOUR_DEVICE_DESC, ), ( - _(i18n_keys.FORM__PAIR_DEVICES) - if self.connect_type == "ble" - else _(i18n_keys.FORM__START_THE_CONNECTION), - _(i18n_keys.FORM__PAIR_DEVICES_DESC) - if self.connect_type == "ble" - else _(i18n_keys.FORM__START_THE_CONNECTION_DESC), + i18n_keys.FORM__PAIR_DEVICES + if is_ble + else i18n_keys.FORM__START_THE_CONNECTION, + i18n_keys.FORM__PAIR_DEVICES_DESC + if is_ble + else i18n_keys.FORM__START_THE_CONNECTION_DESC, ), - ] - logo = "A:/res/ok-logo-96.png" - url = ( - "https://help.onekey.so/articles/11461081" - if self.connect_type == "ble" - else "https://help.onekey.so/articles/11461081#h_01HMWVPP85HWYTZGPQQTB300VX" - ) - elif target == self.mm: - title = _(i18n_keys.ITEM__METAMASK_WALLET) - subtitle = _(i18n_keys.CONTENT__BROWSER_EXTENSION) - steps = [ + ], + "logo": "A:/res/ok-logo-96.png", + "url": "https://help.onekey.so/articles/11461081" + if is_ble + else "https://help.onekey.so/articles/11461081#h_01HMWVPP85HWYTZGPQQTB300VX", + }, + "metamask": { + "title": i18n_keys.ITEM__METAMASK_WALLET, + "subtitle": i18n_keys.CONTENT__BROWSER_EXTENSION, + "steps": [ ( - _(i18n_keys.FORM__ACCESS_WALLET), - _(i18n_keys.FORM__OPEN_METAMASK_IN_YOUR_BROWSER), + i18n_keys.FORM__ACCESS_WALLET, + i18n_keys.FORM__OPEN_METAMASK_IN_YOUR_BROWSER, ), ( - _(i18n_keys.FORM__CONNECT_HARDWARE_WALLET), - _(i18n_keys.FORM__CONNECT_HARDWARE_WALLET_DESC), + i18n_keys.FORM__CONNECT_HARDWARE_WALLET, + i18n_keys.FORM__CONNECT_HARDWARE_WALLET_DESC, ), ( - _(i18n_keys.FORM__UNLOCK_ACCOUNT), - _(i18n_keys.FORM__UNLOCK_ACCOUNT_DESC), + i18n_keys.FORM__UNLOCK_ACCOUNT, + i18n_keys.FORM__UNLOCK_ACCOUNT_DESC, ), - ] - logo = "A:/res/mm-logo-96.png" - url = "https://help.onekey.so/articles/11461106" - else: - title = _(i18n_keys.ITEM__OKX_WALLET) - subtitle = ( - _(i18n_keys.CONTENT__IOS_ANDROID) - if self.connect_type == "ble" - else _(i18n_keys.CONTENT__BROWSER_EXTENSION) - ) - steps = [ + ], + "logo": "A:/res/mm-logo-96.png", + "url": "https://help.onekey.so/articles/11461106", + }, + "okx": { + "title": i18n_keys.ITEM__OKX_WALLET, + "subtitle": i18n_keys.CONTENT__IOS_ANDROID + if is_ble + else i18n_keys.CONTENT__BROWSER_EXTENSION, + "steps": [ ( - _(i18n_keys.FORM__ACCESS_WALLET), - _(i18n_keys.FORM__ACCESS_WALLET_DESC) - if self.connect_type == "ble" - else _(i18n_keys.FORM__OPEN_THE_OKX_WALLET_EXTENSION), + i18n_keys.FORM__ACCESS_WALLET, + i18n_keys.FORM__ACCESS_WALLET_DESC + if is_ble + else i18n_keys.FORM__OPEN_THE_OKX_WALLET_EXTENSION, ), ( - _(i18n_keys.FORM__CONNECT_VIA_BLUETOOTH) - if self.connect_type == "ble" - else _(i18n_keys.FORM__INSTALL_ONEKEY_BRIDGE), - _(i18n_keys.FORM__CONNECT_VIA_BLUETOOTH_DESC) - if self.connect_type == "ble" - else _(i18n_keys.FORM__INSTALL_ONEKEY_BRIDGE_DESC), + i18n_keys.FORM__CONNECT_VIA_BLUETOOTH + if is_ble + else i18n_keys.FORM__INSTALL_ONEKEY_BRIDGE, + i18n_keys.FORM__CONNECT_VIA_BLUETOOTH_DESC + if is_ble + else i18n_keys.FORM__INSTALL_ONEKEY_BRIDGE_DESC, ), ( - _(i18n_keys.FORM__IMPORT_WALLET_ACCOUNTS), - _(i18n_keys.FORM__IMPORT_WALLET_ACCOUNTS_DESC) - if self.connect_type == "ble" - else _( - i18n_keys.FORM__OKX_EXTENSION_IMPORT_WALLET_ACCOUNTS_DESC - ), + i18n_keys.FORM__IMPORT_WALLET_ACCOUNTS, + i18n_keys.FORM__IMPORT_WALLET_ACCOUNTS_DESC + if is_ble + else i18n_keys.FORM__OKX_EXTENSION_IMPORT_WALLET_ACCOUNTS_DESC, ), - ] - logo = "A:/res/okx-logo-96.png" - url = ( - " https://help.onekey.so/articles/11461103" - if self.connect_type == "ble" - else "https://help.onekey.so/articles/11461103" - ) - ConnectWalletTutorial(title, subtitle, steps, url, logo) + ], + "logo": "A:/res/okx-logo-96.png", + "url": "https://help.onekey.so/articles/11461103", + }, + } + assert wallet_type in configs, f"Invalid wallet type: {wallet_type}" + return configs[wallet_type] + + @staticmethod + def show_wallet_tutorial(wallet_type, connect_type): + config = ConnectWalletGuide._get_wallet_tutorial_config( + wallet_type, connect_type + ) + from trezor.lvglui.scrs.template import ConnectWalletTutorial + + ConnectWalletTutorial( + _(config["title"]), + _(config["subtitle"]), + [(_(step[0]), _(step[1])) for step in config["steps"]], + config["url"], + config["logo"], + ) + + def on_click(self, event_obj): + code = event_obj.code + target = event_obj.get_target() + if code == lv.EVENT.CLICKED: + if target == self.onekey: + wallet_type = "onekey" + elif target == self.mm: + wallet_type = "metamask" + elif target == self.okx: + wallet_type = "okx" + else: + raise ValueError(f"Invalid wallet type: {target}") + + ConnectWalletGuide.show_wallet_tutorial(wallet_type, self.connect_type) def _load_scr(self, scr: "Screen", back: bool = False) -> None: lv.scr_load(scr) @@ -2798,7 +2829,8 @@ def on_click(self, event_obj): def _load_scr(self, scr: "Screen", back: bool = False) -> None: lv.scr_load(scr) - def connect_onekey(self, target): + @staticmethod + def connect_onekey(target, bitcoin_only: bool = False): from trezor.qr import get_encoder if passphrase.is_enabled(): @@ -2819,8 +2851,10 @@ def connect_onekey(self, target): _(i18n_keys.ITEM__ONEKEY_WALLET) ), _(i18n_keys.CONTENT__OPEN_ONEKEY_SCAN_THE_QRCODE), - _(i18n_keys.CONTENT__BTC_SOL_ETH_N_EVM_NETWORKS), - "A:/res/support-chains-ok-qr.png", + _(i18n_keys.CONTENT__BTC_SOL_ETH_N_EVM_NETWORKS) + if not bitcoin_only + else None, + "A:/res/support-chains-ok-qr.png" if not bitcoin_only else None, None, encoder=encoder, ) @@ -2935,7 +2969,7 @@ async def handle_airgap_response(self, screen): if await DUMMY_CONTEXT.wait(screen.request()): screen.destroy() - AirGapSetting(self) + WalletScreen(self) else: screen.destroy() @@ -3030,11 +3064,11 @@ def eventhandler(self, event_obj): if code == lv.EVENT.CLICKED: if utils.lcd_resume(): return - if target == self.nav_back.nav_btn: - self.destroy() - elif hasattr(self, "btn_yes") and target == self.btn_yes: - DynQr(self.encoder) - self.destroy(100) + if target == self.nav_back.nav_btn: + self.destroy() + elif hasattr(self, "btn_yes") and target == self.btn_yes: + DynQr(self.encoder) + self.destroy(100) def on_nav_back(self, event_obj): code = event_obj.code @@ -7482,8 +7516,12 @@ def collect_animation_targets(self) -> list: targets.append(self.advanced_zone) if hasattr(self, "air_gap") and self.air_gap: targets.append(self.air_gap) - if hasattr(self, "description") and self.description: - targets.append(self.description) + if hasattr(self, "usb") and self.usb: + targets.append(self.usb) + if hasattr(self, "ble") and self.ble: + targets.append(self.ble) + # if hasattr(self, "description") and self.description: + # targets.append(self.description) if hasattr(self, "danger_zone") and self.danger_zone: targets.append(self.danger_zone) return targets @@ -7507,6 +7545,8 @@ def __init__(self, prev_scr=None): ) self.passphrase = ListItemBtn(self.container, _(i18n_keys.ITEM__PASSPHRASE)) self.turbo_mode = ListItemBtn(self.container, _(i18n_keys.TITLE__TURBO_MODE)) + if utils.BITCOIN_ONLY: + self.turbo_mode.add_flag(lv.obj.FLAG.HIDDEN) self.trezor_mode = ListItemBtnWithSwitch( self.container, _(i18n_keys.ITEM__COMPATIBLE_WITH_TREZOR) ) @@ -7529,40 +7569,66 @@ def __init__(self, prev_scr=None): self.advanced_zone.set_text(_(i18n_keys.TITLE__ADVANCED)) self.advanced_zone.align_to(self.container, lv.ALIGN.OUT_BOTTOM_LEFT, 12, 28) - self.air_gap = ListItemBtnWithSwitch( - self.content_area, _(i18n_keys.ITEM__AIR_GAP_MODE) + self.container_advanced = ContainerFlexCol( + self.content_area, + self.advanced_zone, + align=lv.ALIGN.OUT_BOTTOM_LEFT, + pos=(-12, 16), + padding_row=2, ) - self.air_gap.add_style( - StyleWrapper().bg_color(lv_colors.ONEKEY_BLACK_3).bg_opa(lv.OPA.COVER), 0 + switch_style = ( + StyleWrapper().bg_color(lv_colors.ONEKEY_BLACK_3).bg_opa(lv.OPA.COVER) ) - self.air_gap.set_style_radius(40, 0) - self.air_gap.align_to(self.advanced_zone, lv.ALIGN.OUT_BOTTOM_LEFT, -12, 16) - - self.description = lv.label(self.content_area) - self.description.set_size(456, lv.SIZE.CONTENT) - self.description.set_long_mode(lv.label.LONG.WRAP) - self.description.set_style_text_color(lv_colors.ONEKEY_GRAY, lv.STATE.DEFAULT) - self.description.set_style_text_font(font_GeistRegular26, lv.STATE.DEFAULT) - self.description.set_style_text_line_space(3, 0) - self.description.align_to(self.air_gap, lv.ALIGN.OUT_BOTTOM_LEFT, 12, 16) + self.air_gap = ListItemBtnWithSwitch( + self.container_advanced, _(i18n_keys.ITEM__AIR_GAP_MODE) + ) + self.air_gap.add_style(switch_style, 0) + self.usb = ListItemBtnWithSwitch( + self.container_advanced, _(i18n_keys.ITEM__USB) + ) + self.usb.add_style(switch_style, 0) + self.ble = ListItemBtnWithSwitch( + self.container_advanced, _(i18n_keys.ITEM__BLUETOOTH) + ) + self.ble.add_style(switch_style, 0) + # self.description = lv.label(self.content_area) + # self.description.set_size(456, lv.SIZE.CONTENT) + # self.description.set_long_mode(lv.label.LONG.WRAP) + # self.description.set_style_text_color(lv_colors.ONEKEY_GRAY, lv.STATE.DEFAULT) + # self.description.set_style_text_font(font_GeistRegular26, lv.STATE.DEFAULT) + # self.description.set_style_text_line_space(3, 0) + # self.description.align_to(self.air_gap, lv.ALIGN.OUT_BOTTOM_LEFT, 12, 16) air_gap_enabled = storage_device.is_airgap_mode() if air_gap_enabled: self.air_gap.add_state() - self.description.set_text( - _( - i18n_keys.CONTENT__BLUETOOTH_USB_AND_NFT_TRANSFER_FUNCTIONS_HAVE_BEEN_DISABLED - ) - ) + self.usb.disable() + self.ble.disable() + # self.description.set_text( + # _( + # i18n_keys.CONTENT__BLUETOOTH_USB_AND_NFT_TRANSFER_FUNCTIONS_HAVE_BEEN_DISABLED + # ) + # ) else: self.air_gap.clear_state() - self.description.set_text( - _( - i18n_keys.CONTENT__AFTER_ENABLING_THE_AIRGAP_BLUETOOTH_USB_AND_NFC_TRANSFER_WILL_BE_DISABLED_SIMULTANEOUSLY - ) - ) - self.air_gap.add_event_cb(self.on_event, lv.EVENT.VALUE_CHANGED, None) - self.air_gap.add_event_cb(self.on_event, lv.EVENT.READY, None) - self.air_gap.add_event_cb(self.on_event, lv.EVENT.CANCEL, None) + if storage_device.is_usb_enabled(): + self.usb.add_state() + else: + self.usb.clear_state() + if uart.is_ble_opened(): + self.ble.add_state() + else: + self.ble.clear_state() + # self.description.set_text( + # _( + # i18n_keys.CONTENT__AFTER_ENABLING_THE_AIRGAP_BLUETOOTH_USB_AND_NFC_TRANSFER_WILL_BE_DISABLED_SIMULTANEOUSLY + # ) + # ) + + self.container_advanced.add_event_cb( + self.on_event, lv.EVENT.VALUE_CHANGED, None + ) + self.container_advanced.add_event_cb(self.on_event, lv.EVENT.READY, None) + self.container_advanced.add_event_cb(self.on_event, lv.EVENT.CANCEL, None) # Danger Zone: Reset Device self.danger_zone = lv.label(self.content_area) self.danger_zone.set_size(456, lv.SIZE.CONTENT) @@ -7570,7 +7636,9 @@ def __init__(self, prev_scr=None): self.danger_zone.set_style_text_color(lv_colors.WHITE_2, lv.STATE.DEFAULT) self.danger_zone.set_style_text_font(font_GeistSemiBold30, lv.STATE.DEFAULT) self.danger_zone.set_text(_(i18n_keys.TITLE__DANGER_ZONE)) - self.danger_zone.align_to(self.description, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 40) + self.danger_zone.align_to( + self.container_advanced, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 40 + ) self.rest_device = ListItemBtn( self.content_area, _(i18n_keys.ITEM__RESET_DEVICE), @@ -7646,33 +7714,47 @@ def on_event(self, event_obj): enable=False, callback_obj=self.air_gap, ) + elif target == self.usb.switch: + if target.has_state(lv.STATE.CHECKED): + utils.enable_usb() + else: + utils.disable_usb() + elif target == self.ble.switch: + if target.has_state(lv.STATE.CHECKED): + utils.enable_ble() + else: + utils.disable_ble() elif code == lv.EVENT.READY: if not storage_device.is_airgap_mode(): - self.description.set_text( - _( - i18n_keys.CONTENT__BLUETOOTH_USB_AND_NFT_TRANSFER_FUNCTIONS_HAVE_BEEN_DISABLED - ) - ) - self.danger_zone.align_to( - self.description, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 40 - ) - self.rest_device.align_to( - self.danger_zone, lv.ALIGN.OUT_BOTTOM_MID, -12, 16 - ) + # self.description.set_text( + # _( + # i18n_keys.CONTENT__BLUETOOTH_USB_AND_NFT_TRANSFER_FUNCTIONS_HAVE_BEEN_DISABLED + # ) + # ) + # self.danger_zone.align_to( + # self.description, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 40 + # ) + # self.rest_device.align_to( + # self.danger_zone, lv.ALIGN.OUT_BOTTOM_MID, -12, 16 + # ) utils.enable_airgap_mode() + self.usb.disable() + self.ble.disable() else: - self.description.set_text( - _( - i18n_keys.CONTENT__AFTER_ENABLING_THE_AIRGAP_BLUETOOTH_USB_AND_NFC_TRANSFER_WILL_BE_DISABLED_SIMULTANEOUSLY - ) - ) - self.danger_zone.align_to( - self.description, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 40 - ) - self.rest_device.align_to( - self.danger_zone, lv.ALIGN.OUT_BOTTOM_MID, -12, 16 - ) + # self.description.set_text( + # _( + # i18n_keys.CONTENT__AFTER_ENABLING_THE_AIRGAP_BLUETOOTH_USB_AND_NFC_TRANSFER_WILL_BE_DISABLED_SIMULTANEOUSLY + # ) + # ) + # self.danger_zone.align_to( + # self.description, lv.ALIGN.OUT_BOTTOM_LEFT, 0, 40 + # ) + # self.rest_device.align_to( + # self.danger_zone, lv.ALIGN.OUT_BOTTOM_MID, -12, 16 + # ) utils.disable_airgap_mode() + self.usb.enable() + self.ble.enable() elif code == lv.EVENT.CANCEL: if storage_device.is_airgap_mode(): self.air_gap.add_state() diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index 57520fa0fd..5232efa6f8 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -7864,6 +7864,7 @@ class PolkadotSignTx(protobuf.MessageType): address_n: "list[int]" raw_tx: "bytes" network: "str" + prefix: "int | None" def __init__( self, @@ -7871,6 +7872,7 @@ def __init__( raw_tx: "bytes", network: "str", address_n: "list[int] | None" = None, + prefix: "int | None" = None, ) -> None: pass diff --git a/core/src/trezor/uart.py b/core/src/trezor/uart.py index f931b48758..968693cc16 100644 --- a/core/src/trezor/uart.py +++ b/core/src/trezor/uart.py @@ -2,7 +2,7 @@ from micropython import const from typing import TYPE_CHECKING -from storage import device +import storage.device as storage_device from trezor import config, io, log, loop, motor, utils, workflow from trezor.lvglui import StatusBar from trezor.ui import display @@ -118,8 +118,8 @@ async def handle_fingerprint(): warning_level = 3 elif isinstance(e, fingerprint.NotMatch): # increase failed count - device.finger_failed_count_incr() - failed_count = device.finger_failed_count() + storage_device.finger_failed_count_incr() + failed_count = storage_device.finger_failed_count() if failed_count >= utils.MAX_FP_ATTEMPTS: from trezor.lvglui.scrs.pinscreen import InputPin @@ -153,8 +153,8 @@ async def handle_fingerprint(): if __debug__: print(f"fingerprint match {match_id}") # motor.vibrate(motor.SUCCESS) - if device.is_passphrase_pin_enabled(): - device.set_passphrase_pin_enabled(False) + if storage_device.is_passphrase_pin_enabled(): + storage_device.set_passphrase_pin_enabled(False) # # 1. publish signal if fingerprints.has_takers(): if __debug__: @@ -181,10 +181,10 @@ async def handle_fingerprint(): async def handle_usb_state(): while True: try: - utils.AIRGAP_MODE_CHANGED = False + utils.USB_STATE_CHANGED = False usb_state = loop.wait(io.USB_STATE) state, enable = await usb_state - if enable is not None and not device.is_airgap_mode(): + if enable is not None and utils.is_usb_enabled(): import usb usb.bus.connect_ctrl(enable) @@ -209,9 +209,13 @@ async def handle_usb_state(): if utils.BATTERY_CAP: StatusBar.get_instance().set_battery_img(utils.BATTERY_CAP, False) _request_charging_status() - if not utils.AIRGAP_MODE_CHANGED: # not enable or disable airgap mode - usb_auto_lock = device.is_usb_lock_enabled() - if usb_auto_lock and device.is_initialized() and config.has_pin(): + if not utils.USB_STATE_CHANGED: # not enable or disable airgap mode + usb_auto_lock = storage_device.is_usb_lock_enabled() + if ( + usb_auto_lock + and storage_device.is_initialized() + and config.has_pin() + ): from trezor.lvglui.scrs import fingerprints from trezor.crypto import se_thd89 @@ -226,7 +230,7 @@ async def handle_usb_state(): elif not usb_auto_lock and not state: await safe_reloop(ack=False) else: - utils.AIRGAP_MODE_CHANGED = False + utils.USB_STATE_CHANGED = False base.reload_settings_from_storage() except Exception as exec: if __debug__: @@ -371,7 +375,7 @@ async def _deal_ble_pair(value): close_camera() flashled_close() - if not device.is_initialized(): + if not storage_device.is_initialized(): from trezor.lvglui.scrs.ble import PairForbiddenScreen PairForbiddenScreen() @@ -418,7 +422,7 @@ async def _deal_button_press(value: bytes) -> None: if res == _PRESS_SHORT: if display.backlight(): display.backlight(0) - if device.is_initialized(): + if storage_device.is_initialized(): if utils.is_initialization_processing(): return utils.AUTO_POWER_OFF = True @@ -450,7 +454,8 @@ async def _deal_button_press(value: bytes) -> None: close_camera() PowerOff( True - if not utils.is_initialization_processing() and device.is_initialized() + if not utils.is_initialization_processing() + and storage_device.is_initialized() else False ) await loop.sleep(200) @@ -543,7 +548,7 @@ async def _deal_pair_res(value: bytes) -> None: motor.vibrate(motor.ERROR) StatusBar.get_instance().show_ble(StatusBar.BLE_STATE_ENABLED) - if device.is_initialized(): + if storage_device.is_initialized(): if PENDING_PAIR_CODE is not None: PENDING_PAIR_FAILED = True if PAIR_ERROR_SCREEN is None or PAIR_ERROR_SCREEN.destroyed: @@ -552,7 +557,7 @@ async def _deal_pair_res(value: bytes) -> None: workflow.spawn(show_pairing_error()) else: motor.vibrate(motor.SUCCESS) - if device.is_initialized(): + if storage_device.is_initialized(): from trezor.ui.layouts import show_pairing_success workflow.spawn(show_pairing_success()) @@ -578,17 +583,17 @@ async def _deal_ble_status(value: bytes) -> None: return StatusBar.get_instance().show_ble(StatusBar.BLE_STATE_ENABLED) if config.is_unlocked(): - device.set_ble_status(enable=True) + storage_device.set_ble_status(enable=True) elif res == _BLE_STATUS_CLOSED: utils.BLE_CONNECTED = False - if not device.is_initialized(): + if not storage_device.is_initialized(): StatusBar.get_instance().show_ble(StatusBar.BLE_STATE_ENABLED) ctrl_ble(True) return BLE_ENABLED = False StatusBar.get_instance().show_ble(StatusBar.BLE_STATE_DISABLED) if config.is_unlocked(): - device.set_ble_status(enable=False) + storage_device.set_ble_status(enable=False) def _retrieve_flashled_brightness(value: bytes) -> None: @@ -731,9 +736,12 @@ def ctrl_ble(enable: bool) -> None: """Request to open or close ble. @param enable: True to open, False to close """ + global BLE_ENABLED if enable: + BLE_ENABLED = True BLE_CTRL.ctrl(0x81, b"\x01") else: + BLE_ENABLED = False BLE_CTRL.ctrl(0x81, b"\x02") @@ -849,7 +857,7 @@ def stop_mode(reset_timer: bool = False): wireless_charge = True utils.enter_lowpower( - reset_timer, device.get_autoshutdown_delay_ms(), lp_timer_enable + reset_timer, storage_device.get_autoshutdown_delay_ms(), lp_timer_enable ) if wireless_charge: fetch_battery_temperature() diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py index ca59b294ae..ff3d8b3f51 100644 --- a/core/src/trezor/utils.py +++ b/core/src/trezor/utils.py @@ -87,7 +87,7 @@ def board_version() -> str: CHARGE_WIRELESS_STATUS = CHARGE_WIRELESS_STOP CHARGE_ENABLE: bool | None = None CHARGING = False -AIRGAP_MODE_CHANGED = False +USB_STATE_CHANGED = False RESTART_MAIN_LOOP = False if __debug__: @@ -167,7 +167,7 @@ def turn_on_lcd_if_possible(timeouts_ms: int | None = None) -> bool: def lcd_resume(timeouts_ms: int | None = None) -> bool: from trezor.ui import display - from storage import device + import storage.device as storage_device from apps import base from trezor import config, uart @@ -176,12 +176,12 @@ def lcd_resume(timeouts_ms: int | None = None) -> bool: # if ChargingPromptScr.has_instance(): # ChargingPromptScr.get_instance().destroy() uart.ctrl_wireless_charge(False) - if display.backlight() != device.get_brightness() or timeouts_ms: + if display.backlight() != storage_device.get_brightness() or timeouts_ms: global AUTO_POWER_OFF from trezor.lvglui.scrs.homescreen import BacklightSetting if not BacklightSetting.page_is_visible(): - display.backlight(device.get_brightness()) + display.backlight(storage_device.get_brightness()) AUTO_POWER_OFF = False from trezor.lvglui.scrs import fingerprints @@ -206,14 +206,14 @@ async def internal_reloop(): async def turn_off_lcd(): from trezor.ui import display from trezor import loop, wire - from storage import device + import storage.device as storage_device if display.backlight(): global AUTO_POWER_OFF display.backlight(0) AUTO_POWER_OFF = True await wire.signal_ack() - if device.is_initialized(): + if storage_device.is_initialized(): global RESTART_MAIN_LOOP RESTART_MAIN_LOOP = True loop.clear() @@ -233,39 +233,81 @@ def is_low_battery(): return False -def disable_airgap_mode(): - from storage import device - from trezor import uart - from trezor.lvglui import StatusBar +def is_usb_enabled() -> bool: + import storage.device as storage_device + + airgap_enabled = storage_device.is_airgap_mode() + if airgap_enabled: + return False - global AIRGAP_MODE_CHANGED + return storage_device.is_usb_enabled() - device.enable_airgap_mode(False) - StatusBar.get_instance().show_air_gap_mode_tips(False) - uart.ctrl_ble(enable=True) - AIRGAP_MODE_CHANGED = True + +def disable_usb(persist: bool = True) -> None: + import usb + import storage.device as storage_device + + global USB_STATE_CHANGED + if persist: + storage_device.set_usb_status(False) + usb.bus.connect_ctrl(False) + USB_STATE_CHANGED = True + + +def enable_usb(restore: bool = False) -> None: import usb + import storage.device as storage_device - usb.bus.connect_ctrl(True) + global USB_STATE_CHANGED + if restore: + usb.bus.connect_ctrl(storage_device.is_usb_enabled()) + else: + storage_device.set_usb_status(True) + usb.bus.connect_ctrl(True) + USB_STATE_CHANGED = True -def enable_airgap_mode(): - from storage import device + +def disable_ble(persist_backup: bool = False) -> None: + import storage.device as storage_device + from trezor import uart + + if persist_backup: + storage_device.set_ble_status_backup(uart.is_ble_opened()) + uart.ctrl_ble(False) + + +def enable_ble(restore_backup: bool = False) -> None: from trezor import uart + import storage.device as storage_device + + if restore_backup: + uart.ctrl_ble(storage_device.ble_enabled_backup()) + else: + uart.ctrl_ble(True) + + +def disable_airgap_mode(): + import storage.device as storage_device from trezor.lvglui import StatusBar - global AIRGAP_MODE_CHANGED + storage_device.enable_airgap_mode(False) + StatusBar.get_instance().show_air_gap_mode_tips(False) + enable_ble(restore_backup=True) + enable_usb(restore=True) + + +def enable_airgap_mode(): + import storage.device as storage_device + from trezor.lvglui import StatusBar - device.enable_airgap_mode(True) + storage_device.enable_airgap_mode(True) StatusBar.get_instance().show_air_gap_mode_tips(True) - uart.ctrl_ble(enable=False) + disable_ble(persist_backup=True) + disable_usb(persist=False) from trezorio import nfc nfc.pwr_ctrl(False) - AIRGAP_MODE_CHANGED = True - import usb - - usb.bus.connect_ctrl(False) def show_app_guide(): diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index e422267d61..1d2478828a 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -9913,6 +9913,7 @@ class PolkadotSignTx(protobuf.MessageType): 1: protobuf.Field("address_n", "uint32", repeated=True, required=False), 2: protobuf.Field("raw_tx", "bytes", repeated=False, required=True), 3: protobuf.Field("network", "string", repeated=False, required=True), + 4: protobuf.Field("prefix", "uint32", repeated=False, required=False), } def __init__( @@ -9921,10 +9922,12 @@ def __init__( raw_tx: "bytes", network: "str", address_n: Optional[Sequence["int"]] = None, + prefix: Optional["int"] = None, ) -> None: self.address_n: Sequence["int"] = address_n if address_n is not None else [] self.raw_tx = raw_tx self.network = network + self.prefix = prefix class PolkadotSignedTx(protobuf.MessageType): diff --git a/tools/i18n.py b/tools/i18n.py index e99481ec3f..8ae82e98ae 100644 --- a/tools/i18n.py +++ b/tools/i18n.py @@ -98,7 +98,7 @@ def main(): while True: response = client.keys( LOKALISE_PROJECT_ID, - {"include_translations": 1, "limit": PAGE_SIZE, "page": page}, + {"include_translations": 1, "limit": PAGE_SIZE, "page": page, "replace_breaks": 1}, ) items = response.items if not items: