feat(legacy): Verify signatures of all original internal inputs in replacement transactions.

release/22.05
Andrew Kozlik 2 years ago committed by Martin Milata
parent 706b74aaeb
commit 903fa9b471

@ -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;

@ -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),

@ -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",

Loading…
Cancel
Save