diff --git a/legacy/firmware/signing.c b/legacy/firmware/signing.c index 9f37150aa..f18100cad 100644 --- a/legacy/firmware/signing.c +++ b/legacy/firmware/signing.c @@ -57,6 +57,9 @@ enum { #if !BITCOIN_ONLY STAGE_REQUEST_3_PREV_EXTRADATA, #endif + STAGE_REQUEST_3_ORIG_INPUT, + STAGE_REQUEST_3_ORIG_OUTPUT, + STAGE_REQUEST_3_ORIG_NONLEGACY_INPUT, STAGE_REQUEST_4_INPUT, STAGE_REQUEST_4_OUTPUT, STAGE_REQUEST_NONLEGACY_INPUT, @@ -138,10 +141,7 @@ typedef struct { static TxInfo info; /* Variables specific to replacement transactions. */ -static bool is_replacement; // Is this a replacement transaction? -static bool have_orig_verif_input; // Is orig_verif_input, sig and node set? -static uint32_t orig_verif_input_idx; // Index of orig_verif_input in orig tx. -static TxInputType orig_verif_input; // The input for signature verification. +static bool is_replacement; // Is this a replacement transaction? static TxInfo orig_info; static uint8_t orig_hash[32]; // TXID of the original transaction. @@ -228,7 +228,7 @@ foreach I (idx1): Request input I2 orig_hash, orig_index STAGE_REQUEST_1_ORIG_INPUT Check I matches I2 Add I2 to original segwit sub-hashes - Add I2 to orig_hash_inputs_check + Add I2 to orig_info.hash_inputs_check if (Decred) Return I @@ -266,6 +266,34 @@ foreach I (idx1): Request prevhash extra data (if applicable) STAGE_REQUEST_3_PREV_EXTRADATA Calculate hash of streamed tx, compare to prevhash I +if (is_replacement) + foreach orig I (idx1): + if (orig idx1 is not legacy) + Request input I, orig_hash, idx1 STAGE_REQUEST_3_ORIG_NONLEGACY_INPUT + Add I to OuterTransactionChecksum + Verify signature of I if I is internal + else + foreach orig I (idx2): + Request input I, orig_hash, idx2 STAGE_REQUEST_3_ORIG_INPUT + Add I to InnerTransactionChecksum + Add I to LegacyTransactionDigest + if idx1 == idx2 + Add I to OuterTransactionChecksum + Save signature for verification + + Ensure InnerTransactionChecksum matches orig_info.hash_inputs_check + + foreach orig O (idx2): + Request output O, orig_hash, idx2 STAGE_REQUEST_3_ORIG_OUTPUT + Add O to InnerTransactionChecksum + Add O to LegacyTransactionDigest + + Ensure InnerTransactionChecksum matches orig_hash_outputs + Verify signature of LegacyTransactionDigest + +Ensure OuterTransactionChecksum matches orig_info.hash_inputs_check + + Phase2: sign inputs, check that nothing changed =============================================== @@ -371,9 +399,8 @@ void send_req_1_orig_meta(void) { resp.request_type = RequestType_TXMETA; resp.has_details = true; resp.details.has_tx_hash = true; - resp.details.tx_hash.size = input.orig_hash.size; - memcpy(resp.details.tx_hash.bytes, input.orig_hash.bytes, - resp.details.tx_hash.size); + resp.details.tx_hash.size = sizeof(orig_hash); + memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size); msg_write(MessageType_MessageType_TxRequest, &resp); } @@ -385,9 +412,8 @@ void send_req_1_orig_input(void) { resp.details.has_request_index = true; resp.details.request_index = idx2; resp.details.has_tx_hash = true; - resp.details.tx_hash.size = input.orig_hash.size; - memcpy(resp.details.tx_hash.bytes, input.orig_hash.bytes, - resp.details.tx_hash.size); + resp.details.tx_hash.size = sizeof(orig_hash); + memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size); msg_write(MessageType_MessageType_TxRequest, &resp); } @@ -409,9 +435,8 @@ void send_req_2_orig_output(void) { resp.details.has_request_index = true; resp.details.request_index = idx2; resp.details.has_tx_hash = true; - resp.details.tx_hash.size = output.orig_hash.size; - memcpy(resp.details.tx_hash.bytes, output.orig_hash.bytes, - resp.details.tx_hash.size); + resp.details.tx_hash.size = sizeof(orig_hash); + memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size); msg_write(MessageType_MessageType_TxRequest, &resp); } @@ -500,6 +525,45 @@ void send_req_3_prev_extradata(uint32_t chunk_offset, uint32_t chunk_len) { } #endif +void send_req_3_orig_nonlegacy_input(void) { + signing_stage = STAGE_REQUEST_3_ORIG_NONLEGACY_INPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXORIGINPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx1; + resp.details.has_tx_hash = true; + resp.details.tx_hash.size = sizeof(orig_hash); + memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size); + msg_write(MessageType_MessageType_TxRequest, &resp); +} + +void send_req_3_orig_input(void) { + signing_stage = STAGE_REQUEST_3_ORIG_INPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXORIGINPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx2; + resp.details.has_tx_hash = true; + resp.details.tx_hash.size = sizeof(orig_hash); + memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size); + msg_write(MessageType_MessageType_TxRequest, &resp); +} + +void send_req_3_orig_output(void) { + signing_stage = STAGE_REQUEST_3_ORIG_OUTPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXORIGOUTPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx2; + resp.details.has_tx_hash = true; + resp.details.tx_hash.size = sizeof(orig_hash); + memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size); + msg_write(MessageType_MessageType_TxRequest, &resp); +} + void send_req_4_input(void) { signing_stage = STAGE_REQUEST_4_INPUT; resp.has_request_type = true; @@ -650,6 +714,36 @@ void phase2_request_next_input(void) { } } +void phase2_request_orig_input(void) { + if (idx1 < orig_info.inputs_count) { + if (idx1 == 0) { + // Reset outer transaction check. + hasher_Reset(&hasher_check); + } + + if (idx1 == orig_info.next_legacy_input) { + idx2 = 0; + send_req_3_orig_input(); + } else { + send_req_3_orig_nonlegacy_input(); + } + } else { + // Ensure that the original transaction inputs haven't changed for the outer + // transaction check. + uint8_t hash[32]; + hasher_Final(&hasher_check, hash); + if (memcmp(hash, orig_info.hash_inputs_check, 32) != 0) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Transaction has changed during signing")); + signing_abort(); + return; + } + + idx1 = 0; + phase2_request_next_input(); + } +} + static bool extract_input_multisig_fp(TxInfo *tx_info, const TxInputType *txinput) { if (txinput->has_multisig && !tx_info->multisig_fp_mismatch) { @@ -985,8 +1079,6 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, memzero(&output, sizeof(TxOutputType)); memzero(&resp, sizeof(TxRequest)); is_replacement = false; - have_orig_verif_input = false; - orig_verif_input_idx = 0xffffffff; signing = true; progress = 0; // we step by 500/inputs_count per input in phase1 and phase2 @@ -1495,7 +1587,13 @@ static bool signing_check_prevtx_hash(void) { } else #endif { - phase2_request_next_input(); + if (is_replacement) { + // Verify original transaction. + phase2_request_orig_input(); + } else { + // Proceed to transaction signing. + phase2_request_next_input(); + } } } @@ -1643,6 +1741,14 @@ static bool save_signature(TxInputType *txinput) { } static bool signing_add_orig_input(TxInputType *orig_input) { + // hash all input data to check it later + if (!tx_input_check_hash(&orig_info.hasher_check, orig_input)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to hash input")); + signing_abort(); + return false; + } + if (!fill_input_script_pubkey(coin, &root, orig_input)) { fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to derive scriptPubKey")); @@ -1681,7 +1787,7 @@ static bool signing_add_orig_input(TxInputType *orig_input) { return false; } - // Add input to original TXID computation before script_sig is overwritten. + // Add input to original TXID computation. if (!tx_serialize_input_hash(&tp, orig_input)) { fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize input")); @@ -1689,44 +1795,13 @@ static bool signing_add_orig_input(TxInputType *orig_input) { return false; } - // The first original non-multisig input that has address_n set and a - // signature gets chosen as the verification input. Set script_sig for legacy - // digest computation. - // NOTE: Supporting replacement transactions where all internal inputs are - // multisig would require checking the signatures of all of the original - // internal inputs or not allowing unverified external inputs in transactions - // where multisig inputs are present. - if (!have_orig_verif_input && orig_input->address_n_count != 0 && - !orig_input->has_multisig && - ((orig_input->has_script_sig && orig_input->script_sig.size != 0) || - (orig_input->has_witness && orig_input->witness.size > 1))) { - // Save the signature before script_sig is overwritten. - if (!save_signature(orig_input)) { - return false; - } - - // Derive node.public_key and fill script_sig with the legacy scriptPubKey - // (aka BIP-143 script code), which is what our code expects here in order - // to properly compute the legacy transaction digest or BIP-143 transaction - // digest. - if (!derive_node(orig_input) || !fill_input_script_sig(orig_input)) { - return false; + // Remember the first original internal legacy input. + if ((orig_input->script_type == InputScriptType_SPENDMULTISIG || + orig_input->script_type == InputScriptType_SPENDADDRESS) && + !coin->force_bip143 && !coin->overwintered) { + if (orig_info.next_legacy_input == 0xffffffff) { + orig_info.next_legacy_input = idx2; } - - // Save the verification input with script_sig set to scriptPubKey. - memcpy(&orig_verif_input, orig_input, sizeof(TxInputType)); - orig_verif_input_idx = idx2; - have_orig_verif_input = true; - } else { - orig_input->script_sig.size = 0; - } - - // Add input to original legacy digest computation now that script_sig is set. - if (!tx_serialize_input_hash(&ti, orig_input)) { - fsm_sendFailure(FailureType_Failure_ProcessError, - _("Failed to serialize input")); - signing_abort(); - return false; } return true; @@ -1748,10 +1823,8 @@ static bool signing_add_orig_output(TxOutputType *orig_output) { return false; } - // Add output to the original legacy digest computation (ti) and to the - // original TXID computation (tp). - if (!tx_serialize_output_hash(&ti, &orig_bin_output) || - !tx_serialize_output_hash(&tp, &orig_bin_output)) { + // Add output to original TXID computation. + if (!tx_serialize_output_hash(&tp, &orig_bin_output)) { fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize output")); signing_abort(); @@ -2158,39 +2231,30 @@ static void signing_hash_zip244(const TxInfo *tx_info, } #endif -static bool signing_check_orig_tx(void) { - uint8_t hash[32] = {0}; +static bool signing_verify_orig_nonlegacy_input(TxInputType *orig_input) { + // Nothing to verify for external inputs. + if (orig_input->script_type == InputScriptType_EXTERNAL) { + return true; + } - // Finalize original TXID computation and ensure it matches orig_hash. - tx_hash_final(&tp, hash, true); - if (memcmp(hash, orig_hash, sizeof(orig_hash)) != 0) { - // This may happen if incorrect information is supplied in the TXORIGINPUT - // or TXORIGOUTPUT responses or if the device is loaded with the wrong seed, - // because we derive the scriptPubKeys of change-outputs from the seed using - // the provided path. - fsm_sendFailure(FailureType_Failure_ProcessError, - _("Invalid original TXID.")); - signing_abort(); + // Save the signature before script_sig is overwritten. + if (!save_signature(orig_input)) { return false; } - if (!have_orig_verif_input) { - fsm_sendFailure(FailureType_Failure_ProcessError, - _("The original transaction must specify address_n for at " - "least one input.")); - signing_abort(); + // Derive node.public_key and fill script_sig with the legacy scriptPubKey + // (aka BIP-143 script code), which is what our code expects here in order + // to properly compute the BIP-143 transaction digest. + if (!derive_node(orig_input) || !fill_input_script_sig(orig_input)) { return false; } - // Finish computation of BIP-143/BIP-341/ZIP-243 sub-hashes. - tx_info_finish(&orig_info); - // Compute the signed digest and verify signature. - uint32_t hash_type = signing_hash_type(&orig_verif_input); + uint8_t hash[32] = {0}; + uint32_t hash_type = signing_hash_type(orig_input); bool valid = false; - if (orig_verif_input.script_type == InputScriptType_SPENDTAPROOT) { - signing_hash_bip341(&orig_info, orig_verif_input_idx, hash_type & 0xff, - hash); + if (orig_input->script_type == InputScriptType_SPENDTAPROOT) { + signing_hash_bip341(&orig_info, idx1, hash_type & 0xff, hash); uint8_t output_public_key[32] = {0}; valid = (zkp_bip340_tweak_public_key(node.public_key + 1, NULL, output_public_key) == 0) && @@ -2198,18 +2262,11 @@ static bool signing_check_orig_tx(void) { } else { #if !BITCOIN_ONLY if (coin->overwintered) { - signing_hash_zip243(&orig_info, &orig_verif_input, hash); + signing_hash_zip243(&orig_info, orig_input, hash); } else #endif { - if (is_segwit_input_script_type(&orig_verif_input) || - coin->force_bip143) { - signing_hash_bip143(&orig_info, &orig_verif_input, hash); - } else { - // Finalize legacy digest computation. - hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4); - tx_hash_final(&ti, hash, false); - } + signing_hash_bip143(&orig_info, orig_input, hash); } #ifdef USE_SECP256K1_ZKP_ECDSA @@ -2232,6 +2289,142 @@ static bool signing_check_orig_tx(void) { return valid; } +static bool signing_verify_orig_legacy_input(void) { + // Finalize legacy digest computation. + uint32_t hash_type = signing_hash_type(&input); + hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4); + + // Compute the signed digest and verify signature. + uint8_t hash[32] = {0}; + tx_hash_final(&ti, hash, false); + + bool valid = false; +#ifdef USE_SECP256K1_ZKP_ECDSA + if (coin->curve->params == &secp256k1) { + valid = zkp_ecdsa_verify_digest(coin->curve->params, node.public_key, sig, + hash) == 0; + } else +#endif + { + valid = ecdsa_verify_digest(coin->curve->params, node.public_key, sig, + hash) == 0; + } + + if (!valid) { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature.")); + signing_abort(); + } + + return valid; +} + +static bool signing_hash_orig_input(TxInputType *orig_input) { + if (idx2 == 0) { + uint32_t branch_id = 0; +#if !BITCOIN_ONLY + branch_id = orig_info.branch_id; +#endif + tx_init(&ti, orig_info.inputs_count, orig_info.outputs_count, + orig_info.version, orig_info.lock_time, orig_info.expiry, branch_id, + 0, coin->curve->hasher_sign, coin->overwintered, + orig_info.version_group_id, orig_info.timestamp); + // Reset the inner transaction check. + hasher_Reset(&orig_info.hasher_check); + } + + // Add input to the inner transaction check. + if (!tx_input_check_hash(&orig_info.hasher_check, orig_input)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to hash input")); + signing_abort(); + return false; + } + + if (idx2 == idx1) { + // Add input to the outer transaction check. + if (!tx_input_check_hash(&hasher_check, orig_input)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to hash input")); + signing_abort(); + return false; + } + + // Save the signature before script_sig is overwritten. + if (!save_signature(orig_input)) { + return false; + } + + // Derive node.public_key and fill script_sig with the legacy + // scriptPubKey which is what our code expects here in order to properly + // compute the transaction digest. + if (!derive_node(orig_input) || !fill_input_script_sig(orig_input)) { + return false; + } + + memcpy(&input, orig_input, sizeof(input)); + } else { + if (orig_info.next_legacy_input == idx1 && idx2 > idx1 && + (orig_input->script_type == InputScriptType_SPENDADDRESS || + orig_input->script_type == InputScriptType_SPENDMULTISIG)) { + orig_info.next_legacy_input = idx2; + } + orig_input->script_sig.size = 0; + } + + // Add input to original legacy digest computation now that script_sig is + // set. + if (!tx_serialize_input_hash(&ti, orig_input)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to serialize input")); + signing_abort(); + return false; + } + + return true; +} + +static bool signing_hash_orig_output(TxOutputType *orig_output) { + if (compile_output(coin, amount_unit, &root, orig_output, &bin_output, + false) <= 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile output")); + signing_abort(); + return false; + } + + // Add the output to the inner transaction check. + tx_output_hash(&orig_info.hasher_check, &bin_output, coin->decred); + + // Add the output to original legacy digest computation + if (!tx_serialize_output_hash(&ti, &bin_output)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to serialize output")); + signing_abort(); + return false; + } + + return true; +} + +static bool signing_check_orig_tx(void) { + uint8_t hash[32] = {0}; + + // Finalize original TXID computation and ensure it matches orig_hash. + tx_hash_final(&tp, hash, true); + if (memcmp(hash, orig_hash, sizeof(orig_hash)) != 0) { + // This may happen if incorrect information is supplied in the TXORIGINPUT + // or TXORIGOUTPUT responses or if the device is loaded with the wrong seed, + // because we derive the scriptPubKeys of change-outputs from the seed using + // the provided path. + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Invalid original TXID.")); + signing_abort(); + return false; + } + + return true; +} + static void phase1_finish(void) { #if !BITCOIN_ONLY if (coin->decred) { @@ -2240,8 +2433,9 @@ static void phase1_finish(void) { } #endif - // Compute BIP143 hashPrevouts, hashSequence and hashOutputs. + // Finish computation of BIP-143/BIP-341/ZIP-243 sub-hashes. tx_info_finish(&info); + tx_info_finish(&orig_info); if (is_replacement) { if (!signing_check_orig_tx()) { @@ -2254,11 +2448,17 @@ static void phase1_finish(void) { } if (taproot_only) { - // All internal inputs are Taproot. We do not need to verify them so we - // proceed directly to Phase 2, where the transaction will be signed. We can - // trust the amounts and scriptPubKeys, because if an invalid value is - // provided then all issued signatures will be invalid. - phase2_request_next_input(); + // All internal inputs are Taproot. We do not need to verify that their + // parameters match previous transactions. We can trust the amounts and + // scriptPubKeys, because if an invalid value is provided then all issued + // signatures will be invalid. + if (is_replacement) { + // Verify original transaction. + phase2_request_orig_input(); + } else { + // Proceed directly to transaction signing. + phase2_request_next_input(); + } #if !BITCOIN_ONLY } else if (coin->overwintered && info.version == 5) { // ZIP-244 transactions are treated same as Taproot. @@ -2711,11 +2911,6 @@ void signing_txack(TransactionType *tx) { return; } - // Initialize computation of original legacy digest. - tx_init(&ti, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, - tx->expiry, tx->branch_id, 0, coin->curve->hasher_sign, - coin->overwintered, tx->version_group_id, tx->timestamp); - // Initialize computation of original TXID. tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, tx->expiry, tx->branch_id, tx->extra_data_len, @@ -3000,6 +3195,79 @@ void signing_txack(TransactionType *tx) { } return; #endif + + case STAGE_REQUEST_3_ORIG_NONLEGACY_INPUT: + if (!signing_validate_input(tx->inputs)) { + return; + } + + // Add input to the outer transaction check. + if (!tx_input_check_hash(&hasher_check, tx->inputs)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to hash input")); + signing_abort(); + return; + } + + if (!signing_verify_orig_nonlegacy_input(tx->inputs)) { + return; + } + + idx1++; + phase2_request_orig_input(); + return; + + case STAGE_REQUEST_3_ORIG_INPUT: + if (!signing_validate_input(tx->inputs) || + !signing_hash_orig_input(tx->inputs)) { + return; + } + + idx2++; + if (idx2 < orig_info.inputs_count) { + send_req_3_orig_input(); + } else { + // Ensure that the original transaction inputs haven't changed for the + // inner transaction check. + if (!tx_info_check_inputs_hash(&orig_info)) { + return; + } + + // Reset the inner transaction check. + hasher_Reset(&orig_info.hasher_check); + idx2 = 0; + send_req_3_orig_output(); + } + + return; + + case STAGE_REQUEST_3_ORIG_OUTPUT: + if (!signing_validate_output(tx->outputs) || + !signing_hash_orig_output(tx->outputs)) { + return; + } + + idx2++; + if (idx2 < orig_info.outputs_count) { + send_req_3_orig_output(); + } else { + // Ensure that the original transaction outputs haven't changed for the + // inner transaction check. + if (!tx_info_check_outputs_hash(&orig_info)) { + return; + } + + // Verify original signature. + if (!signing_verify_orig_legacy_input()) { + return; + } + + idx1++; + phase2_request_orig_input(); + } + + return; + case STAGE_REQUEST_4_INPUT: if (!signing_validate_input(&tx->inputs[0])) { return; diff --git a/tests/device_tests/bitcoin/test_signtx_replacement.py b/tests/device_tests/bitcoin/test_signtx_replacement.py index ea31895a3..5b230e5cd 100644 --- a/tests/device_tests/bitcoin/test_signtx_replacement.py +++ b/tests/device_tests/bitcoin/test_signtx_replacement.py @@ -133,10 +133,10 @@ def test_p2pkh_fee_bump(client: Client): request_meta(TXHASH_beafc7), request_input(0, TXHASH_beafc7), request_output(0, TXHASH_beafc7), - request_orig_input(0, TXHASH_50f6f1), (tt, request_orig_input(0, TXHASH_50f6f1)), - (tt, request_orig_output(0, TXHASH_50f6f1)), - (tt, request_orig_output(1, TXHASH_50f6f1)), + request_orig_input(0, TXHASH_50f6f1), + request_orig_output(0, TXHASH_50f6f1), + request_orig_output(1, TXHASH_50f6f1), request_input(0), request_output(0), request_output(1), diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index a8c3b2a98..95799cbc0 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -276,11 +276,11 @@ "T1_bitcoin-test_signtx_replacement.py::test_attack_steal_change": "3a79850fe95abbd2ca342fc62ba7f6404c4f437977764839a8146789ac811fce", "T1_bitcoin-test_signtx_replacement.py::test_p2pkh_fee_bump": "a386c4c61f5fe3b073db6b8725193f5bf7881d7b6fae175ff7d879f7c94eb795", "T1_bitcoin-test_signtx_replacement.py::test_p2tr_fee_bump": "00f4f745f88f1072f495e513d6a7747db494bb7c785c32d167652fe1d79509be", -"T1_bitcoin-test_signtx_replacement.py::test_p2tr_invalid_signature": "1f46b6ec89be1742dc4649153e6d13b172c9e93be7c91063650f814e631b757c", +"T1_bitcoin-test_signtx_replacement.py::test_p2tr_invalid_signature": "00f4f745f88f1072f495e513d6a7747db494bb7c785c32d167652fe1d79509be", "T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_finalize": "7b3b6ebee78fed0e64c9689c64c5fbf73099812c5452b2fd2dbe2c23cd69b669", "T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_in_p2sh_fee_bump_from_external": "3d7d6ad6eafe3399f9d3aa5bb358e4c04f83a98b68ccfa8a124b7e63c0f6df78", "T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_in_p2sh_remove_change": "14544554428651eb22515a88418c71f66de68e581ea2df5b2259843b1931e8fc", -"T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_invalid_signature": "ee190f0d9f972f005c706a818872a18778aa570737b65e5bebbe6edbe6396008", +"T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_invalid_signature": "7b3b6ebee78fed0e64c9689c64c5fbf73099812c5452b2fd2dbe2c23cd69b669", "T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_op_return_fee_bump": "897291942225c61f9b641c7a1287960e7c21d80ada6c75414399d76edc41054c", "T1_bitcoin-test_signtx_segwit.py::test_attack_change_input_address": "fb82fd6c801028181ea89cc517c1401ca78d319b1b9d60e19e5b1cc0107a083f", "T1_bitcoin-test_signtx_segwit.py::test_attack_mixed_inputs": "614206bc232bf0a07846361bd21a2a0520c8384f052ba362d227e17c33013270",