feat(legacy): Support Zcash version 5 transaction format.

pull/2251/head
Andrew Kozlik 2 years ago committed by matejcik
parent c45ca3314c
commit 26d1fad2aa

@ -0,0 +1 @@
Support Zcash version 5 transaction format.

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

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

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

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

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

Loading…
Cancel
Save