|
|
|
@ -4,10 +4,11 @@ from typing import TYPE_CHECKING
|
|
|
|
|
|
|
|
|
|
from trezor import wire
|
|
|
|
|
from trezor.enums import InputScriptType
|
|
|
|
|
from trezor.messages import AuthorizeCoinJoin, GetAddress, UnlockPath
|
|
|
|
|
|
|
|
|
|
from apps.common import coininfo
|
|
|
|
|
from apps.common.keychain import get_keychain
|
|
|
|
|
from apps.common.paths import PATTERN_BIP44, PathSchema
|
|
|
|
|
from apps.common.paths import PATTERN_BIP44, SLIP25_PURPOSE, PathSchema
|
|
|
|
|
|
|
|
|
|
from . import authorization
|
|
|
|
|
from .common import BITCOIN_NAMES
|
|
|
|
@ -19,8 +20,6 @@ if TYPE_CHECKING:
|
|
|
|
|
from trezor.protobuf import MessageType
|
|
|
|
|
|
|
|
|
|
from trezor.messages import (
|
|
|
|
|
AuthorizeCoinJoin,
|
|
|
|
|
GetAddress,
|
|
|
|
|
GetOwnershipId,
|
|
|
|
|
GetOwnershipProof,
|
|
|
|
|
GetPublicKey,
|
|
|
|
@ -66,6 +65,10 @@ PATTERN_BIP49 = "m/49'/coin_type'/account'/change/address_index"
|
|
|
|
|
PATTERN_BIP84 = "m/84'/coin_type'/account'/change/address_index"
|
|
|
|
|
# BIP-86 for taproot: https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
|
|
|
|
|
PATTERN_BIP86 = "m/86'/coin_type'/account'/change/address_index"
|
|
|
|
|
# SLIP-25 for CoinJoin: https://github.com/satoshilabs/slips/blob/master/slip-0025.md
|
|
|
|
|
# Only account=0 and script_type=1 are supported for now.
|
|
|
|
|
PATTERN_SLIP25_TAPROOT = "m/10025'/coin_type'/0'/1'/change/address_index"
|
|
|
|
|
PATTERN_SLIP25_TAPROOT_EXTERNAL = "m/10025'/coin_type'/0'/1'/0/address_index"
|
|
|
|
|
|
|
|
|
|
# compatibility patterns, will be removed in the future
|
|
|
|
|
PATTERN_GREENADDRESS_A = "m/[1,4]/address_index"
|
|
|
|
@ -151,13 +154,16 @@ def validate_path_against_script_type(
|
|
|
|
|
|
|
|
|
|
elif coin.taproot and script_type == InputScriptType.SPENDTAPROOT:
|
|
|
|
|
patterns.append(PATTERN_BIP86)
|
|
|
|
|
patterns.append(PATTERN_SLIP25_TAPROOT)
|
|
|
|
|
|
|
|
|
|
return any(
|
|
|
|
|
PathSchema.parse(pattern, coin.slip44).match(address_n) for pattern in patterns
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_schemas_for_coin(coin: coininfo.CoinInfo) -> Iterable[PathSchema]:
|
|
|
|
|
def get_schemas_for_coin(
|
|
|
|
|
coin: coininfo.CoinInfo, unlock_pattern: str | None = None
|
|
|
|
|
) -> Iterable[PathSchema]:
|
|
|
|
|
# basic patterns
|
|
|
|
|
patterns = [
|
|
|
|
|
PATTERN_BIP44,
|
|
|
|
@ -206,6 +212,9 @@ def get_schemas_for_coin(coin: coininfo.CoinInfo) -> Iterable[PathSchema]:
|
|
|
|
|
if coin.taproot:
|
|
|
|
|
patterns.append(PATTERN_BIP86)
|
|
|
|
|
|
|
|
|
|
if unlock_pattern:
|
|
|
|
|
patterns.append(unlock_pattern)
|
|
|
|
|
|
|
|
|
|
schemas = [PathSchema.parse(pattern, coin.slip44) for pattern in patterns]
|
|
|
|
|
|
|
|
|
|
# Some wallets such as Electron-Cash (BCH) store coins on Bitcoin paths.
|
|
|
|
@ -234,23 +243,44 @@ def get_coin_by_name(coin_name: str | None) -> coininfo.CoinInfo:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_keychain_for_coin(
|
|
|
|
|
ctx: wire.Context, coin_name: str | None
|
|
|
|
|
ctx: wire.Context, coin_name: str | None, unlock_pattern: str | None = None
|
|
|
|
|
) -> tuple[Keychain, coininfo.CoinInfo]:
|
|
|
|
|
coin = get_coin_by_name(coin_name)
|
|
|
|
|
schemas = get_schemas_for_coin(coin)
|
|
|
|
|
schemas = get_schemas_for_coin(coin, unlock_pattern)
|
|
|
|
|
slip21_namespaces = [[b"SLIP-0019"], [b"SLIP-0024"]]
|
|
|
|
|
keychain = await get_keychain(ctx, coin.curve_name, schemas, slip21_namespaces)
|
|
|
|
|
return keychain, coin
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _get_unlock_pattern(
|
|
|
|
|
msg: MessageType, auth_msg: MessageType | None = None
|
|
|
|
|
) -> str | None:
|
|
|
|
|
if AuthorizeCoinJoin.is_type_of(msg):
|
|
|
|
|
return PATTERN_SLIP25_TAPROOT
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
auth_msg is not None
|
|
|
|
|
and (AuthorizeCoinJoin.is_type_of(auth_msg) or UnlockPath.is_type_of(auth_msg))
|
|
|
|
|
and auth_msg.address_n
|
|
|
|
|
and auth_msg.address_n[0] == SLIP25_PURPOSE
|
|
|
|
|
):
|
|
|
|
|
if GetAddress.is_type_of(msg):
|
|
|
|
|
return PATTERN_SLIP25_TAPROOT_EXTERNAL
|
|
|
|
|
else:
|
|
|
|
|
return PATTERN_SLIP25_TAPROOT
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def with_keychain(func: HandlerWithCoinInfo[MsgOut]) -> Handler[MsgIn, MsgOut]:
|
|
|
|
|
async def wrapper(
|
|
|
|
|
ctx: wire.Context,
|
|
|
|
|
msg: MsgIn,
|
|
|
|
|
auth_msg: MessageType | None = None,
|
|
|
|
|
) -> MsgOut:
|
|
|
|
|
keychain, coin = await get_keychain_for_coin(ctx, msg.coin_name)
|
|
|
|
|
if auth_msg:
|
|
|
|
|
unlock_pattern = _get_unlock_pattern(msg, auth_msg)
|
|
|
|
|
keychain, coin = await get_keychain_for_coin(ctx, msg.coin_name, unlock_pattern)
|
|
|
|
|
if auth_msg is not None and AuthorizeCoinJoin.is_type_of(auth_msg):
|
|
|
|
|
auth_obj = authorization.from_cached_message(auth_msg)
|
|
|
|
|
return await func(ctx, msg, keychain, coin, auth_obj)
|
|
|
|
|
else:
|
|
|
|
|