mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-27 08:38:07 +00:00
fix(core/bitcoin): fix CoinJoin authorization with new cache
This commit is contained in:
parent
dd655422f1
commit
aaa3ce6117
@ -553,7 +553,7 @@ message AuthorizeCoinJoin {
|
|||||||
|
|
||||||
required string coordinator = 1; // coordinator identifier to approve as a prefix in commitment data (max. 18 ASCII characters)
|
required string coordinator = 1; // coordinator identifier to approve as a prefix in commitment data (max. 18 ASCII characters)
|
||||||
required uint64 max_total_fee = 2; // maximum total fees
|
required uint64 max_total_fee = 2; // maximum total fees
|
||||||
optional uint32 fee_per_anonymity = 3; // fee per anonymity set in units of 10^-9 percent
|
optional uint32 fee_per_anonymity = 3 [default=0]; // fee per anonymity set in units of 10^-9 percent
|
||||||
repeated uint32 address_n = 4; // prefix of the BIP-32 path leading to the account (m / purpose' / coin_type' / account')
|
repeated uint32 address_n = 4; // prefix of the BIP-32 path leading to the account (m / purpose' / coin_type' / account')
|
||||||
optional string coin_name = 5 [default='Bitcoin']; // coin to use
|
optional string coin_name = 5 [default='Bitcoin']; // coin to use
|
||||||
optional InputScriptType script_type = 6 [default=SPENDADDRESS]; // used to distinguish between various address formats (non-segwit, segwit, etc.)
|
optional InputScriptType script_type = 6 [default=SPENDADDRESS]; // used to distinguish between various address formats (non-segwit, segwit, etc.)
|
||||||
|
@ -8,7 +8,7 @@ from . import workflow_handlers
|
|||||||
|
|
||||||
if False:
|
if False:
|
||||||
import protobuf
|
import protobuf
|
||||||
from typing import Iterable, NoReturn, Protocol
|
from typing import NoReturn
|
||||||
from trezor.messages.Features import Features
|
from trezor.messages.Features import Features
|
||||||
from trezor.messages.Initialize import Initialize
|
from trezor.messages.Initialize import Initialize
|
||||||
from trezor.messages.EndSession import EndSession
|
from trezor.messages.EndSession import EndSession
|
||||||
@ -19,15 +19,6 @@ if False:
|
|||||||
from trezor.messages.DoPreauthorized import DoPreauthorized
|
from trezor.messages.DoPreauthorized import DoPreauthorized
|
||||||
from trezor.messages.CancelAuthorization import CancelAuthorization
|
from trezor.messages.CancelAuthorization import CancelAuthorization
|
||||||
|
|
||||||
if False:
|
|
||||||
|
|
||||||
class Authorization(Protocol):
|
|
||||||
def expected_wire_types(self) -> Iterable[int]:
|
|
||||||
...
|
|
||||||
|
|
||||||
def __del__(self) -> None:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def get_features() -> Features:
|
def get_features() -> Features:
|
||||||
import storage.recovery
|
import storage.recovery
|
||||||
@ -144,16 +135,15 @@ async def handle_DoPreauthorized(
|
|||||||
ctx: wire.Context, msg: DoPreauthorized
|
ctx: wire.Context, msg: DoPreauthorized
|
||||||
) -> protobuf.MessageType:
|
) -> protobuf.MessageType:
|
||||||
from trezor.messages.PreauthorizedRequest import PreauthorizedRequest
|
from trezor.messages.PreauthorizedRequest import PreauthorizedRequest
|
||||||
|
from apps.common import authorization
|
||||||
|
|
||||||
authorization: Authorization = storage.cache.get(
|
if not authorization.is_set():
|
||||||
storage.cache.APP_BASE_AUTHORIZATION
|
|
||||||
)
|
|
||||||
if not authorization:
|
|
||||||
raise wire.ProcessError("No preauthorized operation")
|
raise wire.ProcessError("No preauthorized operation")
|
||||||
|
|
||||||
req = await ctx.call_any(
|
wire_types = authorization.get_wire_types()
|
||||||
PreauthorizedRequest(), *authorization.expected_wire_types()
|
utils.ensure(bool(wire_types), "Unsupported preauthorization found")
|
||||||
)
|
|
||||||
|
req = await ctx.call_any(PreauthorizedRequest(), *wire_types)
|
||||||
|
|
||||||
handler = workflow_handlers.find_registered_handler(
|
handler = workflow_handlers.find_registered_handler(
|
||||||
ctx.iface, req.MESSAGE_WIRE_TYPE
|
ctx.iface, req.MESSAGE_WIRE_TYPE
|
||||||
@ -161,28 +151,15 @@ async def handle_DoPreauthorized(
|
|||||||
if handler is None:
|
if handler is None:
|
||||||
return wire.unexpected_message()
|
return wire.unexpected_message()
|
||||||
|
|
||||||
return await handler(ctx, req, authorization) # type: ignore
|
return await handler(ctx, req, authorization.get()) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def set_authorization(authorization: Authorization) -> None:
|
|
||||||
previous: Authorization = storage.cache.get(storage.cache.APP_BASE_AUTHORIZATION)
|
|
||||||
if previous:
|
|
||||||
previous.__del__()
|
|
||||||
storage.cache.set(storage.cache.APP_BASE_AUTHORIZATION, authorization)
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_CancelAuthorization(
|
async def handle_CancelAuthorization(
|
||||||
ctx: wire.Context, msg: CancelAuthorization
|
ctx: wire.Context, msg: CancelAuthorization
|
||||||
) -> protobuf.MessageType:
|
) -> protobuf.MessageType:
|
||||||
authorization: Authorization = storage.cache.get(
|
from apps.common import authorization
|
||||||
storage.cache.APP_BASE_AUTHORIZATION
|
|
||||||
)
|
|
||||||
if not authorization:
|
|
||||||
raise wire.ProcessError("No preauthorized operation")
|
|
||||||
|
|
||||||
authorization.__del__()
|
|
||||||
storage.cache.set(storage.cache.APP_BASE_AUTHORIZATION, b"")
|
|
||||||
|
|
||||||
|
authorization.clear()
|
||||||
return Success(message="Authorization cancelled")
|
return Success(message="Authorization cancelled")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,64 +1,59 @@
|
|||||||
from micropython import const
|
from micropython import const
|
||||||
|
|
||||||
from trezor.messages import MessageType
|
from trezor import wire
|
||||||
|
from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin
|
||||||
|
|
||||||
|
from apps.common import authorization
|
||||||
|
|
||||||
from .common import BIP32_WALLET_DEPTH
|
from .common import BIP32_WALLET_DEPTH
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import Iterable
|
import protobuf
|
||||||
from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin
|
|
||||||
from trezor.messages.GetOwnershipProof import GetOwnershipProof
|
from trezor.messages.GetOwnershipProof import GetOwnershipProof
|
||||||
from trezor.messages.SignTx import SignTx
|
from trezor.messages.SignTx import SignTx
|
||||||
from trezor.messages.TxInput import TxInput
|
from trezor.messages.TxInput import TxInput
|
||||||
|
|
||||||
from apps.common.coininfo import CoinInfo
|
from apps.common.coininfo import CoinInfo
|
||||||
from apps.common.keychain import Keychain
|
|
||||||
|
|
||||||
_ROUND_ID_LEN = const(32)
|
_ROUND_ID_LEN = const(32)
|
||||||
FEE_PER_ANONYMITY_DECIMALS = const(9)
|
FEE_PER_ANONYMITY_DECIMALS = const(9)
|
||||||
|
|
||||||
|
|
||||||
class CoinJoinAuthorization:
|
class CoinJoinAuthorization:
|
||||||
def __init__(
|
def __init__(self, params: AuthorizeCoinJoin) -> None:
|
||||||
self, msg: AuthorizeCoinJoin, keychain: Keychain, coin: CoinInfo
|
self.params = params
|
||||||
) -> None:
|
|
||||||
self.coordinator = msg.coordinator
|
|
||||||
self.remaining_fee = msg.max_total_fee
|
|
||||||
self.fee_per_anonymity = msg.fee_per_anonymity or 0
|
|
||||||
self.address_n = msg.address_n
|
|
||||||
self.keychain = keychain
|
|
||||||
self.coin = coin
|
|
||||||
self.script_type = msg.script_type
|
|
||||||
|
|
||||||
def __del__(self) -> None:
|
|
||||||
self.keychain.__del__()
|
|
||||||
|
|
||||||
def expected_wire_types(self) -> Iterable[int]:
|
|
||||||
return (MessageType.SignTx, MessageType.GetOwnershipProof)
|
|
||||||
|
|
||||||
def check_get_ownership_proof(self, msg: GetOwnershipProof) -> bool:
|
def check_get_ownership_proof(self, msg: GetOwnershipProof) -> bool:
|
||||||
# Check whether the current authorization matches the parameters of the request.
|
# Check whether the current params matches the parameters of the request.
|
||||||
return (
|
return (
|
||||||
len(msg.address_n) >= BIP32_WALLET_DEPTH
|
len(msg.address_n) >= BIP32_WALLET_DEPTH
|
||||||
and msg.address_n[:-BIP32_WALLET_DEPTH] == self.address_n
|
and msg.address_n[:-BIP32_WALLET_DEPTH] == self.params.address_n
|
||||||
and msg.coin_name == self.coin.coin_name
|
and msg.coin_name == self.params.coin_name
|
||||||
and msg.script_type == self.script_type
|
and msg.script_type == self.params.script_type
|
||||||
and len(msg.commitment_data) >= _ROUND_ID_LEN
|
and len(msg.commitment_data) >= _ROUND_ID_LEN
|
||||||
and msg.commitment_data[:-_ROUND_ID_LEN] == self.coordinator.encode()
|
and msg.commitment_data[:-_ROUND_ID_LEN] == self.params.coordinator.encode()
|
||||||
)
|
)
|
||||||
|
|
||||||
def check_sign_tx_input(self, txi: TxInput, coin: CoinInfo) -> bool:
|
def check_sign_tx_input(self, txi: TxInput, coin: CoinInfo) -> bool:
|
||||||
# Check whether the current input matches the parameters of the request.
|
# Check whether the current input matches the parameters of the request.
|
||||||
return (
|
return (
|
||||||
len(txi.address_n) >= BIP32_WALLET_DEPTH
|
len(txi.address_n) >= BIP32_WALLET_DEPTH
|
||||||
and txi.address_n[:-BIP32_WALLET_DEPTH] == self.address_n
|
and txi.address_n[:-BIP32_WALLET_DEPTH] == self.params.address_n
|
||||||
and coin.coin_name == self.coin.coin_name
|
and coin.coin_name == self.params.coin_name
|
||||||
and txi.script_type == self.script_type
|
and txi.script_type == self.params.script_type
|
||||||
)
|
)
|
||||||
|
|
||||||
def approve_sign_tx(self, msg: SignTx, fee: int) -> bool:
|
def approve_sign_tx(self, msg: SignTx, fee: int) -> bool:
|
||||||
if self.remaining_fee < fee or msg.coin_name != self.coin.coin_name:
|
if self.params.max_total_fee < fee or msg.coin_name != self.params.coin_name:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.remaining_fee -= fee
|
self.params.max_total_fee -= fee
|
||||||
|
authorization.set(self.params)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def from_cached_message(auth_msg: protobuf.MessageType) -> CoinJoinAuthorization:
|
||||||
|
if not isinstance(auth_msg, AuthorizeCoinJoin):
|
||||||
|
raise wire.ProcessError("Appropriate params was not found")
|
||||||
|
|
||||||
|
return CoinJoinAuthorization(auth_msg)
|
||||||
|
@ -1,75 +1,71 @@
|
|||||||
from micropython import const
|
from micropython import const
|
||||||
|
|
||||||
from trezor import ui
|
from trezor import ui, wire
|
||||||
from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin
|
from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin
|
||||||
from trezor.messages.Success import Success
|
from trezor.messages.Success import Success
|
||||||
from trezor.strings import format_amount
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.layouts import confirm_action, confirm_coinjoin
|
from trezor.ui.layouts import confirm_action, confirm_coinjoin
|
||||||
|
|
||||||
from apps.base import set_authorization
|
from apps.common import authorization
|
||||||
from apps.common.paths import validate_path
|
from apps.common.paths import validate_path
|
||||||
|
|
||||||
from .authorization import FEE_PER_ANONYMITY_DECIMALS, CoinJoinAuthorization
|
from .authorization import FEE_PER_ANONYMITY_DECIMALS
|
||||||
from .common import BIP32_WALLET_DEPTH
|
from .common import BIP32_WALLET_DEPTH
|
||||||
from .keychain import get_keychain_for_coin, validate_path_against_script_type
|
from .keychain import validate_path_against_script_type, with_keychain
|
||||||
from .sign_tx.layout import format_coin_amount
|
from .sign_tx.layout import format_coin_amount
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from trezor import wire
|
from apps.common.coininfo import CoinInfo
|
||||||
|
from apps.common.keychain import Keychain
|
||||||
|
|
||||||
_MAX_COORDINATOR_LEN = const(18)
|
_MAX_COORDINATOR_LEN = const(18)
|
||||||
|
|
||||||
|
|
||||||
async def authorize_coinjoin(ctx: wire.Context, msg: AuthorizeCoinJoin) -> Success:
|
@with_keychain
|
||||||
# We cannot use the @with_keychain decorator here, because we need the keychain
|
async def authorize_coinjoin(
|
||||||
# to survive the function exit. The ownership of the keychain is transferred to
|
ctx: wire.Context, msg: AuthorizeCoinJoin, keychain: Keychain, coin: CoinInfo
|
||||||
# the CoinJoinAuthorization object, which takes care of its destruction.
|
) -> Success:
|
||||||
keychain, coin = await get_keychain_for_coin(ctx, msg.coin_name)
|
if len(msg.coordinator) > _MAX_COORDINATOR_LEN or not all(
|
||||||
|
32 <= ord(x) <= 126 for x in msg.coordinator
|
||||||
|
):
|
||||||
|
raise wire.DataError("Invalid coordinator name.")
|
||||||
|
|
||||||
try:
|
if not msg.address_n:
|
||||||
if len(msg.coordinator) > _MAX_COORDINATOR_LEN or not all(
|
raise wire.DataError("Empty path not allowed.")
|
||||||
32 <= ord(x) <= 126 for x in msg.coordinator
|
|
||||||
):
|
|
||||||
raise wire.DataError("Invalid coordinator name.")
|
|
||||||
|
|
||||||
if not msg.address_n:
|
validation_path = msg.address_n + [0] * BIP32_WALLET_DEPTH
|
||||||
raise wire.DataError("Empty path not allowed.")
|
await validate_path(
|
||||||
|
ctx,
|
||||||
|
keychain,
|
||||||
|
validation_path,
|
||||||
|
validate_path_against_script_type(
|
||||||
|
coin, address_n=validation_path, script_type=msg.script_type
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
validation_path = msg.address_n + [0] * BIP32_WALLET_DEPTH
|
await confirm_action(
|
||||||
await validate_path(
|
ctx,
|
||||||
ctx,
|
"coinjoin_coordinator",
|
||||||
keychain,
|
title="Authorize CoinJoin",
|
||||||
validation_path,
|
description="Do you really want to take part in a CoinJoin transaction at:\n{}",
|
||||||
validate_path_against_script_type(
|
description_param=msg.coordinator,
|
||||||
coin, address_n=validation_path, script_type=msg.script_type
|
description_param_font=ui.MONO,
|
||||||
),
|
icon=ui.ICON_RECOVERY,
|
||||||
|
)
|
||||||
|
|
||||||
|
if msg.fee_per_anonymity:
|
||||||
|
fee_per_anonymity: str | None = format_amount(
|
||||||
|
msg.fee_per_anonymity, FEE_PER_ANONYMITY_DECIMALS
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
await confirm_action(
|
|
||||||
ctx,
|
|
||||||
"coinjoin_coordinator",
|
|
||||||
title="Authorize CoinJoin",
|
|
||||||
description="Do you really want to take part in a CoinJoin transaction at:\n{}",
|
|
||||||
description_param=msg.coordinator,
|
|
||||||
description_param_font=ui.MONO,
|
|
||||||
icon=ui.ICON_RECOVERY,
|
|
||||||
)
|
|
||||||
|
|
||||||
fee_per_anonymity = None
|
fee_per_anonymity = None
|
||||||
if msg.fee_per_anonymity is not None:
|
|
||||||
fee_per_anonymity = format_amount(
|
|
||||||
msg.fee_per_anonymity, FEE_PER_ANONYMITY_DECIMALS
|
|
||||||
)
|
|
||||||
await confirm_coinjoin(
|
|
||||||
ctx,
|
|
||||||
fee_per_anonymity,
|
|
||||||
format_coin_amount(msg.max_total_fee, coin, msg.amount_unit),
|
|
||||||
)
|
|
||||||
|
|
||||||
set_authorization(CoinJoinAuthorization(msg, keychain, coin))
|
await confirm_coinjoin(
|
||||||
|
ctx,
|
||||||
|
fee_per_anonymity,
|
||||||
|
format_coin_amount(msg.max_total_fee, coin, msg.amount_unit),
|
||||||
|
)
|
||||||
|
|
||||||
except BaseException:
|
authorization.set(msg)
|
||||||
keychain.__del__()
|
|
||||||
raise
|
|
||||||
|
|
||||||
return Success(message="CoinJoin authorized")
|
return Success(message="CoinJoin authorized")
|
||||||
|
@ -7,19 +7,20 @@ from apps.common import coininfo
|
|||||||
from apps.common.keychain import get_keychain
|
from apps.common.keychain import get_keychain
|
||||||
from apps.common.paths import PATTERN_BIP44, PathSchema
|
from apps.common.paths import PATTERN_BIP44, PathSchema
|
||||||
|
|
||||||
|
from . import authorization
|
||||||
from .common import BITCOIN_NAMES
|
from .common import BITCOIN_NAMES
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import Awaitable, Callable, Iterable, TypeVar
|
from typing import Awaitable, Callable, Iterable, TypeVar
|
||||||
from typing_extensions import Protocol
|
from typing_extensions import Protocol
|
||||||
|
|
||||||
|
from protobuf import MessageType
|
||||||
|
|
||||||
from trezor.messages.TxInputType import EnumTypeInputScriptType
|
from trezor.messages.TxInputType import EnumTypeInputScriptType
|
||||||
|
|
||||||
from apps.common.keychain import Keychain, MsgOut, Handler
|
from apps.common.keychain import Keychain, MsgOut, Handler
|
||||||
from apps.common.paths import Bip32Path
|
from apps.common.paths import Bip32Path
|
||||||
|
|
||||||
from .authorization import CoinJoinAuthorization
|
|
||||||
|
|
||||||
class MsgWithCoinName(Protocol):
|
class MsgWithCoinName(Protocol):
|
||||||
coin_name: str
|
coin_name: str
|
||||||
|
|
||||||
@ -189,14 +190,13 @@ def with_keychain(func: HandlerWithCoinInfo[MsgOut]) -> Handler[MsgIn, MsgOut]:
|
|||||||
async def wrapper(
|
async def wrapper(
|
||||||
ctx: wire.Context,
|
ctx: wire.Context,
|
||||||
msg: MsgIn,
|
msg: MsgIn,
|
||||||
authorization: CoinJoinAuthorization | None = None,
|
auth_msg: MessageType | None = None,
|
||||||
) -> MsgOut:
|
) -> MsgOut:
|
||||||
if authorization:
|
keychain, coin = await get_keychain_for_coin(ctx, msg.coin_name)
|
||||||
keychain = authorization.keychain
|
if auth_msg:
|
||||||
coin = get_coin_by_name(msg.coin_name)
|
auth_obj = authorization.from_cached_message(auth_msg)
|
||||||
return await func(ctx, msg, keychain, coin, authorization)
|
return await func(ctx, msg, keychain, coin, auth_obj)
|
||||||
else:
|
else:
|
||||||
keychain, coin = await get_keychain_for_coin(ctx, msg.coin_name)
|
|
||||||
with keychain:
|
with keychain:
|
||||||
return await func(ctx, msg, keychain, coin)
|
return await func(ctx, msg, keychain, coin)
|
||||||
|
|
||||||
|
@ -263,6 +263,9 @@ class CoinJoinApprover(Approver):
|
|||||||
super().__init__(tx, coin)
|
super().__init__(tx, coin)
|
||||||
self.authorization = authorization
|
self.authorization = authorization
|
||||||
|
|
||||||
|
if authorization.params.coin_name != tx.coin_name:
|
||||||
|
raise wire.DataError("Coin name does not match authorization.")
|
||||||
|
|
||||||
# Upper bound on the user's contribution to the weight of the transaction.
|
# Upper bound on the user's contribution to the weight of the transaction.
|
||||||
self.our_weight = tx_weight.TxWeightCalculator(
|
self.our_weight = tx_weight.TxWeightCalculator(
|
||||||
tx.inputs_count, tx.outputs_count
|
tx.inputs_count, tx.outputs_count
|
||||||
@ -352,7 +355,7 @@ class CoinJoinApprover(Approver):
|
|||||||
decimal_divisor: float = pow(10, FEE_PER_ANONYMITY_DECIMALS + 2)
|
decimal_divisor: float = pow(10, FEE_PER_ANONYMITY_DECIMALS + 2)
|
||||||
return (
|
return (
|
||||||
self.coordinator_fee_base
|
self.coordinator_fee_base
|
||||||
* self.authorization.fee_per_anonymity
|
* self.authorization.params.fee_per_anonymity
|
||||||
/ decimal_divisor
|
/ decimal_divisor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
53
core/src/apps/common/authorization.py
Normal file
53
core/src/apps/common/authorization.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import protobuf
|
||||||
|
import storage.cache
|
||||||
|
from trezor import messages, utils
|
||||||
|
from trezor.messages import MessageType
|
||||||
|
|
||||||
|
if False:
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
WIRE_TYPES: dict[int, tuple[int, ...]] = {
|
||||||
|
MessageType.AuthorizeCoinJoin: (MessageType.SignTx, MessageType.GetOwnershipProof),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def is_set() -> bool:
|
||||||
|
return bool(storage.cache.get(storage.cache.APP_COMMON_AUTHORIZATION_TYPE))
|
||||||
|
|
||||||
|
|
||||||
|
def set(auth_message: protobuf.MessageType) -> None:
|
||||||
|
buffer = bytearray(protobuf.count_message(auth_message))
|
||||||
|
writer = utils.BufferWriter(buffer)
|
||||||
|
protobuf.dump_message(writer, auth_message)
|
||||||
|
storage.cache.set(
|
||||||
|
storage.cache.APP_COMMON_AUTHORIZATION_TYPE,
|
||||||
|
auth_message.MESSAGE_WIRE_TYPE.to_bytes(2, "big"),
|
||||||
|
)
|
||||||
|
storage.cache.set(storage.cache.APP_COMMON_AUTHORIZATION_DATA, buffer)
|
||||||
|
|
||||||
|
|
||||||
|
def get() -> protobuf.MessageType | None:
|
||||||
|
stored_auth_type = storage.cache.get(storage.cache.APP_COMMON_AUTHORIZATION_TYPE)
|
||||||
|
if not stored_auth_type:
|
||||||
|
return None
|
||||||
|
|
||||||
|
msg_wire_type = int.from_bytes(stored_auth_type, "big")
|
||||||
|
msg_type = messages.get_type(msg_wire_type)
|
||||||
|
buffer = storage.cache.get(storage.cache.APP_COMMON_AUTHORIZATION_DATA)
|
||||||
|
reader = utils.BufferReader(buffer)
|
||||||
|
|
||||||
|
return protobuf.load_message(reader, msg_type)
|
||||||
|
|
||||||
|
|
||||||
|
def get_wire_types() -> Iterable[int]:
|
||||||
|
stored_auth_type = storage.cache.get(storage.cache.APP_COMMON_AUTHORIZATION_TYPE)
|
||||||
|
if not stored_auth_type:
|
||||||
|
return ()
|
||||||
|
|
||||||
|
msg_wire_type = int.from_bytes(stored_auth_type, "big")
|
||||||
|
return WIRE_TYPES.get(msg_wire_type, ())
|
||||||
|
|
||||||
|
|
||||||
|
def clear() -> None:
|
||||||
|
storage.cache.set(storage.cache.APP_COMMON_AUTHORIZATION_TYPE, b"")
|
||||||
|
storage.cache.set(storage.cache.APP_COMMON_AUTHORIZATION_DATA, b"")
|
@ -14,7 +14,8 @@ _SESSION_ID_LENGTH = 32
|
|||||||
APP_COMMON_SEED = 0
|
APP_COMMON_SEED = 0
|
||||||
APP_CARDANO_PASSPHRASE = 1
|
APP_CARDANO_PASSPHRASE = 1
|
||||||
APP_MONERO_LIVE_REFRESH = 2
|
APP_MONERO_LIVE_REFRESH = 2
|
||||||
APP_BASE_AUTHORIZATION = 3
|
APP_COMMON_AUTHORIZATION_TYPE = 3
|
||||||
|
APP_COMMON_AUTHORIZATION_DATA = 4
|
||||||
|
|
||||||
# Keys that are valid across sessions
|
# Keys that are valid across sessions
|
||||||
APP_COMMON_SEED_WITHOUT_PASSPHRASE = 0 | _SESSIONLESS_FLAG
|
APP_COMMON_SEED_WITHOUT_PASSPHRASE = 0 | _SESSIONLESS_FLAG
|
||||||
@ -52,7 +53,8 @@ class SessionCache(DataCache):
|
|||||||
64, # APP_COMMON_SEED
|
64, # APP_COMMON_SEED
|
||||||
50, # APP_CARDANO_PASSPHRASE
|
50, # APP_CARDANO_PASSPHRASE
|
||||||
1, # APP_MONERO_LIVE_REFRESH
|
1, # APP_MONERO_LIVE_REFRESH
|
||||||
128, # APP_BASE_AUTHORIZATION
|
2, # APP_COMMON_AUTHORIZATION_TYPE
|
||||||
|
128, # APP_COMMON_AUTHORIZATION_DATA
|
||||||
)
|
)
|
||||||
self.last_usage = 0
|
self.last_usage = 0
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -23,7 +23,7 @@ class AuthorizeCoinJoin(p.MessageType):
|
|||||||
coordinator: str,
|
coordinator: str,
|
||||||
max_total_fee: int,
|
max_total_fee: int,
|
||||||
address_n: Optional[List[int]] = None,
|
address_n: Optional[List[int]] = None,
|
||||||
fee_per_anonymity: Optional[int] = None,
|
fee_per_anonymity: int = 0,
|
||||||
coin_name: str = "Bitcoin",
|
coin_name: str = "Bitcoin",
|
||||||
script_type: EnumTypeInputScriptType = 0,
|
script_type: EnumTypeInputScriptType = 0,
|
||||||
amount_unit: EnumTypeAmountUnit = 0,
|
amount_unit: EnumTypeAmountUnit = 0,
|
||||||
@ -41,7 +41,7 @@ class AuthorizeCoinJoin(p.MessageType):
|
|||||||
return {
|
return {
|
||||||
1: ('coordinator', p.UnicodeType, p.FLAG_REQUIRED),
|
1: ('coordinator', p.UnicodeType, p.FLAG_REQUIRED),
|
||||||
2: ('max_total_fee', p.UVarintType, p.FLAG_REQUIRED),
|
2: ('max_total_fee', p.UVarintType, p.FLAG_REQUIRED),
|
||||||
3: ('fee_per_anonymity', p.UVarintType, None),
|
3: ('fee_per_anonymity', p.UVarintType, 0), # default=0
|
||||||
4: ('address_n', p.UVarintType, p.FLAG_REPEATED),
|
4: ('address_n', p.UVarintType, p.FLAG_REPEATED),
|
||||||
5: ('coin_name', p.UnicodeType, "Bitcoin"), # default=Bitcoin
|
5: ('coin_name', p.UnicodeType, "Bitcoin"), # default=Bitcoin
|
||||||
6: ('script_type', p.EnumType("InputScriptType", (0, 1, 2, 3, 4,)), 0), # default=SPENDADDRESS
|
6: ('script_type', p.EnumType("InputScriptType", (0, 1, 2, 3, 4,)), 0), # default=SPENDADDRESS
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from common import unittest, await_result, H_
|
from common import unittest, await_result, H_
|
||||||
|
|
||||||
|
import storage.cache
|
||||||
from trezor import wire
|
from trezor import wire
|
||||||
from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin
|
from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin
|
||||||
from trezor.messages.TxInput import TxInput
|
from trezor.messages.TxInput import TxInput
|
||||||
@ -23,11 +24,12 @@ class TestApprover(unittest.TestCase):
|
|||||||
self.msg_auth = AuthorizeCoinJoin(
|
self.msg_auth = AuthorizeCoinJoin(
|
||||||
coordinator="www.example.com",
|
coordinator="www.example.com",
|
||||||
max_total_fee=40000,
|
max_total_fee=40000,
|
||||||
fee_per_anonymity=self.fee_per_anonymity_percent * 10**9,
|
fee_per_anonymity=int(self.fee_per_anonymity_percent * 10**9),
|
||||||
address_n=[H_(84), H_(0), H_(0)],
|
address_n=[H_(84), H_(0), H_(0)],
|
||||||
coin_name=self.coin.coin_name,
|
coin_name=self.coin.coin_name,
|
||||||
script_type=InputScriptType.SPENDWITNESS,
|
script_type=InputScriptType.SPENDWITNESS,
|
||||||
)
|
)
|
||||||
|
storage.cache.start_session()
|
||||||
|
|
||||||
def test_coinjoin_lots_of_inputs(self):
|
def test_coinjoin_lots_of_inputs(self):
|
||||||
denomination = 10000000
|
denomination = 10000000
|
||||||
@ -74,7 +76,7 @@ class TestApprover(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
coordinator_fee = self.fee_per_anonymity_percent / 100 * len(outputs) * denomination
|
coordinator_fee = int(self.fee_per_anonymity_percent / 100 * len(outputs) * denomination)
|
||||||
fees = coordinator_fee + 10000
|
fees = coordinator_fee + 10000
|
||||||
total_coordinator_fee = coordinator_fee * len(outputs)
|
total_coordinator_fee = coordinator_fee * len(outputs)
|
||||||
|
|
||||||
@ -103,7 +105,7 @@ class TestApprover(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
authorization = CoinJoinAuthorization(self.msg_auth, None, self.coin)
|
authorization = CoinJoinAuthorization(self.msg_auth)
|
||||||
tx = SignTx(outputs_count=len(outputs), inputs_count=len(inputs), coin_name=self.coin.coin_name, lock_time=0)
|
tx = SignTx(outputs_count=len(outputs), inputs_count=len(inputs), coin_name=self.coin.coin_name, lock_time=0)
|
||||||
approver = CoinJoinApprover(tx, self.coin, authorization)
|
approver = CoinJoinApprover(tx, self.coin, authorization)
|
||||||
signer = Bitcoin(tx, None, self.coin, approver)
|
signer = Bitcoin(tx, None, self.coin, approver)
|
||||||
@ -123,7 +125,7 @@ class TestApprover(unittest.TestCase):
|
|||||||
await_result(approver.approve_tx(TxInfo(signer, tx), []))
|
await_result(approver.approve_tx(TxInfo(signer, tx), []))
|
||||||
|
|
||||||
def test_coinjoin_input_account_depth_mismatch(self):
|
def test_coinjoin_input_account_depth_mismatch(self):
|
||||||
authorization = CoinJoinAuthorization(self.msg_auth, None, self.coin)
|
authorization = CoinJoinAuthorization(self.msg_auth)
|
||||||
tx = SignTx(outputs_count=201, inputs_count=100, coin_name=self.coin.coin_name, lock_time=0)
|
tx = SignTx(outputs_count=201, inputs_count=100, coin_name=self.coin.coin_name, lock_time=0)
|
||||||
approver = CoinJoinApprover(tx, self.coin, authorization)
|
approver = CoinJoinApprover(tx, self.coin, authorization)
|
||||||
|
|
||||||
@ -139,7 +141,7 @@ class TestApprover(unittest.TestCase):
|
|||||||
await_result(approver.add_internal_input(txi))
|
await_result(approver.add_internal_input(txi))
|
||||||
|
|
||||||
def test_coinjoin_input_account_path_mismatch(self):
|
def test_coinjoin_input_account_path_mismatch(self):
|
||||||
authorization = CoinJoinAuthorization(self.msg_auth, None, self.coin)
|
authorization = CoinJoinAuthorization(self.msg_auth)
|
||||||
tx = SignTx(outputs_count=201, inputs_count=100, coin_name=self.coin.coin_name, lock_time=0)
|
tx = SignTx(outputs_count=201, inputs_count=100, coin_name=self.coin.coin_name, lock_time=0)
|
||||||
approver = CoinJoinApprover(tx, self.coin, authorization)
|
approver = CoinJoinApprover(tx, self.coin, authorization)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from common import unittest, H_
|
from common import unittest, H_
|
||||||
|
|
||||||
|
import storage.cache
|
||||||
from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin
|
from trezor.messages.AuthorizeCoinJoin import AuthorizeCoinJoin
|
||||||
from trezor.messages.GetOwnershipProof import GetOwnershipProof
|
from trezor.messages.GetOwnershipProof import GetOwnershipProof
|
||||||
from trezor.messages.SignTx import SignTx
|
from trezor.messages.SignTx import SignTx
|
||||||
@ -19,13 +20,14 @@ class TestAuthorization(unittest.TestCase):
|
|||||||
self.msg_auth = AuthorizeCoinJoin(
|
self.msg_auth = AuthorizeCoinJoin(
|
||||||
coordinator="www.example.com",
|
coordinator="www.example.com",
|
||||||
max_total_fee=40000,
|
max_total_fee=40000,
|
||||||
fee_per_anonymity=0.003 * 10**9,
|
fee_per_anonymity=int(0.003 * 10**9),
|
||||||
address_n=[H_(84), H_(0), H_(0)],
|
address_n=[H_(84), H_(0), H_(0)],
|
||||||
coin_name=self.coin.coin_name,
|
coin_name=self.coin.coin_name,
|
||||||
script_type=InputScriptType.SPENDWITNESS,
|
script_type=InputScriptType.SPENDWITNESS,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.authorization = CoinJoinAuthorization(self.msg_auth, None, self.coin)
|
self.authorization = CoinJoinAuthorization(self.msg_auth)
|
||||||
|
storage.cache.start_session()
|
||||||
|
|
||||||
def test_ownership_proof_account_depth_mismatch(self):
|
def test_ownership_proof_account_depth_mismatch(self):
|
||||||
# Account depth mismatch.
|
# Account depth mismatch.
|
||||||
|
@ -23,7 +23,7 @@ class AuthorizeCoinJoin(p.MessageType):
|
|||||||
coordinator: str,
|
coordinator: str,
|
||||||
max_total_fee: int,
|
max_total_fee: int,
|
||||||
address_n: Optional[List[int]] = None,
|
address_n: Optional[List[int]] = None,
|
||||||
fee_per_anonymity: Optional[int] = None,
|
fee_per_anonymity: int = 0,
|
||||||
coin_name: str = "Bitcoin",
|
coin_name: str = "Bitcoin",
|
||||||
script_type: EnumTypeInputScriptType = 0,
|
script_type: EnumTypeInputScriptType = 0,
|
||||||
amount_unit: EnumTypeAmountUnit = 0,
|
amount_unit: EnumTypeAmountUnit = 0,
|
||||||
@ -41,7 +41,7 @@ class AuthorizeCoinJoin(p.MessageType):
|
|||||||
return {
|
return {
|
||||||
1: ('coordinator', p.UnicodeType, p.FLAG_REQUIRED),
|
1: ('coordinator', p.UnicodeType, p.FLAG_REQUIRED),
|
||||||
2: ('max_total_fee', p.UVarintType, p.FLAG_REQUIRED),
|
2: ('max_total_fee', p.UVarintType, p.FLAG_REQUIRED),
|
||||||
3: ('fee_per_anonymity', p.UVarintType, None),
|
3: ('fee_per_anonymity', p.UVarintType, 0), # default=0
|
||||||
4: ('address_n', p.UVarintType, p.FLAG_REPEATED),
|
4: ('address_n', p.UVarintType, p.FLAG_REPEATED),
|
||||||
5: ('coin_name', p.UnicodeType, "Bitcoin"), # default=Bitcoin
|
5: ('coin_name', p.UnicodeType, "Bitcoin"), # default=Bitcoin
|
||||||
6: ('script_type', p.EnumType("InputScriptType", (0, 1, 2, 3, 4,)), 0), # default=SPENDADDRESS
|
6: ('script_type', p.EnumType("InputScriptType", (0, 1, 2, 3, 4,)), 0), # default=SPENDADDRESS
|
||||||
|
Loading…
Reference in New Issue
Block a user