From 68bbcbdc4ddec11ce96c8c3bbff9c99154e96d19 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Mon, 2 Jan 2023 20:10:34 +0100 Subject: [PATCH] feat(legacy): Implement CoinJoin signing. --- legacy/firmware/.changelog.d/2718.added.4 | 1 + legacy/firmware/fsm.c | 9 +- legacy/firmware/fsm_msg_coin.h | 16 +- .../firmware/protob/messages-bitcoin.options | 4 +- legacy/firmware/signing.c | 334 ++++++++++++++++-- legacy/firmware/signing.h | 3 +- .../bitcoin/test_authorize_coinjoin.py | 6 - tests/ui_tests/fixtures.json | 6 + 8 files changed, 333 insertions(+), 46 deletions(-) create mode 100644 legacy/firmware/.changelog.d/2718.added.4 diff --git a/legacy/firmware/.changelog.d/2718.added.4 b/legacy/firmware/.changelog.d/2718.added.4 new file mode 100644 index 0000000000..6146c8b2e5 --- /dev/null +++ b/legacy/firmware/.changelog.d/2718.added.4 @@ -0,0 +1 @@ +Implement coinjoin signing. diff --git a/legacy/firmware/fsm.c b/legacy/firmware/fsm.c index 85888c74c8..5f7ddf0ce4 100644 --- a/legacy/firmware/fsm.c +++ b/legacy/firmware/fsm.c @@ -103,10 +103,11 @@ static uint32_t unlock_path = 0; return; \ } -#define CHECK_UNLOCKED \ - if (!session_isUnlocked()) { \ - layoutHome(); \ - return; \ +#define CHECK_UNLOCKED \ + if (!session_isUnlocked()) { \ + fsm_sendFailure(FailureType_Failure_ProcessError, _("Locked")); \ + layoutHome(); \ + return; \ } #define CHECK_PARAM(cond, errormsg) \ diff --git a/legacy/firmware/fsm_msg_coin.h b/legacy/firmware/fsm_msg_coin.h index 68708cd662..5bf6094415 100644 --- a/legacy/firmware/fsm_msg_coin.h +++ b/legacy/firmware/fsm_msg_coin.h @@ -168,7 +168,15 @@ void fsm_msgSignTx(const SignTx *msg) { CHECK_PARAM(msg->inputs_count + msg->outputs_count >= msg->inputs_count, _("Value overflow")); - CHECK_PIN + const AuthorizeCoinJoin *authorization = NULL; + if (authorization_type == MessageType_MessageType_AuthorizeCoinJoin) { + authorization = config_getCoinJoinAuthorization(); + if (authorization == NULL) { + return; + } + } else { + CHECK_PIN + } PathSchema unlock = fsm_getUnlockedSchema(MessageType_MessageType_SignTx); @@ -184,11 +192,13 @@ void fsm_msgSignTx(const SignTx *msg) { const HDNode *node = fsm_getDerivedNode(coin->curve_name, NULL, 0, NULL); if (!node) return; - signing_init(msg, coin, node, unlock); + signing_init(msg, coin, node, authorization, unlock); } void fsm_msgTxAck(TxAck *msg) { - CHECK_UNLOCKED + if (!signing_is_preauthorized()) { + CHECK_UNLOCKED + } CHECK_PARAM(msg->has_tx, _("No transaction provided")); diff --git a/legacy/firmware/protob/messages-bitcoin.options b/legacy/firmware/protob/messages-bitcoin.options index b88340d011..9768f4cadf 100644 --- a/legacy/firmware/protob/messages-bitcoin.options +++ b/legacy/firmware/protob/messages-bitcoin.options @@ -11,7 +11,6 @@ Address.address max_size:130 Address.mac type:FT_IGNORE SignTx.coin_name max_size:21 -SignTx.coinjoin_request type:FT_IGNORE SignMessage.address_n max_count:8 SignMessage.message max_size:1024 @@ -96,8 +95,9 @@ AuthorizeCoinJoin.coordinator max_size:37 AuthorizeCoinJoin.address_n max_count:8 AuthorizeCoinJoin.coin_name max_size:21 +CoinJoinRequest.mask_public_key max_size:33 +CoinJoinRequest.signature max_size:64 # Unused messages. TxAckPaymentRequest skip_message:true PaymentRequestMemo skip_message:true -CoinJoinRequest skip_message:true diff --git a/legacy/firmware/signing.c b/legacy/firmware/signing.c index 3214f684f6..1543794467 100644 --- a/legacy/firmware/signing.c +++ b/legacy/firmware/signing.c @@ -103,7 +103,7 @@ static uint64_t total_in, external_in, total_out, change_out; static uint64_t orig_total_in, orig_external_in, orig_total_out, orig_change_out; static uint32_t progress, progress_step, progress_meta_step; -static uint32_t tx_weight; +static uint32_t tx_weight, tx_base_weight, our_weight, our_inputs_len; PathSchema unlocked_schema; typedef struct { @@ -149,6 +149,13 @@ static bool is_replacement; // Is this a replacement transaction? static TxInfo orig_info; static uint8_t orig_hash[32]; // TXID of the original transaction. +/* Variables specific to CoinJoin transactions. */ +static secbool is_coinjoin; // Is this a CoinJoin transaction? +static uint64_t coinjoin_coordination_fee_base; +static AuthorizeCoinJoin coinjoin_authorization; +static CoinJoinRequest coinjoin_request; +static Hasher coinjoin_request_hasher; + /* A marker for in_address_n_count to indicate a mismatch in bip32 paths in input */ #define BIP32_NOCHANGEALLOWED 1 @@ -649,6 +656,12 @@ void phase1_request_next_input(void) { idx2 = 0; } + if (to.is_segwit) { + tx_base_weight += TXSIZE_SEGWIT_OVERHEAD; + tx_weight += TXSIZE_SEGWIT_OVERHEAD + to.inputs_len; + our_weight += TXSIZE_SEGWIT_OVERHEAD + our_inputs_len; + } + send_req_2_output(); } } @@ -924,32 +937,46 @@ static bool fill_input_script_pubkey(TxInputType *in) { static bool validate_path(InputScriptType script_type, pb_size_t address_n_count, const uint32_t *address_n, bool has_multisig) { - // Sanity check not critical for security. The main reason for this is that we - // are not comfortable with using the same private key in multiple signature - // schemes (ECDSA and Schnorr) and we want to be sure that the user went - // through a warning screen before we sign the input. - if (!coin_path_check(coin, script_type, address_n_count, address_n, - has_multisig, unlocked_schema, true)) { - if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict && - !coin_path_check(coin, script_type, address_n_count, address_n, - has_multisig, unlocked_schema, false)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path")); + if (is_coinjoin == sectrue) { + // Check whether the authorization matches the parameters of the input. + if (address_n_count != + coinjoin_authorization.address_n_count + BIP32_WALLET_DEPTH || + memcmp(address_n, coinjoin_authorization.address_n, + sizeof(uint32_t) * coinjoin_authorization.address_n_count) != + 0 || + script_type != coinjoin_authorization.script_type) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Unauthorized path")); signing_abort(); return false; } + } else { + // Sanity check not critical for security. The main reason for this is that + // we are not comfortable with using the same private key in multiple + // signature schemes (ECDSA and Schnorr) and we want to be sure that the + // user went through a warning screen before we sign the input. + if (!coin_path_check(coin, script_type, address_n_count, address_n, + has_multisig, unlocked_schema, true)) { + if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict && + !coin_path_check(coin, script_type, address_n_count, address_n, + has_multisig, unlocked_schema, false)) { + fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path")); + signing_abort(); + return false; + } - if (!foreign_address_confirmed) { - if (signing_stage < STAGE_REQUEST_3_INPUT) { - if (!fsm_layoutPathWarning()) { + if (!foreign_address_confirmed) { + if (signing_stage < STAGE_REQUEST_3_INPUT) { + if (!fsm_layoutPathWarning()) { + signing_abort(); + return false; + } + foreign_address_confirmed = true; + } else { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Transaction has changed during signing")); signing_abort(); return false; } - foreign_address_confirmed = true; - } else { - fsm_sendFailure(FailureType_Failure_ProcessError, - _("Transaction has changed during signing")); - signing_abort(); - return false; } } } @@ -1131,8 +1158,47 @@ static bool tx_info_init(TxInfo *tx_info, uint32_t inputs_count, return true; } +static bool init_coinjoin(const SignTx *msg, + const AuthorizeCoinJoin *authorization) { + if (!msg->has_coinjoin_request) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Missing coinjoin request.")); + signing_abort(); + return false; + } + + if (strcmp(coin->coin_name, authorization->coin_name) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Unauthorized operation.")); + } + + memcpy(&coinjoin_authorization, authorization, + sizeof(coinjoin_authorization)); + memcpy(&coinjoin_request, &msg->coinjoin_request, sizeof(coinjoin_request)); + + // Begin hashing the CoinJoin request. + hasher_Init(&coinjoin_request_hasher, HASHER_SHA2); + hasher_Update(&coinjoin_request_hasher, (const uint8_t *)"CJR1", 4); + size_t coordinator_len = + strnlen(authorization->coordinator, sizeof(authorization->coordinator)); + tx_script_hash(&coinjoin_request_hasher, coordinator_len, + (const uint8_t *)authorization->coordinator); + uint32_t slip44 = coin->coin_type & PATH_UNHARDEN_MASK; + hasher_Update(&coinjoin_request_hasher, (uint8_t *)&slip44, sizeof(slip44)); + hasher_Update(&coinjoin_request_hasher, + (const uint8_t *)&coinjoin_request.fee_rate, 4); + hasher_Update(&coinjoin_request_hasher, + (const uint8_t *)&coinjoin_request.no_fee_threshold, 8); + hasher_Update(&coinjoin_request_hasher, + (const uint8_t *)&coinjoin_request.min_registrable_amount, 8); + hasher_Update(&coinjoin_request_hasher, + coinjoin_request.mask_public_key.bytes, 33); + ser_length_hash(&coinjoin_request_hasher, msg->inputs_count); + return true; +} + void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root, - PathSchema unlock) { + const AuthorizeCoinJoin *authorization, PathSchema unlock) { coin = _coin; amount_unit = msg->has_amount_unit ? msg->amount_unit : AmountUnit_BITCOIN; serialize = msg->has_serialize ? msg->serialize : true; @@ -1162,7 +1228,10 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root, } #endif - tx_weight = 4 * size; + tx_base_weight = 4 * size; + tx_weight = tx_base_weight; + our_weight = tx_base_weight; + our_inputs_len = 0; foreign_address_confirmed = false; taproot_only = true; @@ -1178,10 +1247,14 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root, orig_external_in = 0; orig_total_out = 0; orig_change_out = 0; + coinjoin_coordination_fee_base = 0; memzero(external_inputs, sizeof(external_inputs)); memzero(&input, sizeof(TxInputType)); memzero(&output, sizeof(TxOutputType)); memzero(&resp, sizeof(TxRequest)); + memzero(&coinjoin_authorization, sizeof(coinjoin_authorization)); + memzero(&coinjoin_request, sizeof(coinjoin_request)); + memzero(&coinjoin_request_hasher, sizeof(coinjoin_request_hasher)); is_replacement = false; unlocked_schema = unlock; signing = true; @@ -1190,6 +1263,13 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root, // this means 50 % per phase. progress_step = (500 << PROGRESS_PRECISION) / info.inputs_count; + is_coinjoin = (authorization != NULL) ? sectrue : secfalse; + if (is_coinjoin == sectrue) { + if (!init_coinjoin(msg, authorization)) { + return; + } + } + uint32_t branch_id = 0; #if !BITCOIN_ONLY branch_id = info.branch_id; @@ -1561,6 +1641,59 @@ static bool tx_info_check_outputs_hash(TxInfo *tx_info) { return true; } +static bool coinjoin_add_input(TxInputType *txi) { + // Masks for the signable and no_fee bits in coinjoin_flags. + const uint8_t COINJOIN_FLAGS_SIGNABLE = 0x01; + const uint8_t COINJOIN_FLAGS_NO_FEE = 0x02; + + hasher_Update(&coinjoin_request_hasher, (uint8_t *)&txi->coinjoin_flags, 1); + + if (txi->script_type == InputScriptType_EXTERNAL) { + return true; + } + + // Compute the masking bit for the signable bit in coinjoin flags. + static CONFIDENTIAL uint8_t output_private_key[32] = {0}; + uint8_t shared_secret[65] = {0}; + bool res = (zkp_bip340_tweak_private_key(node.private_key, NULL, + output_private_key) == 0); + res = res && (ecdh_multiply(&secp256k1, output_private_key, + coinjoin_request.mask_public_key.bytes, + shared_secret) == 0); + memzero(&output_private_key, sizeof(output_private_key)); + if (!res) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive shared secret.")); + signing_abort(); + return false; + } + + Hasher mask_hasher = {0}; + uint8_t mask[SHA256_DIGEST_LENGTH] = {0}; + hasher_Init(&mask_hasher, HASHER_SHA2); + hasher_Update(&mask_hasher, shared_secret + 1, 32); + tx_prevout_hash(&mask_hasher, txi); + hasher_Final(&mask_hasher, mask); + + // Ensure that the input can be signed. + bool signable = (txi->coinjoin_flags ^ mask[0]) & COINJOIN_FLAGS_SIGNABLE; + if (!signable) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Unauthorized input")); + signing_abort(); + return false; + } + + // Add to coordination_fee_base, except for remixes and small inputs which are + // not charged a coordination fee. + bool no_fee = txi->coinjoin_flags & COINJOIN_FLAGS_NO_FEE; + if (txi->amount > coinjoin_request.no_fee_threshold && !no_fee) { + if (!add_amount(&coinjoin_coordination_fee_base, txi->amount)) { + return false; + } + } + return true; +} + static bool signing_add_input(TxInputType *txinput) { // hash all input data to check it later (relevant for fee computation) if (!tx_input_check_hash(&info.hasher_check, txinput)) { @@ -1583,6 +1716,12 @@ static bool signing_add_input(TxInputType *txinput) { return false; } + if (is_coinjoin == sectrue) { + if (!coinjoin_add_input(txinput)) { + return false; + } + } + #if !BITCOIN_ONLY if (coin->decred) { if (serialize) { @@ -1671,6 +1810,12 @@ static bool signing_add_output(TxOutputType *txoutput) { bool is_change = is_change_output(&info, txoutput); + uint32_t output_weight = tx_output_weight(coin, txoutput); + tx_weight += output_weight; + if (is_change) { + our_weight += output_weight; + } + // Don't allow adding new external outputs in replacement transactions. There // is actually nothing wrong with adding new external outputs, but the only // way to pay for them would be by supplying a new (verified) external input, @@ -1704,7 +1849,7 @@ static bool signing_add_output(TxOutputType *txoutput) { // Skip confirmation of change-outputs and skip output confirmation altogether // in replacement transactions. - bool skip_confirm = is_change || is_replacement; + bool skip_confirm = is_change || is_replacement || (is_coinjoin == sectrue); int co = compile_output(coin, amount_unit, &root, txoutput, &bin_output, !skip_confirm); if (!skip_confirm) { @@ -1957,7 +2102,7 @@ static bool signing_add_orig_output(TxOutputType *orig_output) { return true; } -static bool signing_confirm_tx(void) { +static bool payment_confirm_tx(void) { if (has_unverified_external_input) { layoutConfirmUnverifiedExternalInputs(); if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { @@ -2089,6 +2234,130 @@ static bool signing_confirm_tx(void) { return true; } +static bool coinjoin_confirm_tx(void) { + // Minimum registrable output amount accepted by the CoinJoin coordinator. The + // CoinJoin request may specify an even lower amount. + const uint64_t MIN_REGISTRABLE_OUTPUT_AMOUNT = 5000; + + // Largest possible weight of an output supported by Trezor (P2TR or P2WSH). + const uint64_t MAX_OUTPUT_WEIGHT = 4 * (8 + 1 + 1 + 1 + 32); + + // Public keys for CoinJoin request signatures. + const uint8_t COINJOIN_REQ_PUBKEY[] = { + 0x02, 0x57, 0x03, 0xbb, 0xe1, 0x5b, 0xb0, 0x8e, 0x98, 0x21, 0xfe, + 0x64, 0xaf, 0xf6, 0xb2, 0xef, 0x1a, 0x31, 0x60, 0xe3, 0x79, 0x9d, + 0xd8, 0xf0, 0xce, 0xbf, 0x2c, 0x79, 0xe8, 0x67, 0xdd, 0x12, 0x5d}; +#if DEBUG_LINK + // secp256k1 public key of m/0h for "all all ... all" seed. + const uint8_t COINJOIN_REQ_PUBKEY_DEBUG[] = { + 0x03, 0x0f, 0xdf, 0x5e, 0x28, 0x9b, 0x5a, 0xef, 0x53, 0x62, 0x90, + 0x95, 0x3a, 0xe8, 0x1c, 0xe6, 0x0e, 0x84, 0x1f, 0xf9, 0x56, 0xf3, + 0x66, 0xac, 0x12, 0x3f, 0xa6, 0x9d, 0xb3, 0xc7, 0x9f, 0x21, 0xb0}; +#endif + + // Finish hashing the CoinJoin request. + hasher_Update(&coinjoin_request_hasher, info.hash_prevouts, + sizeof(info.hash_prevouts)); + hasher_Update(&coinjoin_request_hasher, info.hash_outputs, + sizeof(info.hash_outputs)); + + // Verify the CoinJoin request signature. + uint8_t coinjoin_request_digest[SHA256_DIGEST_LENGTH] = {0}; + hasher_Final(&coinjoin_request_hasher, coinjoin_request_digest); +#if DEBUG_LINK + if (ecdsa_verify_digest(&secp256k1, COINJOIN_REQ_PUBKEY_DEBUG, + coinjoin_request.signature.bytes, + coinjoin_request_digest) == 0) { + // success + } else +#endif + if (ecdsa_verify_digest(&secp256k1, COINJOIN_REQ_PUBKEY, + coinjoin_request.signature.bytes, + coinjoin_request_digest) == 0) { + // success + } else { + fsm_sendFailure(FailureType_Failure_DataError, + _("Invalid signature in coinjoin request.")); + signing_abort(); + return false; + } + + if (has_unverified_external_input) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Unverifiable external input.")); + signing_abort(); + return false; + } + + uint64_t mining_fee = 0; + if (total_out <= total_in) { + mining_fee = total_in - total_out; + } + + // The maximum mining fee that the user should be paying. + uint64_t our_max_mining_fee = + coinjoin_authorization.max_fee_per_kvbyte * ((our_weight + 3) / 4) / 1000; + + // The coordination fee for the user's inputs. + uint64_t our_coordination_fee = + MIN(coinjoin_request.fee_rate, + coinjoin_authorization.max_coordinator_fee_rate) * + coinjoin_coordination_fee_base / FEE_RATE_DECIMALS / 100; + + // Total fees that the user is paying. + uint64_t our_fees = 0; + if (change_out <= total_in - external_in) { + our_fees = total_in - external_in - change_out; + } + + // For the next step we need to estimate an upper bound on the mining fee used + // by the coordinator. The coordinator does not include the base weight of the + // transaction when computing the mining fee, so we take this into account. + uint64_t max_fee_per_output = + MAX_OUTPUT_WEIGHT * mining_fee / (tx_weight - tx_base_weight); + + // Calculate the minimum registrable output amount in a CoinJoin plus the + // mining fee that it would cost to register. Amounts below this value are + // left to the coordinator or miners and effectively constitute an extra fee + // for the user. + uint64_t min_allowed_output_amount_plus_fee = + MIN(coinjoin_request.min_registrable_amount, + MIN_REGISTRABLE_OUTPUT_AMOUNT) + + max_fee_per_output; + + if (our_fees > our_coordination_fee + our_max_mining_fee + + min_allowed_output_amount_plus_fee) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Total fee over threshold.")); + signing_abort(); + return false; + } + + if (coinjoin_authorization.max_rounds < 1) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Exceeded number of coinjoin rounds.")); + signing_abort(); + return false; + } + + coinjoin_authorization.max_rounds -= 1; + if (coinjoin_authorization.max_rounds >= 1) { + config_setCoinJoinAuthorization(&coinjoin_authorization); + } else { + config_setCoinJoinAuthorization(NULL); + } + + return true; +} + +static bool signing_confirm_tx(void) { + if (is_coinjoin == sectrue) { + return coinjoin_confirm_tx(); + } else { + return payment_confirm_tx(); + } +} + static uint32_t signing_hash_type(const TxInputType *txinput) { uint32_t hash_type = SIGHASH_ALL; if (txinput->script_type == InputScriptType_SPENDTAPROOT) { @@ -2851,12 +3120,17 @@ void signing_txack(TransactionType *tx) { return; } - tx_weight += tx_input_weight(coin, &tx->inputs[0]); + uint32_t input_weight = tx_input_weight(coin, &tx->inputs[0]); #if !BITCOIN_ONLY if (coin->decred) { - tx_weight += tx_decred_witness_weight(&tx->inputs[0]); + input_weight += tx_decred_witness_weight(&tx->inputs[0]); } #endif + tx_weight += input_weight; + if (is_internal_input_script_type(tx->inputs[0].script_type)) { + our_weight += input_weight; + our_inputs_len += 1; + } if (tx->inputs[0].script_type != InputScriptType_SPENDTAPROOT && tx->inputs[0].script_type != InputScriptType_EXTERNAL) { @@ -2884,9 +3158,6 @@ void signing_txack(TransactionType *tx) { } } } else if (is_segwit_input_script_type(tx->inputs[0].script_type)) { - if (!to.is_segwit) { - tx_weight += TXSIZE_SEGWIT_OVERHEAD + to.inputs_len; - } #if !ENABLE_SEGWIT_NONSEGWIT_MIXING // don't mix segwit and non-segwit inputs if (idx1 == 0) { @@ -3006,7 +3277,6 @@ void signing_txack(TransactionType *tx) { !signing_add_output(&tx->outputs[0])) { return; } - tx_weight += tx_output_weight(coin, &tx->outputs[0]); if (tx->outputs[0].has_orig_hash) { memcpy(&output, &tx->outputs[0], sizeof(output)); @@ -3663,3 +3933,7 @@ void signing_abort(void) { memzero(&root, sizeof(root)); memzero(&node, sizeof(node)); } + +bool signing_is_preauthorized(void) { + return signing && (is_coinjoin == sectrue); +} diff --git a/legacy/firmware/signing.h b/legacy/firmware/signing.h index 85ee17d1a5..4405bf7ec4 100644 --- a/legacy/firmware/signing.h +++ b/legacy/firmware/signing.h @@ -29,8 +29,9 @@ #include "messages-bitcoin.pb.h" void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root, - PathSchema unlock); + const AuthorizeCoinJoin *authorization, PathSchema unlock); void signing_abort(void); void signing_txack(TransactionType *tx); +bool signing_is_preauthorized(void); #endif diff --git a/tests/device_tests/bitcoin/test_authorize_coinjoin.py b/tests/device_tests/bitcoin/test_authorize_coinjoin.py index 5c08261fd0..fcee1652a4 100644 --- a/tests/device_tests/bitcoin/test_authorize_coinjoin.py +++ b/tests/device_tests/bitcoin/test_authorize_coinjoin.py @@ -44,7 +44,6 @@ ROUND_ID_LEN = 32 SLIP25_PATH = parse_path("m/10025h") -@pytest.mark.skip_t1 @pytest.mark.setup_client(pin=PIN) def test_sign_tx(client: Client): # NOTE: FAKE input tx @@ -251,7 +250,6 @@ def test_sign_tx(client: Client): ) -@pytest.mark.skip_t1 def test_sign_tx_large(client: Client): # NOTE: FAKE input tx @@ -474,7 +472,6 @@ def test_sign_tx_spend(client: Client): ) -@pytest.mark.skip_t1 def test_wrong_coordinator(client: Client): # Ensure that a preauthorized GetOwnershipProof fails if the commitment_data doesn't match the coordinator. @@ -501,7 +498,6 @@ def test_wrong_coordinator(client: Client): ) -@pytest.mark.skip_t1 def test_wrong_account_type(client: Client): params = { "client": client, @@ -528,7 +524,6 @@ def test_wrong_account_type(client: Client): ) -@pytest.mark.skip_t1 def test_cancel_authorization(client: Client): # Ensure that a preauthorized GetOwnershipProof fails if the commitment_data doesn't match the coordinator. @@ -693,7 +688,6 @@ def test_get_address(client: Client): ) -@pytest.mark.skip_t1 def test_multisession_authorization(client: Client): # Authorize CoinJoin with www.example1.com in session 1. btc.authorize_coinjoin( diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 493a72243d..011f0c64cc 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -1,9 +1,15 @@ { "T1": { "device_tests": { +"T1_bitcoin-test_authorize_coinjoin.py::test_cancel_authorization": "60e18060db1756fd0199dd381b41c4b20da89ed1acfe3e1378e2f511d97732bf", "T1_bitcoin-test_authorize_coinjoin.py::test_get_address": "402c3f89f6ad5fd3bc78f804b376c36c918fc685cc2c77b38c6ae030af738d22", "T1_bitcoin-test_authorize_coinjoin.py::test_get_public_key": "9b3c916759b79048a4ab3e3fe8ce0ea0cf8d4ae6cfb66a5d712f21edfdb01782", +"T1_bitcoin-test_authorize_coinjoin.py::test_multisession_authorization": "d2ac218c6bc361f732a65daee2ae7183ca935a7b246545ebe6346dbd33d0a702", +"T1_bitcoin-test_authorize_coinjoin.py::test_sign_tx": "f630f414daeeb8e0aa3d01ed38a512cb9bfb83abd0c022e01dd02d6824ade61d", +"T1_bitcoin-test_authorize_coinjoin.py::test_sign_tx_large": "4365228d89327c72b2d0fa4d487fb28c19ed832e94d8c0c71db83b060c06074a", "T1_bitcoin-test_authorize_coinjoin.py::test_sign_tx_spend": "edeb75022cc6bff15d1274ba9bac4cf41dd8ea5771436010ae07fd441dc73b69", +"T1_bitcoin-test_authorize_coinjoin.py::test_wrong_account_type": "60e18060db1756fd0199dd381b41c4b20da89ed1acfe3e1378e2f511d97732bf", +"T1_bitcoin-test_authorize_coinjoin.py::test_wrong_coordinator": "60e18060db1756fd0199dd381b41c4b20da89ed1acfe3e1378e2f511d97732bf", "T1_bitcoin-test_bcash.py::test_attack_change_input": "6111e313995d38c3970c92e48047fe4088c83666c64c6c859f69a232ad62829b", "T1_bitcoin-test_bcash.py::test_send_bch_change": "6111e313995d38c3970c92e48047fe4088c83666c64c6c859f69a232ad62829b", "T1_bitcoin-test_bcash.py::test_send_bch_multisig_change": "0962a2e630e06b6d20282cc241be40f41bc1648d0a26247c7c008f32a197d0cb",