mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-17 21:22:10 +00:00
chore(core): Rework checking of CoinJoin parameters.
[no changelog]
This commit is contained in:
parent
8be6689150
commit
94d1a49eea
@ -19,7 +19,7 @@ if TYPE_CHECKING:
|
||||
|
||||
from apps.common.coininfo import CoinInfo
|
||||
|
||||
FEE_PER_ANONYMITY_DECIMALS = const(9)
|
||||
FEE_RATE_DECIMALS = const(8)
|
||||
|
||||
|
||||
class CoinJoinAuthorization:
|
||||
@ -47,11 +47,11 @@ class CoinJoinAuthorization:
|
||||
and txi.script_type == self.params.script_type
|
||||
)
|
||||
|
||||
def approve_sign_tx(self, msg: SignTx, fee: int) -> bool:
|
||||
if self.params.max_total_fee < fee or msg.coin_name != self.params.coin_name:
|
||||
def approve_sign_tx(self, msg: SignTx) -> bool:
|
||||
if self.params.max_rounds < 1 or msg.coin_name != self.params.coin_name:
|
||||
return False
|
||||
|
||||
self.params.max_total_fee -= fee
|
||||
self.params.max_rounds -= 1
|
||||
authorization.set(self.params)
|
||||
return True
|
||||
|
||||
|
@ -2,23 +2,25 @@ from micropython import const
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import ui, wire
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.messages import AuthorizeCoinJoin, Success
|
||||
from trezor.strings import format_amount
|
||||
from trezor.ui.layouts import confirm_action, confirm_coinjoin
|
||||
from trezor.ui.layouts import confirm_action, confirm_coinjoin, confirm_metadata
|
||||
|
||||
from apps.common import authorization
|
||||
from apps.common import authorization, safety_checks
|
||||
from apps.common.paths import validate_path
|
||||
|
||||
from .authorization import FEE_PER_ANONYMITY_DECIMALS
|
||||
from .authorization import FEE_RATE_DECIMALS
|
||||
from .common import BIP32_WALLET_DEPTH
|
||||
from .keychain import validate_path_against_script_type, with_keychain
|
||||
from .sign_tx.layout import format_coin_amount
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from apps.common.coininfo import CoinInfo
|
||||
from apps.common.keychain import Keychain
|
||||
|
||||
_MAX_COORDINATOR_LEN = const(36)
|
||||
_MAX_ROUNDS = const(500)
|
||||
_MAX_COORDINATOR_FEE_RATE = 5 * pow(10, FEE_RATE_DECIMALS) # 5 %
|
||||
|
||||
|
||||
@with_keychain
|
||||
@ -30,19 +32,21 @@ async def authorize_coinjoin(
|
||||
):
|
||||
raise wire.DataError("Invalid coordinator name.")
|
||||
|
||||
if msg.max_rounds > _MAX_ROUNDS and safety_checks.is_strict():
|
||||
raise wire.DataError("The number of rounds is unexpectedly large.")
|
||||
|
||||
if (
|
||||
msg.max_coordinator_fee_rate > _MAX_COORDINATOR_FEE_RATE
|
||||
and safety_checks.is_strict()
|
||||
):
|
||||
raise wire.DataError("The coordination fee rate is unexpectedly large.")
|
||||
|
||||
if msg.max_fee_per_kvbyte > 10 * coin.maxfee_kb and safety_checks.is_strict():
|
||||
raise wire.DataError("The fee per vbyte is unexpectedly large.")
|
||||
|
||||
if not msg.address_n:
|
||||
raise wire.DataError("Empty path not allowed.")
|
||||
|
||||
validation_path = msg.address_n + [0] * BIP32_WALLET_DEPTH
|
||||
await validate_path(
|
||||
ctx,
|
||||
keychain,
|
||||
validation_path,
|
||||
validate_path_against_script_type(
|
||||
coin, address_n=validation_path, script_type=msg.script_type
|
||||
),
|
||||
)
|
||||
|
||||
await confirm_action(
|
||||
ctx,
|
||||
"coinjoin_coordinator",
|
||||
@ -53,17 +57,27 @@ async def authorize_coinjoin(
|
||||
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:
|
||||
fee_per_anonymity = None
|
||||
max_fee_per_vbyte = format_amount(msg.max_fee_per_kvbyte, 3)
|
||||
await confirm_coinjoin(ctx, coin.coin_name, msg.max_rounds, max_fee_per_vbyte)
|
||||
|
||||
await confirm_coinjoin(
|
||||
validation_path = msg.address_n + [0] * BIP32_WALLET_DEPTH
|
||||
await validate_path(
|
||||
ctx,
|
||||
fee_per_anonymity,
|
||||
format_coin_amount(msg.max_total_fee, coin, msg.amount_unit),
|
||||
keychain,
|
||||
validation_path,
|
||||
validate_path_against_script_type(
|
||||
coin, address_n=validation_path, script_type=msg.script_type
|
||||
),
|
||||
)
|
||||
|
||||
if msg.max_fee_per_kvbyte > coin.maxfee_kb:
|
||||
await confirm_metadata(
|
||||
ctx,
|
||||
"fee_over_threshold",
|
||||
"High mining fee",
|
||||
"The mining fee of\n{} sats/vbyte\nis unexpectedly high.",
|
||||
max_fee_per_vbyte,
|
||||
ButtonRequestType.FeeOverThreshold,
|
||||
)
|
||||
|
||||
authorization.set(msg)
|
||||
|
@ -7,7 +7,7 @@ from trezor.ui.components.common.confirm import INFO
|
||||
|
||||
from apps.common import safety_checks
|
||||
|
||||
from ..authorization import FEE_PER_ANONYMITY_DECIMALS
|
||||
from ..authorization import FEE_RATE_DECIMALS
|
||||
from ..common import input_is_external_unverified
|
||||
from ..keychain import validate_path_against_script_type
|
||||
from . import helpers, tx_weight
|
||||
@ -335,18 +335,6 @@ class CoinJoinApprover(Approver):
|
||||
# Upper bound on the user's contribution to the weight of the transaction.
|
||||
self.our_weight = tx_weight.TxWeightCalculator()
|
||||
|
||||
# base for coordinator fee to be multiplied by fee_per_anonymity
|
||||
self.coordinator_fee_base = 0
|
||||
|
||||
# size of the current group of outputs
|
||||
self.group_size = 0
|
||||
|
||||
# number of our change outputs in the current group
|
||||
self.group_our_count = 0
|
||||
|
||||
# amount of each output in the current group
|
||||
self.group_amount = 0
|
||||
|
||||
async def add_internal_input(self, txi: TxInput) -> None:
|
||||
self.our_weight.add_input(txi)
|
||||
if not self.authorization.check_sign_tx_input(txi, self.coin):
|
||||
@ -374,7 +362,6 @@ class CoinJoinApprover(Approver):
|
||||
def add_change_output(self, txo: TxOutput, script_pubkey: bytes) -> None:
|
||||
super().add_change_output(txo, script_pubkey)
|
||||
self.our_weight.add_output(script_pubkey)
|
||||
self.group_our_count += 1
|
||||
|
||||
async def add_payment_request(
|
||||
self, msg: TxAckPaymentRequest, keychain: Keychain
|
||||
@ -395,11 +382,16 @@ class CoinJoinApprover(Approver):
|
||||
async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> None:
|
||||
await super().approve_tx(tx_info, orig_txs)
|
||||
|
||||
max_fee_per_vbyte = self.authorization.params.max_fee_per_kvbyte / 1000
|
||||
max_coordinator_fee_rate = (
|
||||
self.authorization.params.max_coordinator_fee_rate
|
||||
/ pow(10, FEE_RATE_DECIMALS + 2)
|
||||
)
|
||||
|
||||
# The mining fee of the transaction as a whole.
|
||||
mining_fee = self.total_in - self.total_out
|
||||
|
||||
# mining_fee > (coin.maxfee per byte * tx size)
|
||||
if mining_fee > (self.coin.maxfee_kb / 1000) * (self.weight.get_total() / 4):
|
||||
if mining_fee > max_fee_per_vbyte * self.weight.get_total() / 4:
|
||||
raise wire.ProcessError("Mining fee over threshold")
|
||||
|
||||
# The maximum mining fee that the user should be paying assuming that participants share
|
||||
@ -410,30 +402,19 @@ class CoinJoinApprover(Approver):
|
||||
/ (self.weight.get_total() - self.MAX_OUTPUT_WEIGHT)
|
||||
)
|
||||
|
||||
# The coordinator fee for the user's outputs.
|
||||
our_coordinator_fee = self._get_coordinator_fee()
|
||||
# The maximum coordination fee for the user's inputs.
|
||||
our_max_coordinator_fee = max_coordinator_fee_rate * (
|
||||
self.total_in - self.external_in
|
||||
)
|
||||
|
||||
# Total fees that the user is paying.
|
||||
our_fees = self.total_in - self.external_in - self.change_out
|
||||
|
||||
if our_fees > our_coordinator_fee + our_max_mining_fee:
|
||||
raise wire.ProcessError("Total fee over threshold")
|
||||
if our_fees > our_max_coordinator_fee + our_max_mining_fee:
|
||||
raise wire.ProcessError("Total fee over threshold.")
|
||||
|
||||
if not self.authorization.approve_sign_tx(tx_info.tx, our_fees):
|
||||
raise wire.ProcessError("Fees exceed authorized limit")
|
||||
|
||||
# Coordinator fee calculation.
|
||||
|
||||
def _get_coordinator_fee(self) -> float:
|
||||
# Add the coordinator fee for the last group of outputs.
|
||||
self._new_group(0)
|
||||
|
||||
decimal_divisor: float = pow(10, FEE_PER_ANONYMITY_DECIMALS + 2)
|
||||
return (
|
||||
self.coordinator_fee_base
|
||||
* self.authorization.params.fee_per_anonymity
|
||||
/ decimal_divisor
|
||||
)
|
||||
if not self.authorization.approve_sign_tx(tx_info.tx):
|
||||
raise wire.ProcessError("Exceeded number of CoinJoin rounds.")
|
||||
|
||||
def _add_output(self, txo: TxOutput, script_pubkey: bytes) -> None:
|
||||
super()._add_output(txo, script_pubkey)
|
||||
@ -441,27 +422,3 @@ class CoinJoinApprover(Approver):
|
||||
# All CoinJoin outputs must be accompanied by a signed payment request.
|
||||
if txo.payment_req_index is None:
|
||||
raise wire.DataError("Missing payment request.")
|
||||
|
||||
# Assumption: CoinJoin outputs are grouped by amount. (If this assumption is
|
||||
# not satisfied, then we will compute a lower coordinator fee, which may lead
|
||||
# us to wrongfully decline the transaction.)
|
||||
if self.group_amount != txo.amount:
|
||||
self._new_group(txo.amount)
|
||||
|
||||
self.group_size += 1
|
||||
|
||||
def _new_group(self, amount: int) -> None:
|
||||
# Add the base coordinator fee for the previous group of outputs.
|
||||
# Skip groups of size 1, because those must be change-outputs.
|
||||
if self.group_size > 1:
|
||||
self.coordinator_fee_base += (
|
||||
self.group_our_count * self.group_size * self.group_amount
|
||||
)
|
||||
|
||||
# Check whether our outputs gained any anonymity.
|
||||
if self.group_our_count and self.group_size > self.group_our_count:
|
||||
self.anonymity = True
|
||||
|
||||
self.group_size = 0
|
||||
self.group_our_count = 0
|
||||
self.group_amount = amount
|
||||
|
@ -988,14 +988,17 @@ async def confirm_modify_fee(
|
||||
|
||||
|
||||
async def confirm_coinjoin(
|
||||
ctx: wire.GenericContext, fee_per_anonymity: str | None, total_fee: str
|
||||
ctx: wire.GenericContext, coin_name: str, max_rounds: int, max_fee_per_vbyte: str
|
||||
) -> None:
|
||||
text = Text("Authorize CoinJoin", ui.ICON_RECOVERY, new_lines=False)
|
||||
if fee_per_anonymity is not None:
|
||||
text.normal("Fee per anonymity set:\n")
|
||||
text.bold(f"{fee_per_anonymity} %\n")
|
||||
text.normal("Maximum total fees:\n")
|
||||
text.bold(total_fee)
|
||||
text.normal("Coin name: ")
|
||||
text.bold(f"{coin_name}\n")
|
||||
text.br_half()
|
||||
text.normal("Maximum rounds: ")
|
||||
text.bold(f"{max_rounds}\n")
|
||||
text.br_half()
|
||||
text.normal("Maximum mining fee:\n")
|
||||
text.bold(f"{max_fee_per_vbyte} sats/vbyte")
|
||||
await raise_if_cancelled(
|
||||
interact(ctx, HoldToConfirm(text), "coinjoin_final", ButtonRequestType.Other)
|
||||
)
|
||||
|
@ -409,7 +409,7 @@ async def confirm_modify_fee(
|
||||
|
||||
|
||||
async def confirm_coinjoin(
|
||||
ctx: wire.GenericContext, fee_per_anonymity: str | None, total_fee: str
|
||||
ctx: wire.GenericContext, coin_name: str, max_rounds: int, max_fee_per_vbyte: str
|
||||
) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -24,13 +24,14 @@ class TestApprover(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.coin = coins.by_name('Bitcoin')
|
||||
self.fee_per_anonymity_percent = 0.003
|
||||
self.max_fee_rate_percent = 0.3
|
||||
self.coordinator_name = "www.example.com"
|
||||
|
||||
self.msg_auth = AuthorizeCoinJoin(
|
||||
coordinator=self.coordinator_name,
|
||||
max_total_fee=40000,
|
||||
fee_per_anonymity=int(self.fee_per_anonymity_percent * 10**9),
|
||||
max_rounds=10,
|
||||
max_coordinator_fee_rate=int(self.max_fee_rate_percent * 10**8),
|
||||
max_fee_per_kvbyte=7000,
|
||||
address_n=[H_(84), H_(0), H_(0)],
|
||||
coin_name=self.coin.coin_name,
|
||||
script_type=InputScriptType.SPENDWITNESS,
|
||||
@ -39,13 +40,15 @@ class TestApprover(unittest.TestCase):
|
||||
|
||||
def test_coinjoin_lots_of_inputs(self):
|
||||
denomination = 10000000
|
||||
coordinator_fee = int(self.max_fee_rate_percent / 100 * denomination)
|
||||
fees = coordinator_fee + 500
|
||||
|
||||
# Other's inputs.
|
||||
inputs = [
|
||||
TxInput(
|
||||
prev_hash=b"",
|
||||
prev_index=0,
|
||||
amount=denomination + 1000000 * (i + 1),
|
||||
amount=denomination,
|
||||
script_pubkey=bytes(22),
|
||||
script_type=InputScriptType.EXTERNAL,
|
||||
sequence=0xffffffff,
|
||||
@ -54,13 +57,12 @@ class TestApprover(unittest.TestCase):
|
||||
]
|
||||
|
||||
# Our input.
|
||||
inputs.insert(
|
||||
30,
|
||||
inputs.insert(30,
|
||||
TxInput(
|
||||
prev_hash=b"",
|
||||
prev_index=0,
|
||||
address_n=[H_(84), H_(0), H_(0), 0, 1],
|
||||
amount=denomination + 1000000,
|
||||
amount=denomination,
|
||||
script_type=InputScriptType.SPENDWITNESS,
|
||||
sequence=0xffffffff,
|
||||
)
|
||||
@ -70,7 +72,7 @@ class TestApprover(unittest.TestCase):
|
||||
outputs = [
|
||||
TxOutput(
|
||||
address="",
|
||||
amount=denomination,
|
||||
amount=denomination-fees,
|
||||
script_type=OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
) for i in range(99)
|
||||
@ -82,32 +84,7 @@ class TestApprover(unittest.TestCase):
|
||||
TxOutput(
|
||||
address="",
|
||||
address_n=[H_(84), H_(0), H_(0), 0, 2],
|
||||
amount=denomination,
|
||||
script_type=OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
)
|
||||
)
|
||||
|
||||
coordinator_fee = int(self.fee_per_anonymity_percent / 100 * len(outputs) * denomination)
|
||||
fees = coordinator_fee + 10000
|
||||
total_coordinator_fee = coordinator_fee * len(outputs)
|
||||
|
||||
# Other's change-outputs.
|
||||
outputs.extend(
|
||||
TxOutput(
|
||||
address="",
|
||||
amount=1000000 * (i + 1) - fees,
|
||||
script_type=OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
) for i in range(99)
|
||||
)
|
||||
|
||||
# Our change-output.
|
||||
outputs.append(
|
||||
TxOutput(
|
||||
address="",
|
||||
address_n=[H_(84), H_(0), H_(0), 1, 1],
|
||||
amount=1000000 - fees,
|
||||
amount=denomination-fees,
|
||||
script_type=OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
)
|
||||
@ -117,7 +94,7 @@ class TestApprover(unittest.TestCase):
|
||||
outputs.append(
|
||||
TxOutput(
|
||||
address="",
|
||||
amount=total_coordinator_fee,
|
||||
amount=coordinator_fee * len(outputs),
|
||||
script_type=OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
)
|
||||
|
@ -19,8 +19,9 @@ class TestAuthorization(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.msg_auth = AuthorizeCoinJoin(
|
||||
coordinator="www.example.com",
|
||||
max_total_fee=40000,
|
||||
fee_per_anonymity=int(0.003 * 10**9),
|
||||
max_rounds=3,
|
||||
max_coordinator_fee_rate=int(0.3 * 10**8),
|
||||
max_fee_per_kvbyte=7000,
|
||||
address_n=[H_(84), H_(0), H_(0)],
|
||||
coin_name=self.coin.coin_name,
|
||||
script_type=InputScriptType.SPENDWITNESS,
|
||||
@ -102,10 +103,10 @@ class TestAuthorization(unittest.TestCase):
|
||||
|
||||
msg = SignTx(outputs_count=10, inputs_count=21, coin_name=self.coin.coin_name, lock_time=0)
|
||||
|
||||
self.assertTrue(self.authorization.approve_sign_tx(msg, 10000))
|
||||
self.assertTrue(self.authorization.approve_sign_tx(msg, 20000))
|
||||
self.assertFalse(self.authorization.approve_sign_tx(msg, 10001))
|
||||
self.assertTrue(self.authorization.approve_sign_tx(msg, 10000))
|
||||
self.assertTrue(self.authorization.approve_sign_tx(msg))
|
||||
self.assertTrue(self.authorization.approve_sign_tx(msg))
|
||||
self.assertTrue(self.authorization.approve_sign_tx(msg))
|
||||
self.assertFalse(self.authorization.approve_sign_tx(msg))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -60,8 +60,9 @@ def test_sign_tx(client: Client):
|
||||
btc.authorize_coinjoin(
|
||||
client,
|
||||
coordinator="www.example.com",
|
||||
max_total_fee=10_010,
|
||||
fee_per_anonymity=5_000_000, # 0.005 %
|
||||
max_rounds=2,
|
||||
max_coordinator_fee_rate=50_000_000, # 0.5 %
|
||||
max_fee_per_kvbyte=3500,
|
||||
n=parse_path("m/84h/1h/0h"),
|
||||
coin_name="Testnet",
|
||||
script_type=messages.InputScriptType.SPENDWITNESS,
|
||||
@ -141,21 +142,21 @@ def test_sign_tx(client: Client):
|
||||
messages.TxOutputType(
|
||||
# tb1qr5p6f5sk09sms57ket074vywfymuthlgud7xyx
|
||||
address_n=parse_path("m/84h/1h/0h/1/2"),
|
||||
amount=7_289_000 - 50_000 - 5 - 5_000,
|
||||
amount=7_289_000 - 50_000 - 36_445 - 490,
|
||||
script_type=messages.OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
),
|
||||
# Other's change output.
|
||||
messages.TxOutputType(
|
||||
address="tb1q9cqhdr9ydetjzrct6tyeuccws9505hl96azwxk",
|
||||
amount=100_000 - 50_000 - 5 - 5_000,
|
||||
amount=100_000 - 50_000 - 500 - 490,
|
||||
script_type=messages.OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
),
|
||||
# Coordinator's output.
|
||||
messages.TxOutputType(
|
||||
address="mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q",
|
||||
amount=10,
|
||||
amount=36_945,
|
||||
script_type=messages.OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
),
|
||||
@ -217,7 +218,7 @@ def test_sign_tx(client: Client):
|
||||
|
||||
assert (
|
||||
serialized_tx.hex()
|
||||
== "010000000001028abbd1cf69e00fbf60fa3ba475dccdbdba4a859ffa6bfd1ee820a75b1be2b7e50000000000ffffffff0ab6ad3ba09261cfb4fa1d3680cb19332a8fe4d9de9ea89aa565bd83a2c082f90100000000ffffffff0550c3000000000000160014b7a51ede0a66ae36558a3ab097ad86bbd800786150c3000000000000160014167dae080bca35c9ea49c0c8335dcc4b252a1d70cb616e00000000001600141d03a4d2167961b853d6cadfeab08e4937c5dfe8c3af0000000000001600142e01768ca46e57210f0bd2c99e630e8168fa5fe50a000000000000001976a914a579388225827d9f2fe9014add644487808c695d88ac0002473044022010bcbb2ae63db4bfdfdce298bcf3e302e2b1923d02ff57a2155eaae65fdb2949022026289b6d04d7615bf53b7aa0030b25619c465d639b233297b10d0da9ce1a6ca4012103505647c017ff2156eb6da20fae72173d3b681a1d0a629f95f49e884db300689f00000000"
|
||||
== "010000000001028abbd1cf69e00fbf60fa3ba475dccdbdba4a859ffa6bfd1ee820a75b1be2b7e50000000000ffffffff0ab6ad3ba09261cfb4fa1d3680cb19332a8fe4d9de9ea89aa565bd83a2c082f90100000000ffffffff0550c3000000000000160014b7a51ede0a66ae36558a3ab097ad86bbd800786150c3000000000000160014167dae080bca35c9ea49c0c8335dcc4b252a1d7011e56d00000000001600141d03a4d2167961b853d6cadfeab08e4937c5dfe872bf0000000000001600142e01768ca46e57210f0bd2c99e630e8168fa5fe551900000000000001976a914a579388225827d9f2fe9014add644487808c695d88ac000247304402204df07c5baacca264696cc4270665cb759be05387dead8942bd41f20309ceb29002203e685b8e9483435d9b70006bb424b5fef7249415a0f212abdf202b5d62859698012103505647c017ff2156eb6da20fae72173d3b681a1d0a629f95f49e884db300689f00000000"
|
||||
)
|
||||
|
||||
# Test for a second time.
|
||||
@ -231,8 +232,8 @@ def test_sign_tx(client: Client):
|
||||
preauthorized=True,
|
||||
)
|
||||
|
||||
# Test for a third time, fees should exceed max_total_fee.
|
||||
with pytest.raises(TrezorFailure, match="Fees exceed authorized limit"):
|
||||
# Test for a third time, number of rounds should be exceeded.
|
||||
with pytest.raises(TrezorFailure, match="Exceeded number of CoinJoin rounds"):
|
||||
btc.sign_tx(
|
||||
client,
|
||||
"Testnet",
|
||||
@ -251,8 +252,9 @@ def test_unfair_fee(client: Client):
|
||||
btc.authorize_coinjoin(
|
||||
client,
|
||||
coordinator="www.example.com",
|
||||
max_total_fee=10_000,
|
||||
fee_per_anonymity=5_000_000, # 0.005 %
|
||||
max_rounds=1,
|
||||
max_fee_rate=50_000_000, # 0.5 %
|
||||
max_fee_per_kvbyte=3500,
|
||||
n=parse_path("m/84h/1h/0h"),
|
||||
coin_name="Testnet",
|
||||
script_type=messages.InputScriptType.SPENDWITNESS,
|
||||
@ -302,21 +304,21 @@ def test_unfair_fee(client: Client):
|
||||
messages.TxOutputType(
|
||||
# tb1qr5p6f5sk09sms57ket074vywfymuthlgud7xyx
|
||||
address_n=parse_path("m/84h/1h/0h/1/2"),
|
||||
amount=7_289_000 - 50_000 - 5 - 6_000, # unfair mining fee
|
||||
amount=7_289_000 - 50_000 - 36_445 - 600, # unfair mining fee
|
||||
script_type=messages.OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
),
|
||||
# Other's change output.
|
||||
messages.TxOutputType(
|
||||
address="tb1q9cqhdr9ydetjzrct6tyeuccws9505hl96azwxk",
|
||||
amount=100_000 - 50_000 - 5 - 4_000,
|
||||
amount=100_000 - 50_000 - 500 - 400,
|
||||
script_type=messages.OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
),
|
||||
# Coordinator's output.
|
||||
messages.TxOutputType(
|
||||
address="mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q",
|
||||
amount=10,
|
||||
amount=36_945,
|
||||
script_type=messages.OutputScriptType.PAYTOWITNESS,
|
||||
payment_req_index=0,
|
||||
),
|
||||
@ -350,8 +352,10 @@ def test_wrong_coordinator(client: Client):
|
||||
|
||||
btc.authorize_coinjoin(
|
||||
client,
|
||||
max_total_fee=50_000,
|
||||
coordinator="www.example.com",
|
||||
max_rounds=10,
|
||||
max_coordinator_fee_rate=50_000_000, # 0.5 %
|
||||
max_fee_per_kvbyte=3500,
|
||||
n=parse_path("m/84h/1h/0h"),
|
||||
coin_name="Testnet",
|
||||
script_type=messages.InputScriptType.SPENDWITNESS,
|
||||
@ -374,8 +378,10 @@ def test_cancel_authorization(client: Client):
|
||||
|
||||
btc.authorize_coinjoin(
|
||||
client,
|
||||
max_total_fee=50_000,
|
||||
coordinator="www.example.com",
|
||||
max_rounds=10,
|
||||
max_coordinator_fee_rate=50_000_000, # 0.5 %
|
||||
max_fee_per_kvbyte=3500,
|
||||
n=parse_path("m/84h/1h/0h"),
|
||||
coin_name="Testnet",
|
||||
script_type=messages.InputScriptType.SPENDWITNESS,
|
||||
@ -399,8 +405,10 @@ def test_multisession_authorization(client: Client):
|
||||
# Authorize CoinJoin with www.example1.com in session 1.
|
||||
btc.authorize_coinjoin(
|
||||
client,
|
||||
max_total_fee=50_000,
|
||||
coordinator="www.example1.com",
|
||||
max_rounds=10,
|
||||
max_coordinator_fee_rate=50_000_000, # 0.5 %
|
||||
max_fee_per_kvbyte=3500,
|
||||
n=parse_path("m/84h/1h/0h"),
|
||||
coin_name="Testnet",
|
||||
script_type=messages.InputScriptType.SPENDWITNESS,
|
||||
@ -413,8 +421,10 @@ def test_multisession_authorization(client: Client):
|
||||
# Authorize CoinJoin with www.example2.com in session 2.
|
||||
btc.authorize_coinjoin(
|
||||
client,
|
||||
max_total_fee=50_000,
|
||||
coordinator="www.example2.com",
|
||||
max_rounds=10,
|
||||
max_coordinator_fee_rate=50_000_000, # 0.5 %
|
||||
max_fee_per_kvbyte=3500,
|
||||
n=parse_path("m/84h/1h/0h"),
|
||||
coin_name="Testnet",
|
||||
script_type=messages.InputScriptType.SPENDWITNESS,
|
||||
|
@ -229,8 +229,9 @@ def test_experimental_features(client: Client):
|
||||
btc.authorize_coinjoin(
|
||||
client,
|
||||
coordinator="www.example.com",
|
||||
max_total_fee=10_010,
|
||||
fee_per_anonymity=5_000_000, # 0.005 %
|
||||
max_rounds=10,
|
||||
max_coordinator_fee_rate=50_000_000, # 0.5 %
|
||||
max_fee_per_kvbyte=3500,
|
||||
n=parse_path("m/84h/1h/0h"),
|
||||
coin_name="Testnet",
|
||||
script_type=messages.InputScriptType.SPENDWITNESS,
|
||||
|
@ -600,11 +600,11 @@
|
||||
"TT_binance-test_sign_tx.py::test_binance_sign_message[message0-expected_response0]": "fab7b62cab76ae2e4370d9ce113569b3aa2d089a5dbc365c8920731f756a4f37",
|
||||
"TT_binance-test_sign_tx.py::test_binance_sign_message[message1-expected_response1]": "805fc5ef8074c3f5cfee5f7128c2cd068fef42f4f01f9450578f50e791ff811f",
|
||||
"TT_binance-test_sign_tx.py::test_binance_sign_message[message2-expected_response2]": "323e0a474e71ede187ee1332e42952aeca501b42da95f88b2bad5445a3db858c",
|
||||
"TT_bitcoin-test_authorize_coinjoin.py::test_cancel_authorization": "9887e0f4da5c7800f832396e50391beb03229c8edcb5b0e078433703cac6e0d3",
|
||||
"TT_bitcoin-test_authorize_coinjoin.py::test_multisession_authorization": "fd412d086cf4ff677f6ae266e88de725549505d9a2abc1c2ba36f8f854461694",
|
||||
"TT_bitcoin-test_authorize_coinjoin.py::test_sign_tx": "c48bbbaf032eacb42567e49f5b4b82ed51fe97bb613e165dca2c51207199f236",
|
||||
"TT_bitcoin-test_authorize_coinjoin.py::test_cancel_authorization": "2e5cffe7bd0dc6034852d21612fba8cf1ee3c45a14e76140a4c2786f360f54f0",
|
||||
"TT_bitcoin-test_authorize_coinjoin.py::test_multisession_authorization": "65c9435e10e3dede19169a9b02d373bd5acb22eba0ba3b31324271025d65d5a7",
|
||||
"TT_bitcoin-test_authorize_coinjoin.py::test_sign_tx": "87b81a29fe6e27fdfedfdbb1953b3d0178786749eadbb0fe01509c1af8075de5",
|
||||
"TT_bitcoin-test_authorize_coinjoin.py::test_unfair_fee": "ab1aa516510b627b8ffc65391c1c113922ab08f48baf295861a9b597f27f8ea1",
|
||||
"TT_bitcoin-test_authorize_coinjoin.py::test_wrong_coordinator": "9887e0f4da5c7800f832396e50391beb03229c8edcb5b0e078433703cac6e0d3",
|
||||
"TT_bitcoin-test_authorize_coinjoin.py::test_wrong_coordinator": "2e5cffe7bd0dc6034852d21612fba8cf1ee3c45a14e76140a4c2786f360f54f0",
|
||||
"TT_bitcoin-test_bcash.py::test_attack_change_input": "b824d3eb233f6ba2567dd052fa4b52e9a1f170fe4a39af55c1cc262683f188b9",
|
||||
"TT_bitcoin-test_bcash.py::test_send_bch_change": "b824d3eb233f6ba2567dd052fa4b52e9a1f170fe4a39af55c1cc262683f188b9",
|
||||
"TT_bitcoin-test_bcash.py::test_send_bch_external_presigned": "4ea82258a094d49829ab7240465de9cb06198d7b0bb1a56b66bfbda5ccc676c1",
|
||||
@ -1452,7 +1452,7 @@
|
||||
"TT_test_msg_applysettings.py::test_apply_settings_passphrase": "b7af5acc4678edc97c6cab669554b20ec63560d8bd36c40f9c292b40992b590c",
|
||||
"TT_test_msg_applysettings.py::test_apply_settings_passphrase_on_device": "ec6ae42f5d061e40d6c4512a743bc2ac1564455a50a838b6f42fd4c7225d8079",
|
||||
"TT_test_msg_applysettings.py::test_apply_settings_rotation": "1f6da326281b5e4ddff444f96403683badae200f1d4b5cc6b044aeab99141d16",
|
||||
"TT_test_msg_applysettings.py::test_experimental_features": "d06a7f434a7a76a996ef0a9b1c2dc44b508bb9c361551f65f171fd762c0f4d77",
|
||||
"TT_test_msg_applysettings.py::test_experimental_features": "0908b33f42fc4a3405dcfa919e2e00171999a589897d2eac04a720227a7a4bb5",
|
||||
"TT_test_msg_applysettings.py::test_label_too_long": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1",
|
||||
"TT_test_msg_applysettings.py::test_safety_checks": "71ac970ca0d87f1ef70a8605dcc4db478a9190333307ff37ee90dd3d3e97e0fa",
|
||||
"TT_test_msg_backup_device.py::test_backup_bip39": "9b572b12da20b516222ecb0a76fba6d6de5193647405b67625140ed3ed45049c",
|
||||
|
Loading…
Reference in New Issue
Block a user