async def confirm_withdrawal(ctx: wire.Context, withdrawal: CardanoTxWithdrawal) -> None: props: list[PropertyType] = [ ("Confirm withdrawal", None), ] if withdrawal.path: props.append(( f"for account {format_account_number(withdrawal.path)}:", address_n_to_str(to_account_path(withdrawal.path)), )) else: assert withdrawal.script_hash is not None # validate_withdrawal props.append( ("for script:", format_script_hash(withdrawal.script_hash))) props.append(("Amount:", format_coin_amount(withdrawal.amount))) await confirm_properties( ctx, "confirm_withdrawal", title="Confirm transaction", props=props, br_code=ButtonRequestType.Other, )
async def confirm_certificate(ctx: wire.Context, certificate: CardanoTxCertificate) -> None: # stake pool registration requires custom confirmation logic not covered # in this call assert certificate.type != CardanoCertificateType.STAKE_POOL_REGISTRATION props: list[PropertyType] = [ ("Confirm:", CERTIFICATE_TYPE_NAMES[certificate.type]), ] if certificate.path: props.append(( f"for account {format_account_number(certificate.path)}:", address_n_to_str(to_account_path(certificate.path)), ), ) else: assert certificate.script_hash is not None # validate_certificate props.append( ("for script:", format_script_hash(certificate.script_hash))) if certificate.type == CardanoCertificateType.STAKE_DELEGATION: assert certificate.pool is not None # validate_certificate props.append(("to pool:", format_stake_pool_id(certificate.pool))) await confirm_properties( ctx, "confirm_certificate", title="Confirm transaction", props=props, br_code=ButtonRequestType.Other, )
async def show_native_script( ctx: wire.Context, script: CardanoNativeScript, indices: list[int] | None = None, ) -> None: script_heading = "Script" if indices is None: indices = [] if indices: script_heading += " " + ".".join(str(i) for i in indices) script_type_name_suffix = "" if script.type == CardanoNativeScriptType.PUB_KEY: if script.key_path: script_type_name_suffix = "path" elif script.key_hash: script_type_name_suffix = "hash" props: list[PropertyType] = [( f"{script_heading} - {SCRIPT_TYPE_NAMES[script.type]} {script_type_name_suffix}:", None, )] if script.type == CardanoNativeScriptType.PUB_KEY: assert script.key_hash is not None or script.key_path # validate_script if script.key_hash: props.append((None, format_key_hash(script.key_hash, True))) elif script.key_path: props.append((address_n_to_str(script.key_path), None)) elif script.type == CardanoNativeScriptType.N_OF_K: assert script.required_signatures_count is not None # validate_script props.append(( f"Requires {script.required_signatures_count} out of {len(script.scripts)} signatures.", None, )) elif script.type == CardanoNativeScriptType.INVALID_BEFORE: assert script.invalid_before is not None # validate_script props.append((str(script.invalid_before), None)) elif script.type == CardanoNativeScriptType.INVALID_HEREAFTER: assert script.invalid_hereafter is not None # validate_script props.append((str(script.invalid_hereafter), None)) if script.type in ( CardanoNativeScriptType.ALL, CardanoNativeScriptType.ANY, CardanoNativeScriptType.N_OF_K, ): assert script.scripts # validate_script props.append((f"Contains {len(script.scripts)} nested scripts.", None)) await confirm_properties( ctx, "verify_script", title="Verify script", props=props, br_code=ButtonRequestType.Other, ) for i, sub_script in enumerate(script.scripts): await show_native_script(ctx, sub_script, indices + [i + 1])
async def get_address(ctx: wire.Context, msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Address: if msg.show_display: # skip soft-validation for silent calls await validate_path( ctx, keychain, msg.address_n, validate_path_against_script_type(coin, msg), ) node = keychain.derive(msg.address_n) address = addresses.get_address(msg.script_type, coin, node, msg.multisig) address_short = addresses.address_short(coin, address) if coin.segwit and msg.script_type == InputScriptType.SPENDWITNESS: address_qr = address.upper() # bech32 address elif coin.cashaddr_prefix is not None: address_qr = address.upper() # cashaddr address else: address_qr = address # base58 address if msg.multisig: multisig_xpub_magic = coin.xpub_magic if coin.segwit and not msg.ignore_xpub_magic: if (msg.script_type == InputScriptType.SPENDWITNESS and coin.xpub_magic_multisig_segwit_native is not None): multisig_xpub_magic = coin.xpub_magic_multisig_segwit_native elif (msg.script_type == InputScriptType.SPENDP2SHWITNESS and coin.xpub_magic_multisig_segwit_p2sh is not None): multisig_xpub_magic = coin.xpub_magic_multisig_segwit_p2sh if msg.show_display: if msg.multisig: if msg.multisig.nodes: pubnodes = msg.multisig.nodes else: pubnodes = [hd.node for hd in msg.multisig.pubkeys] multisig_index = multisig_pubkey_index(msg.multisig, node.public_key()) title = f"Multisig {msg.multisig.m} of {len(pubnodes)}" await show_address( ctx, address=address_short, address_qr=address_qr, title=title, multisig_index=multisig_index, xpubs=_get_xpubs(coin, multisig_xpub_magic, pubnodes), ) else: title = address_n_to_str(msg.address_n) await show_address(ctx, address=address_short, address_qr=address_qr, title=title) return Address(address=address)
async def get_address(ctx: Context, msg: RippleGetAddress, keychain: Keychain) -> RippleAddress: await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = node.public_key() address = address_from_public_key(pubkey) if msg.show_display: title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, title=title) return RippleAddress(address=address)
async def get_address(ctx: wire.Context, msg: MoneroGetAddress, keychain: Keychain) -> MoneroAddress: await paths.validate_path(ctx, keychain, msg.address_n) creds = misc.get_creds(keychain, msg.address_n, msg.network_type) addr = creds.address have_subaddress = msg.account is not None and msg.minor is not None have_payment_id = msg.payment_id is not None if (msg.account is None) != (msg.minor is None): raise wire.ProcessError("Invalid subaddress indexes") if have_payment_id and have_subaddress: raise wire.DataError("Subaddress cannot be integrated") if have_payment_id: assert msg.payment_id is not None if len(msg.payment_id) != 8: raise ValueError("Invalid payment ID length") addr = addresses.encode_addr( net_version(msg.network_type, False, True), crypto_helpers.encodepoint(creds.spend_key_public), crypto_helpers.encodepoint(creds.view_key_public), msg.payment_id, ) if have_subaddress: assert msg.account is not None assert msg.minor is not None pub_spend, pub_view = monero.generate_sub_address_keys( creds.view_key_private, creds.spend_key_public, msg.account, msg.minor) addr = addresses.encode_addr( net_version(msg.network_type, True, False), crypto_helpers.encodepoint(pub_spend), crypto_helpers.encodepoint(pub_view), ) if msg.show_display: title = paths.address_n_to_str(msg.address_n) await show_address( ctx, address=addr, address_qr="monero:" + addr, title=title, ) return MoneroAddress(address=addr.encode())
async def show_cardano_address( ctx: wire.Context, address_parameters: CardanoAddressParametersType, address: str, protocol_magic: int, ) -> None: network_name = None if not protocol_magics.is_mainnet(protocol_magic): network_name = protocol_magics.to_ui_string(protocol_magic) title = f"{ADDRESS_TYPE_NAMES[address_parameters.address_type]} address" address_extra = None title_qr = title if address_parameters.address_type in ( CardanoAddressType.BYRON, CardanoAddressType.BASE, CardanoAddressType.BASE_KEY_SCRIPT, CardanoAddressType.POINTER, CardanoAddressType.ENTERPRISE, CardanoAddressType.REWARD, ): if address_parameters.address_n: address_extra = address_n_to_str(address_parameters.address_n) title_qr = address_n_to_str(address_parameters.address_n) elif address_parameters.address_n_staking: address_extra = address_n_to_str( address_parameters.address_n_staking) title_qr = address_n_to_str(address_parameters.address_n_staking) await show_address( ctx, address=address, title=title, network=network_name, address_extra=address_extra, title_qr=title_qr, )
def _format_stake_credential( path: list[int], script_hash: bytes | None, key_hash: bytes | None ) -> tuple[str, str]: if path: return ( f"for account {format_account_number(path)}:", address_n_to_str(to_account_path(path)), ) elif key_hash: return ("for key hash:", bech32.encode(bech32.HRP_STAKE_KEY_HASH, key_hash)) elif script_hash: return ("for script:", bech32.encode(bech32.HRP_SCRIPT_HASH, script_hash)) else: # should be unreachable unless there's a bug in validation raise ValueError
async def confirm_stake_pool_owner( ctx: wire.Context, keychain: seed.Keychain, owner: CardanoPoolOwner, protocol_magic: int, network_id: int, ) -> None: props: list[tuple[str, str | None]] = [] if owner.staking_key_path: props.append(("Pool owner:", address_n_to_str(owner.staking_key_path))) props.append( ( derive_human_readable_address( keychain, CardanoAddressParametersType( address_type=CardanoAddressType.REWARD, address_n=owner.staking_key_path, ), protocol_magic, network_id, ), None, ) ) else: assert owner.staking_key_hash is not None # validate_pool_owners props.append( ( "Pool owner:", derive_human_readable_address( keychain, CardanoAddressParametersType( address_type=CardanoAddressType.REWARD, staking_key_hash=owner.staking_key_hash, ), protocol_magic, network_id, ), ) ) await confirm_properties( ctx, "confirm_pool_owners", title="Confirm transaction", props=props, br_code=ButtonRequestType.Other, )
async def get_address(ctx: Context, msg: TezosGetAddress, keychain: Keychain) -> TezosAddress: await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pk = seed.remove_ed25519_prefix(node.public_key()) pkh = hashlib.blake2b(pk, outlen=helpers.PUBLIC_KEY_HASH_SIZE).digest() address = helpers.base58_encode_check( pkh, prefix=helpers.TEZOS_ED25519_ADDRESS_PREFIX) if msg.show_display: title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, title=title) return TezosAddress(address=address)
async def get_address(ctx: Context, msg: StellarGetAddress, keychain: Keychain) -> StellarAddress: await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = seed.remove_ed25519_prefix(node.public_key()) address = helpers.address_from_public_key(pubkey) if msg.show_display: title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, case_sensitive=False, title=title) return StellarAddress(address=address)
async def get_address(ctx, msg: BinanceGetAddress, keychain: Keychain): HRP = "bnb" await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) pubkey = node.public_key() address = address_from_public_key(pubkey, HRP) if msg.show_display: title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, address_qr=address, title=title) return BinanceAddress(address=address)
async def get_address(ctx: Context, msg: EthereumGetAddress, keychain: Keychain) -> EthereumAddress: await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) if len(msg.address_n) > 1: # path has slip44 network identifier network = networks.by_slip44(msg.address_n[1] & 0x7FFF_FFFF) else: network = None address = address_from_bytes(node.ethereum_pubkeyhash(), network) if msg.show_display: title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, title=title) return EthereumAddress(address=address)
async def confirm_required_signer( ctx: wire.Context, required_signer: CardanoTxRequiredSigner ) -> None: assert ( required_signer.key_hash is not None or required_signer.key_path ) # _validate_required_signer formatted_signer = ( bech32.encode(bech32.HRP_REQUIRED_SIGNER_KEY_HASH, required_signer.key_hash) if required_signer.key_hash is not None else address_n_to_str(required_signer.key_path) ) await confirm_properties( ctx, "confirm_required_signer", title="Confirm transaction", props=[("Required signer", formatted_signer)], br_code=ButtonRequestType.Other, )
async def confirm_witness_request( ctx: wire.Context, witness_path: list[int], ) -> None: if is_multisig_path(witness_path): path_title = "multi-sig path" elif is_minting_path(witness_path): path_title = "token minting path" else: path_title = "path" await confirm_text( ctx, "confirm_total", title="Confirm transaction", data=address_n_to_str(witness_path), description=f"Sign transaction with {path_title}:", br_code=ButtonRequestType.Other, )
async def get_address(ctx: Context, msg: NEMGetAddress, keychain: Keychain) -> NEMAddress: validate_network(msg.network) await validate_path(ctx, keychain, msg.address_n, check_path(msg.address_n, msg.network)) node = keychain.derive(msg.address_n) address = node.nem_address(msg.network) if msg.show_display: title = address_n_to_str(msg.address_n) await show_address( ctx, address=address, address_qr=address.upper(), title=title, network=get_network_str(msg.network), ) return NEMAddress(address=address)
async def get_address(ctx, msg, keychain): await paths.validate_path(ctx, keychain, msg.address_n) creds = misc.get_creds(keychain, msg.address_n, msg.network_type) addr = creds.address if msg.payment_id: if len(msg.payment_id) != 8: raise ValueError("Invalid payment ID length") addr = addresses.encode_addr( net_version(msg.network_type, False, True), crypto.encodepoint(creds.spend_key_public), crypto.encodepoint(creds.view_key_public), msg.payment_id, ) if msg.account or msg.minor: if msg.payment_id: raise ValueError("Subaddress cannot be integrated") pub_spend, pub_view = monero.generate_sub_address_keys( creds.view_key_private, creds.spend_key_public, msg.account, msg.minor) addr = addresses.encode_addr( net_version(msg.network_type, True, False), crypto.encodepoint(pub_spend), crypto.encodepoint(pub_view), ) if msg.show_display: title = paths.address_n_to_str(msg.address_n) await show_address( ctx, address=addr.decode(), address_qr="monero:" + addr.decode(), title=title, ) return MoneroAddress(address=addr)
async def confirm_catalyst_registration( ctx: wire.Context, public_key: str, staking_path: list[int], reward_address: str, nonce: int, ) -> None: await confirm_properties( ctx, "confirm_catalyst_registration", title="Confirm transaction", props=[ ("Catalyst voting key registration", None), ("Voting public key:", public_key), ( f"Staking key for account {format_account_number(staking_path)}:", address_n_to_str(staking_path), ), ("Rewards go to:", reward_address), ("Nonce:", str(nonce)), ], br_code=ButtonRequestType.Other, )
async def get_address(ctx: Context, msg: EthereumGetAddress, keychain: Keychain) -> EthereumAddress: await paths.validate_path(ctx, keychain, msg.address_n) node = keychain.derive(msg.address_n) seckey = node.private_key() public_key = secp256k1.publickey(seckey, False) # uncompressed address_bytes = sha3_256(public_key[1:], keccak=True).digest()[12:] if len(msg.address_n) > 1: # path has slip44 network identifier network = networks.by_slip44(msg.address_n[1] & 0x7FFF_FFFF) else: network = None address = address_from_bytes(address_bytes, network) if msg.show_display: title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, address_qr=address, title=title) return EthereumAddress(address=address)
async def get_address(ctx: wire.Context, msg: GetAddress, keychain: Keychain, coin: CoinInfo) -> Address: if msg.show_display: # skip soft-validation for silent calls await validate_path( ctx, keychain, msg.address_n, validate_path_against_script_type(coin, msg), ) node = keychain.derive(msg.address_n) address = addresses.get_address(msg.script_type, coin, node, msg.multisig) address_short = addresses.address_short(coin, address) address_case_sensitive = True if coin.segwit and msg.script_type in ( InputScriptType.SPENDWITNESS, InputScriptType.SPENDTAPROOT, ): address_case_sensitive = False # bech32 address elif coin.cashaddr_prefix is not None: address_case_sensitive = False # cashaddr address mac: bytes | None = None multisig_xpub_magic = coin.xpub_magic if msg.multisig: if coin.segwit and not msg.ignore_xpub_magic: if (msg.script_type == InputScriptType.SPENDWITNESS and coin.xpub_magic_multisig_segwit_native is not None): multisig_xpub_magic = coin.xpub_magic_multisig_segwit_native elif (msg.script_type == InputScriptType.SPENDP2SHWITNESS and coin.xpub_magic_multisig_segwit_p2sh is not None): multisig_xpub_magic = coin.xpub_magic_multisig_segwit_p2sh else: # Attach a MAC for single-sig addresses, but only if the path is standard # or if the user explicitly confirms a non-standard path. if msg.show_display or (keychain.is_in_keychain(msg.address_n) and validate_path_against_script_type(coin, msg)): mac = get_address_mac(address, coin.slip44, keychain) if msg.show_display: if msg.multisig: if msg.multisig.nodes: pubnodes = msg.multisig.nodes else: pubnodes = [hd.node for hd in msg.multisig.pubkeys] multisig_index = multisig_pubkey_index(msg.multisig, node.public_key()) title = f"Multisig {msg.multisig.m} of {len(pubnodes)}" await show_address( ctx, address=address_short, case_sensitive=address_case_sensitive, title=title, multisig_index=multisig_index, xpubs=_get_xpubs(coin, multisig_xpub_magic, pubnodes), ) else: title = address_n_to_str(msg.address_n) await show_address( ctx, address=address_short, address_qr=address, case_sensitive=address_case_sensitive, title=title, ) return Address(address=address, mac=mac)
async def show_warning_path(ctx: wire.Context, path: list[int], title: str) -> None: await confirm_path_warning(ctx, address_n_to_str(path), path_type=title)