From 26d1fad2aa9f1c00a4ec23f7a7bd5be959613ebd Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Mon, 2 May 2022 09:04:22 +0200 Subject: [PATCH] feat(legacy): Support Zcash version 5 transaction format. --- legacy/firmware/.changelog.d/2031.added | 1 + legacy/firmware/signing.c | 209 +++++++++++++++++++---- legacy/firmware/transaction.c | 70 +++++--- legacy/firmware/transaction.h | 4 +- tests/device_tests/zcash/test_sign_tx.py | 3 +- tests/ui_tests/fixtures.json | 7 + 6 files changed, 244 insertions(+), 50 deletions(-) create mode 100644 legacy/firmware/.changelog.d/2031.added diff --git a/legacy/firmware/.changelog.d/2031.added b/legacy/firmware/.changelog.d/2031.added new file mode 100644 index 000000000..5255aee47 --- /dev/null +++ b/legacy/firmware/.changelog.d/2031.added @@ -0,0 +1 @@ +Support Zcash version 5 transaction format. diff --git a/legacy/firmware/signing.c b/legacy/firmware/signing.c index 53a910c84..e59052c38 100644 --- a/legacy/firmware/signing.c +++ b/legacy/firmware/signing.c @@ -116,6 +116,7 @@ typedef struct { uint32_t timestamp; #if !BITCOIN_ONLY uint32_t branch_id; + uint8_t hash_header[32]; #endif Hasher hasher_prevouts; Hasher hasher_amounts; @@ -874,7 +875,7 @@ static bool tx_info_init(TxInfo *tx_info, uint32_t inputs_count, return false; } - if (tx_info->version != 4) { + if (tx_info->version != 4 && tx_info->version != 5) { fsm_sendFailure(FailureType_Failure_DataError, _("Unsupported transaction version.")); signing_abort(); @@ -891,13 +892,27 @@ static bool tx_info_init(TxInfo *tx_info, uint32_t inputs_count, #if !BITCOIN_ONLY if (coin->overwintered) { - // ZIP-243 - hasher_InitParam(&tx_info->hasher_prevouts, HASHER_BLAKE2B_PERSONAL, - "ZcashPrevoutHash", 16); - hasher_InitParam(&tx_info->hasher_sequences, HASHER_BLAKE2B_PERSONAL, - "ZcashSequencHash", 16); - hasher_InitParam(&tx_info->hasher_outputs, HASHER_BLAKE2B_PERSONAL, - "ZcashOutputsHash", 16); + if (tx_info->version == 5) { + // ZIP-244 + hasher_InitParam(&tx_info->hasher_prevouts, HASHER_BLAKE2B_PERSONAL, + "ZTxIdPrevoutHash", 16); + hasher_InitParam(&tx_info->hasher_amounts, HASHER_BLAKE2B_PERSONAL, + "ZTxTrAmountsHash", 16); + hasher_InitParam(&tx_info->hasher_scriptpubkeys, HASHER_BLAKE2B_PERSONAL, + "ZTxTrScriptsHash", 16); + hasher_InitParam(&tx_info->hasher_sequences, HASHER_BLAKE2B_PERSONAL, + "ZTxIdSequencHash", 16); + hasher_InitParam(&tx_info->hasher_outputs, HASHER_BLAKE2B_PERSONAL, + "ZTxIdOutputsHash", 16); + } else { + // ZIP-243 + hasher_InitParam(&tx_info->hasher_prevouts, HASHER_BLAKE2B_PERSONAL, + "ZcashPrevoutHash", 16); + hasher_InitParam(&tx_info->hasher_sequences, HASHER_BLAKE2B_PERSONAL, + "ZcashSequencHash", 16); + hasher_InitParam(&tx_info->hasher_outputs, HASHER_BLAKE2B_PERSONAL, + "ZcashOutputsHash", 16); + } } else #endif { @@ -969,8 +984,13 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, next_legacy_input = 0xffffffff; + uint32_t branch_id = 0; +#if !BITCOIN_ONLY + branch_id = info.branch_id; +#endif + tx_init(&to, info.inputs_count, info.outputs_count, info.version, - info.lock_time, info.expiry, 0, coin->curve->hasher_sign, + info.lock_time, info.expiry, branch_id, 0, coin->curve->hasher_sign, coin->overwintered, info.version_group_id, info.timestamp); #if !BITCOIN_ONLY @@ -979,7 +999,7 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, to.is_decred = true; tx_init(&ti, info.inputs_count, info.outputs_count, info.version, - info.lock_time, info.expiry, 0, coin->curve->hasher_sign, + info.lock_time, info.expiry, branch_id, 0, coin->curve->hasher_sign, coin->overwintered, info.version_group_id, info.timestamp); ti.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); ti.is_decred = true; @@ -1301,6 +1321,29 @@ static bool tx_info_add_output(TxInfo *tx_info, return true; } +#if !BITCOIN_ONLY +static void txinfo_fill_zip244_header_hash(TxInfo *tx_info) { + // `T.1: header_digest` field. + // https://zips.z.cash/zip-0244#t-1-header-digest + Hasher hasher = {0}; + hasher_InitParam(&hasher, HASHER_BLAKE2B_PERSONAL, "ZTxIdHeadersHash", 16); + + // T.1a: version (4-byte little-endian version identifier including + // overwintered flag) + uint32_t ver = tx_info->version | TX_OVERWINTERED; + hasher_Update(&hasher, (const uint8_t *)&ver, 4); + // T.1b: version_group_id (4-byte little-endian version group identifier) + hasher_Update(&hasher, (const uint8_t *)&tx_info->version_group_id, 4); + // T.1c: consensus_branch_id (4-byte little-endian consensus branch id) + hasher_Update(&hasher, (const uint8_t *)&tx_info->branch_id, 4); + // T.1d: lock_time (4-byte little-endian nLockTime value) + hasher_Update(&hasher, (const uint8_t *)&tx_info->lock_time, 4); + // T.1e: expiry_height (4-byte little-endian block height) + hasher_Update(&hasher, (const uint8_t *)&tx_info->expiry, 4); + hasher_Final(&hasher, tx_info->hash_header); +} +#endif + static void tx_info_finish(TxInfo *tx_info) { hasher_Final(&tx_info->hasher_prevouts, tx_info->hash_prevouts); hasher_Final(&tx_info->hasher_amounts, tx_info->hash_amounts); @@ -1323,6 +1366,12 @@ static void tx_info_finish(TxInfo *tx_info) { memcpy(tx_info->hash_outputs143, tx_info->hash_outputs, sizeof(tx_info->hash_outputs)); } + +#if !BITCOIN_ONLY + if (coin->overwintered && tx_info->version == 5) { + txinfo_fill_zip244_header_hash(tx_info); + } +#endif } static bool signing_add_input(TxInputType *txinput) { @@ -1918,6 +1967,7 @@ static void signing_hash_bip341(const TxInfo *tx_info, uint32_t i, static void signing_hash_zip243(const TxInfo *tx_info, const TxInputType *txinput, uint8_t *hash) { uint32_t hash_type = signing_hash_type(txinput); + const uint8_t null_bytes[32] = {0}; uint8_t personal[16] = {0}; memcpy(personal, "ZcashSigHash", 12); memcpy(personal + 12, &tx_info->branch_id, 4); @@ -1938,18 +1988,17 @@ static void signing_hash_zip243(const TxInfo *tx_info, // 5. hashOutputs hasher_Update(&hasher_preimage, tx_info->hash_outputs, 32); // 6. hashJoinSplits - hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); + hasher_Update(&hasher_preimage, null_bytes, 32); // 7. hashShieldedSpends - hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); + hasher_Update(&hasher_preimage, null_bytes, 32); // 8. hashShieldedOutputs - hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); + hasher_Update(&hasher_preimage, null_bytes, 32); // 9. nLockTime hasher_Update(&hasher_preimage, (const uint8_t *)&tx_info->lock_time, 4); // 10. expiryHeight hasher_Update(&hasher_preimage, (const uint8_t *)&tx_info->expiry, 4); // 11. valueBalance - hasher_Update(&hasher_preimage, - (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00", 8); + hasher_Update(&hasher_preimage, null_bytes, 8); // 12. nHashType hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, 4); // 13a. outpoint @@ -1966,6 +2015,80 @@ static void signing_hash_zip243(const TxInfo *tx_info, } #endif +#if !BITCOIN_ONLY +static void signing_hash_zip244(const TxInfo *tx_info, + const TxInputType *txinput, uint8_t *hash) { + Hasher hasher = {0}; + + // `S.2g: txin_sig_digest` field for signature digest computation. + // https://zips.z.cash/zip-0244#s-2g-txin-sig-digest + uint8_t txin_sig_digest[32] = {0}; + hasher_InitParam(&hasher, HASHER_BLAKE2B_PERSONAL, "Zcash___TxInHash", 16); + // S.2g.i: prevout (field encoding) + tx_prevout_hash(&hasher, txinput); + // S.2g.ii: value (8-byte signed little-endian) + hasher_Update(&hasher, (const uint8_t *)&txinput->amount, 8); + // S.2g.iii: scriptPubKey (field encoding) + tx_script_hash(&hasher, txinput->script_pubkey.size, + txinput->script_pubkey.bytes); + // S.2g.iv: nSequence (4-byte unsigned little-endian) + hasher_Update(&hasher, (const uint8_t *)&txinput->sequence, 4); + hasher_Final(&hasher, txin_sig_digest); + + // `S.2: transparent_sig_digest` field for signature digest computation. + // https://zips.z.cash/zip-0244#s-2-transparent-sig-digest + uint8_t transparent_sig_digest[32] = {0}; + hasher_InitParam(&hasher, HASHER_BLAKE2B_PERSONAL, "ZTxIdTranspaHash", 16); + uint32_t hash_type = signing_hash_type(txinput); + // S.2a: hash_type (1 byte) + hasher_Update(&hasher, (const uint8_t *)&hash_type, 1); + // S.2b: prevouts_sig_digest (32-byte hash) + hasher_Update(&hasher, tx_info->hash_prevouts, + sizeof(tx_info->hash_prevouts)); + // S.2c: amounts_sig_digest (32-byte hash) + hasher_Update(&hasher, tx_info->hash_amounts, sizeof(tx_info->hash_amounts)); + // S.2d: scriptpubkeys_sig_digest (32-byte hash) + hasher_Update(&hasher, tx_info->hash_scriptpubkeys, + sizeof(tx_info->hash_scriptpubkeys)); + // S.2e: sequence_sig_digest (32-byte hash) + hasher_Update(&hasher, tx_info->hash_sequences, + sizeof(tx_info->hash_sequences)); + // S.2f: outputs_sig_digest (32-byte hash) + hasher_Update(&hasher, tx_info->hash_outputs, sizeof(tx_info->hash_outputs)); + // S.2g: txin_sig_digest (32-byte hash) + hasher_Update(&hasher, txin_sig_digest, sizeof(txin_sig_digest)); + hasher_Final(&hasher, transparent_sig_digest); + + // `S.3: sapling_digest` field. Empty Sapling bundle. + uint8_t sapling_digest[32] = {0}; + hasher_InitParam(&hasher, HASHER_BLAKE2B_PERSONAL, "ZTxIdSaplingHash", 16); + hasher_Final(&hasher, sapling_digest); + + // `S.4: orchard_digest` field. Empty Orchard bundle. + uint8_t orchard_digest[32] = {0}; + hasher_InitParam(&hasher, HASHER_BLAKE2B_PERSONAL, "ZTxIdOrchardHash", 16); + hasher_Final(&hasher, orchard_digest); + + // Final transaction signature digest. + // https://zips.z.cash/zip-0244#id13 + uint8_t personal[16] = {0}; + memcpy(personal, "ZcashTxHash_", 12); + memcpy(personal + 12, &tx_info->branch_id, 4); + hasher_InitParam(&hasher, HASHER_BLAKE2B_PERSONAL, personal, + sizeof(personal)); + // S.1: header_digest (32-byte hash output) + hasher_Update(&hasher, tx_info->hash_header, sizeof(tx_info->hash_header)); + // S.2: transparent_sig_digest (32-byte hash output) + hasher_Update(&hasher, transparent_sig_digest, + sizeof(transparent_sig_digest)); + // S.3: sapling_digest (32-byte hash output) + hasher_Update(&hasher, sapling_digest, sizeof(sapling_digest)); + // S.4: orchard_digest (32-byte hash output) + hasher_Update(&hasher, orchard_digest, sizeof(orchard_digest)); + hasher_Final(&hasher, hash); +} +#endif + static bool signing_check_orig_tx(void) { uint8_t hash[32] = {0}; @@ -2067,6 +2190,11 @@ static void phase1_finish(void) { // trust the amounts and scriptPubKeys, because if an invalid value is // provided then all issued signatures will be invalid. phase2_request_next_input(); +#if !BITCOIN_ONLY + } else if (coin->overwintered && info.version == 5) { + // ZIP-244 transactions are treated same as Taproot. + phase2_request_next_input(); +#endif } else { // There are internal non-Taproot inputs. We need to verify all inputs, // because we can't trust any amounts or scriptPubKeys. If we did, then an @@ -2478,6 +2606,15 @@ void signing_txack(TransactionType *tx) { } if (tx->inputs[0].has_orig_hash) { +#if !BITCOIN_ONLY + if (coin->overwintered && info.version != 4) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Replacement transactions are not supported.")); + signing_abort(); + return; + } +#endif + memcpy(&input, &tx->inputs[0], sizeof(input)); phase1_request_orig_input(); } else { @@ -2510,13 +2647,14 @@ void signing_txack(TransactionType *tx) { // Initialize computation of original legacy digest. tx_init(&ti, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, - tx->expiry, 0, coin->curve->hasher_sign, coin->overwintered, - tx->version_group_id, tx->timestamp); + 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->extra_data_len, coin->curve->hasher_sign, - coin->overwintered, tx->version_group_id, tx->timestamp); + tx->expiry, tx->branch_id, tx->extra_data_len, + coin->curve->hasher_sign, coin->overwintered, + tx->version_group_id, tx->timestamp); phase1_request_orig_input(); return; @@ -2676,8 +2814,9 @@ void signing_txack(TransactionType *tx) { return; } tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, - tx->expiry, tx->extra_data_len, coin->curve->hasher_sign, - coin->overwintered, tx->version_group_id, tx->timestamp); + tx->expiry, tx->branch_id, tx->extra_data_len, + coin->curve->hasher_sign, coin->overwintered, + tx->version_group_id, tx->timestamp); #if !BITCOIN_ONLY if (coin->decred) { tp.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); @@ -2798,8 +2937,9 @@ void signing_txack(TransactionType *tx) { PROGRESS_PRECISION); if (idx2 == 0) { tx_init(&ti, info.inputs_count, info.outputs_count, info.version, - info.lock_time, info.expiry, 0, coin->curve->hasher_sign, - coin->overwintered, info.version_group_id, info.timestamp); + info.lock_time, info.expiry, tx->branch_id, 0, + coin->curve->hasher_sign, coin->overwintered, + info.version_group_id, info.timestamp); hasher_Reset(&hasher_check); } // check inputs are the same as those in phase 1 @@ -2928,14 +3068,23 @@ void signing_txack(TransactionType *tx) { uint8_t hash[32] = {0}; #if !BITCOIN_ONLY if (coin->overwintered) { - if (info.version != 4) { + if (info.version == 4) { + signing_hash_zip243(&info, &tx->inputs[0], hash); + } else if (info.version == 5) { + if (!fill_input_script_pubkey(coin, &root, &tx->inputs[0])) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive scriptPubKey")); + signing_abort(); + return; + } + signing_hash_zip244(&info, &tx->inputs[0], hash); + } else { fsm_sendFailure( FailureType_Failure_DataError, _("Unsupported version for overwintered transaction")); signing_abort(); return; } - signing_hash_zip243(&info, &tx->inputs[0], hash); } else #endif { @@ -3059,15 +3208,17 @@ void signing_txack(TransactionType *tx) { if (idx1 == 0) { // witness tx_init(&to, info.inputs_count, info.outputs_count, info.version, - info.lock_time, info.expiry, 0, coin->curve->hasher_sign, - coin->overwintered, info.version_group_id, info.timestamp); + info.lock_time, info.expiry, tx->branch_id, 0, + coin->curve->hasher_sign, coin->overwintered, + info.version_group_id, info.timestamp); to.is_decred = true; } // witness hash tx_init(&ti, info.inputs_count, info.outputs_count, info.version, - info.lock_time, info.expiry, 0, coin->curve->hasher_sign, - coin->overwintered, info.version_group_id, info.timestamp); + info.lock_time, info.expiry, tx->branch_id, 0, + coin->curve->hasher_sign, coin->overwintered, + info.version_group_id, info.timestamp); ti.version |= (DECRED_SERIALIZE_WITNESS_SIGNING << 16); ti.is_decred = true; if (!tx_info_check_input(&info, &tx->inputs[0]) || diff --git a/legacy/firmware/transaction.c b/legacy/firmware/transaction.c index 44afe9aa3..49a9a9aea 100644 --- a/legacy/firmware/transaction.c +++ b/legacy/firmware/transaction.c @@ -620,17 +620,27 @@ uint32_t tx_serialize_script(uint32_t size, const uint8_t *data, uint8_t *out) { } uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) { - int r = 4; + int r = 0; #if !BITCOIN_ONLY if (tx->is_zcashlike && tx->version >= 3) { uint32_t ver = tx->version | TX_OVERWINTERED; - memcpy(out, &ver, 4); - memcpy(out + 4, &(tx->version_group_id), 4); + memcpy(out + r, &ver, 4); + r += 4; + memcpy(out + r, &(tx->version_group_id), 4); r += 4; + if (tx->version == 5) { + memcpy(out + r, &(tx->branch_id), 4); + r += 4; + memcpy(out + r, &(tx->lock_time), 4); + r += 4; + memcpy(out + r, &(tx->expiry), 4); + r += 4; + } } else #endif { - memcpy(out, &(tx->version), 4); + memcpy(out + r, &(tx->version), 4); + r += 4; #if !BITCOIN_ONLY if (tx->timestamp) { memcpy(out + r, &(tx->timestamp), 4); @@ -801,22 +811,42 @@ uint32_t tx_serialize_middle_hash(TxStruct *tx) { } uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out) { - memcpy(out, &(tx->lock_time), 4); + uint32_t r = 0; #if !BITCOIN_ONLY - if (tx->is_zcashlike && tx->version == 4) { - memcpy(out + 4, &(tx->expiry), 4); - memzero(out + 8, 8); // valueBalance - out[16] = 0x00; // nShieldedSpend - out[17] = 0x00; // nShieldedOutput - out[18] = 0x00; // nJoinSplit - return 19; - } - if (tx->is_decred) { - memcpy(out + 4, &(tx->expiry), 4); - return 8; - } + if (tx->is_zcashlike) { + if (tx->version == 4) { + memcpy(out, &(tx->lock_time), 4); + r += 4; + memcpy(out + r, &(tx->expiry), 4); + r += 4; + memzero(out + r, 8); // valueBalance + r += 8; + out[r] = 0x00; // nShieldedSpend + r += 1; + out[r] = 0x00; // nShieldedOutput + r += 1; + out[r] = 0x00; // nJoinSplit + r += 1; + } else if (tx->version == 5) { + out[r] = 0x00; // nSpendsSapling + r += 1; + out[r] = 0x00; // nOutputsSapling + r += 1; + out[r] = 0x00; // nActionsOrchard + r += 1; + } + } else if (tx->is_decred) { + memcpy(out, &(tx->lock_time), 4); + r += 4; + memcpy(out + r, &(tx->expiry), 4); + r += 4; + } else #endif - return 4; + { + memcpy(out, &(tx->lock_time), 4); + r += 4; + } + return r; } uint32_t tx_serialize_footer_hash(TxStruct *tx) { @@ -913,13 +943,15 @@ uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t expiry, - uint32_t extra_data_len, HasherType hasher_sign, bool is_zcashlike, + uint32_t branch_id, uint32_t extra_data_len, + HasherType hasher_sign, bool is_zcashlike, uint32_t version_group_id, uint32_t timestamp) { tx->inputs_len = inputs_len; tx->outputs_len = outputs_len; tx->version = version; tx->lock_time = lock_time; tx->expiry = expiry; + tx->branch_id = branch_id; tx->have_inputs = 0; tx->have_outputs = 0; tx->extra_data_len = extra_data_len; diff --git a/legacy/firmware/transaction.h b/legacy/firmware/transaction.h index 8bc68bb9d..5d6094e0b 100644 --- a/legacy/firmware/transaction.h +++ b/legacy/firmware/transaction.h @@ -39,6 +39,7 @@ typedef struct { uint32_t timestamp; uint32_t lock_time; uint32_t expiry; + uint32_t branch_id; bool is_segwit; bool is_decred; bool is_zcashlike; @@ -97,7 +98,8 @@ uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t expiry, - uint32_t extra_data_len, HasherType hasher_sign, bool is_zcashlike, + uint32_t branch_id, uint32_t extra_data_len, + HasherType hasher_sign, bool is_zcashlike, uint32_t version_group_id, uint32_t timestamp); uint32_t tx_serialize_header_hash(TxStruct *tx); uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input); diff --git a/tests/device_tests/zcash/test_sign_tx.py b/tests/device_tests/zcash/test_sign_tx.py index 6937b62b0..e97e210a2 100644 --- a/tests/device_tests/zcash/test_sign_tx.py +++ b/tests/device_tests/zcash/test_sign_tx.py @@ -49,7 +49,7 @@ TXHASH_4b6cec = bytes.fromhex( VERSION_GROUP_ID = 0x26A7270A BRANCH_ID = 0xC2D6D0B4 -pytestmark = [pytest.mark.altcoin, pytest.mark.zcash, pytest.mark.skip_t1] +pytestmark = [pytest.mark.altcoin, pytest.mark.zcash] def test_version_group_id_missing(client: Client): @@ -271,6 +271,7 @@ def test_one_two(client: Client): ) +@pytest.mark.skip_t1 def test_external_presigned(client: Client): inp1 = messages.TxInputType( # tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 5c152e67b..8521b6e3c 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -594,6 +594,13 @@ "T1_test_session_id_and_passphrase.py::test_session_enable_passphrase": "a3c4b11a1e0086e2e364013391188cd19aaa5e5893372249eb7a7e3217c91e96", "T1_test_session_id_and_passphrase.py::test_session_with_passphrase": "65a57f452581e8dd73d0f044a11abd81610cff1ea4e6f93883b1915f7a534b35", "T1_webauthn-test_u2f_counter.py::test_u2f_counter": "7aa71ff8b3d16c8288c9ac826f95f45a0e09d395355e94c96b98b32137dedd62", +"T1_zcash-test_sign_tx.py::test_one_two": "33efe57e5c258f82aad93da79692003ec401f187054a345efc94ba072f40c266", +"T1_zcash-test_sign_tx.py::test_refuse_replacement_tx": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", +"T1_zcash-test_sign_tx.py::test_send_to_multisig": "7abb28e1ea3370476e1aaae3c9ec42f1f97a3779d0aee09739ff60654207de75", +"T1_zcash-test_sign_tx.py::test_spend_multisig": "6e7b2c538445e4985849e05e36fe2d38bed68071aae14badadb2b8f74155aa1e", +"T1_zcash-test_sign_tx.py::test_spend_v4_input": "a468c73ba7f1ef62159df9bba30542b8c0883000fd89097e3fdba9591be634c6", +"T1_zcash-test_sign_tx.py::test_spend_v5_input": "55a6631eefd0419009abe409124f84f3edc02bf630227fe730db72e9d4cc4710", +"T1_zcash-test_sign_tx.py::test_version_group_id_missing": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "TT_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-0-bnb1hgm0p7khfk85zpz-68e2cb5a": "a8acaff76064949f9b800493cb3c8a1fb56f206bda9a85a80fd008475d2a946b", "TT_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-1-bnb1egswqkszzfc2uq7-1adfb691": "8b7387f0d82f78aa15f848a0995507db80f51416956d49ecd893ea49e7b64523", "TT_binance-test_get_public_key.py::test_binance_get_public_key": "3c69e84d0e572797271fac9265e3c6901a801eeaf25811884199e2b92691b48e",