From fcf6fa85f0689cf6c97eba9001cbd1852f946c30 Mon Sep 17 00:00:00 2001 From: guowei0105 Date: Thu, 19 Dec 2024 11:42:27 +0800 Subject: [PATCH 1/3] add benfen support --- common/protob/messages-benfen.proto | 87 ++++ common/protob/messages.proto | 10 + core/SConscript.firmware | 3 +- core/embed/firmware/version.h | 2 +- core/src/all_modules.py | 14 + core/src/apps/benfen/__init__.py | 8 + core/src/apps/benfen/get_address.py | 41 ++ core/src/apps/benfen/helper.py | 57 +++ core/src/apps/benfen/layout.py | 54 +++ core/src/apps/benfen/sign_message.py | 47 +++ core/src/apps/benfen/sign_tx.py | 235 +++++++++++ core/src/apps/benfen/tx_parser.py | 432 ++++++++++++++++++++ core/src/apps/workflow_handlers.py | 8 + core/src/trezor/enums/MessageType.py | 8 + core/src/trezor/enums/__init__.py | 8 + core/src/trezor/lvglui/res/chain-benfen.png | Bin 0 -> 1496 bytes core/src/trezor/messages.py | 132 ++++++ python/src/trezorlib/benfen.py | 55 +++ python/src/trezorlib/cli/benfen.py | 90 ++++ python/src/trezorlib/cli/trezorctl.py | 3 + python/src/trezorlib/messages.py | 150 +++++++ 21 files changed, 1442 insertions(+), 2 deletions(-) create mode 100644 common/protob/messages-benfen.proto create mode 100755 core/src/apps/benfen/__init__.py create mode 100755 core/src/apps/benfen/get_address.py create mode 100755 core/src/apps/benfen/helper.py create mode 100755 core/src/apps/benfen/layout.py create mode 100755 core/src/apps/benfen/sign_message.py create mode 100755 core/src/apps/benfen/sign_tx.py create mode 100755 core/src/apps/benfen/tx_parser.py create mode 100644 core/src/trezor/lvglui/res/chain-benfen.png create mode 100644 python/src/trezorlib/benfen.py create mode 100644 python/src/trezorlib/cli/benfen.py mode change 100755 => 100644 python/src/trezorlib/cli/trezorctl.py diff --git a/common/protob/messages-benfen.proto b/common/protob/messages-benfen.proto new file mode 100644 index 000000000..29e224eb3 --- /dev/null +++ b/common/protob/messages-benfen.proto @@ -0,0 +1,87 @@ +syntax = "proto2"; +package hw.trezor.messages.benfen; + +// Sugar for easier handling in Java +option java_package = "com.satoshilabs.trezor.lib.protobuf"; +option java_outer_classname = "TrezorMessageSui"; + +/** + * Request: Address at the specified index + * @start + * @next BenfenAddress + */ +message BenfenGetAddress { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + optional bool show_display = 2; // optionally show on display before sending the result +} + +/** + * Response: Address for the given index + * @end + */ +message BenfenAddress { + optional string address = 1; // Benfen address as hex-encoded string +} + +/** + * Request: ask device to sign Sui transaction + * @start + * @next BenfenSignedTx/SuiTxRequest + */ +message BenfenSignTx { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + required bytes raw_tx = 2; // serialized raw transaction + optional bytes data_initial_chunk = 3 [default='']; // The initial data chunk (<= 1024 bytes) + optional bytes coin_type = 4; + optional uint32 data_length = 5; // Length of transaction payload +} + + +/** + * Response: signature for transaction + * @end + */ +message BenfenSignedTx { + required bytes public_key = 1; // public key for the private key used to sign tx + required bytes signature = 2; // the signature of the raw transaction +} + +/** + * Response: Device asks for more data from transaction payload, or returns the signature. + * If data_length is set, device awaits that many more bytes of payload. + * Otherwise, the signature fields contain the computed transaction signature. All three fields will be present. + * @end + * @next SuiTxAck + */ + message BenfenTxRequest { + optional uint32 data_length = 1; // Number of bytes being requested (<= 1024) + optional bytes public_key = 2; // public key for the private key used to sign tx + optional bytes signature = 3; // the signature of the raw transaction +} + +/** + * Request: Transaction payload data. + * @next BenfenTxRequest + */ + message BenfenTxAck { + required bytes data_chunk = 1; // Bytes from transaction payload (<= 1024 bytes) +} + +/** + * Request: Ask device to sign message + * @next SuiMessageSignature + * @next Failure + */ + message BenfenSignMessage { + repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node + required bytes message = 2; // message to be signed +} + +/** + * Response: Signed message + * @end + */ +message BenfenMessageSignature { + required bytes signature = 1; // signature of the message + required string address = 2; // address used to sign the message +} diff --git a/common/protob/messages.proto b/common/protob/messages.proto index c1718d3bd..62f6e20ac 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -554,6 +554,16 @@ enum MessageType { MessageType_AlephiumSignMessage = 12109 [(wire_in) = true]; MessageType_AlephiumMessageSignature = 12110 [(wire_out) = true]; + // benfen + MessageType_BenfenGetAddress = 12201 [(wire_in) = true]; + MessageType_BenfenAddress = 12202 [(wire_out) = true]; + MessageType_BenfenSignTx = 12203 [(wire_in) = true]; + MessageType_BenfenSignedTx = 12204 [(wire_out) = true]; + MessageType_BenfenSignMessage = 12205 [(wire_in) = true]; + MessageType_BenfenMessageSignature = 12206 [(wire_out) = true]; + MessageType_BenfenTxRequest = 12207 [(wire_out) = true]; + MessageType_BenfenTxAck = 12208 [(wire_in) = true]; + //onekey MessageType_DeviceBackToBoot = 903 [(wire_in) = true]; diff --git a/core/SConscript.firmware b/core/SConscript.firmware index b16f7f3b1..868d78a44 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -15,7 +15,7 @@ USE_THD89 = ARGUMENTS.get('USE_THD89', '0') FEATURE_FLAGS = { "RDI": True, "SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot) - "SYSTEM_VIEW": False, + "SYSTEM_VIEW": True, } CCFLAGS_MOD = '' @@ -967,6 +967,7 @@ if FROZEN: SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/scdo/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/alephium/*.py')) + SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/benfen/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/ur_registry/*.py')) SOURCE_PY.extend(Glob(SOURCE_PY_DIR + 'apps/ur_registry/*/*.py')) diff --git a/core/embed/firmware/version.h b/core/embed/firmware/version.h index fda6134cb..6cb8a9d33 100644 --- a/core/embed/firmware/version.h +++ b/core/embed/firmware/version.h @@ -9,7 +9,7 @@ #define FIX_VERSION_BUILD 99 #define ONEKEY_VERSION_MAJOR 4 -#define ONEKEY_VERSION_MINOR 11 +#define ONEKEY_VERSION_MINOR 12 #define ONEKEY_VERSION_PATCH 0 #define ONEKEY_VERSION_BUILD 0 diff --git a/core/src/all_modules.py b/core/src/all_modules.py index c942eb5c7..5cb305ca6 100644 --- a/core/src/all_modules.py +++ b/core/src/all_modules.py @@ -453,6 +453,20 @@ import apps.aptos.sign_tx apps.base import apps.base +apps.benfen +import apps.benfen +apps.benfen.get_address +import apps.benfen.get_address +apps.benfen.helper +import apps.benfen.helper +apps.benfen.layout +import apps.benfen.layout +apps.benfen.sign_message +import apps.benfen.sign_message +apps.benfen.sign_tx +import apps.benfen.sign_tx +apps.benfen.tx_parser +import apps.benfen.tx_parser apps.bitcoin import apps.bitcoin apps.bitcoin.addresses diff --git a/core/src/apps/benfen/__init__.py b/core/src/apps/benfen/__init__.py new file mode 100755 index 000000000..7ef47cbba --- /dev/null +++ b/core/src/apps/benfen/__init__.py @@ -0,0 +1,8 @@ +from apps.common.paths import PATTERN_BIP44_ED25519 + +CURVE = "ed25519" +SLIP44_ID = 728 + +PATTERN = PATTERN_BIP44_ED25519 +PRIMARY_COLOR = 0xCD4937 +ICON = "A:/res/chain-benfen.png" diff --git a/core/src/apps/benfen/get_address.py b/core/src/apps/benfen/get_address.py new file mode 100755 index 000000000..3bfaf62f8 --- /dev/null +++ b/core/src/apps/benfen/get_address.py @@ -0,0 +1,41 @@ +from typing import TYPE_CHECKING + +from trezor.lvglui.scrs import lv +from trezor.messages import BenfenAddress +from trezor.ui.layouts import show_address + +from apps.common import paths, seed +from apps.common.keychain import auto_keychain + +from . import ICON, PRIMARY_COLOR +from .helper import benfen_address_from_pubkey, try_convert_to_bfc_address + +if TYPE_CHECKING: + from trezor.messages import BenfenGetAddress + from trezor.wire import Context + from apps.common.keychain import Keychain + + +@auto_keychain(__name__) +async def get_address( + ctx: Context, msg: BenfenGetAddress, keychain: Keychain +) -> BenfenAddress: + await paths.validate_path(ctx, keychain, msg.address_n) + ctx.primary_color, ctx.icon_path = lv.color_hex(PRIMARY_COLOR), ICON + node = keychain.derive(msg.address_n) + pub_key_bytes = seed.remove_ed25519_prefix(node.public_key()) + address = benfen_address_from_pubkey(pub_key_bytes) + + bfc_address = try_convert_to_bfc_address(address) + if bfc_address: + address = bfc_address + + if msg.show_display: + path = paths.address_n_to_str(msg.address_n) + await show_address( + ctx, + address=address, + address_n=path, + network="BENFEN", + ) + return BenfenAddress(address=address) diff --git a/core/src/apps/benfen/helper.py b/core/src/apps/benfen/helper.py new file mode 100755 index 000000000..d283cea6f --- /dev/null +++ b/core/src/apps/benfen/helper.py @@ -0,0 +1,57 @@ +from ubinascii import hexlify + +from trezor.crypto.hashlib import blake2b, sha256 +from trezor.strings import format_amount + +INTENT_BYTES = b"\x00\x00\x00" +PERSONALMESSAGE_INTENT_BYTES = b"\x03\x00\x00" + + +def benfen_address_from_pubkey(pub_key_bytes: bytes) -> str: + payloads = b"\x00" + pub_key_bytes + h = blake2b(data=payloads, outlen=32).digest() + return f"0x{hexlify(h).decode()}" + + +def try_convert_to_bfc_address(sui_addr: str) -> str | None: + + if len(sui_addr) < 3 or not (sui_addr[0] == "0" and (sui_addr[1] in "xX")): + return None + + hex_part = sui_addr[2:] + if len(hex_part) == 0 or len(hex_part) > 64: + return None + + for c in hex_part: + if not (c.isdigit() or c in "abcdefABCDEF"): + return None + + padding = 64 - len(hex_part) + if padding > 0: + hex_part = "0" * padding + hex_part + + h = sha256() + h.update(hex_part.encode("utf-8")) + digest = h.digest() + + checksum = "".join([f"{b:02x}" for b in digest[:2]]) + + return "BFC" + hex_part + checksum + + +def uleb_encode(num: int) -> bytes: + arr = bytearray() + while num > 0: + val = num & 127 + num = num >> 7 + if num != 0: + val |= 128 + arr.append(val) + return arr + + +def format_benfen_amount(amount: int, currency_symbol: str = "BFC") -> str: + + decimals = 9 + formatted = format_amount(amount, decimals) + return f"{formatted} {currency_symbol}" diff --git a/core/src/apps/benfen/layout.py b/core/src/apps/benfen/layout.py new file mode 100755 index 000000000..2488bdf53 --- /dev/null +++ b/core/src/apps/benfen/layout.py @@ -0,0 +1,54 @@ +from trezor.enums import ButtonRequestType +from trezor.lvglui.i18n import gettext as _, keys as i18n_keys +from trezor.ui.layouts import should_show_details +from trezor.wire import Context + +from .helper import format_benfen_amount + + +async def require_show_overview( + ctx: Context, + to_addr: str, + value: int, + currency_symbol: str = "BFC", +) -> bool: + from trezor.strings import strip_amount + + result = await should_show_details( + ctx, + title=_(i18n_keys.TITLE__SEND_MULTILINE).format( + strip_amount(format_benfen_amount(value, currency_symbol))[0] + ), + address=to_addr, + br_code=ButtonRequestType.SignTx, + ) + return result + + +async def require_confirm_fee( + ctx: Context, + from_address: str, + to_address: str, + value: int, + gas_price: int, + gas_budget: int, + currency_symbol: str = "BFC", +) -> None: + from trezor.ui.layouts.lvgl.altcoin import confirm_total_ethereum + + total_amount = ( + format_benfen_amount(value + gas_price, currency_symbol) + if currency_symbol == "BFC" + else None + ) + fee_currency = currency_symbol if currency_symbol == "BFC" else "BFC" + + await confirm_total_ethereum( + ctx=ctx, + amount=format_benfen_amount(value, currency_symbol), + gas_price=None, + fee_max=format_benfen_amount(gas_price, fee_currency), + from_address=from_address, + to_address=to_address, + total_amount=total_amount, + ) diff --git a/core/src/apps/benfen/sign_message.py b/core/src/apps/benfen/sign_message.py new file mode 100755 index 000000000..f0ab2e000 --- /dev/null +++ b/core/src/apps/benfen/sign_message.py @@ -0,0 +1,47 @@ +from trezor import wire +from trezor.crypto.curve import ed25519 +from trezor.crypto.hashlib import blake2b +from trezor.lvglui.scrs import lv +from trezor.messages import BenfenMessageSignature, BenfenSignMessage + +from apps.common import paths, seed +from apps.common.keychain import Keychain, auto_keychain +from apps.common.signverify import decode_message + +from . import ICON, PRIMARY_COLOR +from .helper import ( + PERSONALMESSAGE_INTENT_BYTES, + benfen_address_from_pubkey, + try_convert_to_bfc_address, + uleb_encode, +) + + +@auto_keychain(__name__) +async def sign_message( + ctx: wire.Context, msg: BenfenSignMessage, keychain: Keychain +) -> BenfenMessageSignature: + + await paths.validate_path(ctx, keychain, msg.address_n) + + node = keychain.derive(msg.address_n) + pub_key_bytes = seed.remove_ed25519_prefix(node.public_key()) + address = benfen_address_from_pubkey(pub_key_bytes) + bfc_address = try_convert_to_bfc_address(address) + if bfc_address is None: + raise wire.DataError("bfc_address is none") + + len_bytes = uleb_encode(len(msg.message)) + intentMessage = PERSONALMESSAGE_INTENT_BYTES + len_bytes + msg.message + + from trezor.ui.layouts import confirm_signverify + + ctx.primary_color, ctx.icon_path = lv.color_hex(PRIMARY_COLOR), ICON + await confirm_signverify( + ctx, "Benfen", decode_message(msg.message), bfc_address, False + ) + + signature = ed25519.sign( + node.private_key(), blake2b(data=intentMessage, outlen=32).digest() + ) + return BenfenMessageSignature(signature=signature, address=bfc_address) diff --git a/core/src/apps/benfen/sign_tx.py b/core/src/apps/benfen/sign_tx.py new file mode 100755 index 000000000..1f737c81a --- /dev/null +++ b/core/src/apps/benfen/sign_tx.py @@ -0,0 +1,235 @@ +import binascii +from typing import Tuple + +from trezor import wire +from trezor.crypto.curve import ed25519 +from trezor.crypto.hashlib import blake2b +from trezor.lvglui.scrs import lv +from trezor.messages import BenfenSignedTx, BenfenSignTx, BenfenTxAck, BenfenTxRequest +from trezor.ui.layouts import confirm_blind_sign_common, confirm_final + +from apps.common import paths, seed +from apps.common.keychain import Keychain, auto_keychain + +from . import ICON, PRIMARY_COLOR +from .helper import INTENT_BYTES, benfen_address_from_pubkey, try_convert_to_bfc_address +from .layout import require_confirm_fee, require_show_overview +from .tx_parser import TransactionParser + + +async def process_transaction( + ctx, address: str, tx_bytes: bytes, coin_type: bytes +) -> bytes: + parser = TransactionParser() + + intent = tx_bytes[:3] + if INTENT_BYTES != intent: + raise wire.DataError("Invalid raw tx") + + if coin_type: + try: + if not all(c < 128 for c in coin_type): + raise wire.DataError("Invalid coin_type encoding") + currency_symbol = coin_type.decode("ascii") + if currency_symbol and "::" in currency_symbol: + currency_symbol = currency_symbol.split("::")[-1] + ALLOWED_TOKENS = {"BJPY", "BUSD", "LONG", "BF_USDC", "BF_USDT", "BFC"} + if currency_symbol not in ALLOWED_TOKENS: + raise wire.DataError("Unsupported token type") + except UnicodeDecodeError: + raise wire.DataError("coin_type must be ASCII encoded") + else: + await confirm_blind_sign_common(ctx, address, tx_bytes) + return blake2b(data=tx_bytes, outlen=32).digest() + + parsed_tx = parser.parse_tx(tx_bytes) + if parsed_tx is None: + await confirm_blind_sign_common(ctx, address, tx_bytes) + return blake2b(data=tx_bytes, outlen=32).digest() + + is_valid = validate_transaction(parsed_tx) + + if is_valid: + ( + amount_raw, + recipient_bfc, + sender_bfc, + max_gas_fee, + ) = parse_transaction(parsed_tx) + show_details = await require_show_overview( + ctx, + recipient_bfc, + amount_raw, + currency_symbol, + ) + if show_details: + await require_confirm_fee( + ctx, + from_address=sender_bfc, + to_address=recipient_bfc, + value=amount_raw, + gas_price=max_gas_fee, + gas_budget=max_gas_fee, + currency_symbol=currency_symbol, + ) + else: + await confirm_blind_sign_common(ctx, address, tx_bytes) + return blake2b(data=tx_bytes, outlen=32).digest() + + +@auto_keychain(__name__) +async def sign_tx( + ctx: wire.Context, msg: BenfenSignTx, keychain: Keychain +) -> BenfenSignedTx: + + await paths.validate_path(ctx, keychain, msg.address_n) + + node = keychain.derive(msg.address_n) + pub_key_bytes = seed.remove_ed25519_prefix(node.public_key()) + hex_address = benfen_address_from_pubkey(pub_key_bytes) + address = try_convert_to_bfc_address(hex_address) + if address is None: + raise wire.DataError("Invalid address format") + ctx.primary_color, ctx.icon_path = lv.color_hex(PRIMARY_COLOR), ICON + coin_type = msg.coin_type if msg.coin_type is not None else b"" + if msg.data_length and msg.data_length > 0: + data = await process_data_chunks(ctx, msg) + hash = await process_transaction(ctx, address, data, coin_type) + else: + hash = await process_transaction(ctx, address, msg.raw_tx, coin_type) + + await confirm_final(ctx, "BENFEN") + + signature = ed25519.sign(node.private_key(), hash) + return BenfenSignedTx(public_key=pub_key_bytes, signature=signature) + + +async def process_data_chunks(ctx, msg) -> bytes: + if INTENT_BYTES != msg.data_initial_chunk[:3]: + raise wire.DataError("Invalid raw tx") + + data = bytearray(msg.data_initial_chunk) + data_left = msg.data_length - len(msg.data_initial_chunk) + while data_left > 0: + resp = await send_request_chunk(ctx, data_left) + data_left -= len(resp.data_chunk) + data += resp.data_chunk + + return bytes(data) + + +async def send_request_chunk(ctx: wire.Context, data_left: int) -> BenfenTxAck: + req = BenfenTxRequest() + if data_left <= 1024: + req.data_length = data_left + else: + req.data_length = 1024 + return await ctx.call(req, BenfenTxAck) + + +def parse_transaction(parsed_tx: dict) -> Tuple[int, str, str, int]: + tx_kind = parsed_tx["V1"]["TransactionKind"]["ProgrammableTransaction"] + inputs = tx_kind["Inputs"] + commands = tx_kind["Commands"] + amount_input_index = None + recipient_input_index = None + + for cmd in commands: + if cmd["type"] == "TransferObjects": + address_data = cmd["data"]["address"] + if address_data["type"] == "Input": + recipient_input_index = address_data["index"] + + object_data = cmd["data"]["objects"][0] + if object_data["type"] == "NestedResult": + command_index = object_data["index"][0] + referenced_command = commands[command_index] + if referenced_command["type"] == "SplitCoin": + amount_input_index = referenced_command["data"]["amounts"][0][ + "index" + ] + elif object_data["type"] == "Input": + amount_input_index = object_data["index"] + elif object_data["type"] == "Result": + command_index = object_data["index"] + referenced_command = commands[command_index] + if referenced_command["type"] == "SplitCoin": + amount_input_index = referenced_command["data"]["amounts"][0][ + "index" + ] + + if amount_input_index is None or recipient_input_index is None: + raise wire.DataError("Required commands not found") + + try: + amount_hex = inputs[amount_input_index]["Pure"] + amount_raw = int.from_bytes(binascii.unhexlify(amount_hex), "little") + except Exception: + raise wire.DataError("Invalid amount format") + + try: + recipient_hex = inputs[recipient_input_index]["Pure"] + recipient = "0x" + recipient_hex + recipient_bfc = try_convert_to_bfc_address(recipient) + except Exception: + raise wire.DataError("Invalid recipient address") + + sender = tx_kind["Sender"]["Address"] + sender_bfc = try_convert_to_bfc_address(sender) + + gas_data = parsed_tx["V1"]["GasData"] + gas_budget = gas_data["budget"] + + if not isinstance(amount_raw, int): + raise wire.DataError("Invalid amount type") + if not isinstance(recipient_bfc, str): + raise wire.DataError("Invalid recipient address type") + if not isinstance(sender_bfc, str): + raise wire.DataError("Invalid sender address type") + if not isinstance(gas_budget, int): + raise wire.DataError("Invalid gas budget type") + + return amount_raw, recipient_bfc, sender_bfc, gas_budget + + +def validate_transaction(parsed_tx: dict) -> bool: + + if "V1" not in parsed_tx: + return False + tx_data = parsed_tx["V1"] + if "TransactionKind" not in tx_data: + return False + + if "ProgrammableTransaction" not in tx_data["TransactionKind"]: + return False + + tx_kind = tx_data["TransactionKind"]["ProgrammableTransaction"] + required_fields = ["Inputs", "Commands", "Sender"] + missing_fields = [field for field in required_fields if field not in tx_kind] + if missing_fields: + return False + + commands = tx_kind["Commands"] + if not commands: + return False + + transfer_objects_count = sum( + 1 for cmd in commands if cmd.get("type") == "TransferObjects" + ) + if transfer_objects_count != 1: + return False + + required_commands = {"SplitCoin", "TransferObjects"} + found_commands = {cmd["type"] for cmd in commands if "type" in cmd} + + if not required_commands.issubset(found_commands): + return False + + if "GasData" not in tx_data: + return False + + gas_data = tx_data["GasData"] + if not all(key in gas_data for key in ["budget", "price"]): + return False + + return True diff --git a/core/src/apps/benfen/tx_parser.py b/core/src/apps/benfen/tx_parser.py new file mode 100755 index 000000000..ac4966ade --- /dev/null +++ b/core/src/apps/benfen/tx_parser.py @@ -0,0 +1,432 @@ +import binascii + +ADDRESS_LENGTH = 32 +SEQUENCE_NUMBER_LENGTH = 8 +DIGEST_LENGTH = 32 + +INPUT_TYPE_PURE = 0x00 +INPUT_TYPE_OBJECT = 0x01 + +ARG_TYPE_INPUT = 0x01 +ARG_TYPE_GAS_COIN = 0x00 +ARG_TYPE_RESULT = 0x02 +ARG_TYPE_NESTED_RESULT = 0x03 + + +class ObjectArgType: + IMM_OR_OWNED_OBJECT = 0x00 + + +class CommandType: + MOVE_CALL = 0 + TRANSFER_OBJECTS = 1 + SPLIT_COIN = 2 + MERGE_COINS = 3 + PUBLISH = 4 + MAKE_MOVE_VEC = 5 + UPGRADE = 6 + + +class Address: + def __init__(self, data): + self.data = data + + def to_hex(self): + return f"0x{binascii.hexlify(self.data).decode()}" + + +class ObjectReference: + def __init__(self, address, sequence_number, digest): + self.address = address + self.sequence_number = sequence_number + self.digest = digest + + def to_dict(self): + return { + "objectId": self.address.to_hex(), + "sequenceNumber": self.sequence_number, + "digest": binascii.hexlify(self.digest).decode(), + } + + +class PureData: + def __init__(self, data): + self.data = data + + +class ParsedInput: + def __init__(self, kind, value, index, type): + self.kind = kind + self.value = value + self.index = index + self.type = type + + +class Argument: + def __init__(self, arg_type, index): + self.arg_type = arg_type + self.index = index + + +class Command: + def __init__(self, type, data): + self.type = type + self.data = data + + +class BCSParser: + def __init__(self, data): + self.data = data + self.cursor = 0 + + def read_bytes(self, length: int) -> bytes | None: + if self.cursor + length > len(self.data): + return None + result = self.data[self.cursor : self.cursor + length] + self.cursor += length + return result + + def read_u8(self) -> int | None: + data = self.read_bytes(1) + if data is None: + return None + return int.from_bytes(data, "little") + + def read_u64(self) -> int | None: + data = self.read_bytes(8) + if data is None: + return None + return int.from_bytes(data, "little") + + def read_address(self) -> Address | None: + data = self.read_bytes(ADDRESS_LENGTH) + if data is None: + return None + return Address(data) + + def read_object_reference(self) -> ObjectReference | None: + address = self.read_address() + if address is None: + return None + + sequence_number = self.read_u64() + if sequence_number is None: + return None + + digest_length = self.read_u8() + if digest_length is None or digest_length != DIGEST_LENGTH: + return None + + digest = self.read_bytes(DIGEST_LENGTH) + if digest is None: + return None + + if address.data is None: + return None + + return ObjectReference(address, sequence_number, digest) + + def read_pure_data(self) -> PureData | None: + length = self.read_u8() + if length is None: + return None + data = self.read_bytes(length) + if data is None: + return None + return PureData(list(data)) + + def read_argument(self) -> dict | None: + arg_type = self.read_u8() + if arg_type is None: + return None + + if arg_type == ARG_TYPE_INPUT: + data = self.read_bytes(2) + if data is None: + return None + index = int.from_bytes(data, "little") + return {"type": "Input", "index": index} + elif arg_type == ARG_TYPE_GAS_COIN: + return {"type": "GasCoin"} + elif arg_type == ARG_TYPE_RESULT: + data = self.read_bytes(2) + if data is None: + return None + index = int.from_bytes(data, "little") + return {"type": "Result", "index": index} + elif arg_type == ARG_TYPE_NESTED_RESULT: + data1 = self.read_bytes(2) + if data1 is None: + return None + data2 = self.read_bytes(2) + if data2 is None: + return None + index1 = int.from_bytes(data1, "little") + index2 = int.from_bytes(data2, "little") + return {"type": "NestedResult", "index": [index1, index2]} + else: + return None + + def read_argument_vector(self) -> list | None: + count = self.read_u8() + if count is None: + return None + + arguments = [] + for _ in range(count): + arg = self.read_argument() + if arg is None: + return None + arguments.append(arg) + + return arguments + + def read_split_coin_command(self) -> dict | None: + from_coin = self.read_argument() + if from_coin is None: + return None + + amount_count = self.read_u8() + if amount_count is None: + return None + + amounts = [] + for _ in range(amount_count): + amount = self.read_argument() + if amount is None: + return None + amounts.append(amount) + + return {"type": "SplitCoin", "data": {"coin": from_coin, "amounts": amounts}} + + def read_transfer_objects_command(self) -> dict | None: + objects_count = self.read_u8() + if objects_count is None: + return None + + objects = [] + for _ in range(objects_count): + obj = self.read_argument() + if obj is None: + return None + objects.append(obj) + + if len(objects) != 1: + return None + + address = self.read_argument() + if address is None: + return None + + return { + "type": "TransferObjects", + "data": {"objects": objects, "address": address}, + } + + def read_merge_coins_command(self) -> dict | None: + + to_coin = self.read_argument() + if to_coin is None: + return None + + from_coins_count = self.read_u8() + if from_coins_count is None: + return None + + from_coins = [] + for _ in range(from_coins_count): + coin = self.read_argument() + if coin is None: + return None + from_coins.append(coin) + + return { + "type": "MergeCoins", + "data": {"to_coin": to_coin, "from_coins": from_coins}, + } + + def read_commands(self) -> list | None: + command_count = self.read_u8() + if command_count is None: + return None + + commands = [] + for _ in range(command_count): + command_type = self.read_u8() + if command_type is None: + return None + + command = None + if command_type == CommandType.SPLIT_COIN: + command = self.read_split_coin_command() + elif command_type == CommandType.TRANSFER_OBJECTS: + command = self.read_transfer_objects_command() + elif command_type == CommandType.MERGE_COINS: + command = self.read_merge_coins_command() + else: + return None + + if command is None: + return None + commands.append(command) + + return commands + + +def parse_transaction_inputs(parser): + input_count = parser.read_u8() + + inputs = [] + + for i in range(input_count): + input_type = parser.read_u8() + if input_type == INPUT_TYPE_OBJECT: + sub_type = parser.read_u8() + if sub_type != ObjectArgType.IMM_OR_OWNED_OBJECT: + return None + obj_ref = parser.read_object_reference() + inputs.append( + ParsedInput( + kind="Input", + value={ + "type": "ImmOrOwnedObject", + "objectId": f"0x{binascii.hexlify(obj_ref.address.data).decode()}", + "sequenceNumber": obj_ref.sequence_number, + "digest": binascii.hexlify(obj_ref.digest).decode(), + }, + index=i, + type="object", + ) + ) + + elif input_type == INPUT_TYPE_PURE: + pure_data = parser.read_pure_data() + inputs.append( + ParsedInput( + kind="Input", value={"Pure": pure_data.data}, index=i, type="pure" + ) + ) + else: + return None + + return inputs + + +def parse_gas_data(parser): + payment_count = parser.read_u8() + payments = [] + for _ in range(payment_count): + obj_ref = parser.read_object_reference() + payments.append( + { + "objectId": obj_ref.address.to_hex(), + "sequenceNumber": obj_ref.sequence_number, + } + ) + owner_bytes = parser.read_bytes(ADDRESS_LENGTH) + owner = f"0x{binascii.hexlify(owner_bytes).decode()}" + price = parser.read_u64() + budget = parser.read_u64() + return {"payment": payments, "owner": owner, "price": price, "budget": budget} + + +def parse_transaction_expiration(parser): + expiration_type = parser.read_u8() + if expiration_type == 0: + return {"type": "None", "value": None} + elif expiration_type == 1: + epoch = parser.read_u64() + return {"type": "Epoch", "value": epoch} + else: + return None + + +def parse_transaction(hex_data): + try: + data = binascii.unhexlify(hex_data) + parser = BCSParser(data) + + version = parser.read_u8() + if version is None: + return None + + parser.read_bytes(3) + kind_type = parser.read_u8() + if kind_type is None: + return None + + inputs = parse_transaction_inputs(parser) + if inputs is None: + return None + + commands = parser.read_commands() + if commands is None: + return None + + sender_bytes = parser.read_bytes(ADDRESS_LENGTH) + if sender_bytes is None: + return None + sender = "0x" + binascii.hexlify(sender_bytes).decode() + + gas_data = parse_gas_data(parser) + if gas_data is None: + return None + + expiration = parse_transaction_expiration(parser) + + return { + "version": version, + "kind_type": hex(kind_type), + "sender": sender, + "inputs": inputs, + "commands": commands, + "gas_data": gas_data, + "expiration": expiration, + } + except Exception: + return None + + +class TransactionParser: + def parse_tx(self, tx_hex): + try: + if isinstance(tx_hex, bytes): + tx_hex = binascii.hexlify(tx_hex).decode() + if tx_hex.startswith("0x"): + tx_hex = tx_hex[2:] + + result = parse_transaction(tx_hex) + + if result is None: + return None + + formatted_inputs = [] + for _, input in enumerate(result["inputs"]): + try: + if input.type == "pure": + hex_value = binascii.hexlify( + bytes(input.value["Pure"]) + ).decode() + formatted_inputs.append({"Pure": hex_value}) + else: + formatted_inputs.append(input.value) + except Exception: + return None + + final_result = { + "V1": { + "TransactionKind": { + "ProgrammableTransaction": { + "Inputs": formatted_inputs, + "Commands": result["commands"], + "Sender": {"Address": result["sender"]}, + } + }, + "GasData": result["gas_data"], + "Expiration": result["expiration"], + } + } + return final_result + + except Exception: + return None diff --git a/core/src/apps/workflow_handlers.py b/core/src/apps/workflow_handlers.py index bc079bf36..3fb7665dd 100644 --- a/core/src/apps/workflow_handlers.py +++ b/core/src/apps/workflow_handlers.py @@ -363,6 +363,14 @@ def find_message_handler_module(msg_type: int) -> str: if msg_type == MessageType.AlephiumSignMessage: return "apps.alephium.sign_message" + # benfen + if msg_type == MessageType.BenfenGetAddress: + return "apps.benfen.get_address" + if msg_type == MessageType.BenfenSignTx: + return "apps.benfen.sign_tx" + if msg_type == MessageType.BenfenSignMessage: + return "apps.benfen.sign_message" + raise ValueError diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index 00657d3ec..6008dc807 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -384,6 +384,14 @@ AlephiumBytecodeAck = 12108 AlephiumSignMessage = 12109 AlephiumMessageSignature = 12110 + BenfenGetAddress = 12201 + BenfenAddress = 12202 + BenfenSignTx = 12203 + BenfenSignedTx = 12204 + BenfenSignMessage = 12205 + BenfenMessageSignature = 12206 + BenfenTxRequest = 12207 + BenfenTxAck = 12208 DeviceBackToBoot = 903 RebootToBoardloader = 904 DeviceInfoSettings = 10001 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index eee590530..86a347dd1 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -402,6 +402,14 @@ class MessageType(IntEnum): AlephiumBytecodeAck = 12108 AlephiumSignMessage = 12109 AlephiumMessageSignature = 12110 + BenfenGetAddress = 12201 + BenfenAddress = 12202 + BenfenSignTx = 12203 + BenfenSignedTx = 12204 + BenfenSignMessage = 12205 + BenfenMessageSignature = 12206 + BenfenTxRequest = 12207 + BenfenTxAck = 12208 DeviceBackToBoot = 903 RebootToBoardloader = 904 DeviceInfoSettings = 10001 diff --git a/core/src/trezor/lvglui/res/chain-benfen.png b/core/src/trezor/lvglui/res/chain-benfen.png new file mode 100644 index 0000000000000000000000000000000000000000..ed380e60a51cc8e08055f9aff377d9a77440f020 GIT binary patch literal 1496 zcmV;}1t`EsR7Cvr^X8945Y zje_>v+VauO+=6!b>E_*nb?v*f*>-7OC4q)pR(UnE(Q zZEP@*bb0^hYH{IAv9@uPeE-4;TxRZQq>&+5vYT{ywTKh?mn6>fW%?{54@s2HV`?D2 zdNO+$g=b3;FbU0{g(_s%3+g3)5(P-imI=i%zpqS`;{*a^vL*EpE|Y4B!gv#qDLi=; z5C)ng#k&!}5|^q44I+*Z8Zo7htupi5A-%@T0yRWcv)-=~pwV=SF{UUDQHk6dJsapS zr5xSYbHN6xAoqQBwPpi?VpwEG4euJP*nkXJl_~Yn4H{dDBt@96Q(Jt;n~&(bY7l9t z1~+FgA8S1EYe=K-Vs~JS9oEi-;X78Z`!1`Ro!FQXc3EL_>gOjrqClbfs{q{kDlGB| zKn0i3!vJe6kroK5f?31+=9gjy_D=63iebwKY_J2R#-OH|>-B)rh_RX#&aZQ*Gzv~tSk_1d`d)MXh-+G?wVW9JrY0gJQ3 zPQbP}qCuOIQ8NuFz>cLxDZ>Sm!73S3)na@c>L}k_rfiK7xgjYrU#Rff_i{J zS}5;H!4xD%2f{cVxMdTAxx$H5gCkJh(}G!$7`}5MT?w%_Icn zI@k`jOvo8Rgb%I{LtJ&(NO%cd00fyVR-XJn3@W|Cv}U}>8X8pzTgjdQ-kf=ihs^Nv zIhAqO9$j;j@i8h{tcIf6QBXly6O)BY0e0d&bbBR1-bf|KXVurfWe6XvU>||)6Jbs6 zZjiD(=ivAU3<;oukty7*zGEl)i*6#JC@?Z9x$*Qxn0VLPz6&Z&nH9;Lri1Mu3HbsQ zr%dL!hv?y-A$R`}J{6GR!|Cp+t*{gXGCr>ys80oGKih61;LXtF6$3gx#M*Rgzsg$} zf$SPgm?o`5eHc`^SLGcXxa}G>G)erBBFQ^V*L3pUD|@yEu1D7!NoEj1BXftl!>(_R|VBdDv?5h@{aod}Y9aJ2(9fC6W>YU%XcuKcUn3 zDTs;eqi<^B6K-!}V&=sw-)#nnpsUwtz5>gw<+a9`Zspy}nj@};@4_C>y-zTg+~QFm zG`1iTpy7JR0X>pG>cig#A+m@FhQc=qJJ( TypeGuard["AptosMessagePayload"]: return isinstance(msg, cls) + class BenfenGetAddress(protobuf.MessageType): + address_n: "list[int]" + show_display: "bool | None" + + def __init__( + self, + *, + address_n: "list[int] | None" = None, + show_display: "bool | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["BenfenGetAddress"]: + return isinstance(msg, cls) + + class BenfenAddress(protobuf.MessageType): + address: "str | None" + + def __init__( + self, + *, + address: "str | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["BenfenAddress"]: + return isinstance(msg, cls) + + class BenfenSignTx(protobuf.MessageType): + address_n: "list[int]" + raw_tx: "bytes" + data_initial_chunk: "bytes" + coin_type: "bytes | None" + data_length: "int | None" + + def __init__( + self, + *, + raw_tx: "bytes", + address_n: "list[int] | None" = None, + data_initial_chunk: "bytes | None" = None, + coin_type: "bytes | None" = None, + data_length: "int | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["BenfenSignTx"]: + return isinstance(msg, cls) + + class BenfenSignedTx(protobuf.MessageType): + public_key: "bytes" + signature: "bytes" + + def __init__( + self, + *, + public_key: "bytes", + signature: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["BenfenSignedTx"]: + return isinstance(msg, cls) + + class BenfenTxRequest(protobuf.MessageType): + data_length: "int | None" + public_key: "bytes | None" + signature: "bytes | None" + + def __init__( + self, + *, + data_length: "int | None" = None, + public_key: "bytes | None" = None, + signature: "bytes | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["BenfenTxRequest"]: + return isinstance(msg, cls) + + class BenfenTxAck(protobuf.MessageType): + data_chunk: "bytes" + + def __init__( + self, + *, + data_chunk: "bytes", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["BenfenTxAck"]: + return isinstance(msg, cls) + + class BenfenSignMessage(protobuf.MessageType): + address_n: "list[int]" + message: "bytes" + + def __init__( + self, + *, + message: "bytes", + address_n: "list[int] | None" = None, + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["BenfenSignMessage"]: + return isinstance(msg, cls) + + class BenfenMessageSignature(protobuf.MessageType): + signature: "bytes" + address: "str" + + def __init__( + self, + *, + signature: "bytes", + address: "str", + ) -> None: + pass + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["BenfenMessageSignature"]: + return isinstance(msg, cls) + class BinanceGetAddress(protobuf.MessageType): address_n: "list[int]" show_display: "bool | None" diff --git a/python/src/trezorlib/benfen.py b/python/src/trezorlib/benfen.py new file mode 100644 index 000000000..99f40b07a --- /dev/null +++ b/python/src/trezorlib/benfen.py @@ -0,0 +1,55 @@ +# This file is part of the OneKey project, https://onekey.so/ +# +# Copyright (C) 2021 OneKey Team +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see . + + +from typing import TYPE_CHECKING, AnyStr + +from . import messages +from .tools import expect, prepare_message_bytes + +if TYPE_CHECKING: + from .client import TrezorClient + from .tools import Address + from .protobuf import MessageType + + +@expect(messages.BenfenAddress, field="address", ret_type=str) +def get_address( + client: "TrezorClient", address_n: "Address", show_display: bool = False +) -> "MessageType": + return client.call( + messages.BenfenGetAddress(address_n=address_n, show_display=show_display) + ) + + +@expect(messages.BenfenSignedTx) +def sign_tx(client: "TrezorClient", address_n: "Address", raw_tx: bytes,coin_type: bytes ): + return client.call(messages.BenfenSignTx(address_n=address_n, raw_tx=raw_tx,coin_type=coin_type)) + + +@expect(messages.BenfenMessageSignature) +def sign_message( + client: "TrezorClient", + n: "Address", + message: AnyStr, +) -> "MessageType": + return client.call( + messages.BenfenSignMessage( + address_n=n, + message=prepare_message_bytes(message), + ) + ) diff --git a/python/src/trezorlib/cli/benfen.py b/python/src/trezorlib/cli/benfen.py new file mode 100644 index 000000000..370ed81e7 --- /dev/null +++ b/python/src/trezorlib/cli/benfen.py @@ -0,0 +1,90 @@ +# This file is part of the OneKey project, https://onekey.so/ +# +# Copyright (C) 2021 OneKey Team +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see . + +from typing import TYPE_CHECKING + +import click +import base64 + +from .. import benfen, tools +from . import with_client + +if TYPE_CHECKING: + from ..client import TrezorClient + +PATH_HELP = "BIP-32 path, e.g. m/44'/728'/0'/0'/0'" + + +@click.group(name="benfen") +def cli(): + """Benfen commands.""" + + +@cli.command() +@click.option("-n", "--address", required=True, help=PATH_HELP) +@click.option("-d", "--show-display", is_flag=True) +@with_client +def get_address(client: "TrezorClient", address: str, show_display: bool) -> str: + """Get Benfen address in hex encoding.""" + address_n = tools.parse_path(address) + return benfen.get_address(client, address_n, show_display) + + +@cli.command() +@click.option("-n", "--address", required=True, help=PATH_HELP) +@click.argument("message") +@click.option("-c", "--coin_type", help="coin type", default=None) +@with_client +def sign_raw_tx(client: "TrezorClient", address: str, message: str, coin_type: str | None): + """Sign a base64 encoded of the transaction data.""" + address_n = tools.parse_path(address) + decoded_message = base64.b64decode(message) + + decoded_coin_type = base64.b64decode(coin_type) if coin_type else None + + resp = benfen.sign_tx( + client, + address_n, + decoded_message, + decoded_coin_type + ) + result = { + "public_key": f"0x{resp.public_key.hex()}", + "signature": f"0x{resp.signature.hex()}", + } + return result + + + +@cli.command() +@click.option("-n", "--address", required=True, help=PATH_HELP) +@click.argument("message") +@with_client +def sign_message( + client: "TrezorClient", + address: str, + message: str, +) -> dict: + """Sign message with Benfen address.""" + address_n = tools.parse_path(address) + ret = benfen.sign_message(client, address_n, message) + output = { + "message": message, + "address": ret.address, + "signature": ret.signature.hex(), + } + return output diff --git a/python/src/trezorlib/cli/trezorctl.py b/python/src/trezorlib/cli/trezorctl.py old mode 100755 new mode 100644 index b278fda69..7330bc50a --- a/python/src/trezorlib/cli/trezorctl.py +++ b/python/src/trezorlib/cli/trezorctl.py @@ -67,6 +67,7 @@ ton, scdo, alephium, + benfen, ) F = TypeVar("F", bound=Callable) @@ -115,6 +116,7 @@ "ton": ton.cli, "scdo": scdo.cli, "alephium": alephium.cli, + "benfen": benfen.cli, # firmware aliases: "fw": firmware.cli, "update-firmware": firmware.update, @@ -474,6 +476,7 @@ def wait_for_emulator(obj: TrezorConnection, timeout: float) -> None: cli.add_command(ton.cli) cli.add_command(scdo.cli) cli.add_command(alephium.cli) +cli.add_command(benfen.cli) # diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index d3c35d8ee..19674ce54 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -410,6 +410,14 @@ class MessageType(IntEnum): AlephiumBytecodeAck = 12108 AlephiumSignMessage = 12109 AlephiumMessageSignature = 12110 + BenfenGetAddress = 12201 + BenfenAddress = 12202 + BenfenSignTx = 12203 + BenfenSignedTx = 12204 + BenfenSignMessage = 12205 + BenfenMessageSignature = 12206 + BenfenTxRequest = 12207 + BenfenTxAck = 12208 DeviceBackToBoot = 903 RebootToBoardloader = 904 DeviceInfoSettings = 10001 @@ -1161,6 +1169,148 @@ def __init__( self.application = application +class BenfenGetAddress(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 12201 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False), + 2: protobuf.Field("show_display", "bool", repeated=False, required=False), + } + + def __init__( + self, + *, + address_n: Optional[Sequence["int"]] = None, + show_display: Optional["bool"] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.show_display = show_display + + +class BenfenAddress(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 12202 + FIELDS = { + 1: protobuf.Field("address", "string", repeated=False, required=False), + } + + def __init__( + self, + *, + address: Optional["str"] = None, + ) -> None: + self.address = address + + +class BenfenSignTx(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 12203 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False), + 2: protobuf.Field("raw_tx", "bytes", repeated=False, required=True), + 3: protobuf.Field("data_initial_chunk", "bytes", repeated=False, required=False), + 4: protobuf.Field("coin_type", "bytes", repeated=False, required=False), + 5: protobuf.Field("data_length", "uint32", repeated=False, required=False), + } + + def __init__( + self, + *, + raw_tx: "bytes", + address_n: Optional[Sequence["int"]] = None, + data_initial_chunk: Optional["bytes"] = b'', + coin_type: Optional["bytes"] = None, + data_length: Optional["int"] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.raw_tx = raw_tx + self.data_initial_chunk = data_initial_chunk + self.coin_type = coin_type + self.data_length = data_length + + +class BenfenSignedTx(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 12204 + FIELDS = { + 1: protobuf.Field("public_key", "bytes", repeated=False, required=True), + 2: protobuf.Field("signature", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + public_key: "bytes", + signature: "bytes", + ) -> None: + self.public_key = public_key + self.signature = signature + + +class BenfenTxRequest(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 12207 + FIELDS = { + 1: protobuf.Field("data_length", "uint32", repeated=False, required=False), + 2: protobuf.Field("public_key", "bytes", repeated=False, required=False), + 3: protobuf.Field("signature", "bytes", repeated=False, required=False), + } + + def __init__( + self, + *, + data_length: Optional["int"] = None, + public_key: Optional["bytes"] = None, + signature: Optional["bytes"] = None, + ) -> None: + self.data_length = data_length + self.public_key = public_key + self.signature = signature + + +class BenfenTxAck(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 12208 + FIELDS = { + 1: protobuf.Field("data_chunk", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + data_chunk: "bytes", + ) -> None: + self.data_chunk = data_chunk + + +class BenfenSignMessage(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 12205 + FIELDS = { + 1: protobuf.Field("address_n", "uint32", repeated=True, required=False), + 2: protobuf.Field("message", "bytes", repeated=False, required=True), + } + + def __init__( + self, + *, + message: "bytes", + address_n: Optional[Sequence["int"]] = None, + ) -> None: + self.address_n: Sequence["int"] = address_n if address_n is not None else [] + self.message = message + + +class BenfenMessageSignature(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 12206 + FIELDS = { + 1: protobuf.Field("signature", "bytes", repeated=False, required=True), + 2: protobuf.Field("address", "string", repeated=False, required=True), + } + + def __init__( + self, + *, + signature: "bytes", + address: "str", + ) -> None: + self.signature = signature + self.address = address + + class BinanceGetAddress(protobuf.MessageType): MESSAGE_WIRE_TYPE = 700 FIELDS = { From e41d7d93e6de0f70ed752959c11beff9037eae3f Mon Sep 17 00:00:00 2001 From: guowei0105 Date: Thu, 26 Dec 2024 12:24:17 +0800 Subject: [PATCH 2/3] optimizing code --- core/SConscript.firmware | 2 +- core/src/apps/benfen/layout.py | 3 +-- core/src/apps/benfen/sign_tx.py | 5 ++++- core/src/apps/benfen/tx_parser.py | 10 +++++----- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 868d78a44..b35a4c00d 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -15,7 +15,7 @@ USE_THD89 = ARGUMENTS.get('USE_THD89', '0') FEATURE_FLAGS = { "RDI": True, "SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot) - "SYSTEM_VIEW": True, + "SYSTEM_VIEW": False, } CCFLAGS_MOD = '' diff --git a/core/src/apps/benfen/layout.py b/core/src/apps/benfen/layout.py index 2488bdf53..e4284aaab 100755 --- a/core/src/apps/benfen/layout.py +++ b/core/src/apps/benfen/layout.py @@ -14,7 +14,7 @@ async def require_show_overview( ) -> bool: from trezor.strings import strip_amount - result = await should_show_details( + return await should_show_details( ctx, title=_(i18n_keys.TITLE__SEND_MULTILINE).format( strip_amount(format_benfen_amount(value, currency_symbol))[0] @@ -22,7 +22,6 @@ async def require_show_overview( address=to_addr, br_code=ButtonRequestType.SignTx, ) - return result async def require_confirm_fee( diff --git a/core/src/apps/benfen/sign_tx.py b/core/src/apps/benfen/sign_tx.py index 1f737c81a..32c428c63 100755 --- a/core/src/apps/benfen/sign_tx.py +++ b/core/src/apps/benfen/sign_tx.py @@ -18,7 +18,10 @@ async def process_transaction( - ctx, address: str, tx_bytes: bytes, coin_type: bytes + ctx, + address: str, + tx_bytes: bytes, + coin_type: bytes, ) -> bytes: parser = TransactionParser() diff --git a/core/src/apps/benfen/tx_parser.py b/core/src/apps/benfen/tx_parser.py index ac4966ade..7e99858a3 100755 --- a/core/src/apps/benfen/tx_parser.py +++ b/core/src/apps/benfen/tx_parser.py @@ -273,7 +273,7 @@ def read_commands(self) -> list | None: return commands -def parse_transaction_inputs(parser): +def parse_transaction_inputs(parser) -> list[ParsedInput] | None: input_count = parser.read_u8() inputs = [] @@ -312,7 +312,7 @@ def parse_transaction_inputs(parser): return inputs -def parse_gas_data(parser): +def parse_gas_data(parser) -> dict | None: payment_count = parser.read_u8() payments = [] for _ in range(payment_count): @@ -330,7 +330,7 @@ def parse_gas_data(parser): return {"payment": payments, "owner": owner, "price": price, "budget": budget} -def parse_transaction_expiration(parser): +def parse_transaction_expiration(parser) -> dict | None: expiration_type = parser.read_u8() if expiration_type == 0: return {"type": "None", "value": None} @@ -341,7 +341,7 @@ def parse_transaction_expiration(parser): return None -def parse_transaction(hex_data): +def parse_transaction(hex_data) -> dict | None: try: data = binascii.unhexlify(hex_data) parser = BCSParser(data) @@ -388,7 +388,7 @@ def parse_transaction(hex_data): class TransactionParser: - def parse_tx(self, tx_hex): + def parse_tx(self, tx_hex) -> dict | None: try: if isinstance(tx_hex, bytes): tx_hex = binascii.hexlify(tx_hex).decode() From 55aa042918b4a5762f1b2752e62f05b96b594446 Mon Sep 17 00:00:00 2001 From: guowei0105 Date: Thu, 26 Dec 2024 12:40:12 +0800 Subject: [PATCH 3/3] optimizing code --- core/src/apps/benfen/tx_parser.py | 3 +-- python/src/trezorlib/cli/benfen.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/apps/benfen/tx_parser.py b/core/src/apps/benfen/tx_parser.py index 7e99858a3..ff7142a50 100755 --- a/core/src/apps/benfen/tx_parser.py +++ b/core/src/apps/benfen/tx_parser.py @@ -413,7 +413,7 @@ def parse_tx(self, tx_hex) -> dict | None: except Exception: return None - final_result = { + return { "V1": { "TransactionKind": { "ProgrammableTransaction": { @@ -426,7 +426,6 @@ def parse_tx(self, tx_hex) -> dict | None: "Expiration": result["expiration"], } } - return final_result except Exception: return None diff --git a/python/src/trezorlib/cli/benfen.py b/python/src/trezorlib/cli/benfen.py index 370ed81e7..11b21e702 100644 --- a/python/src/trezorlib/cli/benfen.py +++ b/python/src/trezorlib/cli/benfen.py @@ -62,11 +62,10 @@ def sign_raw_tx(client: "TrezorClient", address: str, message: str, coin_type: s decoded_message, decoded_coin_type ) - result = { + return { "public_key": f"0x{resp.public_key.hex()}", "signature": f"0x{resp.signature.hex()}", } - return result