From 81077a2be980cbab251afafa1723ae707bcc6bb7 Mon Sep 17 00:00:00 2001 From: Ida Date: Wed, 16 Mar 2022 10:09:30 +0100 Subject: [PATCH 01/16] Refactoring functions to get note commitments --- app/rust/include/rslib.h | 9 +--- app/rust/src/commitments.rs | 103 ++++++++++++++++++++++-------------- app/rust/src/pedersen.rs | 18 ++----- app/src/crypto.c | 19 ++++--- 4 files changed, 81 insertions(+), 68 deletions(-) diff --git a/app/rust/include/rslib.h b/app/rust/include/rslib.h index b43f912f..d88d29b9 100644 --- a/app/rust/include/rslib.h +++ b/app/rust/include/rslib.h @@ -47,15 +47,10 @@ void rseed_get_esk(const uint8_t *input, uint8_t *output_ptr); void rseed_get_rcm(const uint8_t *input, uint8_t *output_ptr); -//Pedersen hash -void pedersen_hash_73bytes(const uint8_t *input, uint8_t *output_ptr); - //Commitments -void prepare_input_notecmt(const uint64_t value, const uint8_t *gd, const uint8_t *pkd, uint8_t *output); - -void compute_note_commitment(uint8_t *inputptr, const uint8_t *rcmptr); +void compute_note_commitment(uint8_t *inputptr, const uint8_t *rcmptr,const uint64_t value,const uint8_t *gd, const uint8_t *pkd); -void compute_note_commitment_fullpoint(uint8_t *inputptr, const uint8_t *rcmptr); +void compute_note_commitment_fullpoint(uint8_t *inputptr, const uint8_t *rcmptr,const uint64_t value,const uint8_t *gd, const uint8_t *pkd); void compute_value_commitment(const uint64_t value, const uint8_t *rcmptr, uint8_t *output); diff --git a/app/rust/src/commitments.rs b/app/rust/src/commitments.rs index daa993ce..7364a6ec 100644 --- a/app/rust/src/commitments.rs +++ b/app/rust/src/commitments.rs @@ -175,6 +175,35 @@ fn u64_to_bytes(value: u64) -> [u8; 32] { scalar } +#[inline(never)] +pub fn prepare_and_hash_input_commitment( + value: u64, + g_d_ptr: *const [u8; 32], + pkd_ptr: *const [u8; 32], + output_ptr: *mut [u8; 32], +) { + c_zemu_log_stack(b"entry_preparenotecommit\x00".as_ref()); + let gd = unsafe { &*g_d_ptr }; + let pkd = unsafe { &*pkd_ptr }; + + let mut prepared_msg = [0u8; 73]; + let mut input_hash = [0u8; 73]; + let output_msg = unsafe { &mut *output_ptr }; + + + let vbytes = write_u64_tobytes(value); + input_hash[0..8].copy_from_slice(&vbytes); + + revert(gd, &mut input_hash[8..40]); + revert(pkd, &mut input_hash[40..72]); + + shiftsixbits(&mut input_hash); + prepared_msg.copy_from_slice(&input_hash); + + let h = pedersen_hash_pointbytes(&mut prepared_msg, 582); + output_msg.copy_from_slice(&h); +} + #[inline(never)] pub fn value_commitment_step1(value: u64) -> ExtendedPoint { c_zemu_log_stack(b"insidevaluecommitment\x00".as_ref()); @@ -254,30 +283,52 @@ pub extern "C" fn compute_nullifier( } #[no_mangle] -pub extern "C" fn compute_note_commitment(input_ptr: *mut [u8; 32], rcm_ptr: *const [u8; 32]) { +pub extern "C" fn compute_note_commitment(input_ptr: *mut [u8; 32], + rcm_ptr: *const [u8; 32], + value: u64, + g_d_ptr: *const [u8; 32], + pkd_ptr: *const [u8; 32]) { + + c_zemu_log_stack(b"entry_preparenotecommit\x00".as_ref()); + + let gd = unsafe { &*g_d_ptr }; + let pkd = unsafe { &*pkd_ptr }; + let out = unsafe { &mut *input_ptr }; + + prepare_and_hash_input_commitment(value, gd, pkd, out); + c_zemu_log_stack(b"inside_notecmt\x00".as_ref()); - let inputhash = unsafe { &mut *input_ptr }; let rc = unsafe { &*rcm_ptr }; - let mut e = bytes_to_extended(*inputhash); + let mut e = bytes_to_extended(*out); let s = multiply_with_pedersenbase(rc); add_to_point(&mut e, &s); - inputhash.copy_from_slice(&extended_to_u_bytes(&e)); + out.copy_from_slice(&extended_to_u_bytes(&e)); } + #[no_mangle] pub extern "C" fn compute_note_commitment_fullpoint( input_ptr: *mut [u8; 32], rcm_ptr: *const [u8; 32], -) { + value: u64, + g_d_ptr: *const [u8; 32], + pkd_ptr: *const [u8; 32]) { + c_zemu_log_stack(b"entry_preparenotecommit\x00".as_ref()); + let gd = unsafe { &*g_d_ptr }; + let pkd = unsafe { &*pkd_ptr }; + + let out = unsafe { &mut *input_ptr }; + + prepare_and_hash_input_commitment(value, gd, pkd, out); + c_zemu_log_stack(b"inside_notecmt\x00".as_ref()); - let inputhash = unsafe { &mut *input_ptr }; let rc = unsafe { &*rcm_ptr }; - let mut e = bytes_to_extended(*inputhash); + let mut e = bytes_to_extended(*out); let s = multiply_with_pedersenbase(rc); add_to_point(&mut e, &s); - inputhash.copy_from_slice(&extended_to_bytes(&e)); + out.copy_from_slice(&extended_to_bytes(&e)); } #[no_mangle] @@ -299,30 +350,6 @@ pub extern "C" fn compute_value_commitment( output_msg.copy_from_slice(&vcm); } -#[no_mangle] -pub extern "C" fn prepare_input_notecmt( - value: u64, - g_d_ptr: *const [u8; 32], - pkd_ptr: *const [u8; 32], - output_ptr: *mut [u8; 73], -) { - c_zemu_log_stack(b"entry_preparenotecommit\x00".as_ref()); - let gd = unsafe { &*g_d_ptr }; - let pkd = unsafe { &*pkd_ptr }; - - let output_msg = unsafe { &mut *output_ptr }; - let mut input_hash = [0u8; 73]; - - let vbytes = write_u64_tobytes(value); - input_hash[0..8].copy_from_slice(&vbytes); - - revert(gd, &mut input_hash[8..40]); - revert(pkd, &mut input_hash[40..72]); - - shiftsixbits(&mut input_hash); - output_msg.copy_from_slice(&input_hash); -} - pub fn verify_bindingsig_keys(rcmsum: &[u8; 32], valuecommitsum: &[u8; 32]) -> bool { let v = bytes_to_extended(*valuecommitsum); let r = VALUE_COMMITMENT_RANDOM_BASE.multiply_bits(rcmsum); @@ -341,21 +368,19 @@ mod tests { let rcm = [0u8; 32]; let output = [0u8; 32]; - let inputhash = [0u8; 73]; - - prepare_input_notecmt( + prepare_and_hash_input_commitment( v, gd.as_ptr() as *const [u8; 32], pkd.as_ptr() as *const [u8; 32], - inputhash.as_ptr() as *mut [u8; 73], - ); - pedersen_hash_73bytes( - inputhash.as_ptr() as *const [u8; 73], output.as_ptr() as *mut [u8; 32], ); + compute_note_commitment( output.as_ptr() as *mut [u8; 32], rcm.as_ptr() as *const [u8; 32], + v, + gd.as_ptr() as *const [u8; 32], + pkd.as_ptr() as *const [u8; 32] ); assert_eq!( diff --git a/app/rust/src/pedersen.rs b/app/rust/src/pedersen.rs index a5454ba6..007c5a95 100644 --- a/app/rust/src/pedersen.rs +++ b/app/rust/src/pedersen.rs @@ -267,15 +267,6 @@ pub fn pedersen_hash_pointbytes(m: &[u8], bitsize: u32) -> [u8; 32] { extended_to_bytes(&result_point) } -//assumption here that ceil(bitsize / 8) == m.len(), so appended with zero bits to fill the bytes -#[no_mangle] -pub extern "C" fn pedersen_hash_73bytes(input: *const [u8; 73], output_ptr: *mut [u8; 32]) { - let input_msg = unsafe { &*input }; - let output_msg = unsafe { &mut *output_ptr }; - - let h = pedersen_hash_pointbytes(input_msg, 582); - output_msg.copy_from_slice(&h); -} #[cfg(test)] mod tests { @@ -314,11 +305,10 @@ mod tests { #[test] fn test_pedersen_ledger2() { let msg = [0u8; 73]; - let output = [0u8; 32]; - pedersen_hash_73bytes( - msg.as_ptr() as *const [u8; 73], - output.as_ptr() as *mut [u8; 32], - ); + let mut output = [0u8; 32]; + + let h = pedersen_hash_pointbytes(&msg, 582); + output.copy_from_slice(&h); assert_eq!( output, [ diff --git a/app/src/crypto.c b/app/src/crypto.c index a6e7d139..566ceb08 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -620,6 +620,9 @@ zxerr_t crypto_checkspend_sapling(uint8_t *buffer, uint16_t bufferLen, const uin CLOSE_TRY; return zxerr_unknown; } + + // if we want to have this also return nk, we need to change the tmp_checkspend structure so that step1 + // uses an extra 32 uint8's for nk in step 1. Then step 6 will no longer be necessary. zip32_child_ask_nsk(tmp.step1.zip32_seed, tmp.step2.ask, tmp.step2.nsk, item->path); randomized_secret(tmp.step2.ask, (uint8_t *)item->alpha, tmp.step2.ask); @@ -631,7 +634,7 @@ zxerr_t crypto_checkspend_sapling(uint8_t *buffer, uint16_t bufferLen, const uin return zxerr_unknown; } - compute_value_commitment(item->value,item->rcm,tmp.step4.cv); + compute_value_commitment(item->value,item->rcm,tmp.step4.cv); // TODO: I think its rcmvalue for value commitments... if (MEMCMP(tmp.step4.cv, start_spenddata + INDEX_SPEND_VALUECMT + i *SPEND_TX_LEN,VALUE_COMMITMENT_SIZE) != 0){ MEMZERO(&tmp, sizeof(tmp_checkspend)); MEMZERO(out,bufferLen); @@ -640,9 +643,10 @@ zxerr_t crypto_checkspend_sapling(uint8_t *buffer, uint16_t bufferLen, const uin } group_hash_from_div(item->div, tmp.step5.gd); - prepare_input_notecmt(item->value, tmp.step5.gd, item->pkd, tmp_buf->pedersen_input); - pedersen_hash_73bytes(tmp_buf->pedersen_input,tmp_buf->pedersen_hash); - compute_note_commitment_fullpoint(tmp_buf->pedersen_hash, start_spendolddata + INDEX_SPEND_OLD_RCM + i * SPEND_OLD_TX_LEN); + + compute_note_commitment_fullpoint(tmp_buf->pedersen_hash, start_spendolddata + INDEX_SPEND_OLD_RCM + i * SPEND_OLD_TX_LEN,item->value, tmp.step5.gd, item->pkd); + + // TODO: we could get nk at the same time as ask and nsk nsk_to_nk(tmp.step5.nsk,tmp.step6.nk); uint64_t notepos = 0; { @@ -752,13 +756,12 @@ zxerr_t crypto_checkoutput_sapling(uint8_t *buffer, uint16_t bufferLen, const ui CLOSE_TRY; return zxerr_unknown; } + group_hash_from_div(item->div, ncm.step2.gd); + rseed_get_rcm(item->rseed,rcm); - prepare_input_notecmt(item->value, ncm.step2.gd, item->pkd, ncm.step3.pedersen_input); + compute_note_commitment(ncm.step4.notecommitment,rcm,item->value, ncm.step2.gd, item->pkd); - pedersen_hash_73bytes(ncm.step3.pedersen_input,ncm.step4.notecommitment); - rseed_get_rcm(item->rseed,rcm); - compute_note_commitment(ncm.step4.notecommitment,rcm); compute_value_commitment(item->value, item->rcmvalue, ncm.step4.valuecommitment); if (MEMCMP(ncm.step4.valuecommitment, start_outputdata + INDEX_OUTPUT_VALUECMT + i * OUTPUT_TX_LEN,VALUE_COMMITMENT_SIZE) != 0){ From ff1fb93d486b7210918fa7435d1fc8e043869f4e Mon Sep 17 00:00:00 2001 From: Ida Date: Wed, 16 Mar 2022 11:09:20 +0100 Subject: [PATCH 02/16] renaming rcm to rcmvalue for consistency --- app/src/crypto.c | 4 ++-- app/src/nvdata.c | 2 +- app/src/nvdata.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/crypto.c b/app/src/crypto.c index 566ceb08..2eb916ec 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -388,7 +388,7 @@ zxerr_t crypto_extract_spend_proofkeyandrnd(uint8_t *buffer, uint16_t bufferLen) MEMZERO(&tmp, sizeof(tmp_spendinfo_s)); CHECK_APP_CANARY(); - MEMCPY(out+AK_SIZE+NSK_SIZE, next->rcm, RCM_SIZE); + MEMCPY(out+AK_SIZE+NSK_SIZE, next->rcmvalue, RCM_SIZE); MEMCPY(out+AK_SIZE+NSK_SIZE+RCM_SIZE, next->alpha,ALPHA_SIZE); if(!spendlist_more_extract()){ @@ -634,7 +634,7 @@ zxerr_t crypto_checkspend_sapling(uint8_t *buffer, uint16_t bufferLen, const uin return zxerr_unknown; } - compute_value_commitment(item->value,item->rcm,tmp.step4.cv); // TODO: I think its rcmvalue for value commitments... + compute_value_commitment(item->value,item->rcmvalue,tmp.step4.cv); // TODO: I think its rcmvalue for value commitments... if (MEMCMP(tmp.step4.cv, start_spenddata + INDEX_SPEND_VALUECMT + i *SPEND_TX_LEN,VALUE_COMMITMENT_SIZE) != 0){ MEMZERO(&tmp, sizeof(tmp_checkspend)); MEMZERO(out,bufferLen); diff --git a/app/src/nvdata.c b/app/src/nvdata.c index 1ed36266..f9758db8 100644 --- a/app/src/nvdata.c +++ b/app/src/nvdata.c @@ -200,7 +200,7 @@ zxerr_t spendlist_append_item(uint32_t p, uint64_t v, uint8_t *div, uint8_t *pkd newitem.value = v; MEMCPY(newitem.div, div, DIV_SIZE); MEMCPY(newitem.pkd, pkd, PKD_SIZE); - MEMCPY(newitem.rcm, rcm, RCM_SIZE); + MEMCPY(newitem.rcmvalue, rcm, RCM_SIZE); MEMCPY(newitem.alpha, alpha, ALPHA_SIZE); MEMCPY_NV((void *) &N_spendlist.items[transaction_header.spendlist_len], diff --git a/app/src/nvdata.h b/app/src/nvdata.h index 5a7ea938..77fd0325 100644 --- a/app/src/nvdata.h +++ b/app/src/nvdata.h @@ -45,7 +45,7 @@ typedef struct { uint64_t value; uint8_t div[DIV_SIZE]; uint8_t pkd[PKD_SIZE]; - uint8_t rcm[RCM_SIZE]; + uint8_t rcmvalue[RCM_SIZE]; uint8_t alpha[ALPHA_SIZE]; } spend_item_t; From 398c0850662d1929492a9a0c0c2a76b7a05653d6 Mon Sep 17 00:00:00 2001 From: Ida Date: Wed, 16 Mar 2022 14:34:58 +0100 Subject: [PATCH 03/16] ivk --- app/rust/include/rslib.h | 4 +- app/rust/src/zip32.rs | 235 ++++------------------------- app/src/apdu_handler.c | 7 +- app/src/common/app_main.h | 1 - app/src/crypto.c | 26 +--- app/src/crypto.h | 3 +- js/src/common.js | 1 - js/src/index.js | 6 - tests_zemu/tests/zcashtool.test.ts | 26 ---- 9 files changed, 38 insertions(+), 271 deletions(-) diff --git a/app/rust/include/rslib.h b/app/rust/include/rslib.h index d88d29b9..2b57b0d4 100644 --- a/app/rust/include/rslib.h +++ b/app/rust/include/rslib.h @@ -13,7 +13,7 @@ void ask_to_ak(const uint8_t *ask_ptr, uint8_t *ak_ptr); void nsk_to_nk(const uint8_t *nsk_ptr, uint8_t *nk_ptr); -void get_ivk(const uint8_t *ak_ptr, const uint8_t *nk_ptr, uint8_t *ivk_ptr); +void get_ivk(const uint8_t *ak_ptr, const uint32_t pos, uint8_t *ivk_ptr); void get_pkd(const uint8_t *ivk_ptr, const uint8_t *diversifier_ptr, uint8_t *pkd); @@ -33,7 +33,7 @@ void zip32_master(const uint8_t *seed_ptr, uint8_t *sk_ptr, uint8_t *dk_ptr); void zip32_child(const uint8_t *seed_ptr, uint8_t *dk, uint8_t *ask, uint8_t *nsk, const uint32_t pos); -void zip32_child_ida(const uint8_t *seed_ptr, uint8_t *dk, uint8_t *ak, uint8_t *nk, const uint32_t pos); +void get_dk(const uint8_t *seed_ptr, uint8_t *dk, const uint32_t pos); void zip32_child_ask_nsk(const uint8_t *seed_ptr, uint8_t *ask, uint8_t *nsk, const uint32_t pos); diff --git a/app/rust/src/zip32.rs b/app/rust/src/zip32.rs index c6f0f569..54ee6f14 100644 --- a/app/rust/src/zip32.rs +++ b/app/rust/src/zip32.rs @@ -416,67 +416,7 @@ pub fn derive_zip32_ovk_fromseedandpath(seed: &[u8; 32], path: &[u32]) -> [u8; 3 } #[inline(never)] -pub fn derive_zip32_child_fromseedandpath(seed: &[u8; 32], path: &[u32]) -> [u8; 96] { - //ASSERT: len(path) == len(harden) - - let mut tmp = master_spending_key_zip32(seed); //64 - let mut key = [0u8; 32]; //32 - let mut chain = [0u8; 32]; //32 - - key.copy_from_slice(&tmp[..32]); - chain.copy_from_slice(&tmp[32..]); - - let mut ask = Fr::from_bytes_wide(&prf_expand(&key, &[0x00])); - - let mut nsk = Fr::from_bytes_wide(&prf_expand(&key, &[0x01])); - - let mut expkey: [u8; 96]; - expkey = expandedspendingkey_zip32(&key); //96 - //master divkey - let mut divkey = [0u8; 32]; - divkey.copy_from_slice(&diversifier_key_zip32(&key)); //32 - for &p in path { - //compute expkey needed for zip32 child derivation - //non-hardened child - let hardened = (p & 0x8000_0000) != 0; - let c = p & 0x7FFF_FFFF; - if hardened { - let mut le_i = [0; 4]; - LittleEndian::write_u32(&mut le_i, c + (1 << 31)); - //make index LE - //zip32 child derivation - tmp = bolos::blake2b_expand_vec_four(&chain, &[0x11], &expkey, &divkey, &le_i); - //64 - } else { - //WARNING: CURRENTLY COMPUTING NON-HARDENED PATHS DO NOT FIT IN MEMORY - let fvk = full_viewingkey(&key); - let mut le_i = [0; 4]; - LittleEndian::write_u32(&mut le_i, c); - tmp = bolos::blake2b_expand_vec_four(&chain, &[0x12], &fvk, &divkey, &le_i); - } - //extract key and chainkey - key.copy_from_slice(&tmp[..32]); - chain.copy_from_slice(&tmp[32..]); - - let ask_cur = Fr::from_bytes_wide(&prf_expand(&key, &[0x13])); - let nsk_cur = Fr::from_bytes_wide(&prf_expand(&key, &[0x14])); - - ask += ask_cur; - nsk += nsk_cur; - - //new divkey from old divkey and key - update_dk_zip32(&key, &mut divkey); - update_exk_zip32(&key, &mut expkey); - } - let mut result = [0u8; 96]; - result[0..32].copy_from_slice(&divkey); - result[32..64].copy_from_slice(&ask.to_bytes()); - result[64..96].copy_from_slice(&nsk.to_bytes()); - result -} - -#[inline(never)] -pub fn derive_zip32_child_fromseedandpath_ida(seed: &[u8; 32], path: &[u32]) -> [u8; 160] { +pub fn derive_zip32_child_fromseedandpath(seed: &[u8; 32], path: &[u32]) -> [u8; 160] { //ASSERT: len(path) == len(harden) let mut tmp = master_spending_key_zip32(seed); //64 @@ -559,14 +499,25 @@ pub extern "C" fn nsk_to_nk(nsk_ptr: *const [u8; 32], nk_ptr: *mut [u8; 32]) { #[no_mangle] pub extern "C" fn get_ivk( - ak_ptr: *const [u8; 32], - nk_ptr: *const [u8; 32], + seed_ptr: *const [u8; 32], + pos: u32, ivk_ptr: *mut [u8; 32], ) { - let ak = unsafe { &*ak_ptr }; - let nk = unsafe { &*nk_ptr }; + let seed = unsafe { &*seed_ptr }; + let mut ak = [0u8; 32]; + let mut nk = [0u8; 32]; + let ivk = unsafe { &mut *ivk_ptr }; + + const FIRSTVALUE: u32 = 32 ^ 0x8000_0000; + const COIN_TYPE: u32 = 133 ^ 0x8000_0000; //hardened, fixed value from https://github.com/adityapk00/librustzcash/blob/master/zcash_client_backend/src/constants/mainnet.rs + let k = derive_zip32_child_fromseedandpath(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet + + // k = dk || ask || nsk || ak || nk + ak.copy_from_slice(&k[96..128]); + nk.copy_from_slice(&k[128..160]); + let tmp_ivk = aknk_to_ivk(&ak, &nk); ivk.copy_from_slice(&tmp_ivk) } @@ -621,26 +572,20 @@ pub extern "C" fn zip32_child( } #[no_mangle] -pub extern "C" fn zip32_child_ida( +pub extern "C" fn get_dk( seed_ptr: *const [u8; 32], dk_ptr: *mut [u8; 32], - ak_ptr: *mut [u8; 32], - nk_ptr: *mut [u8; 32], pos: u32, ) { let seed = unsafe { &*seed_ptr }; let dk = unsafe { &mut *dk_ptr }; - let ak = unsafe { &mut *ak_ptr }; - let nk = unsafe { &mut *nk_ptr }; const FIRSTVALUE: u32 = 32 ^ 0x8000_0000; const COIN_TYPE: u32 = 133 ^ 0x8000_0000; //hardened, fixed value from https://github.com/adityapk00/librustzcash/blob/master/zcash_client_backend/src/constants/mainnet.rs - let k = derive_zip32_child_fromseedandpath_ida(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet + let k = derive_zip32_child_fromseedandpath(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet // k = dk || ask || nsk || ak || nk dk.copy_from_slice(&k[0..32]); - ak.copy_from_slice(&k[96..128]); - nk.copy_from_slice(&k[128..160]); } #[no_mangle] @@ -658,7 +603,7 @@ pub extern "C" fn zip32_child_proof_key( const FIRSTVALUE: u32 = 32 ^ 0x8000_0000; const COIN_TYPE: u32 = 133 ^ 0x8000_0000; //hardened, fixed value from https://github.com/adityapk00/librustzcash/blob/master/zcash_client_backend/src/constants/mainnet.rs - let k = derive_zip32_child_fromseedandpath_ida(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet + let k = derive_zip32_child_fromseedandpath(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet // k = dk || ask || nsk || ak || nk dk.copy_from_slice(&k[0..32]); @@ -684,24 +629,6 @@ pub extern "C" fn zip32_child_ask_nsk( nsk.copy_from_slice(&k[64..96]); } -#[no_mangle] -pub extern "C" fn zip32_child_ask_nsk_ida( - seed_ptr: *const [u8; 32], - ask_ptr: *mut [u8; 32], - nsk_ptr: *mut [u8; 32], - pos: u32, -) { - let seed = unsafe { &*seed_ptr }; - let ask = unsafe { &mut *ask_ptr }; - let nsk = unsafe { &mut *nsk_ptr }; - - const FIRSTVALUE: u32 = 32 ^ 0x8000_0000; - const COIN_TYPE: u32 = 133 ^ 0x8000_0000; //hardened, fixed value from https://github.com/adityapk00/librustzcash/blob/master/zcash_client_backend/src/constants/mainnet.rs - let k = derive_zip32_child_fromseedandpath_ida(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet; - ask.copy_from_slice(&k[32..64]); - nsk.copy_from_slice(&k[64..96]); -} - #[no_mangle] pub extern "C" fn get_diversifier_list( sk_ptr: *const [u8; 32], @@ -812,6 +739,7 @@ mod tests { assert_eq!(keys[0..32], dk); } + #[test] fn test_zip32_childaddress() { let seed = [0u8; 32]; @@ -828,67 +756,11 @@ mod tests { let mut nsk = [0u8; 32]; nsk.copy_from_slice(&keys[64..96]); - let ask_test: [u8; 32] = [ - 0x66, 0x5e, 0xd6, 0xf7, 0xb7, 0x93, 0xaf, 0xa1, 0x82, 0x21, 0xe1, 0x57, 0xba, 0xd5, - 0x43, 0x3c, 0x54, 0x23, 0xf4, 0xfe, 0xc9, 0x46, 0xe0, 0x8e, 0xd6, 0x30, 0xa0, 0xc6, - 0x0a, 0x1f, 0xac, 0x02, - ]; - - assert_eq!(ask, ask_test); - - let nk: [u8; 32] = sapling_nsk_to_nk(&nsk); - let ak: [u8; 32] = sapling_ask_to_ak(&ask); - - let ivk_test: [u8; 32] = [ - 0x2c, 0x57, 0xfb, 0x12, 0x8c, 0x35, 0xa4, 0x4d, 0x2d, 0x5b, 0xf2, 0xfd, 0x21, 0xdc, - 0x3b, 0x44, 0x11, 0x4c, 0x36, 0x6c, 0x9c, 0x49, 0x60, 0xc4, 0x91, 0x66, 0x17, 0x38, - 0x3e, 0x89, 0xfd, 0x00, - ]; - let ivk = aknk_to_ivk(&ak, &nk); - - assert_eq!(ivk, ivk_test); - - let mut listbytes = [0u8; 110]; - ff1aes_list(&dk, &mut listbytes); - let default_d = default_diversifier_fromlist(&listbytes); - - let pk_d = default_pkd(&ivk, &default_d); - - assert_eq!( - default_d, - [0x10, 0xaa, 0x8e, 0xe1, 0xe1, 0x91, 0x48, 0xe7, 0x49, 0x7d, 0x3c] - ); - assert_eq!( - pk_d, - [ - 0xb3, 0xbe, 0x9e, 0xb3, 0xe7, 0xa9, 0x61, 0x17, 0x95, 0x17, 0xae, 0x28, 0xab, 0x19, - 0xb4, 0x84, 0xae, 0x17, 0x2f, 0x1f, 0x33, 0xd1, 0x16, 0x33, 0xe9, 0xec, 0x05, 0xee, - 0xa1, 0xe8, 0xa9, 0xd6 - ] - ); - } - - #[test] - fn test_zip32_childaddress_ida() { - let seed = [0u8; 32]; - - let p: u32 = 0x8000_0001; - let keys = derive_zip32_child_fromseedandpath_ida(&seed, &[p]); - - let mut dk = [0u8; 32]; - dk.copy_from_slice(&keys[0..32]); - - let mut ask = [0u8; 32]; - ask.copy_from_slice(&keys[32..64]); - - let mut nsk = [0u8; 32]; - nsk.copy_from_slice(&keys[64..96]); - - let mut ak_ida = [0u8; 32]; - ak_ida.copy_from_slice(&keys[96..128]); + let mut ak_derived = [0u8; 32]; + ak_derived.copy_from_slice(&keys[96..128]); - let mut nk_ida = [0u8; 32]; - nk_ida.copy_from_slice(&keys[128..160]); + let mut nk_derived = [0u8; 32]; + nk_derived.copy_from_slice(&keys[128..160]); let ask_test: [u8; 32] = [ 0x66, 0x5e, 0xd6, 0xf7, 0xb7, 0x93, 0xaf, 0xa1, 0x82, 0x21, 0xe1, 0x57, 0xba, 0xd5, @@ -901,8 +773,8 @@ mod tests { let nk: [u8; 32] = sapling_nsk_to_nk(&nsk); let ak: [u8; 32] = sapling_ask_to_ak(&ask); - assert_eq!(ak, ak_ida); - assert_eq!(nk, nk_ida); + assert_eq!(ak, ak_derived); + assert_eq!(nk, nk_derived); let ivk_test: [u8; 32] = [ 0x2c, 0x57, 0xfb, 0x12, 0x8c, 0x35, 0xa4, 0x4d, 0x2d, 0x5b, 0xf2, 0xfd, 0x21, 0xdc, @@ -933,6 +805,7 @@ mod tests { ); } + #[test] fn test_zip32_childaddress_ledgerkey() { //e91db3f6c120a86ece0de8d21d452dcdcb708d563494e60a6cee676f5047ded7 @@ -955,60 +828,6 @@ mod tests { let mut nsk = [0u8; 32]; nsk.copy_from_slice(&keys[64..96]); - let nk: [u8; 32] = sapling_nsk_to_nk(&nsk); - let ak: [u8; 32] = sapling_ask_to_ak(&ask); - - let ivk = aknk_to_ivk(&ak, &nk); - - let mut ivk_ledger = [0u8; 32]; - hex::decode_to_slice( - "6dfadf175921e6fbfa093c8f7c704a0bdb07328474f56c833dfcfa5301082d03", - &mut ivk_ledger, - ) - .expect("dec"); - assert_eq!(ivk, ivk_ledger); - - let mut list = [0u8; 110]; - ff1aes_list(&dk, &mut list); - let default_d = default_diversifier_fromlist(&list); - - let pk_d = default_pkd(&ivk, &default_d); - - assert_eq!( - default_d, - [198, 158, 151, 156, 103, 99, 193, 176, 146, 56, 220] - ); - assert_eq!( - pk_d, - [ - 107, 213, 220, 191, 53, 54, 13, 249, 93, 202, 223, 140, 15, 162, 93, 203, 237, 170, - 246, 5, 117, 56, 184, 18, 208, 102, 86, 114, 110, 162, 118, 103 - ] - ); - } - - #[test] - fn test_zip32_childaddress_ledgerkey_ida() { - //e91db3f6c120a86ece0de8d21d452dcdcb708d563494e60a6cee676f5047ded7 - let s = hex::decode("b08e3d98da431cef4566a13c1bb348b982f7d8e743b43bb62557ba51994b1257") - .expect("error"); - let seed: [u8; 32] = s.as_slice().try_into().expect("er"); - - const FIRSTVALUE: u32 = 32 ^ 0x8000_0000; - const COIN_TYPE: u32 = 133 ^ 0x8000_0000; - - let p: u32 = 1000 | 0x8000_0000; - let keys = derive_zip32_child_fromseedandpath_ida(&seed, &[FIRSTVALUE, COIN_TYPE, p]); - - let mut dk = [0u8; 32]; - dk.copy_from_slice(&keys[0..32]); - - let mut ask = [0u8; 32]; - ask.copy_from_slice(&keys[32..64]); - - let mut nsk = [0u8; 32]; - nsk.copy_from_slice(&keys[64..96]); - let mut ak = [0u8; 32]; ak.copy_from_slice(&keys[96..128]); diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c index 6c7f1c17..120662b6 100644 --- a/app/src/apdu_handler.c +++ b/app/src/apdu_handler.c @@ -204,7 +204,7 @@ __Z_INLINE void handleGetKeyIVK(volatile uint32_t *flags, key_state.kind = key_ivk; uint16_t replyLen = 0; - zxerr_t err = crypto_ivk_sapling_ida(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 2, zip32path, &replyLen); + zxerr_t err = crypto_ivk_sapling(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 2, zip32path, &replyLen); if (err != zxerr_ok) { *tx = 0; THROW(APDU_CODE_DATA_INVALID); @@ -635,11 +635,6 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { break; } - case INS_GET_IVK_IDA: { - handleGetKeyIVK(flags, tx, rx); - break; - } - case INS_GET_IVK: { handleGetKeyIVK(flags, tx, rx); break; diff --git a/app/src/common/app_main.h b/app/src/common/app_main.h index 1ea586a9..81d47b45 100644 --- a/app/src/common/app_main.h +++ b/app/src/common/app_main.h @@ -58,7 +58,6 @@ #define INS_GET_IVK 0xf0 #define INS_GET_OVK 0xf1 -#define INS_GET_IVK_IDA 0xf2 #define INS_CRASH_TEST 0xff #if defined(APP_TESTING) diff --git a/app/src/crypto.c b/app/src/crypto.c index 2eb916ec..6b74beac 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -1251,7 +1251,7 @@ typedef struct { } tmp_sapling_addr_s; -zxerr_t crypto_ivk_sapling_ida(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint16_t *replyLen) { +zxerr_t crypto_ivk_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint16_t *replyLen) { MEMZERO(buffer, bufferLen); zemu_log_stack("crypto_ivk_sapling"); @@ -1270,10 +1270,9 @@ zxerr_t crypto_ivk_sapling_ida(uint8_t *buffer, uint16_t bufferLen, uint32_t p, // Temporarily get sk from Ed25519 crypto_fillSaplingSeed(tmp.step1.zip32_seed); CHECK_APP_CANARY(); - zip32_child_ida(tmp.step1.zip32_seed, tmp.step2.dk, tmp.step3.ak, tmp.step3.nk, p); - CHECK_APP_CANARY(); - get_ivk(tmp.step3.ak, tmp.step3.nk, out); + get_ivk(tmp.step1.zip32_seed, p, out); + CHECK_APP_CANARY(); MEMZERO(&tmp, sizeof(tmp_sapling_addr_s)); @@ -1338,9 +1337,7 @@ zxerr_t crypto_diversifier_with_startindex(uint8_t *buffer, uint16_t bufferLen, crypto_fillSaplingSeed(tmp.step1.zip32_seed); CHECK_APP_CANARY(); - zip32_child_ida(tmp.step1.zip32_seed, tmp.step2.dk, tmp.step3.ak, tmp.step3.nk, p); - MEMZERO(tmp.step3.ak,sizeof_field(tmp_sapling_addr_s, step3.ak)); - MEMZERO(tmp.step3.nk,sizeof_field(tmp_sapling_addr_s, step3.nk)); + get_dk(tmp.step1.zip32_seed, tmp.step2.dk,p); CHECK_APP_CANARY(); get_diversifier_list_withstartindex(tmp.step2.dk,startindex,buffer); @@ -1410,15 +1407,8 @@ zxerr_t crypto_fillAddress_with_diversifier_sapling(uint8_t *buffer, uint16_t bu crypto_fillSaplingSeed(tmp.step1.zip32_seed); CHECK_APP_CANARY(); - zip32_child_ida(tmp.step1.zip32_seed, tmp.step2.dk, tmp.step3.ak, tmp.step3.nk, p); - CHECK_APP_CANARY(); - MEMZERO(tmp.step2.dk, sizeof_field(tmp_sapling_addr_s, step2.dk)); - - get_ivk(tmp.step3.ak, tmp.step3.nk, tmp.step3.ivk); - CHECK_APP_CANARY(); - MEMZERO(tmp.step3.ak, sizeof_field(tmp_sapling_addr_s, step3.ak)); - MEMZERO(tmp.step3.nk, sizeof_field(tmp_sapling_addr_s, step3.nk)); + get_ivk(tmp.step1.zip32_seed, p, tmp.step3.ivk); zemu_log_stack("get_pkd"); @@ -1475,7 +1465,7 @@ zxerr_t crypto_fillAddress_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t crypto_fillSaplingSeed(tmp.step1.zip32_seed); CHECK_APP_CANARY(); - zip32_child_ida(tmp.step1.zip32_seed, tmp.step2.dk, tmp.step3.ak, tmp.step3.nk, p); + get_dk(tmp.step1.zip32_seed, tmp.step2.dk,p); CHECK_APP_CANARY(); bool found = false; @@ -1498,10 +1488,8 @@ zxerr_t crypto_fillAddress_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t return zxerr_unknown; } - get_ivk(tmp.step3.ak, tmp.step3.nk, tmp.step3.ivk); + get_ivk(tmp.step1.zip32_seed, p, tmp.step3.ivk); CHECK_APP_CANARY(); - MEMZERO(tmp.step3.ak, sizeof_field(tmp_sapling_addr_s, step3.ak)); - MEMZERO(tmp.step3.nk, sizeof_field(tmp_sapling_addr_s, step3.nk)); zemu_log_stack("get_pkd"); get_pkd(tmp.step3.ivk, out->diversifier, out->pkd); diff --git a/app/src/crypto.h b/app/src/crypto.h index 2bbbb840..f76bf2f4 100644 --- a/app/src/crypto.h +++ b/app/src/crypto.h @@ -46,8 +46,7 @@ zxerr_t crypto_fillAddress_with_diversifier_sapling(uint8_t *buffer, uint16_t bu zxerr_t crypto_diversifier_with_startindex(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint8_t *startindex, uint16_t *replylen); -//zxerr_t crypto_ivk_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint16_t *replyLen); -zxerr_t crypto_ivk_sapling_ida(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint16_t *replyLen); +zxerr_t crypto_ivk_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint16_t *replyLen); zxerr_t crypto_ovk_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint16_t *replyLen); diff --git a/js/src/common.js b/js/src/common.js index fd133999..0d49ff3f 100644 --- a/js/src/common.js +++ b/js/src/common.js @@ -13,7 +13,6 @@ export const INS = { SIGN_SAPLING: 0x12, GET_IVK_SAPLING: 0xf0, GET_OVK_SAPLING: 0xf1, - GET_IVK_SAPLING_IDA: 0xf2, INIT_TX: 0xa0, KEY_EXCHANGE: 0xaa, diff --git a/js/src/index.js b/js/src/index.js index ed9da9e6..14f4021c 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -412,12 +412,6 @@ export default class ZCashApp { } } - async getivkida(path) { - const buf = Buffer.alloc(4); - buf.writeUInt32LE(path, 0); - return this.transport.send(CLA, INS.GET_IVK_SAPLING_IDA, P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, buf, [0x9000]).then(processIVKResponse, processErrorResponse); - } - async getivk(path) { const buf = Buffer.alloc(4); buf.writeUInt32LE(path, 0); diff --git a/tests_zemu/tests/zcashtool.test.ts b/tests_zemu/tests/zcashtool.test.ts index 5779344f..deebfd2a 100644 --- a/tests_zemu/tests/zcashtool.test.ts +++ b/tests_zemu/tests/zcashtool.test.ts @@ -72,32 +72,6 @@ describe('Zcashtool tests', function () { } }) - test.each(models)('get ivk ida', async function (m) { - const sim = new Zemu(m.path) - try { - await sim.start({ ...defaultOptions, model: m.name }) - const app = new ZCashApp(sim.getTransport()) - - // const ivkreq = app.getivk(1000); - const ivkreq = app.getivkida(1000) - - await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot(), 600000) - - const clickSchedule = m.name == 'nanos' ? [2, 0] : [3, 0] - await sim.navigateAndCompareSnapshots('.', `${m.prefix.toLowerCase()}-getivk`, clickSchedule) - - const ivk = await ivkreq - console.log(ivk) - expect(ivk.return_code).toEqual(0x9000) - - const expected_ivk_raw = '6dfadf175921e6fbfa093c8f7c704a0bdb07328474f56c833dfcfa5301082d03' - const ivk_raw = ivk.ivk_raw.toString('hex') - expect(ivk_raw).toEqual(expected_ivk_raw) - } finally { - await sim.close() - } - }) - test.each(models)('get outgoing viewing key', async function (m) { const sim = new Zemu(m.path) try { From 915cc469afa168c88a37adbd5f914be5b8144081 Mon Sep 17 00:00:00 2001 From: Ida Date: Wed, 16 Mar 2022 15:55:57 +0100 Subject: [PATCH 04/16] removing get_dk from c interface, renaming get_ivk to zip32_ivk --- app/rust/include/rslib.h | 10 +++---- app/rust/src/zip32.rs | 57 ++++++++++++++++++++++------------------ app/src/crypto.c | 20 ++++---------- 3 files changed, 41 insertions(+), 46 deletions(-) diff --git a/app/rust/include/rslib.h b/app/rust/include/rslib.h index 2b57b0d4..7bff4bac 100644 --- a/app/rust/include/rslib.h +++ b/app/rust/include/rslib.h @@ -13,8 +13,6 @@ void ask_to_ak(const uint8_t *ask_ptr, uint8_t *ak_ptr); void nsk_to_nk(const uint8_t *nsk_ptr, uint8_t *nk_ptr); -void get_ivk(const uint8_t *ak_ptr, const uint32_t pos, uint8_t *ivk_ptr); - void get_pkd(const uint8_t *ivk_ptr, const uint8_t *diversifier_ptr, uint8_t *pkd); void group_hash_from_div(const uint8_t *diversifier_ptr, uint8_t *gd); @@ -25,18 +23,18 @@ void get_diversifier_fromlist(const uint8_t *diversifier_list, uint8_t *diversif bool is_valid_diversifier(const uint8_t *diversifier); -void get_diversifier_list_withstartindex(const uint8_t *sk_ptr, const uint8_t *startindex, uint8_t *diversifier_list); +void get_diversifier_list_withstartindex(const uint8_t *seed_ptr, const uint32_t pos, const uint8_t *startindex, uint8_t *diversifier_list); -void get_default_diversifier_list_withstartindex(const uint8_t *sk_ptr, uint8_t *startindex, uint8_t *diversifier_list); +void get_default_diversifier_list_withstartindex(const uint8_t *seed_ptr, const uint32_t pos, uint8_t *startindex, uint8_t *diversifier_list); void zip32_master(const uint8_t *seed_ptr, uint8_t *sk_ptr, uint8_t *dk_ptr); void zip32_child(const uint8_t *seed_ptr, uint8_t *dk, uint8_t *ask, uint8_t *nsk, const uint32_t pos); -void get_dk(const uint8_t *seed_ptr, uint8_t *dk, const uint32_t pos); - void zip32_child_ask_nsk(const uint8_t *seed_ptr, uint8_t *ask, uint8_t *nsk, const uint32_t pos); +void zip32_ivk(const uint8_t *ak_ptr, uint8_t *ivk_ptr, const uint32_t pos); + void zip32_ovk(const uint8_t *seed_ptr, uint8_t *ovk, const uint32_t pos); void zip32_child_proof_key(const uint8_t *seed_ptr, uint8_t *dk_ptr, uint8_t *ak_ptr, uint8_t *nsk_ptr, diff --git a/app/rust/src/zip32.rs b/app/rust/src/zip32.rs index 54ee6f14..2dacd280 100644 --- a/app/rust/src/zip32.rs +++ b/app/rust/src/zip32.rs @@ -489,6 +489,24 @@ pub fn derive_zip32_child_fromseedandpath(seed: &[u8; 32], path: &[u32]) -> [u8; result } +#[no_mangle] +pub fn get_dk( + seed_ptr: *const [u8; 32], + dk_ptr: *mut [u8; 32], + pos: u32, +) { + let seed = unsafe { &*seed_ptr }; + let dk = unsafe { &mut *dk_ptr }; + + const FIRSTVALUE: u32 = 32 ^ 0x8000_0000; + const COIN_TYPE: u32 = 133 ^ 0x8000_0000; //hardened, fixed value from https://github.com/adityapk00/librustzcash/blob/master/zcash_client_backend/src/constants/mainnet.rs + let k = derive_zip32_child_fromseedandpath(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet + + // k = dk || ask || nsk || ak || nk + dk.copy_from_slice(&k[0..32]); +} + + #[no_mangle] pub extern "C" fn nsk_to_nk(nsk_ptr: *const [u8; 32], nk_ptr: *mut [u8; 32]) { let nsk = unsafe { &*nsk_ptr }; @@ -498,10 +516,10 @@ pub extern "C" fn nsk_to_nk(nsk_ptr: *const [u8; 32], nk_ptr: *mut [u8; 32]) { } #[no_mangle] -pub extern "C" fn get_ivk( +pub extern "C" fn zip32_ivk( seed_ptr: *const [u8; 32], - pos: u32, ivk_ptr: *mut [u8; 32], + pos: u32, ) { let seed = unsafe { &*seed_ptr }; let mut ak = [0u8; 32]; @@ -571,23 +589,6 @@ pub extern "C" fn zip32_child( nsk.copy_from_slice(&k[64..96]); } -#[no_mangle] -pub extern "C" fn get_dk( - seed_ptr: *const [u8; 32], - dk_ptr: *mut [u8; 32], - pos: u32, -) { - let seed = unsafe { &*seed_ptr }; - let dk = unsafe { &mut *dk_ptr }; - - const FIRSTVALUE: u32 = 32 ^ 0x8000_0000; - const COIN_TYPE: u32 = 133 ^ 0x8000_0000; //hardened, fixed value from https://github.com/adityapk00/librustzcash/blob/master/zcash_client_backend/src/constants/mainnet.rs - let k = derive_zip32_child_fromseedandpath(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet - - // k = dk || ask || nsk || ak || nk - dk.copy_from_slice(&k[0..32]); -} - #[no_mangle] pub extern "C" fn zip32_child_proof_key( seed_ptr: *const [u8; 32], @@ -641,26 +642,32 @@ pub extern "C" fn get_diversifier_list( #[no_mangle] pub extern "C" fn get_diversifier_list_withstartindex( - sk_ptr: *const [u8; 32], + seed_ptr: *const [u8; 32], + pos: u32, start_index: *const [u8; 11], diversifier_list_ptr: *mut [u8; 220], ) { - let sk = unsafe { &*sk_ptr }; + let mut dk = [0u8; 32]; + let seed = unsafe { &*seed_ptr }; let start = unsafe { &*start_index }; let diversifier = unsafe { &mut *diversifier_list_ptr }; - ff1aes_list_with_startingindex(sk, start, diversifier); + get_dk(seed,&mut dk,pos); + ff1aes_list_with_startingindex(&mut dk, start, diversifier); } #[no_mangle] pub extern "C" fn get_default_diversifier_list_withstartindex( - sk_ptr: *const [u8; 32], + seed_ptr: *const [u8; 32], + pos: u32, start_index: *mut [u8; 11], diversifier_list_ptr: *mut [u8; 44], ) { - let sk = unsafe { &*sk_ptr }; + let mut dk = [0u8; 32]; + let seed = unsafe { &*seed_ptr }; let start = unsafe { &mut *start_index }; let diversifier = unsafe { &mut *diversifier_list_ptr }; - ff1aes_list_with_startingindex_default(sk, start, diversifier); + get_dk(seed,&mut dk,pos); + ff1aes_list_with_startingindex_default(&mut dk, start, diversifier); } #[no_mangle] diff --git a/app/src/crypto.c b/app/src/crypto.c index 6b74beac..2e9927e8 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -1270,11 +1270,8 @@ zxerr_t crypto_ivk_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint // Temporarily get sk from Ed25519 crypto_fillSaplingSeed(tmp.step1.zip32_seed); CHECK_APP_CANARY(); - - get_ivk(tmp.step1.zip32_seed, p, out); - + zip32_ivk(tmp.step1.zip32_seed, out, p); CHECK_APP_CANARY(); - MEMZERO(&tmp, sizeof(tmp_sapling_addr_s)); } FINALLY @@ -1337,10 +1334,7 @@ zxerr_t crypto_diversifier_with_startindex(uint8_t *buffer, uint16_t bufferLen, crypto_fillSaplingSeed(tmp.step1.zip32_seed); CHECK_APP_CANARY(); - get_dk(tmp.step1.zip32_seed, tmp.step2.dk,p); - CHECK_APP_CANARY(); - - get_diversifier_list_withstartindex(tmp.step2.dk,startindex,buffer); + get_diversifier_list_withstartindex(tmp.step1.zip32_seed,p,startindex,buffer); for(int i = 0; i < DIV_LIST_LENGTH; i++){ if (!is_valid_diversifier(buffer+i*DIV_SIZE)){ MEMZERO(buffer+i*DIV_SIZE,DIV_SIZE); @@ -1408,7 +1402,7 @@ zxerr_t crypto_fillAddress_with_diversifier_sapling(uint8_t *buffer, uint16_t bu CHECK_APP_CANARY(); - get_ivk(tmp.step1.zip32_seed, p, tmp.step3.ivk); + zip32_ivk(tmp.step1.zip32_seed, tmp.step3.ivk, p); zemu_log_stack("get_pkd"); @@ -1465,18 +1459,14 @@ zxerr_t crypto_fillAddress_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t crypto_fillSaplingSeed(tmp.step1.zip32_seed); CHECK_APP_CANARY(); - get_dk(tmp.step1.zip32_seed, tmp.step2.dk,p); - CHECK_APP_CANARY(); - bool found = false; while(!found){ - get_default_diversifier_list_withstartindex(tmp.step2.dk, out->startindex, out->diversifierlist); + get_default_diversifier_list_withstartindex(tmp.step1.zip32_seed, p, out->startindex, out->diversifierlist); uint8_t *ptr = out->diversifierlist; for(uint8_t i = 0; i < DIV_DEFAULT_LIST_LEN; i++, ptr += DIV_SIZE){ if(!found && is_valid_diversifier(ptr)){ MEMCPY(out->diversifier, ptr, DIV_SIZE); MEMZERO(out + DIV_SIZE, MAX_SIZE_BUF_ADDR - DIV_SIZE); - MEMZERO(tmp.step2.dk, sizeof_field(tmp_sapling_addr_s, step2.dk)); found = true; } } @@ -1488,7 +1478,7 @@ zxerr_t crypto_fillAddress_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t return zxerr_unknown; } - get_ivk(tmp.step1.zip32_seed, p, tmp.step3.ivk); + zip32_ivk(tmp.step1.zip32_seed, tmp.step3.ivk, p); CHECK_APP_CANARY(); zemu_log_stack("get_pkd"); From dd84177d79a95fd41773911e3b27d765df262bea Mon Sep 17 00:00:00 2001 From: Ida Date: Wed, 16 Mar 2022 16:39:04 +0100 Subject: [PATCH 05/16] Removing dk from c code --- app/rust/include/rslib.h | 5 ++--- app/rust/src/zip32.rs | 6 ------ app/src/crypto.c | 10 ++-------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/app/rust/include/rslib.h b/app/rust/include/rslib.h index 7bff4bac..869cb256 100644 --- a/app/rust/include/rslib.h +++ b/app/rust/include/rslib.h @@ -29,7 +29,7 @@ void get_default_diversifier_list_withstartindex(const uint8_t *seed_ptr, const void zip32_master(const uint8_t *seed_ptr, uint8_t *sk_ptr, uint8_t *dk_ptr); -void zip32_child(const uint8_t *seed_ptr, uint8_t *dk, uint8_t *ask, uint8_t *nsk, const uint32_t pos); +void zip32_child(const uint8_t *seed_ptr, uint8_t *ask, uint8_t *nsk, const uint32_t pos); void zip32_child_ask_nsk(const uint8_t *seed_ptr, uint8_t *ask, uint8_t *nsk, const uint32_t pos); @@ -37,8 +37,7 @@ void zip32_ivk(const uint8_t *ak_ptr, uint8_t *ivk_ptr, const uint32_t pos); void zip32_ovk(const uint8_t *seed_ptr, uint8_t *ovk, const uint32_t pos); -void zip32_child_proof_key(const uint8_t *seed_ptr, uint8_t *dk_ptr, uint8_t *ak_ptr, uint8_t *nsk_ptr, - const uint32_t pos); +void zip32_child_proof_key(const uint8_t *seed_ptr, uint8_t *ak_ptr, uint8_t *nsk_ptr, const uint32_t pos); //Rseed void rseed_get_esk(const uint8_t *input, uint8_t *output_ptr); diff --git a/app/rust/src/zip32.rs b/app/rust/src/zip32.rs index 2dacd280..0481b8d6 100644 --- a/app/rust/src/zip32.rs +++ b/app/rust/src/zip32.rs @@ -571,20 +571,17 @@ pub extern "C" fn zip32_ovk(seed_ptr: *const [u8; 32], ovk_ptr: *mut [u8; 32], p #[no_mangle] pub extern "C" fn zip32_child( seed_ptr: *const [u8; 32], - dk_ptr: *mut [u8; 32], ask_ptr: *mut [u8; 32], nsk_ptr: *mut [u8; 32], pos: u32, ) { let seed = unsafe { &*seed_ptr }; - let dk = unsafe { &mut *dk_ptr }; let ask = unsafe { &mut *ask_ptr }; let nsk = unsafe { &mut *nsk_ptr }; const FIRSTVALUE: u32 = 32 ^ 0x8000_0000; const COIN_TYPE: u32 = 133 ^ 0x8000_0000; //hardened, fixed value from https://github.com/adityapk00/librustzcash/blob/master/zcash_client_backend/src/constants/mainnet.rs let k = derive_zip32_child_fromseedandpath(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet - dk.copy_from_slice(&k[0..32]); ask.copy_from_slice(&k[32..64]); nsk.copy_from_slice(&k[64..96]); } @@ -592,13 +589,11 @@ pub extern "C" fn zip32_child( #[no_mangle] pub extern "C" fn zip32_child_proof_key( seed_ptr: *const [u8; 32], - dk_ptr: *mut [u8; 32], ak_ptr: *mut [u8; 32], nsk_ptr: *mut [u8; 32], pos: u32, ) { let seed = unsafe { &*seed_ptr }; - let dk = unsafe { &mut *dk_ptr }; let ak = unsafe { &mut *ak_ptr }; let nsk = unsafe { &mut *nsk_ptr }; @@ -607,7 +602,6 @@ pub extern "C" fn zip32_child_proof_key( let k = derive_zip32_child_fromseedandpath(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet // k = dk || ask || nsk || ak || nk - dk.copy_from_slice(&k[0..32]); ak.copy_from_slice(&k[96..128]); nsk.copy_from_slice(&k[64..96]); } diff --git a/app/src/crypto.c b/app/src/crypto.c index 2e9927e8..273ee42f 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -335,13 +335,11 @@ typedef struct { union { // STEP 1 struct { - uint8_t dk[DK_SIZE]; uint8_t zip32_seed[ZIP32_SEED_SIZE]; uint8_t sk[ED25519_SK_SIZE]; } step1; struct { - uint8_t dk[DK_SIZE]; uint8_t ask[ASK_SIZE]; uint8_t nsk[NSK_SIZE]; } step2; @@ -375,7 +373,7 @@ zxerr_t crypto_extract_spend_proofkeyandrnd(uint8_t *buffer, uint16_t bufferLen) crypto_fillSaplingSeed(tmp.step1.zip32_seed); CHECK_APP_CANARY(); - zip32_child_proof_key(tmp.step1.zip32_seed, tmp.step2.dk, out, out + AK_SIZE, next->path); + zip32_child_proof_key(tmp.step1.zip32_seed, out, out + AK_SIZE, next->path); CHECK_APP_CANARY(); } FINALLY @@ -1102,12 +1100,10 @@ typedef struct { union { // STEP 1 struct { - uint8_t dk[DK_SIZE]; uint8_t zip32_seed[ZIP32_SEED_SIZE]; } step1; struct { - uint8_t dk[DK_SIZE]; uint8_t ask[ASK_SIZE]; uint8_t nsk[NSK_SIZE]; } step2; @@ -1161,7 +1157,7 @@ zxerr_t crypto_signspends_sapling(uint8_t *buffer, uint16_t bufferLen, const uin return zxerr_unknown; } - zip32_child(tmp.step1.zip32_seed, tmp.step2.dk, tmp.step2.ask, tmp.step2.nsk, item->path); + zip32_child(tmp.step1.zip32_seed, tmp.step2.ask, tmp.step2.nsk, item->path); randomized_secret(tmp.step2.ask, (uint8_t *)item->alpha, tmp.step3.rsk); sign_redjubjub((uint8_t *)tmp.step3.rsk, (uint8_t *)sighash, (uint8_t *)out); @@ -1231,13 +1227,11 @@ typedef struct { union { // STEP 1 struct { - uint8_t dk[DK_SIZE]; uint8_t zip32_seed[ZIP32_SEED_SIZE]; uint8_t sk[ED25519_SK_SIZE]; } step1; struct { - uint8_t dk[DK_SIZE]; uint8_t ask[ASK_SIZE]; uint8_t nsk[NSK_SIZE]; } step2; From 1ca53856095d5e198db3a9e604a5a552f85ade1b Mon Sep 17 00:00:00 2001 From: Ida Date: Thu, 17 Mar 2022 09:46:00 +0100 Subject: [PATCH 06/16] get pkd directly from seed, position and ptr --- app/rust/include/rslib.h | 2 +- app/rust/src/zip32.rs | 10 +++++--- app/src/crypto.c | 52 +++++++++++++--------------------------- 3 files changed, 25 insertions(+), 39 deletions(-) diff --git a/app/rust/include/rslib.h b/app/rust/include/rslib.h index 869cb256..b177828f 100644 --- a/app/rust/include/rslib.h +++ b/app/rust/include/rslib.h @@ -13,7 +13,7 @@ void ask_to_ak(const uint8_t *ask_ptr, uint8_t *ak_ptr); void nsk_to_nk(const uint8_t *nsk_ptr, uint8_t *nk_ptr); -void get_pkd(const uint8_t *ivk_ptr, const uint8_t *diversifier_ptr, uint8_t *pkd); +void get_pkd(const uint8_t *seed_ptr, const uint32_t pos, const uint8_t *diversifier_ptr, uint8_t *pkd); void group_hash_from_div(const uint8_t *diversifier_ptr, uint8_t *gd); diff --git a/app/rust/src/zip32.rs b/app/rust/src/zip32.rs index 0481b8d6..ee42f5f9 100644 --- a/app/rust/src/zip32.rs +++ b/app/rust/src/zip32.rs @@ -684,15 +684,19 @@ pub extern "C" fn get_diversifier_fromlist( #[no_mangle] pub extern "C" fn get_pkd( - ivk_ptr: *const [u8; 32], + seed_ptr: *const [u8; 32], + pos: u32, diversifier_ptr: *const [u8; 11], pkd_ptr: *mut [u8; 32], ) { - let ivk = unsafe { &*ivk_ptr }; + let mut ivk = [0u8;32]; + let ivk_ptr = { &mut ivk }; let diversifier = unsafe { &*diversifier_ptr }; let pkd = unsafe { &mut *pkd_ptr }; - let tmp_pkd = default_pkd(&ivk, &diversifier); + zip32_ivk(seed_ptr,ivk_ptr, pos); + + let tmp_pkd = default_pkd(ivk_ptr, &diversifier); pkd.copy_from_slice(&tmp_pkd) } diff --git a/app/src/crypto.c b/app/src/crypto.c index 273ee42f..6dd5f0f2 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -1224,24 +1224,8 @@ zxerr_t crypto_hash_messagebuffer(uint8_t *buffer, uint16_t bufferLen, const uin } typedef struct { - union { - // STEP 1 - struct { uint8_t zip32_seed[ZIP32_SEED_SIZE]; - uint8_t sk[ED25519_SK_SIZE]; - } step1; - - struct { - uint8_t ask[ASK_SIZE]; - uint8_t nsk[NSK_SIZE]; - } step2; - // STEP 2 - struct { - uint8_t ivk[IVK_SIZE]; - uint8_t ak[AK_SIZE]; - uint8_t nk[NK_SIZE]; - } step3; - }; + uint8_t sk[ED25519_SK_SIZE]; // Removing this causes a segfault... strange... } tmp_sapling_addr_s; @@ -1262,9 +1246,9 @@ zxerr_t crypto_ivk_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint TRY { // Temporarily get sk from Ed25519 - crypto_fillSaplingSeed(tmp.step1.zip32_seed); + crypto_fillSaplingSeed(tmp.zip32_seed); CHECK_APP_CANARY(); - zip32_ivk(tmp.step1.zip32_seed, out, p); + zip32_ivk(tmp.zip32_seed, out, p); CHECK_APP_CANARY(); MEMZERO(&tmp, sizeof(tmp_sapling_addr_s)); } @@ -1296,9 +1280,9 @@ zxerr_t crypto_ovk_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t p, uint TRY { // Temporarily get sk from Ed25519 - crypto_fillSaplingSeed(tmp.step1.zip32_seed); + crypto_fillSaplingSeed(tmp.zip32_seed); CHECK_APP_CANARY(); - zip32_ovk(tmp.step1.zip32_seed,out,p); + zip32_ovk(tmp.zip32_seed,out,p); MEMZERO(&tmp,sizeof(tmp)); CHECK_APP_CANARY(); } @@ -1325,10 +1309,10 @@ zxerr_t crypto_diversifier_with_startindex(uint8_t *buffer, uint16_t bufferLen, TRY { // Temporarily get sk from Ed25519 - crypto_fillSaplingSeed(tmp.step1.zip32_seed); + crypto_fillSaplingSeed(tmp.zip32_seed); CHECK_APP_CANARY(); - get_diversifier_list_withstartindex(tmp.step1.zip32_seed,p,startindex,buffer); + get_diversifier_list_withstartindex(tmp.zip32_seed,p,startindex,buffer); for(int i = 0; i < DIV_LIST_LENGTH; i++){ if (!is_valid_diversifier(buffer+i*DIV_SIZE)){ MEMZERO(buffer+i*DIV_SIZE,DIV_SIZE); @@ -1392,17 +1376,17 @@ zxerr_t crypto_fillAddress_with_diversifier_sapling(uint8_t *buffer, uint16_t bu TRY { // Temporarily get sk from Ed25519 - crypto_fillSaplingSeed(tmp.step1.zip32_seed); + crypto_fillSaplingSeed(tmp.zip32_seed); CHECK_APP_CANARY(); - zip32_ivk(tmp.step1.zip32_seed, tmp.step3.ivk, p); - zemu_log_stack("get_pkd"); - get_pkd(tmp.step3.ivk, out->diversifier, out->pkd); + get_pkd(tmp.zip32_seed, p, out->diversifier, out->pkd); + + CHECK_APP_CANARY(); - MEMZERO(tmp.step3.ivk, sizeof_field(tmp_sapling_addr_s, step3.ivk)); + MEMZERO(tmp.zip32_seed, sizeof_field(tmp_sapling_addr_s, zip32_seed)); } FINALLY { @@ -1450,12 +1434,12 @@ zxerr_t crypto_fillAddress_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t TRY { // Temporarily get sk from Ed25519 - crypto_fillSaplingSeed(tmp.step1.zip32_seed); + crypto_fillSaplingSeed(tmp.zip32_seed); CHECK_APP_CANARY(); bool found = false; while(!found){ - get_default_diversifier_list_withstartindex(tmp.step1.zip32_seed, p, out->startindex, out->diversifierlist); + get_default_diversifier_list_withstartindex(tmp.zip32_seed, p, out->startindex, out->diversifierlist); uint8_t *ptr = out->diversifierlist; for(uint8_t i = 0; i < DIV_DEFAULT_LIST_LEN; i++, ptr += DIV_SIZE){ if(!found && is_valid_diversifier(ptr)){ @@ -1471,14 +1455,12 @@ zxerr_t crypto_fillAddress_sapling(uint8_t *buffer, uint16_t bufferLen, uint32_t CLOSE_TRY; return zxerr_unknown; } + zemu_log_stack("get_pkd"); - zip32_ivk(tmp.step1.zip32_seed, tmp.step3.ivk, p); - CHECK_APP_CANARY(); + get_pkd(tmp.zip32_seed, p, out->diversifier, out->pkd); - zemu_log_stack("get_pkd"); - get_pkd(tmp.step3.ivk, out->diversifier, out->pkd); CHECK_APP_CANARY(); - MEMZERO(tmp.step3.ivk, sizeof_field(tmp_sapling_addr_s, step3.ivk)); + MEMZERO(tmp.zip32_seed, sizeof_field(tmp_sapling_addr_s, zip32_seed)); } FINALLY { From feca4870e781278b54b2d1fd85402d8c9b14c54c Mon Sep 17 00:00:00 2001 From: Ida Date: Thu, 17 Mar 2022 15:27:10 +0100 Subject: [PATCH 07/16] get esk epk and randomized_secret_from_seed --- app/rust/include/rslib.h | 8 +++----- app/rust/src/commitments.rs | 4 ++-- app/rust/src/note_encryption.rs | 18 +++++++++++++++++- app/rust/src/redjubjub.rs | 22 ++++++++++++++++++++++ app/rust/src/zeccrypto.rs | 2 +- app/rust/src/zip32.rs | 18 ------------------ app/src/crypto.c | 17 ++++++++--------- 7 files changed, 53 insertions(+), 36 deletions(-) diff --git a/app/rust/include/rslib.h b/app/rust/include/rslib.h index b177828f..1ec32388 100644 --- a/app/rust/include/rslib.h +++ b/app/rust/include/rslib.h @@ -29,8 +29,6 @@ void get_default_diversifier_list_withstartindex(const uint8_t *seed_ptr, const void zip32_master(const uint8_t *seed_ptr, uint8_t *sk_ptr, uint8_t *dk_ptr); -void zip32_child(const uint8_t *seed_ptr, uint8_t *ask, uint8_t *nsk, const uint32_t pos); - void zip32_child_ask_nsk(const uint8_t *seed_ptr, uint8_t *ask, uint8_t *nsk, const uint32_t pos); void zip32_ivk(const uint8_t *ak_ptr, uint8_t *ivk_ptr, const uint32_t pos); @@ -40,7 +38,7 @@ void zip32_ovk(const uint8_t *seed_ptr, uint8_t *ovk, const uint32_t pos); void zip32_child_proof_key(const uint8_t *seed_ptr, uint8_t *ak_ptr, uint8_t *nsk_ptr, const uint32_t pos); //Rseed -void rseed_get_esk(const uint8_t *input, uint8_t *output_ptr); +void rseed_get_esk_epk(const uint8_t *seed_ptr, uint8_t *d_ptr, uint8_t *output_esk_ptr, uint8_t *output_epk_ptr); void rseed_get_rcm(const uint8_t *input, uint8_t *output_ptr); @@ -58,8 +56,6 @@ void compute_valueBalance_commitment(const uint64_t u64, uint8_t *output); //Note encryption void blake2b_prf(uint8_t *inputptr, uint8_t *outptr); -void get_epk(uint8_t *esk_ptr, uint8_t *d_ptr, uint8_t *output_ptr); - void ka_to_key(uint8_t *esk_ptr, uint8_t *pkd_ptr, uint8_t *epk_ptr, uint8_t *output_ptr); void prepare_enccompact_input(uint8_t *d, uint64_t value, uint8_t *rcm, uint8_t memotype, uint8_t *output); @@ -67,6 +63,8 @@ void prepare_enccompact_input(uint8_t *d, uint64_t value, uint8_t *rcm, uint8_t //RedJubjub void random_fr(uint8_t *alpha_ptr); +void randomized_secret_from_seed(const uint8_t *seed_ptr,const uint32_t pos, uint8_t *alpha_ptr, uint8_t *output_ptr); + void randomized_secret(uint8_t *sk_ptr, uint8_t *alpha_ptr, uint8_t *output_ptr); void sk_to_pk(uint8_t *sk_ptr, uint8_t *pk_ptr); diff --git a/app/rust/src/commitments.rs b/app/rust/src/commitments.rs index 7364a6ec..a5873021 100644 --- a/app/rust/src/commitments.rs +++ b/app/rust/src/commitments.rs @@ -182,7 +182,7 @@ pub fn prepare_and_hash_input_commitment( pkd_ptr: *const [u8; 32], output_ptr: *mut [u8; 32], ) { - c_zemu_log_stack(b"entry_preparenotecommit\x00".as_ref()); + c_zemu_log_stack(b"prepare_and_hash_intput_for_notecommit\x00".as_ref()); let gd = unsafe { &*g_d_ptr }; let pkd = unsafe { &*pkd_ptr }; @@ -314,7 +314,7 @@ pub extern "C" fn compute_note_commitment_fullpoint( value: u64, g_d_ptr: *const [u8; 32], pkd_ptr: *const [u8; 32]) { - c_zemu_log_stack(b"entry_preparenotecommit\x00".as_ref()); + c_zemu_log_stack(b"entry_preparenotecommit_full\x00".as_ref()); let gd = unsafe { &*g_d_ptr }; let pkd = unsafe { &*pkd_ptr }; diff --git a/app/rust/src/note_encryption.rs b/app/rust/src/note_encryption.rs index 310bde89..8f30159e 100644 --- a/app/rust/src/note_encryption.rs +++ b/app/rust/src/note_encryption.rs @@ -23,7 +23,7 @@ pub extern "C" fn blake2b_prf(input_ptr: *const [u8; 128], out_ptr: *mut [u8; 32 } #[no_mangle] -pub extern "C" fn get_epk( +pub fn get_epk( esk_ptr: *const [u8; 32], d_ptr: *const [u8; 11], output_ptr: *mut [u8; 32], @@ -36,6 +36,22 @@ pub extern "C" fn get_epk( output.copy_from_slice(&epk); } +#[no_mangle] +pub extern "C" fn rseed_get_esk_epk(rseed_ptr: *const [u8; 32], + d_ptr: *const [u8; 11], + output_esk_ptr: *mut [u8; 32], + output_epk_ptr: *mut [u8; 32]) { + let rseed = unsafe { &*rseed_ptr }; +// let d = unsafe { &*d_ptr }; + let output_esk = unsafe { &mut *output_esk_ptr }; + let output_epk = unsafe { &mut *output_epk_ptr }; + rseed_get_esk(rseed, output_esk); + + //let epk = multwithgd(output_esk, d); + get_epk(output_esk,d_ptr,output_epk); + //output_epk.copy_from_slice(&epk); +} + #[no_mangle] pub extern "C" fn ka_to_key( esk_ptr: *const [u8; 32], diff --git a/app/rust/src/redjubjub.rs b/app/rust/src/redjubjub.rs index a49684e3..fe317599 100644 --- a/app/rust/src/redjubjub.rs +++ b/app/rust/src/redjubjub.rs @@ -9,6 +9,7 @@ use crate::commitments::bytes_to_extended; use crate::constants; use crate::constants::*; use crate::pedersen::extended_to_bytes; +use crate::zip32::zip32_child_ask_nsk; #[inline(never)] pub fn h_star(a: &[u8], b: &[u8]) -> Fr { @@ -95,6 +96,27 @@ pub extern "C" fn random_fr(alpha_ptr: *mut [u8; 32]) { alpha.copy_from_slice(&fr.to_bytes()); } +#[no_mangle] +pub extern "C" fn randomized_secret_from_seed( + seed_ptr: *const [u8; 32], + pos: u32, + alpha_ptr: *const [u8; 32], + output_ptr: *mut [u8; 32], +) { + c_zemu_log_stack(b"random_sk\x00".as_ref()); + let mut ask = [0u8;32]; + let mut nsk = [0u8;32]; + let alpha = unsafe { &*alpha_ptr }; + let output = unsafe { &mut *output_ptr }; + + zip32_child_ask_nsk(seed_ptr,&mut ask, &mut nsk, pos); + + let mut skfr = Fr::from_bytes(&ask).unwrap(); + let alphafr = Fr::from_bytes(&alpha).unwrap(); + skfr += alphafr; + output.copy_from_slice(&skfr.to_bytes()); +} + #[no_mangle] pub extern "C" fn randomized_secret( sk_ptr: *const [u8; 32], diff --git a/app/rust/src/zeccrypto.rs b/app/rust/src/zeccrypto.rs index 88cc44ca..069d590f 100644 --- a/app/rust/src/zeccrypto.rs +++ b/app/rust/src/zeccrypto.rs @@ -88,7 +88,7 @@ pub extern "C" fn rseed_get_rcm(rseed_ptr: *const [u8; 32], output_ptr: *mut [u8 } #[no_mangle] -pub extern "C" fn rseed_get_esk(rseed_ptr: *const [u8; 32], output_ptr: *mut [u8; 32]) { +pub fn rseed_get_esk(rseed_ptr: *const [u8; 32], output_ptr: *mut [u8; 32]) { let rseed = unsafe { &*rseed_ptr }; let output = unsafe { &mut *output_ptr }; let p = rseed_generate_esk(rseed); diff --git a/app/rust/src/zip32.rs b/app/rust/src/zip32.rs index ee42f5f9..24bfb306 100644 --- a/app/rust/src/zip32.rs +++ b/app/rust/src/zip32.rs @@ -567,24 +567,6 @@ pub extern "C" fn zip32_ovk(seed_ptr: *const [u8; 32], ovk_ptr: *mut [u8; 32], p ovk.copy_from_slice(&k[0..32]); } -//this function is consistent with zecwallet code -#[no_mangle] -pub extern "C" fn zip32_child( - seed_ptr: *const [u8; 32], - ask_ptr: *mut [u8; 32], - nsk_ptr: *mut [u8; 32], - pos: u32, -) { - let seed = unsafe { &*seed_ptr }; - let ask = unsafe { &mut *ask_ptr }; - let nsk = unsafe { &mut *nsk_ptr }; - - const FIRSTVALUE: u32 = 32 ^ 0x8000_0000; - const COIN_TYPE: u32 = 133 ^ 0x8000_0000; //hardened, fixed value from https://github.com/adityapk00/librustzcash/blob/master/zcash_client_backend/src/constants/mainnet.rs - let k = derive_zip32_child_fromseedandpath(seed, &[FIRSTVALUE, COIN_TYPE, pos]); //consistent with zecwallet - ask.copy_from_slice(&k[32..64]); - nsk.copy_from_slice(&k[64..96]); -} #[no_mangle] pub extern "C" fn zip32_child_proof_key( diff --git a/app/src/crypto.c b/app/src/crypto.c index 6dd5f0f2..cc42bcf2 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -373,6 +373,7 @@ zxerr_t crypto_extract_spend_proofkeyandrnd(uint8_t *buffer, uint16_t bufferLen) crypto_fillSaplingSeed(tmp.step1.zip32_seed); CHECK_APP_CANARY(); + // Gets ak and nsk zip32_child_proof_key(tmp.step1.zip32_seed, out, out + AK_SIZE, next->path); CHECK_APP_CANARY(); } @@ -619,8 +620,7 @@ zxerr_t crypto_checkspend_sapling(uint8_t *buffer, uint16_t bufferLen, const uin return zxerr_unknown; } - // if we want to have this also return nk, we need to change the tmp_checkspend structure so that step1 - // uses an extra 32 uint8's for nk in step 1. Then step 6 will no longer be necessary. + // we later need nsk zip32_child_ask_nsk(tmp.step1.zip32_seed, tmp.step2.ask, tmp.step2.nsk, item->path); randomized_secret(tmp.step2.ask, (uint8_t *)item->alpha, tmp.step2.ask); @@ -632,7 +632,8 @@ zxerr_t crypto_checkspend_sapling(uint8_t *buffer, uint16_t bufferLen, const uin return zxerr_unknown; } - compute_value_commitment(item->value,item->rcmvalue,tmp.step4.cv); // TODO: I think its rcmvalue for value commitments... + // step4.cv = step3.rk. + compute_value_commitment(item->value,item->rcmvalue,tmp.step4.cv); if (MEMCMP(tmp.step4.cv, start_spenddata + INDEX_SPEND_VALUECMT + i *SPEND_TX_LEN,VALUE_COMMITMENT_SIZE) != 0){ MEMZERO(&tmp, sizeof(tmp_checkspend)); MEMZERO(out,bufferLen); @@ -644,7 +645,6 @@ zxerr_t crypto_checkspend_sapling(uint8_t *buffer, uint16_t bufferLen, const uin compute_note_commitment_fullpoint(tmp_buf->pedersen_hash, start_spendolddata + INDEX_SPEND_OLD_RCM + i * SPEND_OLD_TX_LEN,item->value, tmp.step5.gd, item->pkd); - // TODO: we could get nk at the same time as ask and nsk nsk_to_nk(tmp.step5.nsk,tmp.step6.nk); uint64_t notepos = 0; { @@ -664,6 +664,7 @@ zxerr_t crypto_checkspend_sapling(uint8_t *buffer, uint16_t bufferLen, const uin compute_nullifier(tmp_buf->ncm_full, notepos, tmp.step6.nk, tmp_buf->nf); if (MEMCMP(tmp_buf->nf, start_spenddata + INDEX_SPEND_NF + i * SPEND_TX_LEN, NULLIFIER_SIZE) != 0){ //maybe spendlist_reset(); + zemu_log_stack("Nullifier is bad\n"); MEMZERO(out, bufferLen); MEMZERO(&tmp, sizeof(tmp_checkspend)); CLOSE_TRY; @@ -880,9 +881,7 @@ zxerr_t crypto_checkencryptions_sapling(uint8_t *buffer, uint16_t bufferLen, con MEMZERO(out, bufferLen); return zxerr_unknown; } - rseed_get_esk(item->rseed,tmp->step1.esk); - CHECK_APP_CANARY(); - get_epk(tmp->step1.esk, (uint8_t *) item->div, tmp->step1.epk); + rseed_get_esk_epk(item->rseed,(uint8_t *) item->div, tmp->step1.esk, tmp->step1.epk); CHECK_APP_CANARY(); if (MEMCMP(tmp->step1.epk, start_outputdata + INDEX_OUTPUT_EPK + i * OUTPUT_TX_LEN, EPK_SIZE) != 0){ MEMZERO(out, bufferLen); @@ -1157,9 +1156,9 @@ zxerr_t crypto_signspends_sapling(uint8_t *buffer, uint16_t bufferLen, const uin return zxerr_unknown; } - zip32_child(tmp.step1.zip32_seed, tmp.step2.ask, tmp.step2.nsk, item->path); + //zip32_child_ask_nsk(tmp.step1.zip32_seed, tmp.step2.ask, tmp.step2.nsk, item->path); - randomized_secret(tmp.step2.ask, (uint8_t *)item->alpha, tmp.step3.rsk); + randomized_secret_from_seed(tmp.step1.zip32_seed,item->path, (uint8_t *)item->alpha, tmp.step3.rsk); sign_redjubjub((uint8_t *)tmp.step3.rsk, (uint8_t *)sighash, (uint8_t *)out); zxerr_t zxerr = spend_signatures_append(out); if(zxerr != zxerr_ok){ From 5b55e6d5107eb497f4e873c7f2d3ee60cb88299c Mon Sep 17 00:00:00 2001 From: Ida Date: Thu, 17 Mar 2022 16:10:18 +0100 Subject: [PATCH 08/16] Tried to jub jub sign from seed - hit canary --- app/rust/include/rslib.h | 2 +- app/src/crypto.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/rust/include/rslib.h b/app/rust/include/rslib.h index 1ec32388..30ecdf30 100644 --- a/app/rust/include/rslib.h +++ b/app/rust/include/rslib.h @@ -63,7 +63,7 @@ void prepare_enccompact_input(uint8_t *d, uint64_t value, uint8_t *rcm, uint8_t //RedJubjub void random_fr(uint8_t *alpha_ptr); -void randomized_secret_from_seed(const uint8_t *seed_ptr,const uint32_t pos, uint8_t *alpha_ptr, uint8_t *output_ptr); +void randomized_secret_from_seed(uint8_t *seed_ptr, uint32_t pos, uint8_t *alpha_ptr, uint8_t *output_ptr); void randomized_secret(uint8_t *sk_ptr, uint8_t *alpha_ptr, uint8_t *output_ptr); diff --git a/app/src/crypto.c b/app/src/crypto.c index cc42bcf2..47c2825f 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -1155,9 +1155,7 @@ zxerr_t crypto_signspends_sapling(uint8_t *buffer, uint16_t bufferLen, const uin CLOSE_TRY; return zxerr_unknown; } - - //zip32_child_ask_nsk(tmp.step1.zip32_seed, tmp.step2.ask, tmp.step2.nsk, item->path); - + // combining these causes a stack overflow randomized_secret_from_seed(tmp.step1.zip32_seed,item->path, (uint8_t *)item->alpha, tmp.step3.rsk); sign_redjubjub((uint8_t *)tmp.step3.rsk, (uint8_t *)sighash, (uint8_t *)out); zxerr_t zxerr = spend_signatures_append(out); From af716ecd7107550cc9c764f5b99f47a9319292b5 Mon Sep 17 00:00:00 2001 From: Ida Date: Fri, 18 Mar 2022 09:30:33 +0100 Subject: [PATCH 09/16] documenting code --- app/src/crypto.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/app/src/crypto.c b/app/src/crypto.c index 47c2825f..556e2053 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -881,53 +881,80 @@ zxerr_t crypto_checkencryptions_sapling(uint8_t *buffer, uint16_t bufferLen, con MEMZERO(out, bufferLen); return zxerr_unknown; } + // compute random ephemeral private and public keys (esk,epk) from seed and diversifier rseed_get_esk_epk(item->rseed,(uint8_t *) item->div, tmp->step1.esk, tmp->step1.epk); CHECK_APP_CANARY(); + + // compare the computed epk to that provided in the transaction data if (MEMCMP(tmp->step1.epk, start_outputdata + INDEX_OUTPUT_EPK + i * OUTPUT_TX_LEN, EPK_SIZE) != 0){ MEMZERO(out, bufferLen); return zxerr_unknown; } + // get shared key (used as encryption key) from esk, epk and pkd ka_to_key(tmp->step1.esk, (uint8_t *) item->pkd, tmp->step1.epk, tmp->step2.sharedkey); CHECK_APP_CANARY(); + // encode (div, value rseed and memotype) into step2.compactout ready to be encrypted prepare_enccompact_input((uint8_t *) item->div, item->value, (uint8_t *) item->rseed, item->memotype, tmp->step2.compactout); CHECK_APP_CANARY(); MEMZERO(tmp->step2.chachanonce,CHACHA_NONCE_SIZE); + // encrypt the previously obtained encoding, and store it in step2.compactoutput (reusing the same memory for input and output) chacha(tmp->step2.compactout, tmp->step2.compactout, COMPACT_OUT_SIZE, tmp->step2.sharedkey, tmp->step2.chachanonce,1); CHECK_APP_CANARY(); + // check that the computed encryption is the same as that provided in the transaction data if (MEMCMP(tmp->step2.compactout, start_outputdata + INDEX_OUTPUT_ENC + i * OUTPUT_TX_LEN, COMPACT_OUT_SIZE) != 0){ MEMZERO(out, bufferLen); return zxerr_unknown; } + // if an ovk was provided if(item->ovk[0] != 0x00){ zemu_log_stack("OVK SET"); + // copy ovk, the value commitment and note-commitment from flash memory and transaction to + // local tmp structure so as to hash MEMCPY(tmp->step3.ovk, item->ovk + 1, OVK_SIZE); MEMCPY(tmp->step3.valuecmt, start_outputdata + INDEX_OUTPUT_VALUECMT + i* OUTPUT_TX_LEN,VALUE_COMMITMENT_SIZE); MEMCPY(tmp->step3.notecmt, start_outputdata + INDEX_OUTPUT_NOTECMT + i* OUTPUT_TX_LEN,NOTE_COMMITMENT_SIZE); - + // Note that tmp->step4.prfinput is the same memory chunk as the concatenation of + // tmp->step3.ovk || tmp->step3.valuecmt || tmp->step3.notecmt || tmp->step3.epk + // so next we hash that concatenation, and store hash in tmp->step5.outkey blake2b_prf(tmp->step4.prfinput, tmp->step5.outkey); CHECK_APP_CANARY(); + + // get pkd from flash memory, store it in tmp->step5.pkd MEMCPY(tmp->step5.pkd, item->pkd, PKD_SIZE); MEMZERO(tmp->step6.chachanonce,CHACHA_NONCE_SIZE); + // tmp->step6.encciph = tmp->step5.pkd || tmp->step5.esk + // encrypt that, using as encryption key the output of the blake2b PRF + // store resulting ciphertext in tmp->step6.encciph chacha(tmp->step6.encciph, tmp->step6.encciph, ENC_CIPHER_SIZE, tmp->step6.outkey, tmp->step6.chachanonce,1); CHECK_APP_CANARY(); + + // check that the computed encryption is the same as that provided in the transaction data if (MEMCMP(tmp->step6.encciph, start_outputdata + INDEX_OUTPUT_OUT + i * OUTPUT_TX_LEN, ENC_CIPHER_SIZE) != 0){ MEMZERO(out, bufferLen); return zxerr_unknown; } + // if no ovk was provided }else{ zemu_log_stack("OVK NOT SET"); + // copy the contents of flash memory for ovk, and hash it. This hash will be the encryption key MEMCPY(tmp->step3b.hashseed, item->ovk, OVK_SET_SIZE); cx_hash_sha256(tmp->step3b.hashseed, OVK_SET_SIZE, tmp->step3b.outkey, CX_SHA256_SIZE); + // replace the first 0x00 of the copied ovk with 0x01, hash again, this will be + // the first half of the plaintext to encrypt tmp->step3b.hashseed[0] = 0x01; cx_hash_sha256(tmp->step3b.hashseed, OVK_SET_SIZE, tmp->step3b.encciph_part1, CX_SHA256_SIZE); + // replace the first 0x01 of the copied ovk with 0x02, hash again, this will be + // the second half of the plaintext to encrypt tmp->step3b.hashseed[0] = 0x02; cx_hash_sha256(tmp->step3b.hashseed, OVK_SET_SIZE, tmp->step3b.encciph_part2, CX_SHA256_SIZE); MEMZERO(tmp->step3b.chachanonce,CHACHA_NONCE_SIZE); + // tmp->step4b.encciph = tmp->step3b.encciph_part1 || tmp->step3b.encciph_part2 + // encrypt and compare computed encryption to that provided in the transaction data chacha(tmp->step4b.encciph, tmp->step4b.encciph, ENC_CIPHER_SIZE, tmp->step4b.outkey, tmp->step4b.chachanonce,1); if (MEMCMP(tmp->step4b.encciph, start_outputdata + INDEX_OUTPUT_OUT + i * OUTPUT_TX_LEN, ENC_CIPHER_SIZE) != 0){ MEMZERO(out, bufferLen); From 2c54f6207bf75ecd915eae4f2044f7d8e7ad006d Mon Sep 17 00:00:00 2001 From: Ida Date: Fri, 18 Mar 2022 10:01:18 +0100 Subject: [PATCH 10/16] Minor version bump --- app/Makefile.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Makefile.version b/app/Makefile.version index 7fc555d6..dcd9801d 100644 --- a/app/Makefile.version +++ b/app/Makefile.version @@ -1,3 +1,3 @@ APPVERSION_M=3 -APPVERSION_N=0 +APPVERSION_N=1 APPVERSION_P=0 From 74c581d5bb87a00dd640d7830d078c5fbef68e31 Mon Sep 17 00:00:00 2001 From: Ida Date: Fri, 18 Mar 2022 10:34:28 +0100 Subject: [PATCH 11/16] snapshots --- tests_zemu/snapshots/s-mainmenu/00004.png | Bin 386 -> 391 bytes tests_zemu/snapshots/s-mainmenu/00010.png | Bin 386 -> 391 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests_zemu/snapshots/s-mainmenu/00004.png b/tests_zemu/snapshots/s-mainmenu/00004.png index 6f2f1541d69ec177db76bfddf23bbb7a7bda1f53..9441b8172abd6ecd079816e426106024957f233d 100644 GIT binary patch delta 364 zcmV-y0h9iM1BU~UB!5mxL_t(|ob8yw4ul{KM0KhZ71doDjS-12v`wx!oIh?)$2qel% z`DE9z=euM}Ho0|$V*nDlv)U%g{lZiZ)`+E_?Jej_xwzO$;xj5Cgb+dq>53183$4>odJ^aW0000< KMNUMnLSTYu&r delta 359 zcmV-t0hs=W1A+sPB!5XsL_t(|ob8y+4ul{KgmtsN{{wq)4xAN9HVap7+;q9@ARB9say5JIk2yO-WXWi1zY#$suayWeh5J;4j z^2w%S&o{|G+2S@7jsZyI&T6L!I%(~)1)w(3P$tU^QsQr=DSz1(z`1mf zxRfB7zmcG6sP*xW{KVnQgeAy!4^WJJcaQ%tF6{uUB;&i0e~~)_sAgZ(q6z^BHbaj= zsMOhdO^`&-vynRoAhucVIMJCf3`L@zV53C<@aCx~tKX*x67&Rna>)f16W{29ivF^_ z!|iDZKvf6J{9wr_ycpMZ7y*}?aB^Jp^cGwSfKhZ71doDjS-12v`wx!oIh?)$2qel% z`DE9z=euM}Ho0|$V*nDlv)U%g{lZiZ)`+E_?Jej_xwzO$;xj5Cgb+dq>53183$4>odJ^aW0000< KMNUMnLSTYu&r delta 359 zcmV-t0hs=W1A+sPB!5XsL_t(|ob8y+4ul{KgmtsN{{wq)4xAN9HVap7+;q9@ARB9say5JIk2yO-WXWi1zY#$suayWeh5J;4j z^2w%S&o{|G+2S@7jsZyI&T6L!I%(~)1)w(3P$tU^QsQr=DSz1(z`1mf zxRfB7zmcG6sP*xW{KVnQgeAy!4^WJJcaQ%tF6{uUB;&i0e~~)_sAgZ(q6z^BHbaj= zsMOhdO^`&-vynRoAhucVIMJCf3`L@zV53C<@aCx~tKX*x67&Rna>)f16W{29ivF^_ z!|iDZKvf6J{9wr_ycpMZ7y*}?aB^Jp^cGwSfK Date: Fri, 18 Mar 2022 13:56:36 +0100 Subject: [PATCH 12/16] APDU handler error codes --- app/src/apdu_handler.c | 30 +++++++++++++------------- app/src/crypto.c | 3 +-- deps/ledger-zxlib/include/apdu_codes.h | 13 +++++++++++ js/src/common.js | 13 +++++++++++ 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c index 120662b6..12f86d93 100644 --- a/app/src/apdu_handler.c +++ b/app/src/apdu_handler.c @@ -142,7 +142,7 @@ __Z_INLINE void handleInitTX(volatile uint32_t *flags, if (messageLength > FLASH_BUFFER_SIZE) { MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); *tx = 0; - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_DATA_TOO_LONG); } zxerr_t err; @@ -151,7 +151,7 @@ __Z_INLINE void handleInitTX(volatile uint32_t *flags, transaction_reset(); MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); *tx = 0; - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_EXTRACT_TRANSACTION_FAIL); } err = crypto_hash_messagebuffer(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); @@ -159,7 +159,7 @@ __Z_INLINE void handleInitTX(volatile uint32_t *flags, transaction_reset(); MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); *tx = 0; - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_HASH_MSG_BUF_FAIL); } //////////// @@ -288,7 +288,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_UNPROCESSED_TX); } // TODO: check this @@ -302,7 +302,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_PREVOUT_INVALID); } err = crypto_check_sequence(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); @@ -310,7 +310,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_SEQUENCE_INVALID); } err = crypto_check_outputs(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); @@ -318,7 +318,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_OUTPUTS_INVALID); } err = crypto_check_joinsplits(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); @@ -326,14 +326,14 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_JOINSPLIT_INVALID); } // TODO: the valuebalance sometimes fails, maybe bug in emulator? Add check later when it is fixed. err = crypto_check_valuebalance(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); /* if(err != zxerr_ok){ - return 0; + THROW(APDU_CODE_SPEND_INVALID); // TODO: be more specific } */ @@ -342,7 +342,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_SPEND_INVALID); } err = crypto_checkoutput_sapling(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); @@ -350,7 +350,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_OUTPUT_CONTENT_INVALID); } err = crypto_checkencryptions_sapling(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); @@ -358,7 +358,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_ENCRYPTION_INVALID); } set_state(STATE_VERIFIED_ALL_TXDATA); @@ -369,7 +369,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_CHECK_SIGN_TR_FAIL); } err = crypto_signspends_sapling(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); @@ -377,7 +377,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_SIGN_SPEND_FAIL); } err = crypto_hash_messagebuffer(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); @@ -385,7 +385,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); view_idle_show(0, NULL); transaction_reset(); - THROW(APDU_CODE_DATA_INVALID); // TODO: be more specific + THROW(APDU_CODE_HASH_MSG_BUF_FAIL); } set_state(STATE_SIGNED_TX); diff --git a/app/src/crypto.c b/app/src/crypto.c index 556e2053..e3e28a0e 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -463,7 +463,7 @@ zxerr_t crypto_check_sequence(uint8_t *buffer, uint16_t bufferLen, const uint8_t } zxerr_t crypto_check_outputs(uint8_t *buffer, uint16_t bufferLen, const uint8_t *txdata, const uint16_t txdatalen){ - zemu_log_stack("crypto_checkoutputs_sapling"); + zemu_log_stack("crypto_check_outputs"); if(length_t_in_data() + length_spenddata() + length_outputdata() + LENGTH_HASH_DATA != txdatalen){ return zxerr_unknown; } @@ -660,7 +660,6 @@ zxerr_t crypto_checkspend_sapling(uint8_t *buffer, uint16_t bufferLen, const uin return zxerr_unknown; } } - //void compute_nullifier(uint8_t *ncmptr, uint64_t pos, uint8_t *nkptr, uint8_t *outputptr); compute_nullifier(tmp_buf->ncm_full, notepos, tmp.step6.nk, tmp_buf->nf); if (MEMCMP(tmp_buf->nf, start_spenddata + INDEX_SPEND_NF + i * SPEND_TX_LEN, NULLIFIER_SIZE) != 0){ //maybe spendlist_reset(); diff --git a/deps/ledger-zxlib/include/apdu_codes.h b/deps/ledger-zxlib/include/apdu_codes.h index 5f552651..f03cc907 100644 --- a/deps/ledger-zxlib/include/apdu_codes.h +++ b/deps/ledger-zxlib/include/apdu_codes.h @@ -34,6 +34,19 @@ #define APDU_CODE_CONDITIONS_NOT_SATISFIED 0x6985 #define APDU_CODE_COMMAND_NOT_ALLOWED 0x6986 #define APDU_CODE_TX_NOT_INITIALIZED 0x6987 +#define APDU_CODE_DATA_TOO_LONG 0x6988 +#define APDU_CODE_EXTRACT_TRANSACTION_FAIL 0x6989 +#define APDU_CODE_HASH_MSG_BUF_FAIL 0x6990 +#define APDU_CODE_UNPROCESSED_TX 0x6991 +#define APDU_CODE_PREVOUT_INVALID 0x6992 +#define APDU_CODE_SEQUENCE_INVALID 0x6993 +#define APDU_CODE_OUTPUTS_INVALID 0x6994 +#define APDU_CODE_JOINSPLIT_INVALID 0x6995 +#define APDU_CODE_SPEND_INVALID 0x6996 +#define APDU_CODE_OUTPUT_CONTENT_INVALID 0x6997 +#define APDU_CODE_ENCRYPTION_INVALID 0x6998 +#define APDU_CODE_CHECK_SIGN_TR_FAIL 0x6999 +#define APDU_SIGN_SPEND_FAIL 0x69A0 #define APDU_CODE_BAD_KEY_HANDLE 0x6A80 #define APDU_CODE_INVALIDP1P2 0x6B00 diff --git a/js/src/common.js b/js/src/common.js index 0d49ff3f..aea81b78 100644 --- a/js/src/common.js +++ b/js/src/common.js @@ -65,6 +65,19 @@ const ERROR_DESCRIPTION = { 0x6984: "Data is invalid", 0x6985: "Conditions not satisfied", 0x6986: "Transaction rejected", + 0x6988: "Data too long", + 0x6989: "Failed to extract transaction", + 0x6990: "Failed to hash message buffer", + 0x6991: "Transaction extraction incomplete", + 0x6992: "Prevout check failed", + 0x6993: "Sequence check failed", + 0x6994: "Hash of outputs check failed", + 0x6995: "Joinsplit check failed", + 0x6996: "Spend check failed", + 0x6997: "Outputs content check failed", + 0x6998: "Check of encryption failed", + 0x6999: "Check/sign transparent failed", + 0x69a0: "Failed to sign spends", 0x6a80: "Bad key handle", 0x6b00: "Invalid P1/P2", 0x6d00: "Instruction not supported", From b88e2bb6e33b72395948d5addb83c33ef22ca04c Mon Sep 17 00:00:00 2001 From: Ida Date: Wed, 23 Mar 2022 12:33:16 +0100 Subject: [PATCH 13/16] fixing build --- app/Makefile.version | 4 ++-- app/src/apdu_handler.c | 3 ++- tests_zemu/snapshots/s-mainmenu/00004.png | Bin 391 -> 392 bytes tests_zemu/snapshots/s-mainmenu/00010.png | Bin 391 -> 392 bytes 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Makefile.version b/app/Makefile.version index dcd9801d..fd90fc82 100644 --- a/app/Makefile.version +++ b/app/Makefile.version @@ -1,3 +1,3 @@ APPVERSION_M=3 -APPVERSION_N=1 -APPVERSION_P=0 +APPVERSION_N=0 +APPVERSION_P=1 diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c index 80ba2c98..55af309c 100644 --- a/app/src/apdu_handler.c +++ b/app/src/apdu_handler.c @@ -330,10 +330,11 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, } err = crypto_check_valuebalance(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); + /* if(err != zxerr_ok){ THROW(APDU_CODE_SPEND_INVALID); // TODO: be more specific } - + */ err = crypto_checkspend_sapling(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); if (err != zxerr_ok) { MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); diff --git a/tests_zemu/snapshots/s-mainmenu/00004.png b/tests_zemu/snapshots/s-mainmenu/00004.png index 9441b8172abd6ecd079816e426106024957f233d..295aad136a2cf660448b074973471b8648ace687 100644 GIT binary patch delta 365 zcmV-z0h0cQ1Be5VB!5pyL_t(|ob8yw4#OY}L)&Tn{}1kAJ0PJHvyf7jjnwZ}C?Gjt z5|)J!LdacNJr;TGwUxK46JD9&Z+LnvB9#iH-4I*?kinxMTh^`otp0;zXAY-t01}0= zQa;)B+Us4irD|?d;TV8I?y7c*ph3OM7J#!G4P~;-ASM1}O@GC)n4gF1YPzN917qo) zaj_t|e=|wb%gewRv`2P;+qXnPC#lGW;2Ccc#tLI@#*kSM$XvyH9Ou4;|W00000 LNkvXXu0mjf_qVAi delta 364 zcmV-y0h9iS1BU~UB!5mxL_t(|ob8yw4ul{KM0KhZ71doDjS-12v`wx!oIh?)$2qel% z`DE9z=euM}Ho0|$V*nDlv)U%g{lZiZ)`+E_?Jej_xwzO$;xj5Cgb+dq>53183$4>odJ^aW0000< KMNUMnLSTY{+p3ZP diff --git a/tests_zemu/snapshots/s-mainmenu/00010.png b/tests_zemu/snapshots/s-mainmenu/00010.png index 9441b8172abd6ecd079816e426106024957f233d..295aad136a2cf660448b074973471b8648ace687 100644 GIT binary patch delta 365 zcmV-z0h0cQ1Be5VB!5pyL_t(|ob8yw4#OY}L)&Tn{}1kAJ0PJHvyf7jjnwZ}C?Gjt z5|)J!LdacNJr;TGwUxK46JD9&Z+LnvB9#iH-4I*?kinxMTh^`otp0;zXAY-t01}0= zQa;)B+Us4irD|?d;TV8I?y7c*ph3OM7J#!G4P~;-ASM1}O@GC)n4gF1YPzN917qo) zaj_t|e=|wb%gewRv`2P;+qXnPC#lGW;2Ccc#tLI@#*kSM$XvyH9Ou4;|W00000 LNkvXXu0mjf_qVAi delta 364 zcmV-y0h9iS1BU~UB!5mxL_t(|ob8yw4ul{KM0KhZ71doDjS-12v`wx!oIh?)$2qel% z`DE9z=euM}Ho0|$V*nDlv)U%g{lZiZ)`+E_?Jej_xwzO$;xj5Cgb+dq>53183$4>odJ^aW0000< KMNUMnLSTY{+p3ZP From 8494fb1636b691fcdfab7b8acaba27b044fb7d76 Mon Sep 17 00:00:00 2001 From: Ida Date: Wed, 23 Mar 2022 12:55:40 +0100 Subject: [PATCH 14/16] Still trying to fix build I don't actually understand why the build failed on the last one --- app/src/apdu_handler.c | 2 +- app/src/crypto.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c index 55af309c..31201e2c 100644 --- a/app/src/apdu_handler.c +++ b/app/src/apdu_handler.c @@ -329,7 +329,7 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, THROW(APDU_CODE_JOINSPLIT_INVALID); } - err = crypto_check_valuebalance(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); + // err = crypto_check_valuebalance(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); /* if(err != zxerr_ok){ THROW(APDU_CODE_SPEND_INVALID); // TODO: be more specific diff --git a/app/src/crypto.c b/app/src/crypto.c index f594fc34..62a19c05 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -1032,8 +1032,6 @@ zxerr_t crypto_sign_and_check_transparent(uint8_t *buffer, uint16_t bufferLen, c unsigned int info = 0; signature_tr *const signature = (signature_tr *) buffer; - int signatureLength; - BEGIN_TRY { TRY From 44b0cf1916c5a8a86d6b8d07256857e18605934f Mon Sep 17 00:00:00 2001 From: Ida Date: Wed, 23 Mar 2022 13:09:02 +0100 Subject: [PATCH 15/16] Version update --- app/Makefile.version | 2 +- tests_zemu/snapshots/s-mainmenu/00004.png | Bin 392 -> 402 bytes tests_zemu/snapshots/s-mainmenu/00010.png | Bin 392 -> 402 bytes tests_zemu/snapshots/sp-mainmenu/00004.png | Bin 356 -> 362 bytes tests_zemu/snapshots/sp-mainmenu/00010.png | Bin 356 -> 362 bytes tests_zemu/snapshots/x-mainmenu/00004.png | Bin 356 -> 362 bytes tests_zemu/snapshots/x-mainmenu/00010.png | Bin 356 -> 362 bytes 7 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Makefile.version b/app/Makefile.version index fd90fc82..9cd5fd1d 100644 --- a/app/Makefile.version +++ b/app/Makefile.version @@ -1,3 +1,3 @@ APPVERSION_M=3 APPVERSION_N=0 -APPVERSION_P=1 +APPVERSION_P=2 diff --git a/tests_zemu/snapshots/s-mainmenu/00004.png b/tests_zemu/snapshots/s-mainmenu/00004.png index 295aad136a2cf660448b074973471b8648ace687..20ebbb0a0bb603a09e2242a050ad973333e4eb7e 100644 GIT binary patch delta 375 zcmV--0f_#H1Cj%fB!5{+L_t(|ob8y+5`!QNgt1fK|A9T&12araFhrnfqx;>e{7iQt zE~Eqi0N|-C9+T`_ZRT}!z>yLDhPTHgRH++jHw7013UVvZC(FuzR@=zFk?r&~Aj6cU z^3hlOR)0mGREt|E91UP{SG7|Fy=m>T8E|!>NtrAoP>Ih<6Mx$l@p-wv1Eg=%RgB^!e9D_ddie{rDxrPS0MlZ004jo@&OZw VvFA@<)l>ig002ovPDHLkV1h_3tOx)A delta 365 zcmV-z0h0cb1Be5VB!5pyL_t(|ob8yw4#OY}L)&Tn{}1kAJ0PJHvyf7jjnwZ}C?Gjt z5|)J!LdacNJr;TGwUxK46JD9&Z+LnvB9#iH-4I*?kinxMTh^`otp0;zXAY-t01}0= zQa;)B+Us4irD|?d;TV8I?y7c*ph3OM7J#!G4P~;-ASM1}O@GC)n4gF1YPzN917qo) zaj_t|e=|wb%gewRv`2P;+qXnPC#lGW;2Ccc#tLI@#*kSM$XvyH9Ou4;|W00000 LNkvXXu0mjf2X(14 diff --git a/tests_zemu/snapshots/s-mainmenu/00010.png b/tests_zemu/snapshots/s-mainmenu/00010.png index 295aad136a2cf660448b074973471b8648ace687..20ebbb0a0bb603a09e2242a050ad973333e4eb7e 100644 GIT binary patch delta 375 zcmV--0f_#H1Cj%fB!5{+L_t(|ob8y+5`!QNgt1fK|A9T&12araFhrnfqx;>e{7iQt zE~Eqi0N|-C9+T`_ZRT}!z>yLDhPTHgRH++jHw7013UVvZC(FuzR@=zFk?r&~Aj6cU z^3hlOR)0mGREt|E91UP{SG7|Fy=m>T8E|!>NtrAoP>Ih<6Mx$l@p-wv1Eg=%RgB^!e9D_ddie{rDxrPS0MlZ004jo@&OZw VvFA@<)l>ig002ovPDHLkV1h_3tOx)A delta 365 zcmV-z0h0cb1Be5VB!5pyL_t(|ob8yw4#OY}L)&Tn{}1kAJ0PJHvyf7jjnwZ}C?Gjt z5|)J!LdacNJr;TGwUxK46JD9&Z+LnvB9#iH-4I*?kinxMTh^`otp0;zXAY-t01}0= zQa;)B+Us4irD|?d;TV8I?y7c*ph3OM7J#!G4P~;-ASM1}O@GC)n4gF1YPzN917qo) zaj_t|e=|wb%gewRv`2P;+qXnPC#lGW;2Ccc#tLI@#*kSM$XvyH9Ou4;|W00000 LNkvXXu0mjf2X(14 diff --git a/tests_zemu/snapshots/sp-mainmenu/00004.png b/tests_zemu/snapshots/sp-mainmenu/00004.png index eb107b60761e6cdd199107665bc948e117448c0e..fb347713aa626aaf6c6fd0d202f6f19360bc1ab2 100644 GIT binary patch delta 335 zcmaFD^onVMO1+_{i(^Q|oVPcl`3@-vv?iLoYx%3+x%bC~7N54pRa>7f`2YPH_cBZ8 z<+{sQ7!bg(aCR+YoaWNIGmGthG`rV54C%iq{@~`4Bb$G7?PLmhyEftblR0nnUX6|z=v_msoi7W-^u zm|f^C$sd#SF29Dm=}? z41{Admd%~8^5Du_6;H#QtB-YC?)8tqsk)$*_uk)fadj5WI?ml2Qa1lieYk|rzP4KZ z`?=k8fAXJvK>DwPZ;o8{-^7}+Rey@-MM_qBD1^2Z?@h% zHFG;duUFVHb^fbScb$v+Z{Injs@u?Y`)r=j3#a|VsXnV)96cdx+4t9%!)*Pcg{Pkn+L@_;KWN*6xfRoQq=%dH%>0u7*v0pk`C_(- zFPtWpIW1$GzUkw#XkOWw`p7f`2YPH_cBZ8 z<+{sQ7!bg(aCR+YoaWNIGmGthG`rV54C%iq{@~`4Bb$G7?PLmhyEftblR0nnUX6|z=v_msoi7W-^u zm|f^C$sd#SF29Dm=}? z41{Admd%~8^5Du_6;H#QtB-YC?)8tqsk)$*_uk)fadj5WI?ml2Qa1lieYk|rzP4KZ z`?=k8fAXJvK>DwPZ;o8{-^7}+Rey@-MM_qBD1^2Z?@h% zHFG;duUFVHb^fbScb$v+Z{Injs@u?Y`)r=j3#a|VsXnV)96cdx+4t9%!)*Pcg{Pkn+L@_;KWN*6xfRoQq=%dH%>0u7*v0pk`C_(- zFPtWpIW1$GzUkw#XkOWw`p7f`2YPH_cBZ8 z<+{sQ7!bg(aCR+YoaWNIGmGthG`rV54C%iq{@~`4Bb$G7?PLmhyEftblR0nnUX6|z=v_msoi7W-^u zm|f^C$sd#SF29Dm=}? z41{Admd%~8^5Du_6;H#QtB-YC?)8tqsk)$*_uk)fadj5WI?ml2Qa1lieYk|rzP4KZ z`?=k8fAXJvK>DwPZ;o8{-^7}+Rey@-MM_qBD1^2Z?@h% zHFG;duUFVHb^fbScb$v+Z{Injs@u?Y`)r=j3#a|VsXnV)96cdx+4t9%!)*Pcg{Pkn+L@_;KWN*6xfRoQq=%dH%>0u7*v0pk`C_(- zFPtWpIW1$GzUkw#XkOWw`p7f`2YPH_cBZ8 z<+{sQ7!bg(aCR+YoaWNIGmGthG`rV54C%iq{@~`4Bb$G7?PLmhyEftblR0nnUX6|z=v_msoi7W-^u zm|f^C$sd#SF29Dm=}? z41{Admd%~8^5Du_6;H#QtB-YC?)8tqsk)$*_uk)fadj5WI?ml2Qa1lieYk|rzP4KZ z`?=k8fAXJvK>DwPZ;o8{-^7}+Rey@-MM_qBD1^2Z?@h% zHFG;duUFVHb^fbScb$v+Z{Injs@u?Y`)r=j3#a|VsXnV)96cdx+4t9%!)*Pcg{Pkn+L@_;KWN*6xfRoQq=%dH%>0u7*v0pk`C_(- zFPtWpIW1$GzUkw#XkOWw`p Date: Wed, 23 Mar 2022 17:21:54 +0100 Subject: [PATCH 16/16] fixing crypto_check_valuebalance The valuebalance is not the total value, only the sum of values in shielded inputs (positive) and outputs (negative) --- app/Makefile.version | 2 +- app/src/apdu_handler.c | 13 +- app/src/crypto.c | 18 +- app/src/nvdata.c | 13 +- app/src/nvdata.h | 5 +- app/src/parser.c | 2 +- app/src/parser_impl.c | 2 + app/src/parser_impl.h | 13 + deps/ledger-zxlib/include/apdu_codes.h | 1 + deps/ledger-zxlib/include/zxmacros.h | 4 + deps/ledger-zxlib/src/zxmacros.c | 68 ++ js/src/common.js | 1 + .../s-1-tr-in-1-spend-2-sh-out/00000.png | Bin 0 -> 668 bytes .../s-1-tr-in-1-spend-2-sh-out/00001.png | Bin 0 -> 316 bytes .../s-1-tr-in-1-spend-2-sh-out/00002.png | Bin 0 -> 332 bytes .../s-1-tr-in-1-spend-2-sh-out/00003.png | Bin 0 -> 633 bytes .../s-1-tr-in-1-spend-2-sh-out/00004.png | Bin 0 -> 625 bytes .../s-1-tr-in-1-spend-2-sh-out/00005.png | Bin 0 -> 407 bytes .../s-1-tr-in-1-spend-2-sh-out/00006.png | Bin 0 -> 354 bytes .../s-1-tr-in-1-spend-2-sh-out/00007.png | Bin 0 -> 657 bytes .../s-1-tr-in-1-spend-2-sh-out/00008.png | Bin 0 -> 655 bytes .../s-1-tr-in-1-spend-2-sh-out/00009.png | Bin 0 -> 456 bytes .../s-1-tr-in-1-spend-2-sh-out/00010.png | Bin 0 -> 345 bytes .../s-1-tr-in-1-spend-2-sh-out/00011.png | Bin 0 -> 387 bytes .../s-1-tr-in-1-spend-2-sh-out/00012.png | Bin 0 -> 343 bytes .../s-1-tr-in-1-spend-2-sh-out/00013.png | Bin 0 -> 645 bytes .../s-1-tr-in-1-spend-2-sh-out/00014.png | Bin 0 -> 636 bytes .../s-1-tr-in-1-spend-2-sh-out/00015.png | Bin 0 -> 419 bytes .../s-1-tr-in-1-spend-2-sh-out/00016.png | Bin 0 -> 351 bytes .../s-1-tr-in-1-spend-2-sh-out/00017.png | Bin 0 -> 387 bytes .../s-1-tr-in-1-spend-2-sh-out/00018.png | Bin 0 -> 629 bytes .../s-1-tr-in-1-spend-2-sh-out/00019.png | Bin 0 -> 646 bytes .../s-1-tr-in-1-spend-2-sh-out/00020.png | Bin 0 -> 260 bytes .../s-1-tr-in-1-spend-2-sh-out/00021.png | Bin 0 -> 249 bytes .../s-1-tr-in-1-spend-2-sh-out/00022.png | Bin 0 -> 320 bytes .../s-1-tr-out-1-spend-2-sh-out/00000.png | Bin 0 -> 660 bytes .../s-1-tr-out-1-spend-2-sh-out/00001.png | Bin 0 -> 326 bytes .../s-1-tr-out-1-spend-2-sh-out/00002.png | Bin 0 -> 313 bytes .../s-1-tr-out-1-spend-2-sh-out/00003.png | Bin 0 -> 633 bytes .../s-1-tr-out-1-spend-2-sh-out/00004.png | Bin 0 -> 625 bytes .../s-1-tr-out-1-spend-2-sh-out/00005.png | Bin 0 -> 407 bytes .../s-1-tr-out-1-spend-2-sh-out/00006.png | Bin 0 -> 326 bytes .../s-1-tr-out-1-spend-2-sh-out/00007.png | Bin 0 -> 657 bytes .../s-1-tr-out-1-spend-2-sh-out/00008.png | Bin 0 -> 655 bytes .../s-1-tr-out-1-spend-2-sh-out/00009.png | Bin 0 -> 456 bytes .../s-1-tr-out-1-spend-2-sh-out/00010.png | Bin 0 -> 343 bytes .../s-1-tr-out-1-spend-2-sh-out/00011.png | Bin 0 -> 387 bytes .../s-1-tr-out-1-spend-2-sh-out/00012.png | Bin 0 -> 343 bytes .../s-1-tr-out-1-spend-2-sh-out/00013.png | Bin 0 -> 645 bytes .../s-1-tr-out-1-spend-2-sh-out/00014.png | Bin 0 -> 636 bytes .../s-1-tr-out-1-spend-2-sh-out/00015.png | Bin 0 -> 419 bytes .../s-1-tr-out-1-spend-2-sh-out/00016.png | Bin 0 -> 351 bytes .../s-1-tr-out-1-spend-2-sh-out/00017.png | Bin 0 -> 387 bytes .../s-1-tr-out-1-spend-2-sh-out/00018.png | Bin 0 -> 629 bytes .../s-1-tr-out-1-spend-2-sh-out/00019.png | Bin 0 -> 646 bytes .../s-1-tr-out-1-spend-2-sh-out/00020.png | Bin 0 -> 260 bytes .../s-1-tr-out-1-spend-2-sh-out/00021.png | Bin 0 -> 249 bytes .../s-1-tr-out-1-spend-2-sh-out/00022.png | Bin 0 -> 320 bytes tests_zemu/snapshots/s-mainmenu/00004.png | Bin 402 -> 396 bytes tests_zemu/snapshots/s-mainmenu/00010.png | Bin 402 -> 396 bytes .../sp-1-tr-in-1-spend-2-sh-out/00000.png | Bin 0 -> 452 bytes .../sp-1-tr-in-1-spend-2-sh-out/00001.png | Bin 0 -> 694 bytes .../sp-1-tr-in-1-spend-2-sh-out/00002.png | Bin 0 -> 359 bytes .../sp-1-tr-in-1-spend-2-sh-out/00003.png | Bin 0 -> 923 bytes .../sp-1-tr-in-1-spend-2-sh-out/00004.png | Bin 0 -> 626 bytes .../sp-1-tr-in-1-spend-2-sh-out/00005.png | Bin 0 -> 382 bytes .../sp-1-tr-in-1-spend-2-sh-out/00006.png | Bin 0 -> 944 bytes .../sp-1-tr-in-1-spend-2-sh-out/00007.png | Bin 0 -> 692 bytes .../sp-1-tr-in-1-spend-2-sh-out/00008.png | Bin 0 -> 397 bytes .../sp-1-tr-in-1-spend-2-sh-out/00009.png | Bin 0 -> 470 bytes .../sp-1-tr-in-1-spend-2-sh-out/00010.png | Bin 0 -> 375 bytes .../sp-1-tr-in-1-spend-2-sh-out/00011.png | Bin 0 -> 937 bytes .../sp-1-tr-in-1-spend-2-sh-out/00012.png | Bin 0 -> 636 bytes .../sp-1-tr-in-1-spend-2-sh-out/00013.png | Bin 0 -> 407 bytes .../sp-1-tr-in-1-spend-2-sh-out/00014.png | Bin 0 -> 470 bytes .../sp-1-tr-in-1-spend-2-sh-out/00015.png | Bin 0 -> 894 bytes .../sp-1-tr-in-1-spend-2-sh-out/00016.png | Bin 0 -> 490 bytes .../sp-1-tr-in-1-spend-2-sh-out/00017.png | Bin 0 -> 294 bytes .../sp-1-tr-in-1-spend-2-sh-out/00018.png | Bin 0 -> 355 bytes .../sp-1-tr-in-1-spend-2-sh-out/00019.png | Bin 0 -> 496 bytes .../sp-1-tr-out-1-spend-2-sh-out/00000.png | Bin 0 -> 452 bytes .../sp-1-tr-out-1-spend-2-sh-out/00001.png | Bin 0 -> 713 bytes .../sp-1-tr-out-1-spend-2-sh-out/00002.png | Bin 0 -> 362 bytes .../sp-1-tr-out-1-spend-2-sh-out/00003.png | Bin 0 -> 923 bytes .../sp-1-tr-out-1-spend-2-sh-out/00004.png | Bin 0 -> 626 bytes .../sp-1-tr-out-1-spend-2-sh-out/00005.png | Bin 0 -> 358 bytes .../sp-1-tr-out-1-spend-2-sh-out/00006.png | Bin 0 -> 944 bytes .../sp-1-tr-out-1-spend-2-sh-out/00007.png | Bin 0 -> 692 bytes .../sp-1-tr-out-1-spend-2-sh-out/00008.png | Bin 0 -> 396 bytes .../sp-1-tr-out-1-spend-2-sh-out/00009.png | Bin 0 -> 470 bytes .../sp-1-tr-out-1-spend-2-sh-out/00010.png | Bin 0 -> 375 bytes .../sp-1-tr-out-1-spend-2-sh-out/00011.png | Bin 0 -> 937 bytes .../sp-1-tr-out-1-spend-2-sh-out/00012.png | Bin 0 -> 636 bytes .../sp-1-tr-out-1-spend-2-sh-out/00013.png | Bin 0 -> 407 bytes .../sp-1-tr-out-1-spend-2-sh-out/00014.png | Bin 0 -> 470 bytes .../sp-1-tr-out-1-spend-2-sh-out/00015.png | Bin 0 -> 894 bytes .../sp-1-tr-out-1-spend-2-sh-out/00016.png | Bin 0 -> 490 bytes .../sp-1-tr-out-1-spend-2-sh-out/00017.png | Bin 0 -> 294 bytes .../sp-1-tr-out-1-spend-2-sh-out/00018.png | Bin 0 -> 355 bytes .../sp-1-tr-out-1-spend-2-sh-out/00019.png | Bin 0 -> 496 bytes .../sp-1-tr-out-1-spend-2-sh-out/00020.png | Bin 0 -> 493 bytes tests_zemu/snapshots/sp-mainmenu/00004.png | Bin 362 -> 346 bytes tests_zemu/snapshots/sp-mainmenu/00010.png | Bin 362 -> 346 bytes .../x-1-tr-in-1-spend-2-sh-out/00000.png | Bin 0 -> 452 bytes .../x-1-tr-in-1-spend-2-sh-out/00001.png | Bin 0 -> 694 bytes .../x-1-tr-in-1-spend-2-sh-out/00002.png | Bin 0 -> 359 bytes .../x-1-tr-in-1-spend-2-sh-out/00003.png | Bin 0 -> 923 bytes .../x-1-tr-in-1-spend-2-sh-out/00004.png | Bin 0 -> 626 bytes .../x-1-tr-in-1-spend-2-sh-out/00005.png | Bin 0 -> 382 bytes .../x-1-tr-in-1-spend-2-sh-out/00006.png | Bin 0 -> 944 bytes .../x-1-tr-in-1-spend-2-sh-out/00007.png | Bin 0 -> 692 bytes .../x-1-tr-in-1-spend-2-sh-out/00008.png | Bin 0 -> 397 bytes .../x-1-tr-in-1-spend-2-sh-out/00009.png | Bin 0 -> 470 bytes .../x-1-tr-in-1-spend-2-sh-out/00010.png | Bin 0 -> 375 bytes .../x-1-tr-in-1-spend-2-sh-out/00011.png | Bin 0 -> 937 bytes .../x-1-tr-in-1-spend-2-sh-out/00012.png | Bin 0 -> 636 bytes .../x-1-tr-in-1-spend-2-sh-out/00013.png | Bin 0 -> 407 bytes .../x-1-tr-in-1-spend-2-sh-out/00014.png | Bin 0 -> 470 bytes .../x-1-tr-in-1-spend-2-sh-out/00015.png | Bin 0 -> 894 bytes .../x-1-tr-in-1-spend-2-sh-out/00016.png | Bin 0 -> 490 bytes .../x-1-tr-in-1-spend-2-sh-out/00017.png | Bin 0 -> 294 bytes .../x-1-tr-in-1-spend-2-sh-out/00018.png | Bin 0 -> 355 bytes .../x-1-tr-in-1-spend-2-sh-out/00019.png | Bin 0 -> 496 bytes .../x-1-tr-out-1-spend-2-sh-out/00000.png | Bin 0 -> 452 bytes .../x-1-tr-out-1-spend-2-sh-out/00001.png | Bin 0 -> 713 bytes .../x-1-tr-out-1-spend-2-sh-out/00002.png | Bin 0 -> 362 bytes .../x-1-tr-out-1-spend-2-sh-out/00003.png | Bin 0 -> 923 bytes .../x-1-tr-out-1-spend-2-sh-out/00004.png | Bin 0 -> 626 bytes .../x-1-tr-out-1-spend-2-sh-out/00005.png | Bin 0 -> 358 bytes .../x-1-tr-out-1-spend-2-sh-out/00006.png | Bin 0 -> 944 bytes .../x-1-tr-out-1-spend-2-sh-out/00007.png | Bin 0 -> 692 bytes .../x-1-tr-out-1-spend-2-sh-out/00008.png | Bin 0 -> 396 bytes .../x-1-tr-out-1-spend-2-sh-out/00009.png | Bin 0 -> 470 bytes .../x-1-tr-out-1-spend-2-sh-out/00010.png | Bin 0 -> 375 bytes .../x-1-tr-out-1-spend-2-sh-out/00011.png | Bin 0 -> 937 bytes .../x-1-tr-out-1-spend-2-sh-out/00012.png | Bin 0 -> 636 bytes .../x-1-tr-out-1-spend-2-sh-out/00013.png | Bin 0 -> 407 bytes .../x-1-tr-out-1-spend-2-sh-out/00014.png | Bin 0 -> 470 bytes .../x-1-tr-out-1-spend-2-sh-out/00015.png | Bin 0 -> 894 bytes .../x-1-tr-out-1-spend-2-sh-out/00016.png | Bin 0 -> 490 bytes .../x-1-tr-out-1-spend-2-sh-out/00017.png | Bin 0 -> 294 bytes .../x-1-tr-out-1-spend-2-sh-out/00018.png | Bin 0 -> 355 bytes .../x-1-tr-out-1-spend-2-sh-out/00019.png | Bin 0 -> 496 bytes .../x-1-tr-out-1-spend-2-sh-out/00020.png | Bin 0 -> 493 bytes tests_zemu/snapshots/x-mainmenu/00004.png | Bin 362 -> 346 bytes tests_zemu/snapshots/x-mainmenu/00010.png | Bin 362 -> 346 bytes tests_zemu/tests/zcashtool.test.ts | 615 +++++++++++++++++- 147 files changed, 722 insertions(+), 35 deletions(-) create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00000.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00001.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00002.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00003.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00004.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00005.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00006.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00007.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00008.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00009.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00010.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00011.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00012.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00013.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00014.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00015.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00016.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00017.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00018.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00019.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00020.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00021.png create mode 100644 tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00022.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00000.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00001.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00002.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00003.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00004.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00005.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00006.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00007.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00008.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00009.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00010.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00011.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00012.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00013.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00014.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00015.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00016.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00017.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00018.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00019.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00020.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00021.png create mode 100644 tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00022.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00000.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00001.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00002.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00003.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00004.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00005.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00006.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00007.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00008.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00009.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00010.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00011.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00012.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00013.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00014.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00015.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00016.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00017.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00018.png create mode 100644 tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00019.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00000.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00001.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00002.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00003.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00004.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00005.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00006.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00007.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00008.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00009.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00010.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00011.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00012.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00013.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00014.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00015.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00016.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00017.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00018.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00019.png create mode 100644 tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00020.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00000.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00001.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00002.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00003.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00004.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00005.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00006.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00007.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00008.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00009.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00010.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00011.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00012.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00013.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00014.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00015.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00016.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00017.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00018.png create mode 100644 tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00019.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00000.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00001.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00002.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00003.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00004.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00005.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00006.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00007.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00008.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00009.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00010.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00011.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00012.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00013.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00014.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00015.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00016.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00017.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00018.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00019.png create mode 100644 tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00020.png diff --git a/app/Makefile.version b/app/Makefile.version index 9cd5fd1d..88eb37b3 100644 --- a/app/Makefile.version +++ b/app/Makefile.version @@ -1,3 +1,3 @@ APPVERSION_M=3 APPVERSION_N=0 -APPVERSION_P=2 +APPVERSION_P=3 diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c index 31201e2c..a24812c7 100644 --- a/app/src/apdu_handler.c +++ b/app/src/apdu_handler.c @@ -329,12 +329,17 @@ __Z_INLINE void handleCheckandSign(volatile uint32_t *flags, THROW(APDU_CODE_JOINSPLIT_INVALID); } - // err = crypto_check_valuebalance(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); - /* + // /!\ the valuebalance is different to the total value + err = crypto_check_valuebalance(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); + if(err != zxerr_ok){ - THROW(APDU_CODE_SPEND_INVALID); // TODO: be more specific + MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); + view_idle_show(0, NULL); + transaction_reset(); + THROW(APDU_CODE_BAD_VALUEBALANCE); } - */ + + err = crypto_checkspend_sapling(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, message, messageLength); if (err != zxerr_ok) { MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE); diff --git a/app/src/crypto.c b/app/src/crypto.c index 62a19c05..33a50849 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -314,7 +314,7 @@ zxerr_t crypto_extracttx_sapling(uint8_t *buffer, uint16_t bufferLen, const uint start += OUTPUT_INPUT_LEN; } - uint64_t value_flash = get_valuebalance(); + uint64_t value_flash = get_totalvalue(); if (value_flash != 1000){ return zxerr_unknown; } @@ -464,7 +464,7 @@ zxerr_t crypto_check_sequence(uint8_t *buffer, uint16_t bufferLen, const uint8_t zxerr_t crypto_check_outputs(uint8_t *buffer, uint16_t bufferLen, const uint8_t *txdata, const uint16_t txdatalen){ zemu_log_stack("crypto_check_outputs"); - if(length_t_in_data() + length_spenddata() + length_outputdata() + LENGTH_HASH_DATA != txdatalen){ + if(start_sighashdata() + LENGTH_HASH_DATA != txdatalen){ return zxerr_unknown; } @@ -509,15 +509,19 @@ zxerr_t crypto_check_valuebalance(uint8_t *buffer, uint16_t bufferLen, const uin pars_ctx.offset = 0; pars_ctx.buffer = txdata + start_sighashdata() + INDEX_HASH_VALUEBALANCE; pars_ctx.bufferLen = 8; - uint64_t v = 0; - pars_err = _readUInt64(&pars_ctx, &v); + int64_t v = 0; + pars_err = _readInt64(&pars_ctx, &v); if (pars_err != parser_ok){ return 0; } - uint64_t valuebalance = get_valuebalance(); - uint8_t *value_flash = (uint8_t *)&valuebalance; - if(MEMCMP(txdata + start_sighashdata() + INDEX_HASH_VALUEBALANCE, value_flash, 8) != 0){ + int64_t valuebalance = get_valuebalance(); + int64_t *value_flash = (int64_t *)&valuebalance; + if(MEMCMP(&v, value_flash, 8) != 0){ + zemu_log("The value balance in the transaction is: "); + zemu_log_stack_int64(v); + zemu_log("The value balance in flash is: "); + zemu_log_stack_int64(*value_flash); return zxerr_unknown; } return zxerr_ok; diff --git a/app/src/nvdata.c b/app/src/nvdata.c index f9758db8..025ee317 100644 --- a/app/src/nvdata.c +++ b/app/src/nvdata.c @@ -174,6 +174,7 @@ void transaction_reset() { transaction_header.t_out_len = 0; transaction_header.t_sign_index = 0; transaction_header.total_value = 0; + transaction_header.sapling_value = 0; transaction_header.state = 0; transaction_header.spenddata_extract_index = 0; transaction_header.spendlist_len = 0; @@ -192,6 +193,7 @@ zxerr_t spendlist_append_item(uint32_t p, uint64_t v, uint8_t *div, uint8_t *pkd return zxerr_unknown; } + transaction_header.sapling_value += v; transaction_header.total_value += v; uint32_t path = p | 0x80000000; @@ -245,7 +247,7 @@ zxerr_t outputlist_append_item(uint8_t *d, uint8_t *pkd, uint64_t v, uint8_t mem if (transaction_header.outputlist_len >= OUTPUT_LIST_SIZE) { return zxerr_unknown; } - + transaction_header.sapling_value -= v; transaction_header.total_value -= v; output_item_t newitem; @@ -289,10 +291,15 @@ uint8_t outputlist_len() { return transaction_header.outputlist_len; } -uint64_t get_valuebalance() { +// valueBalance is not the total value, but the +// net value of Sapling Spend transfers minus Output transfers. +// i.e. the contents of the Sapling value pool +int64_t get_valuebalance() { + return transaction_header.sapling_value; +} +uint64_t get_totalvalue() { return transaction_header.total_value; } - uint8_t get_state() { return transaction_header.state; } diff --git a/app/src/nvdata.h b/app/src/nvdata.h index 77fd0325..1efd2892 100644 --- a/app/src/nvdata.h +++ b/app/src/nvdata.h @@ -51,6 +51,7 @@ typedef struct { typedef struct { uint64_t total_value; + int64_t sapling_value; uint8_t state; uint8_t t_in_len; uint8_t t_out_len; @@ -107,7 +108,9 @@ void set_state(uint8_t state); #define STATE_SIGNED_TX 6 //metadata flash api -uint64_t get_valuebalance(); +int64_t get_valuebalance(); + +uint64_t get_totalvalue(); bool spendlist_more_sign(); diff --git a/app/src/parser.c b/app/src/parser.c index 0b02982e..b11f7a8f 100644 --- a/app/src/parser.c +++ b/app/src/parser.c @@ -387,7 +387,7 @@ parser_error_t parser_getItem(const parser_context_t *ctx, ZEMU_LOGF(50, "[tx_getItem] type: type_txfee\n") snprintf(outKey, outKeyLen, "Fee"); - return parser_sapling_display_value(get_valuebalance(), outVal, outValLen, pageIdx, pageCount); + return parser_sapling_display_value(get_totalvalue(), outVal, outValLen, pageIdx, pageCount); } default: { diff --git a/app/src/parser_impl.c b/app/src/parser_impl.c index 6e1a4317..2129be7e 100644 --- a/app/src/parser_impl.c +++ b/app/src/parser_impl.c @@ -96,3 +96,5 @@ GEN_DEF_READFIX_UNSIGNED(16) GEN_DEF_READFIX_UNSIGNED(32) GEN_DEF_READFIX_UNSIGNED(64) + +GEN_DEF_READFIX_SIGNED(64) diff --git a/app/src/parser_impl.h b/app/src/parser_impl.h index b3836c91..362b0f82 100644 --- a/app/src/parser_impl.h +++ b/app/src/parser_impl.h @@ -78,6 +78,17 @@ extern "C" { } \ return parser_ok; \ } +#define GEN_DEC_READFIX_SIGNED(BITS) parser_error_t _readInt ## BITS(parser_context_t *ctx, int ## BITS ##_t *value) +#define GEN_DEF_READFIX_SIGNED(BITS) parser_error_t _readInt ## BITS(parser_context_t *ctx, int ## BITS ##_t *value) \ +{ \ + if (value == NULL) return parser_no_data; \ + *value = 0; \ + for(int8_t i=0; i < (BITS>>3); i++, ctx->offset++) { \ + if (ctx->offset >= ctx->bufferLen) return parser_unexpected_buffer_end; \ + *value += (int ## BITS ##_t) *(ctx->buffer + ctx->offset) << (8*i); \ + } \ + return parser_ok; \ +} GEN_DEC_READFIX_UNSIGNED(8); @@ -87,6 +98,8 @@ GEN_DEC_READFIX_UNSIGNED(32); GEN_DEC_READFIX_UNSIGNED(64); +GEN_DEC_READFIX_SIGNED(64); + parser_error_t parser_init(parser_context_t *ctx, const uint8_t *buffer, uint16_t bufferSize); diff --git a/deps/ledger-zxlib/include/apdu_codes.h b/deps/ledger-zxlib/include/apdu_codes.h index f03cc907..ee4b00b7 100644 --- a/deps/ledger-zxlib/include/apdu_codes.h +++ b/deps/ledger-zxlib/include/apdu_codes.h @@ -47,6 +47,7 @@ #define APDU_CODE_ENCRYPTION_INVALID 0x6998 #define APDU_CODE_CHECK_SIGN_TR_FAIL 0x6999 #define APDU_SIGN_SPEND_FAIL 0x69A0 +#define APDU_CODE_BAD_VALUEBALANCE 0x69A1 #define APDU_CODE_BAD_KEY_HANDLE 0x6A80 #define APDU_CODE_INVALIDP1P2 0x6B00 diff --git a/deps/ledger-zxlib/include/zxmacros.h b/deps/ledger-zxlib/include/zxmacros.h index b5bf7e87..44bc0d21 100644 --- a/deps/ledger-zxlib/include/zxmacros.h +++ b/deps/ledger-zxlib/include/zxmacros.h @@ -74,6 +74,10 @@ __Z_INLINE void strncpy_s(char *dst, const char *src, size_t dstSize) { #define sizeof_field(type, member) sizeof(((type *)0)->member) #define array_length(array) (sizeof(array) / sizeof((array)[0])) +void zemu_log_stack_uint64(uint64_t val); + +void zemu_log_stack_int64(int64_t val); + void zemu_trace(const char *file, uint32_t line); #define ZEMU_TRACE() zemu_trace( __func__, __LINE__ ); diff --git a/deps/ledger-zxlib/src/zxmacros.c b/deps/ledger-zxlib/src/zxmacros.c index 5dd172b6..60e9eef8 100644 --- a/deps/ledger-zxlib/src/zxmacros.c +++ b/deps/ledger-zxlib/src/zxmacros.c @@ -52,6 +52,74 @@ void zemu_log_stack(const char *ctx) { void zemu_log_stack(__Z_UNUSED const char *ctx) {} +#endif + +#if defined(ZEMU_LOGGING) && (defined (TARGET_NANOS) || defined(TARGET_NANOX) || defined(TARGET_NANOS2)) +static void x64toa(unsigned long long val, char *buf, unsigned radix, int is_neg) +{ + char *p; + char *firstdig; + char temp; + unsigned digval; + p = buf; *p=0,p[1]='\0'; + if (val==0||radix<2||radix>32||radix&1) return; + if ( is_neg ) *p++ = '-', val = (unsigned long long)(-(long long)val); + firstdig = p; + if(radix--==10) + do { // optimized for fixed division + digval = (unsigned) (val % 10); + val /= 10; + *p++ = (char) (digval + '0'); + } while (val > 0); + else do { temp=radix; + digval = (unsigned) (val & radix ); + while(temp) val>>=1,temp>>=1; + *p++ = digval>9?(char)(digval + 'W'):(char) (digval + '0'); + } while (val>0); + *p-- = '\0'; + do { // reverse string + temp = *p; + *p = *firstdig; + *firstdig = temp; + --p; + ++firstdig; + } while (firstdig < p); +} +//---------------------------------------------------------------------------- +char* l2s(long long v,int sign) { char r,s; + static char buff[33]; r=sign>>8; s=sign; + if(!r) + r=10; + if((r!=10)||(s&&v>=0)) + s=0; + if(r<8) + r=0; + x64toa(v,buff,r,s); return buff; +} + + +void zemu_log_stack_uint64(uint64_t val) { + #define STACK_SHIFT 20 + char buf[70]; + snprintf(buf, sizeof(buf), "%s \n",l2s(val,0)); + zemu_log(buf); + (void) val; +} + +void zemu_log_stack_int64(int64_t val) { + #define STACK_SHIFT 20 + char buf[70]; + snprintf(buf, sizeof(buf), "%s \n",l2s(val,1)); + zemu_log(buf); + (void) val; +} + +#else + +void zemu_log_stack_uint64(__Z_UNUSED uint64_t v) {} +void zemu_log_stack_int64(__Z_UNUSED int64_t v) {} + + #endif diff --git a/js/src/common.js b/js/src/common.js index aea81b78..8f7b5afd 100644 --- a/js/src/common.js +++ b/js/src/common.js @@ -78,6 +78,7 @@ const ERROR_DESCRIPTION = { 0x6998: "Check of encryption failed", 0x6999: "Check/sign transparent failed", 0x69a0: "Failed to sign spends", + 0x69a1: "Bad value balance", 0x6a80: "Bad key handle", 0x6b00: "Invalid P1/P2", 0x6d00: "Instruction not supported", diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00000.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..98d4d28a2c3489a1279e2e78e130888830d9302b GIT binary patch literal 668 zcmV;N0%QG&P)JYWJGtPWz*oO&LtH1w9Q-LX30(BpO6LSFCva_J6u zY(P+PiTBIXLr~Ffv~nA%odbqs{Av2L@Vi!FJ|^dB!I|{<9gq$+`2t66YzCN2yHWRi|5wafgCKQYOH$jyz&AFeU5^ph}&K`303G zmh{R5*y@=nu7wJGL0}VbE?<~D{9+D*u@kZw^$e@9p*9^#R3T$Hz2@>VVe5w8J}Ow) zt&;Mz7(*zCw=;lJb;?tg#VP=&CaKp@WTU7ohh|?^FX9wOWNHx0_l#-Mt zg7NX2hz7)Bq<$6I0bqdct{Ur=x(6xi^pSW7JCHNV9{u;)!^XzOqj5k|>g5c{41Vkw zzW~c>vou%YAv>B+s5DeDi&)6q2%_Zyf2@22YDSC(iva=Q1@#~J8d{gUlkTeZwKA70 zT5n}loUDaUS*av(6KYR;M?croFrA38)TZ=24&F6=JuLvrzZTd95us`UAqFd-n*cQ? zI;p(>&m&f$%`uk^cCAP8gckl(Pi2%dBrT^Uk0000PFNwe2nN}jCkdn6H&4k=9kap26@Y5&0RR9100000@ETvaQJYYVOrwJU O0000mF34CSDXwG z;WOKJd#SO7XVHoU=N&S(9959|?ep#)=V4hvy&zlW$!{7>?lxOo@SUV& zB{9#wRwM9#$-%Rpd4D}lgjs33?eOatkJJl^Wo_u4$eeTU>%2E@B`ac;Jnq+ibyLlrda}LN0$!@=R`EircW)@(0 cz`zDxaYf||d-XE5z5oe%y85}Sb4q9e0L?m$IRF3v literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00003.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..8df733e487cb822c5a1bce8c26cd38b5f2531d63 GIT binary patch literal 633 zcmV-<0*3vGP)7u4^?cyl7?>j|X)Ud^hs>|I@ z4_N!a(xZ*oVDq~~Lxphy_!4q=>0jD9O`=cQKm)v5?0|c$P)AMOm(b&^5V6alFMYw( z^tvQAKd!thI^pcpmE1yzOT!yF(AkXJPEbcjjNYM%?gTXGfqGjE6fOCNH zAlX7m45-j^g1Qh7PeTQN+t}xZ$|)#?7Lgy+)e5}a=i$t|jJ8az-{5L7B-lzpB0lP2 z@mVOe_cDm~a%?j|d;8m?Z;7A-C_Zhe?}gbu8#lfHW-FrcnzN_lsaxaZO$)rvN{3O(C1^}< z6d$kwb_q}DTv=JTT@oJ+?!Rb&gA$FI((w3B}HfaNOaJSeo_ZU4}RMmY6J9(GDf@MK2tSAVe55F2F540xS>6DL&#~YB9q^3Z7SFb@p@6b^_$a zc5qy72XE0tC3X3}D-1jVfDg3RN# z5>WOaXXE8}ue@-p4>GxAVtJRZQH~OkGO3$4Z61 z+(lBNHTt@$ZkfmiK(0*1qeOW*S3S#FJFPkfN<27hQCDK?th8|4eNDGcl{2l;Xsaiz zJYnh7F7ZKbpRG)ph@A{n3DS1{+7{>7n6yC?yjrdGI_8}W3anHYqIN#*sdt#VUbl%& z>xgSeBiEqLr@$fxRQdacvVv$zEf=xY7}*>c_!Ozjn_!m?#>H+A|8y2MriIP&u;iP( ziMn$^hieV^3|e>ktd*cp(z1_lPtvV>aR5@L<>v=`I_?4foy%QsY6tW%!(Nf-L4_>~ zM;F=jhmf!Sg@yQE(42ktke?sy8Tb_B=R~)&v+46gf;ry=STb+`j!X>p2A`hl4~P#j z@Dg;3cn^>93mW@+G4dw_00000000000000$J3qOB6ja6^`sV-u002ovPDHLkV1hS2 Bw{ZXf literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00006.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..017bb6f3490a4ac781c176421854f7847807af0c GIT binary patch literal 354 zcmV-o0iFJdP)2|Nr0?y+xCe5c#q&>Yi!gAb|>^)dk8lhHeOEOyk<-ehUYa)VMX#zNlt4 zYt0uOnGKhC{9e?*t+Xisb%h$K-V!nC2#2yX+F(vU=y*={p441EhciNuctGQ z+@8C3yFZ4o#Kb-|Xr;cyJD)}Kd>0|(TuP9zFRY+zR9I(39#}<1w&U#Aj)pOyW4L<} z+s=_?N!)ZBXHflGWW_TX+>^}unb30UxY(orQ6I&od!j#90RUWwa?jyiB@)_IZ>{DU z<^cUC!HEWaUHo3FY~0T)&Hyi8022NK;Cc1K8v@`Q=K+|DbvghXo}ckp<;i6Kkv!KT zEB3A{ysJn+a07*qoM6N<$f)dc3 AS^xk5 literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00007.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00007.png new file mode 100644 index 0000000000000000000000000000000000000000..6b889c0eb1171d9e24edcaf8463fd7582d635119 GIT binary patch literal 657 zcmV;C0&e|@P)oc0Gi2NuYiArwtV6GGIQYL5w8H+2%6EKhW&wfSL=950G^&S$zWx7H@q_d zEnrGtiz$)$KRa+7yJe+yO}(yPmC#2i48GOI0!+#_Vbw2r&F-AA?b5sASKp>_*$ z6?1zG#13J4G~v=*(q904EwSm~E?jqZ13rSg+1X^_O|||E7#J9^vA~Z8+^X1O*sm4k z=JL#~&+p0c)NQfW0a@1xReZ99N`@M|Gk}H6VtgdiP?~{xHYdK8daiE4RV&+SPuwg% z+}t_R6U@Y|DOog(K2kJD(^$G$wgy5i1)v-HUdVu`ar0=Irw0f2K>emj8m|8{i166% zH)8TKa?vi1XgC1PQa+HW#jQ10_A5zgJ}bbjK*K(uPcQRUt>ER=oBD<$F0>e1f1(iv2EG|T??a~tM0sUk00000NkvXXu0mjffZ#O_ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00008.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00008.png new file mode 100644 index 0000000000000000000000000000000000000000..b632de626ec129575f2ce0ba1636982e9d9f7c88 GIT binary patch literal 655 zcmV;A0&x9_P)co3`D2>{}1ki=fSCBE=wQ*_0)anAVLz5#ANN1ax9-Ty;B>O&)#kp(yW?m#B15C zo!*Bwmq6}ngFU8Fo7&)o8BE`$bbl}=$iK%QO_V3c#*J+ao+By{UyA@V+VeCTDAvbF;5CL zfwLh8V}&_Pwj(|_VLK_feFmg#*oMhPNLK%xv7F0|v2+hDR#qS%qeCIR_+kT?2@C6>H7rzwSiU!N7(4`RokXBVud&n2Of*%(mZEbv_RrfRwMZep6EFKEdhGJV(YB9S(m#Ehsf=13_!ur`ybeaUC=7z2SP-Pd5eza6Cxxr#JiYnb$)SmZ0qq{Eu?yByrTRryS9Ew zp0Nbb%MET(73x$+b_yOK_m-2+osZV5%@*5Eyl8B0;WyTYbg#mFZ?su; z!fPhVs2K0IN^(f^$uzWB<#Q4S=6zYaws+ ygJ;q=CH?1*urV5aqrYJohG7_nVHk#Cypazg@i2JgvsY^X0000^^6%2auDIecj%#K*J z7%R%V>rd>SsZ&p^?>t-PVZ5nuo9vU0%O}m}H?NklHlR}8c|RYT2YGfJC%UndH=e@)ebT=ht$l)tLn8K zK|K>3Fxj21IeoVt-{B^$32M$fn?B~hY9^j?x;(4aGtK++Zr;R#Qr=jC(dmq8JOb$> zNk#TK+vrKXK72@_X2E32rBz%5wNgQ!)Ig8YxG;*b>V42+m+bbHf*O6G+<5Qq46B^$ zobU2RyY4>gThQ(Us%ldbp6&wMVeN=zR@1>4d=th-)h<4!TsE1Q20oPrl-sRm<=(Y?6ZLCkHQD@M-}sb$F^) zT^k=wECR|40Hf;%1K2GB1kz`-i375^g6E=5z&&6w4~u|c@xA2U&(D5I*1}5|00000 h00000004mZ@CSrJ@Yss|*zf=V002ovPDHLkV1i&&vDE+o literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00012.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00012.png new file mode 100644 index 0000000000000000000000000000000000000000..790ac765f038daf4076015134f4593d9e155f649 GIT binary patch literal 343 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!2~2j9iA64Ffht_x;TbZ%y~QGHea&>PaF5l z_x}&X?cm;Cq!A{zH1+oX)W|IgwvMhHMcbzG+_?5d+SYFMgk5ExdgcZ3!HL3urA1AT zTXLDm9*H`6LBY$_(DQIo%0bPd+7sFT_Nx|5d)MxIBWGGo*z$}k&tFd5@U!B%!m)SL z*<2Q-`MC*Ao9>o;{^r@NebT$*r9S96esg=G_+eIC&(hzizml^yoOfJz>o0H0<9!u3 zTOMl7c2oJgc1JOL-dd;4r&GoBKdugTe~DWM4frq`5> literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00013.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00013.png new file mode 100644 index 0000000000000000000000000000000000000000..84968e07fb272ba332aed26b6f40200f50cd3a15 GIT binary patch literal 645 zcmV;00($+4P)~&of5owW2)g}4gx-%3~_)J zW)2TwM&uJGxpVU!@Kb2JFN`lUCl19WlbIP)YAsf@tl%ez>fogLJg5b|Bv40cMH3*v z<&{T`a~vk_8~`pMYbdD!6}m#y`VdaV

?I!TW~tVIxe6O7E1_3xA0(!&!C~G0$3$ z!IT0xV4==I|62=Jn}nivM|PsWG+Py@-S_d^QzGa9iceeWIT5J?)N?JRk#`)n5E-Ch zldND8rn$(|XQwn$KU3rA6psl2&k%B36cbN4Jy9czJF+7OOiX+MU@M~Yn8VZksk=rg zn-=~$D;=PeOOTk}yfr_Tfl`YNhT0DU06H!j;_YblB2;|$qKyNatMZ`%9&KruUvW*% zqs@ty f!AIO|MZ6h*xM(dWMlsXU00000NkvXXu0mjf@SY-u literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00014.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00014.png new file mode 100644 index 0000000000000000000000000000000000000000..394e3bea3a34792d1242717517bcc8966fc1d68f GIT binary patch literal 636 zcmV-?0)zdDP)$%*pg_|O;8TmQ*sp{~DWXs8XphMBSj#%!unKW&mS$>M z^J+w`n~s!8l5-<*;g;utXUK#TxT9;`US#>86?l?k7vpoQU$#rQB4P)2UN74NXE9F- zdjfHz1|%yLElhFJB_eVLPLgxe9q_N<%NHi312?`7x#P_OE9fkL7I4Z0} zMPdg`EtqtrI>3GhU{(Oo38E0raEZ_SClM84%6eLqTpoWlDb?W-p`-+GS+9vNtjMZV zxnDK2v+sl04#16Z^mMrcJYvaEsVnwfX(-w6<;ePi>v9Y)ENrbV)bw{zYU{YmsW6i^ zaA>rb;;tP4SOF4ol@1~oGU<=+J$~Sd7r64a_aT>!xN+mgjniQ94|UMCStqhs06OFZ zEzxVK$ke5wNAU0z8{%V+=5R*xVH(=wt&~;Ckn_@%qe?Ce_cvKmSh|Hx8qUHAQ?YI` ztCv}lX*=AbvD5LOZ0yYfxS!F^A}2Wn%efq)JK)xG;<3wFd$-wY+X*KP&Fy%G`XsMc;l4N8 ztU1RrBV|mCci)hlr1@M{0y&KWMWt3=hyty3d?YN+CC}u0LKkUnYgYa(hgV({lCA9o zqULiBlSK-@VYqSAI*JczBtFRbY_K|mP~d-TnIY;LEm3pJe&NH*MSH+O>!2_v5LaHJ z1I?s{CdNdId^1SQEpNc1sEZfw4hP7;V;dTQSwx|vXCK{uNw?+c1ISH{Qx5mj@g1Pk z3?jjhi$;8w0^~3p3|H{f4+4Mg7Z&srNXA(TUG9D`14lvrPIQZ%%~K8q%-3DWoeX>c zhb4-4gEvo;1mXY;?1H`m{Mhn%hnCD(jP~1r5JCtcgb+dqA%qZO%L@=94#Cm{r@a6G N002ovPDHLkV1g_>v}phU literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00016.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00016.png new file mode 100644 index 0000000000000000000000000000000000000000..fd57bdaefa866f92bd9c80db744ea450b2c8af9a GIT binary patch literal 351 zcmV-l0igbgP)5siJ^}tRK7kY`%Cvd8JE~g-u6#DUfD@CnX=oM+jaK7x22d9O_4QI8t@^;n|c;2!BWW72QHIK|A`+JV>bOZ7O>rx{Mbd5;@5agVNklHlR}8c|RYT2YGfJC%UndH=e@)ebT=ht$l)tLn8K zK|K>3Fxj21IeoVt-{B^$32M$fn?B~hY9^j?x;(4aGtK++Zr;R#Qr=jC(dmq8JOb$> zNk#TK+vrKXK72@_X2E32rBz%5wNgQ!)Ig8YxG;*b>V42+m+bbHf*O6G+<5Qq46B^$ zobU2RyY4>gThQ(Us%ldbp6&wMVeN=zR@1>4d=th-)h<4!TsE1Q20oPrl-sRm<=(Y?6ZLCkHQD@M-}sb$F^) zT^k=wECR|40Hf;%1K2GB1kz`-i375^g6E=5z&&6w4~u|c@xA2U&(D5I*1}5|00000 h00000004mZ@CSrJ@Yss|*zf=V002ovPDHLkV1i&&vDE+o literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00018.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00018.png new file mode 100644 index 0000000000000000000000000000000000000000..217592db295aeb7c75ee6321e661e5fa2d1cb63c GIT binary patch literal 629 zcmV-*0*d{KP)~@t53q++W{kco_boLfV{L z>ULG8AxXmK!XqW6Fz-TlVB?;TJp?L`Y%DExmuwue7g^KO}3W{2TbUo4UUyKGJel45*^jd%vx&}4p$1j0NzgEtEO6hj#p45c> zlp!4$CF4RlkFvLCKkk*Pam+e{k8j6s!Q@$wyDDyDkza+He37Fzeg=nmguD>(R+7EG zt;btZTFBB!V-IuVOXk3L0s$xJK)(61qU>R{t&uSe_jPHQdjsm(BzKV7INZ5`Wj@DN zU0GzbFF@^p4DTFKa~U$cQqpxUO!7_XlsUMC#H8KmzI_202dpqvB7r5th3rxFn0wBu z%)RnB#CL?D(qiT=iqgf8S`dStW08%O@P?rwe{8}-aEr>!gagC78{c63bG5OtvGMh= zzX3Iv$d|ui9pz$RfEq-C7GH*lZCDGYs9;L;$sn&+-f-|+8LT!CFy;YG8jJ;IfI|^x z;JijRRz*a^Z36a1BPLT{lE$VW|2xMkV?T4<-j P00000NkvXXu0mjf?Tack literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00019.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00019.png new file mode 100644 index 0000000000000000000000000000000000000000..db7a548f98d2956f30f002b4d5480f055c06c7c3 GIT binary patch literal 646 zcmV;10(t$3P)sK-#|Ij?VV~3xpO?Zxf zyKLwG%&Q8iSA901qn5_1g;B!KU<;ZAwsvLiw)z3W3iOH|q#LgtC^VwjUF&O8YNZ+c zjmwpL0Ev6zMQU`tB8eN|ylh|T>q%#6>(S704^o+z2Z7?eu3LQ~dYzgZN3!RsC45G} zN!j>1@>WfMyEWB8$#T{DWmE!S|SWq2pmIbJh)g;pvG)eHIRY_rYCj=q?ejrnUZKS1{<;J7?lj! zX1fQl4Jf8@5g8jVC1%lS--LzZY+Q0GwkS~B8(Z5Jz;SY979>s^3?P@ViMw7D)s`1^ z-2i0ULE{pyLZX@TRWk$@T}sr!@FdO1U?2LDdjLNPo&xU%#KRE0-)FrdQ7qILZ>fw? zcwd8!gU_hS@E>Fo6B84^gPKx=8?}Lw-vjUnTbr>MRfh+{owhr2?W!8dbv4cbX()K_ z2Wt{l9o^sR5c7cS6-~r9xrnd4)zT3keLsI7qm>$qGMh2j12TH?O=Pc*{IbR`&$tdF z1JszCFmmsA*YuIAnX&plfCZ0wtYwyVxaaTaIAZljVn7OEeKp3xaxqxmQu7Zo2C?3e`Ry}ycOQeP>p!J ze(j0qPp_FU#mCR-5wwjb9yhB)mL8`e>zVLoW)^1Yw|}u*)|}RyXo{nV$pvRCVu%QAxhmd~C}dQ-Thh0$2%H-F@3(| za1OQqIb#71g#rvl0S2Q07gT@2e>b$d$EwW?+fV=i00000;NtEIzO+B00?<(a0000< KMNUMnLSTYPY;p_$ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00021.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00021.png new file mode 100644 index 0000000000000000000000000000000000000000..006c26abaac6c76b2e871b194165596c0b416694 GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!2~2j9iA5fsRN!ajv*Cu-d?-N*JQxc`taO| z-}VOL1%{U&gdOO)vH0QtN3CIpH=On}yd9NxI9#@L#=GYozkR+NZ@s(h_s<*5szDx| zmTO$v85!1fT#rxFzI@yL`PcK^*L-*W3fIF8>~K zzhRaAS`H?-=QM1i=Tu6{1-oD!M<5AJP1 literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00022.png b/tests_zemu/snapshots/s-1-tr-in-1-spend-2-sh-out/00022.png new file mode 100644 index 0000000000000000000000000000000000000000..1cd31cb2583141eb9a68beca2d3844084ec96496 GIT binary patch literal 320 zcmV-G0l)rE6YWiyP)SiHdEaF+4qWi-*+V{-CYwla!0T1m7 z{D34p*v7mBdAzE`mOX~qQ_x962Oz}rVlOCzr2qf`00000ZomfW_>G2K SgEch(0000J&A-8&zD13jNz8DjUvgmU9r}gtdPuVGp182rhr<6tA%?o;u=-rL_V{doQqG(a{ z4%m5L>))8AQSl4beauBg&Z0ow!A*k2yZ*zz_XeyWB)CnfWAj0@@Q)aYil)GgJ6^kU zdqd9dm-HKO)qceyZTHL85Jc;_W)p=}6iNd1#aXcNr^#Qz-|)y71ZB}h{GKG70rsAO zTo3*|h6=|lqJ4520WGM^5(_We9gQ?KFCBXC?mGZej`S4i+Q80EBADG%TwMn^2MiMe zJZ`e%hqjVb-q8sMwWkQhm~U}->dP($Oy3-SST57o@Bx4nyZR5a_X%G_ECCQ&1xb#! z-pH`ms11h|;Te-Jw)rqE5s+VoE*QrM@$tr9r6l3|}mbj5%JrFwr?M6RAKSWrw~TGuO8%p z_sn6oBrQtRX}Hnnp$H*Z zGQ+fll3iN7E+4?kkRT8lNpDLo1^R%#hm`usoxQ#?a!_!&iwMXW#wi`VvH~(X(9WzE u?ie9RfljGeFPKKSeeJGJnu=!>aWURs226hff;boe00005{|LO4`CbR02c~KU3 zb#?RlKFb8~Z|+G6O|cH0TGCkaU1{SY-b>O|W^ZNRP2WD-{?mktJ29>wlzzV9)g$J0b6H(pTQv`fbC@|v6MWuB!; z{3q;MnJ&zb;_;oSG@`f_HE{y{@L7l}-B;@Jp=d#Wzp$Pye7mPgs literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00002.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..f8058d5138887bbcf2979ecd5eb2e929fc65b620 GIT binary patch literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!2~2j9iA64FfcNBx;TbZ%y~P*o3B|xz~SJP z-|{zFVD_-;XvaQTTkkZ|kx2*q*t*f!CgXG2Lw`-juJQ)+wt!oyGU3s;TSA zB<&S0Tys@+ES;l&B=UcWhxhk?wcGam=sUIb_m$66dsf$7DHF51{^swJqY3vWAF>t- z4|jFGv^VCBn%%KCrZ1nfJ#k{Y>e(|d`M0u0f&MG2DR-;3>z0<~SeZ#(J87u4^?cyl7?>j|X)Ud^hs>|I@ z4_N!a(xZ*oVDq~~Lxphy_!4q=>0jD9O`=cQKm)v5?0|c$P)AMOm(b&^5V6alFMYw( z^tvQAKd!thI^pcpmE1yzOT!yF(AkXJPEbcjjNYM%?gTXGfqGjE6fOCNH zAlX7m45-j^g1Qh7PeTQN+t}xZ$|)#?7Lgy+)e5}a=i$t|jJ8az-{5L7B-lzpB0lP2 z@mVOe_cDm~a%?j|d;8m?Z;7A-C_Zhe?}gbu8#lfHW-FrcnzN_lsaxaZO$)rvN{3O(C1^}< z6d$kwb_q}DTv=JTT@oJ+?!Rb&gA$FI((w3B}HfaNOaJSeo_ZU4}RMmY6J9(GDf@MK2tSAVe55F2F540xS>6DL&#~YB9q^3Z7SFb@p@6b^_$a zc5qy72XE0tC3X3}D-1jVfDg3RN# z5>WOaXXE8}ue@-p4>GxAVtJRZQH~OkGO3$4Z61 z+(lBNHTt@$ZkfmiK(0*1qeOW*S3S#FJFPkfN<27hQCDK?th8|4eNDGcl{2l;Xsaiz zJYnh7F7ZKbpRG)ph@A{n3DS1{+7{>7n6yC?yjrdGI_8}W3anHYqIN#*sdt#VUbl%& z>xgSeBiEqLr@$fxRQdacvVv$zEf=xY7}*>c_!Ozjn_!m?#>H+A|8y2MriIP&u;iP( ziMn$^hieV^3|e>ktd*cp(z1_lPtvV>aR5@L<>v=`I_?4foy%QsY6tW%!(Nf-L4_>~ zM;F=jhmf!Sg@yQE(42ktke?sy8Tb_B=R~)&v+46gf;ry=STb+`j!X>p2A`hl4~P#j z@Dg;3cn^>93mW@+G4dw_00000000000000$J3qOB6ja6^`sV-u002ovPDHLkV1hS2 Bw{ZXf literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00006.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..b866fa50d2ea2c8fb308efb8fef0e3fbb2e455a1 GIT binary patch literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!2~2j9iA64Ffj6Zx;TbZ%y~QGpiqkfN88qw z-|HXny-ieiRlGJgCgSb?WzEeRN(o^+Q-KI&q*$Cg1+QxoYjrYn)zGzLamSgX^p#r<>rr%$!t9THznLqSFLF2`sK#`ccxnI ze--a+k(@MN>dTUMNpq_o&U>$vGWWTl%9U2pl76Z4k`wK1TwPC<>g_yi$X3F;|DH>{ zeB`t>uGis~H$}Es80vn7%&6>C1nX zUfz$F*Hj;Fxpoc0Gi2NuYiArwtV6GGIQYL5w8H+2%6EKhW&wfSL=950G^&S$zWx7H@q_d zEnrGtiz$)$KRa+7yJe+yO}(yPmC#2i48GOI0!+#_Vbw2r&F-AA?b5sASKp>_*$ z6?1zG#13J4G~v=*(q904EwSm~E?jqZ13rSg+1X^_O|||E7#J9^vA~Z8+^X1O*sm4k z=JL#~&+p0c)NQfW0a@1xReZ99N`@M|Gk}H6VtgdiP?~{xHYdK8daiE4RV&+SPuwg% z+}t_R6U@Y|DOog(K2kJD(^$G$wgy5i1)v-HUdVu`ar0=Irw0f2K>emj8m|8{i166% zH)8TKa?vi1XgC1PQa+HW#jQ10_A5zgJ}bbjK*K(uPcQRUt>ER=oBD<$F0>e1f1(iv2EG|T??a~tM0sUk00000NkvXXu0mjffZ#O_ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00008.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00008.png new file mode 100644 index 0000000000000000000000000000000000000000..b632de626ec129575f2ce0ba1636982e9d9f7c88 GIT binary patch literal 655 zcmV;A0&x9_P)co3`D2>{}1ki=fSCBE=wQ*_0)anAVLz5#ANN1ax9-Ty;B>O&)#kp(yW?m#B15C zo!*Bwmq6}ngFU8Fo7&)o8BE`$bbl}=$iK%QO_V3c#*J+ao+By{UyA@V+VeCTDAvbF;5CL zfwLh8V}&_Pwj(|_VLK_feFmg#*oMhPNLK%xv7F0|v2+hDR#qS%qeCIR_+kT?2@C6>H7rzwSiU!N7(4`RokXBVud&n2Of*%(mZEbv_RrfRwMZep6EFKEdhGJV(YB9S(m#Ehsf=13_!ur`ybeaUC=7z2SP-Pd5eza6Cxxr#JiYnb$)SmZ0qq{Eu?yByrTRryS9Ew zp0Nbb%MET(73x$+b_yOK_m-2+osZV5%@*5Eyl8B0;WyTYbg#mFZ?su; z!fPhVs2K0IN^(f^$uzWB<#Q4S=6zYaws+ ygJ;q=CH?1*urV5aqrYJohG7_nVHk#Cypazg@i2JgvsY^X0000S8CQg~3jO+y380)1U(gikOJA%#?u{SXkY3sdzkWXB=m-0V6(&fCzJbALy>pNXM91%Maheg}}*gUX?*PhfPe-Pe=fPk<5hkay)tP=bO%0)pwF4dt)@J8h_a p0iX>90000000000007|6Sph^5*7?HTThagk002ovPDHLkV1kS(m-+wz literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00011.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00011.png new file mode 100644 index 0000000000000000000000000000000000000000..f1a9a6fa0c88547aa93c69909e7a4eaec85cf748 GIT binary patch literal 387 zcmV-}0et?6P)NklHlR}8c|RYT2YGfJC%UndH=e@)ebT=ht$l)tLn8K zK|K>3Fxj21IeoVt-{B^$32M$fn?B~hY9^j?x;(4aGtK++Zr;R#Qr=jC(dmq8JOb$> zNk#TK+vrKXK72@_X2E32rBz%5wNgQ!)Ig8YxG;*b>V42+m+bbHf*O6G+<5Qq46B^$ zobU2RyY4>gThQ(Us%ldbp6&wMVeN=zR@1>4d=th-)h<4!TsE1Q20oPrl-sRm<=(Y?6ZLCkHQD@M-}sb$F^) zT^k=wECR|40Hf;%1K2GB1kz`-i375^g6E=5z&&6w4~u|c@xA2U&(D5I*1}5|00000 h00000004mZ@CSrJ@Yss|*zf=V002ovPDHLkV1i&&vDE+o literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00012.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00012.png new file mode 100644 index 0000000000000000000000000000000000000000..790ac765f038daf4076015134f4593d9e155f649 GIT binary patch literal 343 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!2~2j9iA64Ffht_x;TbZ%y~QGHea&>PaF5l z_x}&X?cm;Cq!A{zH1+oX)W|IgwvMhHMcbzG+_?5d+SYFMgk5ExdgcZ3!HL3urA1AT zTXLDm9*H`6LBY$_(DQIo%0bPd+7sFT_Nx|5d)MxIBWGGo*z$}k&tFd5@U!B%!m)SL z*<2Q-`MC*Ao9>o;{^r@NebT$*r9S96esg=G_+eIC&(hzizml^yoOfJz>o0H0<9!u3 zTOMl7c2oJgc1JOL-dd;4r&GoBKdugTe~DWM4frq`5> literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00013.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00013.png new file mode 100644 index 0000000000000000000000000000000000000000..84968e07fb272ba332aed26b6f40200f50cd3a15 GIT binary patch literal 645 zcmV;00($+4P)~&of5owW2)g}4gx-%3~_)J zW)2TwM&uJGxpVU!@Kb2JFN`lUCl19WlbIP)YAsf@tl%ez>fogLJg5b|Bv40cMH3*v z<&{T`a~vk_8~`pMYbdD!6}m#y`VdaV

?I!TW~tVIxe6O7E1_3xA0(!&!C~G0$3$ z!IT0xV4==I|62=Jn}nivM|PsWG+Py@-S_d^QzGa9iceeWIT5J?)N?JRk#`)n5E-Ch zldND8rn$(|XQwn$KU3rA6psl2&k%B36cbN4Jy9czJF+7OOiX+MU@M~Yn8VZksk=rg zn-=~$D;=PeOOTk}yfr_Tfl`YNhT0DU06H!j;_YblB2;|$qKyNatMZ`%9&KruUvW*% zqs@ty f!AIO|MZ6h*xM(dWMlsXU00000NkvXXu0mjf@SY-u literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00014.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00014.png new file mode 100644 index 0000000000000000000000000000000000000000..394e3bea3a34792d1242717517bcc8966fc1d68f GIT binary patch literal 636 zcmV-?0)zdDP)$%*pg_|O;8TmQ*sp{~DWXs8XphMBSj#%!unKW&mS$>M z^J+w`n~s!8l5-<*;g;utXUK#TxT9;`US#>86?l?k7vpoQU$#rQB4P)2UN74NXE9F- zdjfHz1|%yLElhFJB_eVLPLgxe9q_N<%NHi312?`7x#P_OE9fkL7I4Z0} zMPdg`EtqtrI>3GhU{(Oo38E0raEZ_SClM84%6eLqTpoWlDb?W-p`-+GS+9vNtjMZV zxnDK2v+sl04#16Z^mMrcJYvaEsVnwfX(-w6<;ePi>v9Y)ENrbV)bw{zYU{YmsW6i^ zaA>rb;;tP4SOF4ol@1~oGU<=+J$~Sd7r64a_aT>!xN+mgjniQ94|UMCStqhs06OFZ zEzxVK$ke5wNAU0z8{%V+=5R*xVH(=wt&~;Ckn_@%qe?Ce_cvKmSh|Hx8qUHAQ?YI` ztCv}lX*=AbvD5LOZ0yYfxS!F^A}2Wn%efq)JK)xG;<3wFd$-wY+X*KP&Fy%G`XsMc;l4N8 ztU1RrBV|mCci)hlr1@M{0y&KWMWt3=hyty3d?YN+CC}u0LKkUnYgYa(hgV({lCA9o zqULiBlSK-@VYqSAI*JczBtFRbY_K|mP~d-TnIY;LEm3pJe&NH*MSH+O>!2_v5LaHJ z1I?s{CdNdId^1SQEpNc1sEZfw4hP7;V;dTQSwx|vXCK{uNw?+c1ISH{Qx5mj@g1Pk z3?jjhi$;8w0^~3p3|H{f4+4Mg7Z&srNXA(TUG9D`14lvrPIQZ%%~K8q%-3DWoeX>c zhb4-4gEvo;1mXY;?1H`m{Mhn%hnCD(jP~1r5JCtcgb+dqA%qZO%L@=94#Cm{r@a6G N002ovPDHLkV1g_>v}phU literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00016.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00016.png new file mode 100644 index 0000000000000000000000000000000000000000..fd57bdaefa866f92bd9c80db744ea450b2c8af9a GIT binary patch literal 351 zcmV-l0igbgP)5siJ^}tRK7kY`%Cvd8JE~g-u6#DUfD@CnX=oM+jaK7x22d9O_4QI8t@^;n|c;2!BWW72QHIK|A`+JV>bOZ7O>rx{Mbd5;@5agVNklHlR}8c|RYT2YGfJC%UndH=e@)ebT=ht$l)tLn8K zK|K>3Fxj21IeoVt-{B^$32M$fn?B~hY9^j?x;(4aGtK++Zr;R#Qr=jC(dmq8JOb$> zNk#TK+vrKXK72@_X2E32rBz%5wNgQ!)Ig8YxG;*b>V42+m+bbHf*O6G+<5Qq46B^$ zobU2RyY4>gThQ(Us%ldbp6&wMVeN=zR@1>4d=th-)h<4!TsE1Q20oPrl-sRm<=(Y?6ZLCkHQD@M-}sb$F^) zT^k=wECR|40Hf;%1K2GB1kz`-i375^g6E=5z&&6w4~u|c@xA2U&(D5I*1}5|00000 h00000004mZ@CSrJ@Yss|*zf=V002ovPDHLkV1i&&vDE+o literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00018.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00018.png new file mode 100644 index 0000000000000000000000000000000000000000..217592db295aeb7c75ee6321e661e5fa2d1cb63c GIT binary patch literal 629 zcmV-*0*d{KP)~@t53q++W{kco_boLfV{L z>ULG8AxXmK!XqW6Fz-TlVB?;TJp?L`Y%DExmuwue7g^KO}3W{2TbUo4UUyKGJel45*^jd%vx&}4p$1j0NzgEtEO6hj#p45c> zlp!4$CF4RlkFvLCKkk*Pam+e{k8j6s!Q@$wyDDyDkza+He37Fzeg=nmguD>(R+7EG zt;btZTFBB!V-IuVOXk3L0s$xJK)(61qU>R{t&uSe_jPHQdjsm(BzKV7INZ5`Wj@DN zU0GzbFF@^p4DTFKa~U$cQqpxUO!7_XlsUMC#H8KmzI_202dpqvB7r5th3rxFn0wBu z%)RnB#CL?D(qiT=iqgf8S`dStW08%O@P?rwe{8}-aEr>!gagC78{c63bG5OtvGMh= zzX3Iv$d|ui9pz$RfEq-C7GH*lZCDGYs9;L;$sn&+-f-|+8LT!CFy;YG8jJ;IfI|^x z;JijRRz*a^Z36a1BPLT{lE$VW|2xMkV?T4<-j P00000NkvXXu0mjf?Tack literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00019.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00019.png new file mode 100644 index 0000000000000000000000000000000000000000..db7a548f98d2956f30f002b4d5480f055c06c7c3 GIT binary patch literal 646 zcmV;10(t$3P)sK-#|Ij?VV~3xpO?Zxf zyKLwG%&Q8iSA901qn5_1g;B!KU<;ZAwsvLiw)z3W3iOH|q#LgtC^VwjUF&O8YNZ+c zjmwpL0Ev6zMQU`tB8eN|ylh|T>q%#6>(S704^o+z2Z7?eu3LQ~dYzgZN3!RsC45G} zN!j>1@>WfMyEWB8$#T{DWmE!S|SWq2pmIbJh)g;pvG)eHIRY_rYCj=q?ejrnUZKS1{<;J7?lj! zX1fQl4Jf8@5g8jVC1%lS--LzZY+Q0GwkS~B8(Z5Jz;SY979>s^3?P@ViMw7D)s`1^ z-2i0ULE{pyLZX@TRWk$@T}sr!@FdO1U?2LDdjLNPo&xU%#KRE0-)FrdQ7qILZ>fw? zcwd8!gU_hS@E>Fo6B84^gPKx=8?}Lw-vjUnTbr>MRfh+{owhr2?W!8dbv4cbX()K_ z2Wt{l9o^sR5c7cS6-~r9xrnd4)zT3keLsI7qm>$qGMh2j12TH?O=Pc*{IbR`&$tdF z1JszCFmmsA*YuIAnX&plfCZ0wtYwyVxaaTaIAZljVn7OEeKp3xaxqxmQu7Zo2C?3e`Ry}ycOQeP>p!J ze(j0qPp_FU#mCR-5wwjb9yhB)mL8`e>zVLoW)^1Yw|}u*)|}RyXo{nV$pvRCVu%QAxhmd~C}dQ-Thh0$2%H-F@3(| za1OQqIb#71g#rvl0S2Q07gT@2e>b$d$EwW?+fV=i00000;NtEIzO+B00?<(a0000< KMNUMnLSTYPY;p_$ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00021.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00021.png new file mode 100644 index 0000000000000000000000000000000000000000..006c26abaac6c76b2e871b194165596c0b416694 GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!2~2j9iA5fsRN!ajv*Cu-d?-N*JQxc`taO| z-}VOL1%{U&gdOO)vH0QtN3CIpH=On}yd9NxI9#@L#=GYozkR+NZ@s(h_s<*5szDx| zmTO$v85!1fT#rxFzI@yL`PcK^*L-*W3fIF8>~K zzhRaAS`H?-=QM1i=Tu6{1-oD!M<5AJP1 literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00022.png b/tests_zemu/snapshots/s-1-tr-out-1-spend-2-sh-out/00022.png new file mode 100644 index 0000000000000000000000000000000000000000..1cd31cb2583141eb9a68beca2d3844084ec96496 GIT binary patch literal 320 zcmV-G0l)rE6YWiyP)SiHdEaF+4qWi-*+V{-CYwla!0T1m7 z{D34p*v7mBdAzE`mOX~qQ_x962Oz}rVlOCzr2qf`00000ZomfW_>G2K SgEch(0000adJU2q8iA&&xovu^QY@*g>NQ}@J^hKlNhY_Ua11~scTzh=&`EQbEdX~nnv}^h1D80Lnt!5WF+LyH)%29251dPP z!X*Xq{YwN*L$0Sc{80{HCQL!LK0q<@-97%xs5${MlZ@|1{za+=K+V3WNmT?O*bF@e zAyX&wHIPJlUX9!}0QAk$bad!bYob7xl5ZI)Z$WIktL*V|Binn-EacB zY~)5OHNF$`y^I3_uK-wTX`pzp<|-k!8t|hGtNTcKS9)tW000000C+GbY@My`<#H*b P00000NkvXXu0mjfQ`@5! delta 375 zcmV--0f_#L1Cj%fB!5{+L_t(|ob8y+5`!QNgt1fK|A9T&12araFhrnfqx;>e{7iQt zE~Eqi0N|-C9+T`_ZRT}!z>yLDhPTHgRH++jHw7013UVvZC(FuzR@=zFk?r&~Aj6cU z^3hlOR)0mGREt|E91UP{SG7|Fy=m>T8E|!>NtrAoP>Ih<6Mx$l@p-wv1Eg=%RgB^!e9D_ddie{rDxrPS0MlZ004jo@&OZw VvFA@<)l>ig002ovPDHLkV1iBwtPB7E diff --git a/tests_zemu/snapshots/s-mainmenu/00010.png b/tests_zemu/snapshots/s-mainmenu/00010.png index 20ebbb0a0bb603a09e2242a050ad973333e4eb7e..5a8c1b0256aa06c160934eef680947968beeb9ce 100644 GIT binary patch delta 369 zcmV-%0gnEX1B?TZB!5#$L_t(|ob8y+4ul{KgmtsN{{wr_14&aUlnQKDaK4*AKr)4P z%CZ0e0C+2_$0Em`T6t@ouw{nF@by@PD>adJU2q8iA&&xovu^QY@*g>NQ}@J^hKlNhY_Ua11~scTzh=&`EQbEdX~nnv}^h1D80Lnt!5WF+LyH)%29251dPP z!X*Xq{YwN*L$0Sc{80{HCQL!LK0q<@-97%xs5${MlZ@|1{za+=K+V3WNmT?O*bF@e zAyX&wHIPJlUX9!}0QAk$bad!bYob7xl5ZI)Z$WIktL*V|Binn-EacB zY~)5OHNF$`y^I3_uK-wTX`pzp<|-k!8t|hGtNTcKS9)tW000000C+GbY@My`<#H*b P00000NkvXXu0mjfQ`@5! delta 375 zcmV--0f_#L1Cj%fB!5{+L_t(|ob8y+5`!QNgt1fK|A9T&12araFhrnfqx;>e{7iQt zE~Eqi0N|-C9+T`_ZRT}!z>yLDhPTHgRH++jHw7013UVvZC(FuzR@=zFk?r&~Aj6cU z^3hlOR)0mGREt|E91UP{SG7|Fy=m>T8E|!>NtrAoP>Ih<6Mx$l@p-wv1Eg=%RgB^!e9D_ddie{rDxrPS0MlZ004jo@&OZw VvFA@<)l>ig002ovPDHLkV1iBwtPB7E diff --git a/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00000.png b/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..b82f82a69119261310141622ef5e217b1633e6fe GIT binary patch literal 452 zcmV;#0XzPQP)7y+8~Tb8SQ7Og#oN$})H^q7?=WHJ}v2(!Kf}S>|uU~Xgb5Q3HZUt<#a@19)?n-07 zR!+xyu~jJC3Rs1vUGHw#gt7&FHP%J92ZT$&+u|^`Kc}^58g%y!ri*?ODnX~Qv-QV4 z?#(!paIc}=6uXQ2O?Y}S;3fEzvZ9-WFX87Atykw0N4&&48+axFLu72u^E{5DPa1bP+bTQB+(Ie*k*I&~S6Se4 zlUsHC(S(1eRvtH0+jz?ihz9c1gHcITSkt(c&zJ!beNcbyp7E9$u+W2Y!^jKYz-P~Z zzVs{Ssx2WYB!mz`2q7PUdf{`g)q9?5BTDiDKt6mCz-NUNEPb(x`;~hTU@DIn~MUItXTjq5&83OgW0iJ z&IY?tQF8Lz#3U(DS-5b_{Wld6C0=Bf*rJx*tEJs7Y9>uB;(ZTL-x8+6*4q~{P79K8 z$V71;Nouc)LJZ_LoZ)Q?JX`3IyK`7|mNm(^x8)j@J^3vn0nu2?it8C*!v!wT!%-7; zX}FQ9!kmShaDt7ftx{VEG8>qx-9hMNdVd>^tLgpCFa}S;Rp?0uPG$^l#kv>$Zzz(J z=-2l1cm9<;`m-CJHozN4X1LA3f8p%_pBYG6F>f}d)TSYE3(L(Gt`pNI;of11tSt|X zs8?>-T0HQjRj!e^3b#a_s%$f4U`gh3K`N=zMji)Gxw)KvlC-~(UUf7@Oij8lG^{MV zO$ESR6O$yOn^TL@`tCr!Z=D5K^I?}Q;AeofR}r~czFEBRLMHy~l|8g_?Ob9;a^ zXstE5WI5Rjx#CrZMYVP|P+z(0cj}TUdaJL^ejzP39pzht}C z#=u?q)vk+b^1r>arqA7F*?-QUxSrpDS*tbMZntLIfdlF#&)WY6J&-KOyK{4sdiJm9 z>yNMhvfA#>gP+n{Yf56;+RU>Ueobaz_#nOg?uAo#TYsHc_RaeCC!Ku~XQ$nJ>TF*t zG4*(btH_W38&b<|biG&-9iX6nhC#41;@4d+W&tT?1IcF7hI^~_SU-`Ie5Yt%9mJN` z?eq0)%#>NLq?37UF1Ffyl>6pV$q4i@5@^`}ld18y-q%F7dnO=ZPgg&ebxsLQ07O5W ATL1t6 literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00003.png b/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..4f70c62e571f6c55ac48b7b75df8c37adf336855 GIT binary patch literal 923 zcmV;M17!S(P)WI^lcDF$7fb5=?h%MNt$*Q4~e7oj>Us&-3ip z$8pG8tZ>=Hn)35Jsf>Jzmh`rC0rq&DpF~}a)c%6q9Tx6{28aCFp}o&Mp**$5-$Y#| ztVzdl$PlBF^475NvXCmDx!hE>CsvY2zy%m1%5bJ*sB3f0JVUQg$+h7q2SkwM5zym~ z1$c7?)U5lE!ZnM{>EJB#M*v-~)-=$CB&}M3n%qjJ17vj%LQwfoL%N6dSfOM(;F@!0 z>#9pnYmKU6D2ui-cH;BSv4t&qA1=@C>v_-$%fg)HcIc{ z0^T}+GAYEj;BI5eu0Y>e4`mbqJr#jeW9J42w>8ZL$@d-R-l3CSAwXS-hm2xE=K7IT zsC+?_jUCE+Z7mK9Fb1W-NtmfJM?lx3TTwLVM!`bYU8S}XBNZY2tjjavS)i)|EU^mS zeFSfP9J!!RYwCBPod*o(waW(|n~gFL!amIwcR=r4w#iYB4`qeg|HFkILg_X3K7w?C zqSFPkmP#4cCr0ePaC!loJQIsJ^5Tcy_}Gc%n;4J37smd@@(5tt%>=uz^}&9oU0pud zfYHh2gIkf^yPgfzw`KT}K9gj7M^|De9VK`4{LW=zfikltX4Xirz?&K#Ppf zx1nYvn&uat8m1N^h9c0mgYIEuew@qFN3?SI_iI$%^~l}GIFlu_(;J0M2Y@F;L69|I z16kJB9pGI#hb1Y;c;1CB>r_Ku+uQ15{w^M$;%@m1nF6OAP!vT`zywc%a4U#)nS?Oa zN)5PmU9)az9VAJT0^To5`o(K2#Q}&@XBaPHG`j%4G|`{KYx$F@iX_};R$Skb&|69Z zmCD>WE8v+$^QSH|3nG_MaJ(W(P*bg<2cLrW{jZ3bGnNB9QWMi?Fx7)6AGf}U1;5oj zexZd6E=1&!S)~sYIbv|O{XnskUlKq7o(AP%&J4-6iCTkkI9xmW1DN?;H xUL~$&7WrbeSSNSGQ^~l8DF+lqQ53~P`~#8SOa>Ij7O4OL002ovPDHLkV1gCQu?zqJ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00004.png b/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..0a938aa72c4619ce080955e0df14b7e0cc322cf0 GIT binary patch literal 626 zcmV-&0*(ENP)1Qwjh800000>3qeCyYIU`U)RN} zNa1uBbHeZYu5GHShA7^ql@P*rpscF;1F01YGijYeoaIVe&fD z|JTF`#9SDuDq}8OCBWYuXkOgSts3ukpARv~5y`lf?9FtK<&3lE5h3W!9ASXR6Ui;o zA-UDv91((CjNJCsfF>ULL-Lvc000000000000000000000020gUvKw#kZ)_PZU6uP M07*qoM6N<$g4jkA6951J literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00005.png b/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..03481eb4015b64d00e3f60fb77dfa644b5a6f927 GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|@9hba4!+nDh2#AYZcrPebCi zH)?nOd)!Io*IUB4W`)DsPxbz6X@;)ahXp{opT>nTFQ4+MuxH~tEvcy%hHjF+ZMl6x z=6Q~OF?-83m!_R+J2J8ND?guqMO4; zzG&WS$=93fPAIG@jy?GP%A>uSl|{AY)l9S2AF;k8I=}azaKQ8_jpZ*U=YLb1ow_2f zI7Rj3@w1r!Xh6gvNe04j2Y-ZiBzZv!WWVE*3__A&u zbLFm_Jz~C{_UDDmozEBTo&CO!DJI?i&q0=o=haM4p3i#lnSbrs?+t+|A3w%**QMQe zsXje**XqqnC0`Y7xq13R^8-2g#PYJqKHsKiE}8X4|Cmg~BCg0swmF?&K%s{WF4r(7 WosKlEN-S*$346NwxvXB1wW27Bq9}@@xSbyZ(R?3kbxf`V`QY~9w^#sVp^K&sj zgFAN4kLsbt!%&sbg38;(bDZKi(JgA#lE(K6+dw5y-pAi4gRcgk22I6Y~}4v+MWSl{*Dyl;LX>m86~ zNl_FG`Qq`F^E;p>;%^sJt*;mw`}ineQD=)W zmBl(?Q&%6{K&O$b54N(sd5=F#sEqg66KejMYsnvcf?#^tq1BQYsOvy|+SxRkz zE}c+>8Zv75kOQiDyDU&oW&%aWTGza;ZRD)A&u$9eEPbMS0x>e9O^m7($tZbc1MZ=y zWGSYyca=s4;BHvD8wy5E_f+BDSY|S=;9jA$4JW@0m{Y{(UA>&^UYQ5cBy5>~LfOo- zC(z{I2iWF|26!~ka39FGxAoSj(jlu>I{#%R9CpgUs_n(+%H*qr7dv%iH%Sc zMNt$*@k+v9%5U08sU4oHg^@|ro>jqJhY}`AHChzdp$|oz=CpSerIb%Hb`W#xppHE5 z9lFG!p=^lmZYq=H`6x2R9?oqcvQ#Sb8?01CSbZHjv!Th0Y)he!e#&9fltAYKury8zr%0`A zXHyw-Ia9E)cf8|OuT*ly3=5w-#RZbA22pXu~8KS+;A{R;EU!2d{0Q)3Ml zP%$#rU@FqQ3EzgUq zQD#G}maLbfLu>vrmz$hMXu08JbkkTkaU>xIo3b!Is;TXQee`QRpZJA z^HOgS9<%@wmJs#7+N2-KtKHQub0+|T+;}D3{tN(nV_i}K00000000000000000000 aDf|GwIt}6W4A~|C0000^OwHJz}X(%dTWXzTZXJwNXK{;To(Vb|j4 zz0nh17O$>6>2|B`)sOtuM>|*EDSs-ztvY#k4sQwfg(>L1VZTb$^k2gEY z9O~SkPd)g;_F0yB^~86kTYmCIRdek2|EaZ2J%0Wtsi@hCI%k)!wm4@$Yb(QJM+SzB z$@ALsdcN=3vwHv0seXc=l9#Sm`oG|b$=P?mUMck(OGG-?O0*wo?OV*h`M=ZpnZeuR zBN~=`Io!yX%TG}+q0|HJ(^mA(mt4Jr{xw-S_<5TwYxf))&?-R1c`}Al~>1ZWZbT|k*=uhU*>@Y zQ#05B&oD9G3&nNzkxl$qvUeJ#6&ventAAqv;!kl`0BBMsbYF^R--0lS4#-{qFa&^O zrglTu()L@7!d)x%@~`nvQ@;5%mh5f+U%j($+znl*+>OvS`^s&&Y~X7fZBS=`aiFyz zZk90v##Ogkk43WFJ364cR?O_k-^G{kW%&%a`>rPb!zcc;XjIhBlvmF`X(hdHoWif8 zCYkIXBcz(_A4VVC#42=CCCT)`R-_jI000000000000000007wL7jUs+BNiEr|?-^=|56;;Fjs;-NoCg3u=$0a)!SvayqYcHR|`> z7v5g%I||okzk4*-Zb#Vqt460{4;I~HxX%7E=-5B+680~G`PqMO&S;ZW+v_Q`Y+-xx zg^TVH_vYU}`zcWLU5>r`q$`;~w=-lF2A@>&-nQQRHn-K|_~v~fh2~dwq)xvXx$@+- zP2LgFu?f+or{{0#`+D-wsr;aqA}X)f{^vF;kMP!<+hiAR|2d?d`N`wy4~}1{yfQa4 zTp-4IffS8@XY0Z)*urGXvP#QV88(YBle*q@1h+WV2qsk$008H6`U9 zy2eV@0b|XP+bk70^chV{rlj2cb>~f{DtHlP}P#A zX`1GVq_UyzIn2lcX-ZsyXd6J85{%>cS;mZ$fPI=?$`}H6`T=Rijj2`xH5VkGahQ9T zP7Z|tbs-)yh6$P9N7A42p-gsmDIc}9IW52#lmc&prplcGQ;%)M&|n({3qyB})=HdI zg!Hp5&&VgyMgz1Yt9|ztqU{OffE%6<9uX|}in=HRkJj@*}w z1|7er3w`lqw>Vk_83IFR2xKpnGHgto*nQyg0(Myw%Q$lILvMWS!tzb5=idWkf3Q6R z*m1MK?qhv$J?c=m4=%tNI&+1Q}mY>AsS zvKw$7%g$yLI#0s$8hH?#wb)5(sfPx5D4OV_A~+|cej}o_DoDea&LcTdkgOq+&UBR+ z0^r3Yq%&0Lb_%q}NPQdXMxtweqt-IDkTH~j_8oK&qw@1~mOi45!@pUh@}VkcBjZe# zXm4)}Djfj6AxeVm0T)nZecu7mopV`I@)|F?(N(?Gu=n<3cQbz$w=Z#Je<9QC(GF;u zrfFc3Z-Q_uh;^BSaMel;xOH2zZD=1PNm2qnE=Kw#YOBNnh*LG32Qivm03Vv@&*ioJ z$y8+$?wXa*wbq%}x9R2tQ^t053wV00000 LNkvXXu0mjf%(A)! literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00012.png b/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00012.png new file mode 100644 index 0000000000000000000000000000000000000000..e3d5c8e7fe37ef5ccfe5a3a531bc9b9d7638d5ec GIT binary patch literal 636 zcmV-?0)zdDP)5%2fAK3~@r z7dWcAQOXvwZEKy{1w~X}hf0{{6>MKkyot!6j@%8^#g1}?miKkyHh%}(o3Md%|7;!# z7rfH+Vtd{f-a)F*38_41W=ChhjT8uaa8+ve`e{Vxmf|O=kk#v4qfE?xd5F${8ecTl zBqkP3Y4|*a)JUhIewr$Emat$3^jp~~=mxV^GOH+FXM&Ie*e#3Fzm2FJe0{} zsFXe&6=LQi{l;Wo*7OT=DoaiR5g|>2Xdlj`$S8I$$deRoV!3~o!e=NGIciD$=`_jg zMQ@>Wp`&TB==_k<-_13I8btr<5~wIDP^zXBdLEpGQZE!EJz7}iv!Q*|M2Mtzb1p=i za*o?S$n0icE;ff{4xmeIv+O2gegZSwX7-|WfK%nr4r6|((G!rmiqUG*EI)Z`I`$!L z$oZE17fsh*^aWDe2{h}$uLF`t|KXW*eocPzh$JHpHGGhOG05=2Rz7fe{g?hesj%0w zhoEPE*qTa7K5PCpF$kLrYhD#!2v-S+_W`6RHgl^=Tl4u4mmHCdi)3%6HI_5(o<}S} zPv!^*+>zLPMLHz6x|<`Gpdmb8`|5(mUiz2hH30ws00000000000000000000a5z6e WjfVv^SKvwj0000fSu}ln z?{S6Y_oL6QzwL9}fA0U>t%W92GN=D7*d2cRZqbA34SLd!ya8o`$1X+2aIOAXIB&h! z6q5zL2QrViraXz<)@yjIENR#G0=@mB3(n^+4qE+FDZslky;lC=E~c*a`?PZcYxw-M z57=GYW@J)jzxmeTxF_3r-@bFq)_?dxFaGGaH?NoHIA@o4OnZ0xYUP~#k}#%c?2HTv zw!OA(Z(nUYvi(-WTS0w)ynsRfy*xzD{mHK&pIoCZ-m)*W_?!jyQ8|*9C zX1>_1*)r>ewK9A8BCflg-#^Ud&!yX%TG}+q0|HJ(^mA(mt4Jr{xw-S_<5TwYxf))&?-R1c`}Al~>1ZWZbT|k*=uhU*>@Y zQ#05B&oD9G3&nNzkxl$qvUeJ#6&ventAAqv;!kl`0BBMsbYF^R--0lS4#-{qFa&^O zrglTu()L@7!d)x%@~`nvQ@;5%mh5f+U%j($+znl*+>OvS`^s&&Y~X7fZBS=`aiFyz zZk90v##Ogkk43WFJ364cR?O_k-^G{kW%&%a`>rPb!zcc;XjIhBlvmF`X(hdHoWif8 zCYkIXBcz(_A4VVC#42=CCCT)`R-_jI000000000000000007wL7jUs+eD12qAuR6R z^IT=twBKCZh2pa=)FEBh)tB^5bcr@1n$PfiiIo=a&5Rw9XwSY`%|mBD_$peNJ&_F{ z;Lj2(L(D;RJO^y6qsQZn)7VbX-y-0bSTh4|yg}g&B0;;<$!VmW2IVzhg<0sarUS&d zzMhE6t0wQ!B_d9D3e(i8NvT;j14JKuoQ27H^@ z_fK!bcUaqrhS<-ECB-b)i@4{v_hJSU*AyjvRPU`wUxByT-D%<5neTvAkq|-%`Ci6q zXdvSR^<%BEh8M|UZ7zqqQqM^2TKmEsRmchnVyE0b%x@czNDwbNLZo@&C-A~^YG`j1 z96@H`aO5u3Baxx%HOYGKeSrQzQIL9NMfNjaXmNY!sH5JAFwx<~F(JHM74N_Q5CND)dPPNoF6sicW9w zY$(2y1D}KX{aGL)nh&c%Nll50V=yat35ksqNePPJl=5WMg8i(8Yz(@*rm)^XQba*e z1np$IXWnBOGwC}{rUc_cXkI!*Qxx->r>dD>gUY?N#;~C`w80sGjGNpH2++%}F8LvM z_-#TP0@MMkCfeEwYpPN;7n73mj7_Sc9|n3MBW`8q^KU>0whayJnC{GmS%NTv;TgwZ zzRcKnPbP#ALJ0XUh}6)mqz68F;7>h4%|OD`CBb`qU4mTojMkeVyZc7!YY}GNpjR2u zmu7@auufbpeJ~4DG|e$`4+Kvy3k<$1ua5w|m$lx=u@DZ+il$c>|2iC%T8;&y&FY=Q zc))AdL^Gg#)bdl+w9JVtEJNd7h08f@mLk0bfh&M{dafTGTD3+DisZ(@q@=3YGUC0q zHf}ncn>e2LFR<_uq7Il>G!I~0h~}R5iA)i1LpgtVAXvC`9)ipeLI@#*5b`7V3s2=m U<9@-?xBvhE07*qoM6N<$g3a=#P5=M^ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00016.png b/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00016.png new file mode 100644 index 0000000000000000000000000000000000000000..0782386f8bcee308930e72c139d5f652069a84aa GIT binary patch literal 490 zcmVXlGsZJQ4!A=hmQeOxo1@I59 zj-c>4VAD1E1J74#$Ep3gdO{~YGvHPm5%wrc(sNEuqq5SL@?HZIQRUMCVqC{h;$oMX zo7bCasipgoxLu9r)pLT|3@F#Mv)VJ&(%^Lu<RWEuYy+UoXyFbW#xpaU_O8@}x<#rm{tG35B$NGZlvI=b&FF)Zcnh8MkYxH`Dbfo70000000000 g00000001oW3)AwBnxSYgdH?_b07*qoM6N<$f?7@HNdN!< literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00017.png b/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00017.png new file mode 100644 index 0000000000000000000000000000000000000000..518993a1e4c73387c538a2f58266982a360d8237 GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*Qg1z7978JRyuIO=cUVDyHDLKE z-yQ!a?(i0$Ca|^RvWe#5i1%v^rV0E?X8yK~k>Nne_7|Lgyw+~rud~$U?32guE(%v1 zoLZD`B>G@My>3))+g$D6UGolbXRXz}zchF(w>GZxS z@rRxl)z-&uVA!eQV8F_;jG=pG0h`5~Z+vh4W#zBiwAo4i|F!%9s|`=&ugg4(ru|FF p5;&*dAj^PbX0`eJS}y70`%8T>n^%dV>bk>TM}C(cF@9*Jbj^5WTF;gKGIO)y zjlJ*9*PE9W@jdm~5r5wEu#TD8y6+Pnh8_O<-8#^Bx$bnOy3REWn^jXeAFev)TXe-F zTky7j?62N_lL@yzwx7O!=&4O1W7^|)7fcr%%i5*3D%0_G;tl)cpW3Io3*7!cdXW1o sV&B=e%knGs=l(tai5cc!1_ryAe9w9}zGZ%X&;TUp>FVdQ&MBb@086r+N&o-= literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00019.png b/tests_zemu/snapshots/sp-1-tr-in-1-spend-2-sh-out/00019.png new file mode 100644 index 0000000000000000000000000000000000000000..6958c981b015b1ea976e5ba479ae4a4b4a5473c4 GIT binary patch literal 496 zcmV?9!o00000006v+FY5tpkW$*Z zKgRe+Be{`p>jAtS3Sf+U0b-02R`ZD;>j9jn);&Mo-6T99zXH?|UIF!d*L5M`0%VHx zivAPSEl6GcAiV-~H{KfI0%U6US1g9M1^okrS3sWc>P947fQ+v687#;c3IG7`Uflg8 zIXu-|$=d4EszclzL;KFwt#-HIt^kF8DKz=>FpmC|QJ3r9%IUWNnMT?ESFrq2N@@5~ zeZ^rsuqbGmF2=wj|IrgEoJ^jiMgL~XF+A!Hrr`7@p><< zpZ0GLA{GPy008h#Y|_x%4~vsrXqfJ;#7fU#&<9L(x%kN|ZSb(aGyH3XeIwR%UcDAW zb>`LD&5jH)iz51bxI_p|&U3;a7So7fFa0nE1z5~D=j!YV*SMUYyvW9a%D&g+_{}}f z4$6k7SHR)ST{oHCul8Bdr&qx6%vVR7!{D>rf6lV!p(!kJxFuZI)MQV#KZf4O>3)I* mag2u}BLDyZ00000T;&%{Hu>xcnlBXq00007y+8~Tb8SQ7Og#oN$})H^q7?=WHJ}v2(!Kf}S>|uU~Xgb5Q3HZUt<#a@19)?n-07 zR!+xyu~jJC3Rs1vUGHw#gt7&FHP%J92ZT$&+u|^`Kc}^58g%y!ri*?ODnX~Qv-QV4 z?#(!paIc}=6uXQ2O?Y}S;3fe*_F&^rf zizYiMrpY-IGvyEU2%(k7kh_R0Gr*qh8IOYlI%B_P^U8Lk3>c&Y^|SVdD>Fb`7PPzF zBE-|z{-s9aMj2onlgM&Zdh%VDG$v(JY3?)t00000;4Y989y!DLk977pr2{`u@)rJ} zQ=g7EWUij4^ALPK;|^$!quu2fhQdu{MjX%~&Om(=t}PXMzt<|7(Q%qmNWHTb7b=nV zHIlpHtTmOqG-Hz>KGlphX9g6B{8oJt%_o1ON0ao|A3WVTL$x3+&^jGnVFZ*t_*v(f zTaV)w^l0K_elc!}awD~x={{ImUR_m;n^JMK2RZMGw!X05S-am83N!=69V`?AWc8?R zp^c;MlQq$gh0V!3_gk*N@nBQ8hCi-~nrE{wg@r-@jE_O)mGl_Pe!)?7FdW~y%BrbYWCchscT3P@~@i_`1Z)mSQ2(^I!+XXZ7IX;%Up$7CPh1x^@$8YPsjZ_i>}Y vqyHyx000000000000000000000AL}%viS$@qb8%B00000NkvXXu0mjf5@$(o literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00002.png b/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..a7265ebce768769599119b7af8b7f2d39dd60048 GIT binary patch literal 362 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|=-#ba4!+nDh2Vbl+hG9*2ut zX3eem@13OocY$k1x{2bp&UZgo%@ONReDKTysEMIee{P-=hpUUryBDr5ul6OCSK54y zV^3B-ef*gd$HJL&ReMbyS8n)s=!-hnzbjRcoZnLMIdqOvy4069xwZ}KZ5B2k zS=!U>G4Joe8j*u9ndbK;>TXs~sA<1${^;J6ire@1+AwD4Zv5$z=I^lAm}i~d%R}l* zjZCEG&)MtzWI^lcDF$7fb5=?h%MNt$*Q4~e7oj>Us&-3ip z$8pG8tZ>=Hn)35Jsf>Jzmh`rC0rq&DpF~}a)c%6q9Tx6{28aCFp}o&Mp**$5-$Y#| ztVzdl$PlBF^475NvXCmDx!hE>CsvY2zy%m1%5bJ*sB3f0JVUQg$+h7q2SkwM5zym~ z1$c7?)U5lE!ZnM{>EJB#M*v-~)-=$CB&}M3n%qjJ17vj%LQwfoL%N6dSfOM(;F@!0 z>#9pnYmKU6D2ui-cH;BSv4t&qA1=@C>v_-$%fg)HcIc{ z0^T}+GAYEj;BI5eu0Y>e4`mbqJr#jeW9J42w>8ZL$@d-R-l3CSAwXS-hm2xE=K7IT zsC+?_jUCE+Z7mK9Fb1W-NtmfJM?lx3TTwLVM!`bYU8S}XBNZY2tjjavS)i)|EU^mS zeFSfP9J!!RYwCBPod*o(waW(|n~gFL!amIwcR=r4w#iYB4`qeg|HFkILg_X3K7w?C zqSFPkmP#4cCr0ePaC!loJQIsJ^5Tcy_}Gc%n;4J37smd@@(5tt%>=uz^}&9oU0pud zfYHh2gIkf^yPgfzw`KT}K9gj7M^|De9VK`4{LW=zfikltX4Xirz?&K#Ppf zx1nYvn&uat8m1N^h9c0mgYIEuew@qFN3?SI_iI$%^~l}GIFlu_(;J0M2Y@F;L69|I z16kJB9pGI#hb1Y;c;1CB>r_Ku+uQ15{w^M$;%@m1nF6OAP!vT`zywc%a4U#)nS?Oa zN)5PmU9)az9VAJT0^To5`o(K2#Q}&@XBaPHG`j%4G|`{KYx$F@iX_};R$Skb&|69Z zmCD>WE8v+$^QSH|3nG_MaJ(W(P*bg<2cLrW{jZ3bGnNB9QWMi?Fx7)6AGf}U1;5oj zexZd6E=1&!S)~sYIbv|O{XnskUlKq7o(AP%&J4-6iCTkkI9xmW1DN?;H xUL~$&7WrbeSSNSGQ^~l8DF+lqQ53~P`~#8SOa>Ij7O4OL002ovPDHLkV1gCQu?zqJ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00004.png b/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..0a938aa72c4619ce080955e0df14b7e0cc322cf0 GIT binary patch literal 626 zcmV-&0*(ENP)1Qwjh800000>3qeCyYIU`U)RN} zNa1uBbHeZYu5GHShA7^ql@P*rpscF;1F01YGijYeoaIVe&fD z|JTF`#9SDuDq}8OCBWYuXkOgSts3ukpARv~5y`lf?9FtK<&3lE5h3W!9ASXR6Ui;o zA-UDv91((CjNJCsfF>ULL-Lvc000000000000000000000020gUvKw#kZ)_PZU6uP M07*qoM6N<$g4jkA6951J literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00005.png b/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..84e36300642d3b4266fa3c504c6535cf0d05c6f3 GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|`hsba4!+nDh2#E?=_(PebDN zH)?nOd)!I2_u+WBIP9p{$=`pJRwP7SFU`9R)WlFMzrHuQCsW301;6OzLsp6{HCt+GT)`Ing4fMblBY2Ty@LEz6Ti<*OvcgG1)Klz9RPHP0JdlSCfoBaL)ainExr$ z`RT05;gd}ArcHm8@i0XC)*fM*iY;?nPpx3N6VJlX@RC!v{n_1_PtP=UZO{3%@Sei{ zu==Yv8+#@1w{czjz4pr{mNo4x7Rrq0JPxGrR5dKRt$pm&{(`5)%W|Fhuf^}@p66hG z!sWh?@)ei+5|$raL_Zz*zCryEH^{?Cz~&jh<4ljdh2QU71BrUN`njxgN@xNAewCX` literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00006.png b/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..69738ba21eef024c6a845c8982aca1d2540c9043 GIT binary patch literal 944 zcmV;h15f;kP)B1wW27Bq9}@@xSbyZ(R?3kbxf`V`QY~9w^#sVp^K&sj zgFAN4kLsbt!%&sbg38;(bDZKi(JgA#lE(K6+dw5y-pAi4gRcgk22I6Y~}4v+MWSl{*Dyl;LX>m86~ zNl_FG`Qq`F^E;p>;%^sJt*;mw`}ineQD=)W zmBl(?Q&%6{K&O$b54N(sd5=F#sEqg66KejMYsnvcf?#^tq1BQYsOvy|+SxRkz zE}c+>8Zv75kOQiDyDU&oW&%aWTGza;ZRD)A&u$9eEPbMS0x>e9O^m7($tZbc1MZ=y zWGSYyca=s4;BHvD8wy5E_f+BDSY|S=;9jA$4JW@0m{Y{(UA>&^UYQ5cBy5>~LfOo- zC(z{I2iWF|26!~ka39FGxAoSj(jlu>I{#%R9CpgUs_n(+%H*qr7dv%iH%Sc zMNt$*@k+v9%5U08sU4oHg^@|ro>jqJhY}`AHChzdp$|oz=CpSerIb%Hb`W#xppHE5 z9lFG!p=^lmZYq=H`6x2R9?oqcvQ#Sb8?01CSbZHjv!Th0Y)he!e#&9fltAYKury8zr%0`A zXHyw-Ia9E)cf8|OuT*ly3=5w-#RZbA22pXu~8KS+;A{R;EU!2d{0Q)3Ml zP%$#rU@FqQ3EzgUq zQD#G}maLbfLu>vrmz$hMXu08JbkkTkaU>xIo3b!Is;TXQee`QRpZJA z^HOgS9<%@wmJs#7+N2-KtKHQub0+|T+;}D3{tN(nV_i}K00000000000000000000 aDf|GwIt}6W4A~|C0000k_k44--OXtS&(`VA7ynmhIj~!actwhVw7y%Iqtha={_LBsA8FIJO)?A5-K!JR zJm=o_Y17v%{TkMPKU&+*Cp9Yc-;MRXPd6D};B^R}d2q&pH%T|m=3HG_VD4X7y>`hG z&Qc+9kvBuUfxP+h^mCH7>>r zY8IWIdT{04=Xw0qQ}d*6-EH0$cj$Eb&sEdX>+MseZ@p5qxx0O}#W{PcY=%B|Mur9c zRZ_c(zE!4%ZeOys-^cr#>p$O{nX}@*UUijxo{+-(HBrXM+^X;1+5atamzSSiCeNV$ zGPovaBMa{_hC4k8x6+o!-YmV=nCJPU!0VgqJ}32mBHhp4%<@$GZ8SAxa;@R91eLIb m6D#=pU8)#?!GZ)DPVZs7HOKCX{z@J_kg%t#pUXO@geCy>P^}*T literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00009.png b/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00009.png new file mode 100644 index 0000000000000000000000000000000000000000..7b0240e3ac1e6f079fec0ee251736e5be529245a GIT binary patch literal 470 zcmV;{0V)28P)!yX%TG}+q0|HJ(^mA(mt4Jr{xw-S_<5TwYxf))&?-R1c`}Al~>1ZWZbT|k*=uhU*>@Y zQ#05B&oD9G3&nNzkxl$qvUeJ#6&ventAAqv;!kl`0BBMsbYF^R--0lS4#-{qFa&^O zrglTu()L@7!d)x%@~`nvQ@;5%mh5f+U%j($+znl*+>OvS`^s&&Y~X7fZBS=`aiFyz zZk90v##Ogkk43WFJ364cR?O_k-^G{kW%&%a`>rPb!zcc;XjIhBlvmF`X(hdHoWif8 zCYkIXBcz(_A4VVC#42=CCCT)`R-_jI000000000000000007wL7jUs+BNiEr|?-^=|56;;Fjs;-NoCg3u=$0a)!SvayqYcHR|`> z7v5g%I||okzk4*-Zb#Vqt460{4;I~HxX%7E=-5B+680~G`PqMO&S;ZW+v_Q`Y+-xx zg^TVH_vYU}`zcWLU5>r`q$`;~w=-lF2A@>&-nQQRHn-K|_~v~fh2~dwq)xvXx$@+- zP2LgFu?f+or{{0#`+D-wsr;aqA}X)f{^vF;kMP!<+hiAR|2d?d`N`wy4~}1{yfQa4 zTp-4IffS8@XY0Z)*urGXvP#QV88(YBle*q@1h+WV2qsk$008H6`U9 zy2eV@0b|XP+bk70^chV{rlj2cb>~f{DtHlP}P#A zX`1GVq_UyzIn2lcX-ZsyXd6J85{%>cS;mZ$fPI=?$`}H6`T=Rijj2`xH5VkGahQ9T zP7Z|tbs-)yh6$P9N7A42p-gsmDIc}9IW52#lmc&prplcGQ;%)M&|n({3qyB})=HdI zg!Hp5&&VgyMgz1Yt9|ztqU{OffE%6<9uX|}in=HRkJj@*}w z1|7er3w`lqw>Vk_83IFR2xKpnGHgto*nQyg0(Myw%Q$lILvMWS!tzb5=idWkf3Q6R z*m1MK?qhv$J?c=m4=%tNI&+1Q}mY>AsS zvKw$7%g$yLI#0s$8hH?#wb)5(sfPx5D4OV_A~+|cej}o_DoDea&LcTdkgOq+&UBR+ z0^r3Yq%&0Lb_%q}NPQdXMxtweqt-IDkTH~j_8oK&qw@1~mOi45!@pUh@}VkcBjZe# zXm4)}Djfj6AxeVm0T)nZecu7mopV`I@)|F?(N(?Gu=n<3cQbz$w=Z#Je<9QC(GF;u zrfFc3Z-Q_uh;^BSaMel;xOH2zZD=1PNm2qnE=Kw#YOBNnh*LG32Qivm03Vv@&*ioJ z$y8+$?wXa*wbq%}x9R2tQ^t053wV00000 LNkvXXu0mjf%(A)! literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00012.png b/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00012.png new file mode 100644 index 0000000000000000000000000000000000000000..e3d5c8e7fe37ef5ccfe5a3a531bc9b9d7638d5ec GIT binary patch literal 636 zcmV-?0)zdDP)5%2fAK3~@r z7dWcAQOXvwZEKy{1w~X}hf0{{6>MKkyot!6j@%8^#g1}?miKkyHh%}(o3Md%|7;!# z7rfH+Vtd{f-a)F*38_41W=ChhjT8uaa8+ve`e{Vxmf|O=kk#v4qfE?xd5F${8ecTl zBqkP3Y4|*a)JUhIewr$Emat$3^jp~~=mxV^GOH+FXM&Ie*e#3Fzm2FJe0{} zsFXe&6=LQi{l;Wo*7OT=DoaiR5g|>2Xdlj`$S8I$$deRoV!3~o!e=NGIciD$=`_jg zMQ@>Wp`&TB==_k<-_13I8btr<5~wIDP^zXBdLEpGQZE!EJz7}iv!Q*|M2Mtzb1p=i za*o?S$n0icE;ff{4xmeIv+O2gegZSwX7-|WfK%nr4r6|((G!rmiqUG*EI)Z`I`$!L z$oZE17fsh*^aWDe2{h}$uLF`t|KXW*eocPzh$JHpHGGhOG05=2Rz7fe{g?hesj%0w zhoEPE*qTa7K5PCpF$kLrYhD#!2v-S+_W`6RHgl^=Tl4u4mmHCdi)3%6HI_5(o<}S} zPv!^*+>zLPMLHz6x|<`Gpdmb8`|5(mUiz2hH30ws00000000000000000000a5z6e WjfVv^SKvwj0000fSu}ln z?{S6Y_oL6QzwL9}fA0U>t%W92GN=D7*d2cRZqbA34SLd!ya8o`$1X+2aIOAXIB&h! z6q5zL2QrViraXz<)@yjIENR#G0=@mB3(n^+4qE+FDZslky;lC=E~c*a`?PZcYxw-M z57=GYW@J)jzxmeTxF_3r-@bFq)_?dxFaGGaH?NoHIA@o4OnZ0xYUP~#k}#%c?2HTv zw!OA(Z(nUYvi(-WTS0w)ynsRfy*xzD{mHK&pIoCZ-m)*W_?!jyQ8|*9C zX1>_1*)r>ewK9A8BCflg-#^Ud&!yX%TG}+q0|HJ(^mA(mt4Jr{xw-S_<5TwYxf))&?-R1c`}Al~>1ZWZbT|k*=uhU*>@Y zQ#05B&oD9G3&nNzkxl$qvUeJ#6&ventAAqv;!kl`0BBMsbYF^R--0lS4#-{qFa&^O zrglTu()L@7!d)x%@~`nvQ@;5%mh5f+U%j($+znl*+>OvS`^s&&Y~X7fZBS=`aiFyz zZk90v##Ogkk43WFJ364cR?O_k-^G{kW%&%a`>rPb!zcc;XjIhBlvmF`X(hdHoWif8 zCYkIXBcz(_A4VVC#42=CCCT)`R-_jI000000000000000007wL7jUs+eD12qAuR6R z^IT=twBKCZh2pa=)FEBh)tB^5bcr@1n$PfiiIo=a&5Rw9XwSY`%|mBD_$peNJ&_F{ z;Lj2(L(D;RJO^y6qsQZn)7VbX-y-0bSTh4|yg}g&B0;;<$!VmW2IVzhg<0sarUS&d zzMhE6t0wQ!B_d9D3e(i8NvT;j14JKuoQ27H^@ z_fK!bcUaqrhS<-ECB-b)i@4{v_hJSU*AyjvRPU`wUxByT-D%<5neTvAkq|-%`Ci6q zXdvSR^<%BEh8M|UZ7zqqQqM^2TKmEsRmchnVyE0b%x@czNDwbNLZo@&C-A~^YG`j1 z96@H`aO5u3Baxx%HOYGKeSrQzQIL9NMfNjaXmNY!sH5JAFwx<~F(JHM74N_Q5CND)dPPNoF6sicW9w zY$(2y1D}KX{aGL)nh&c%Nll50V=yat35ksqNePPJl=5WMg8i(8Yz(@*rm)^XQba*e z1np$IXWnBOGwC}{rUc_cXkI!*Qxx->r>dD>gUY?N#;~C`w80sGjGNpH2++%}F8LvM z_-#TP0@MMkCfeEwYpPN;7n73mj7_Sc9|n3MBW`8q^KU>0whayJnC{GmS%NTv;TgwZ zzRcKnPbP#ALJ0XUh}6)mqz68F;7>h4%|OD`CBb`qU4mTojMkeVyZc7!YY}GNpjR2u zmu7@auufbpeJ~4DG|e$`4+Kvy3k<$1ua5w|m$lx=u@DZ+il$c>|2iC%T8;&y&FY=Q zc))AdL^Gg#)bdl+w9JVtEJNd7h08f@mLk0bfh&M{dafTGTD3+DisZ(@q@=3YGUC0q zHf}ncn>e2LFR<_uq7Il>G!I~0h~}R5iA)i1LpgtVAXvC`9)ipeLI@#*5b`7V3s2=m U<9@-?xBvhE07*qoM6N<$g3a=#P5=M^ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00016.png b/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00016.png new file mode 100644 index 0000000000000000000000000000000000000000..0782386f8bcee308930e72c139d5f652069a84aa GIT binary patch literal 490 zcmVXlGsZJQ4!A=hmQeOxo1@I59 zj-c>4VAD1E1J74#$Ep3gdO{~YGvHPm5%wrc(sNEuqq5SL@?HZIQRUMCVqC{h;$oMX zo7bCasipgoxLu9r)pLT|3@F#Mv)VJ&(%^Lu<RWEuYy+UoXyFbW#xpaU_O8@}x<#rm{tG35B$NGZlvI=b&FF)Zcnh8MkYxH`Dbfo70000000000 g00000001oW3)AwBnxSYgdH?_b07*qoM6N<$f?7@HNdN!< literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00017.png b/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00017.png new file mode 100644 index 0000000000000000000000000000000000000000..518993a1e4c73387c538a2f58266982a360d8237 GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*Qg1z7978JRyuIO=cUVDyHDLKE z-yQ!a?(i0$Ca|^RvWe#5i1%v^rV0E?X8yK~k>Nne_7|Lgyw+~rud~$U?32guE(%v1 zoLZD`B>G@My>3))+g$D6UGolbXRXz}zchF(w>GZxS z@rRxl)z-&uVA!eQV8F_;jG=pG0h`5~Z+vh4W#zBiwAo4i|F!%9s|`=&ugg4(ru|FF p5;&*dAj^PbX0`eJS}y70`%8T>n^%dV>bk>TM}C(cF@9*Jbj^5WTF;gKGIO)y zjlJ*9*PE9W@jdm~5r5wEu#TD8y6+Pnh8_O<-8#^Bx$bnOy3REWn^jXeAFev)TXe-F zTky7j?62N_lL@yzwx7O!=&4O1W7^|)7fcr%%i5*3D%0_G;tl)cpW3Io3*7!cdXW1o sV&B=e%knGs=l(tai5cc!1_ryAe9w9}zGZ%X&;TUp>FVdQ&MBb@086r+N&o-= literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00019.png b/tests_zemu/snapshots/sp-1-tr-out-1-spend-2-sh-out/00019.png new file mode 100644 index 0000000000000000000000000000000000000000..6958c981b015b1ea976e5ba479ae4a4b4a5473c4 GIT binary patch literal 496 zcmV?9!o00000006v+FY5tpkW$*Z zKgRe+Be{`p>jAtS3Sf+U0b-02R`ZD;>j9jn);&Mo-6T99zXH?|UIF!d*L5M`0%VHx zivAPSEl6GcAiV-~H{KfI0%U6US1g9M1^okrS3sWc>P947fQ+v687#;c3IG7`Uflg8 zIXu-|$=d4EszclzL;KFwt#-HIt^kF8DKz=>FpmC|QJ3r9%IUWNnMT?ESFrq2N@@5~ zeZ^rsuqbGmF2=wj|IrgEoJ^jiMgL~XF+A!Hrr`7@p><< zpZ0GLA{GPy008h#Y|_x%4~vsrXqfJ;#7fU#&<9L(x%kN|ZSb(aGyH3XeIwR%UcDAW zb>`LD&5jH)iz51bxI_p|&U3;a7So7fFa0nE1z5~D=j!YV*SMUYyvW9a%D&g+_{}}f z4$6k7SHR)ST{oHCul8Bdr&qx6%vVR7!{D>rf6lV!p(!kJxFuZI)MQV#KZf4O>3)I* mag2u}BLDyZ00000T;&%{Hu>xcnlBXq0000m@pc^ICczva_;%+q;Y}~2#^y3000000N>>2?gku?Qabeh z7~`atT zJ?5X_Jq3C1{or~A_+5A{#{tM}_V*YJ-zn$;9M6C}-n}Ox#{tOvc6~+)x)}igfN#-m z8RQ;Gc6T+O)Y|NBRY6Kgr!hNHN}umCFZqyb+4jiIfTFarTXBmqcFWQ2i&gK7jJ`4t z)DL1+&$ds&!fT9o|7Bm%nFqGDq5-J7yW4kz4pqzhRy9@>4`S~O)zZaPFN+sP+Z9+b z0NFUDj4#w2`=NIuaw(_`+iE$@N!+f?-R;dY9uxxr06gBrTF{^fvs<633PIz8@z!(s z_8WIUty^N6daj;+4k-6f4uS3cAF9x9U4E{!w%)(&4551x&IHQ|*HRC_8r>81n%GMR zV8M^B2J6b2EF>L(HFkeuJvA8W0hl@r-<(@h)6w)@Q2S)DEo2_8HlMh>EvANzO{Z-( j^;7@=00000;5q&QSBwC@5*P^100000NkvXXu0mjf0ZZRO literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/sp-mainmenu/00004.png b/tests_zemu/snapshots/sp-mainmenu/00004.png index fb347713aa626aaf6c6fd0d202f6f19360bc1ab2..07377e48ed4f4dc10b17900c3ae5d3bd48526b1d 100644 GIT binary patch delta 319 zcmV-F0l@z10@?zQB!3}EL_t(|obB0NZiOHa22k5{C+vSo?4=JTgaBfbdzJC_oHu`C zzQIv(+W-In00000fJ=O>zJONFxnG%5dhvv>oZ(VGt1qBcClx%qw+L5&qQ4dDH7TWL zakx)^Y_xL5Ex@ixwvMmflye?#s@Dvg+j2n*(8N8=ZW8E&dVkmV>I)e7%TO&5Hz^O> zPGk`au$#A(`GJoZ`@6OUHO=&XW!N^4)4hq;&(eCaKYR<>f==Rn#6A0ThapS_3a4>o!CKb@QiDB_oJ6IR&lKG|?k4?lg6 zc0N6dnMtNv5IFA?^nXD4fAaBfFF^~?>t86#%wk`yvdL>G000000002|Tfg`3!$P~l R#76)C002ovPDHLkV1laIm2dz6 delta 335 zcmcb`^onVMO1+_{i(^Q|oVPcl`3@-vv?iLoYx%3+x%bC~7N54pRa>7f`2YPH_cBZ8 z<+{sQ7!bg(aCR+YoaWNIGmGthG`rV54C%iq{@~`4Bb$G7?PLmhyEftblR0nnUX6|z=v_msoi7W-^u zm|f^C$sd#SF29Dm=}? z41{Admd%~8^5Du_6;H#QtB-YC?)8tqsk)$*_uk)fadj5WI?ml2Qa1lieYk|rzP4KZ z`?=k8fAXJvK>DwPZ;o8{-^7}+Rey@v6-T(jq diff --git a/tests_zemu/snapshots/sp-mainmenu/00010.png b/tests_zemu/snapshots/sp-mainmenu/00010.png index fb347713aa626aaf6c6fd0d202f6f19360bc1ab2..07377e48ed4f4dc10b17900c3ae5d3bd48526b1d 100644 GIT binary patch delta 319 zcmV-F0l@z10@?zQB!3}EL_t(|obB0NZiOHa22k5{C+vSo?4=JTgaBfbdzJC_oHu`C zzQIv(+W-In00000fJ=O>zJONFxnG%5dhvv>oZ(VGt1qBcClx%qw+L5&qQ4dDH7TWL zakx)^Y_xL5Ex@ixwvMmflye?#s@Dvg+j2n*(8N8=ZW8E&dVkmV>I)e7%TO&5Hz^O> zPGk`au$#A(`GJoZ`@6OUHO=&XW!N^4)4hq;&(eCaKYR<>f==Rn#6A0ThapS_3a4>o!CKb@QiDB_oJ6IR&lKG|?k4?lg6 zc0N6dnMtNv5IFA?^nXD4fAaBfFF^~?>t86#%wk`yvdL>G000000002|Tfg`3!$P~l R#76)C002ovPDHLkV1laIm2dz6 delta 335 zcmcb`^onVMO1+_{i(^Q|oVPcl`3@-vv?iLoYx%3+x%bC~7N54pRa>7f`2YPH_cBZ8 z<+{sQ7!bg(aCR+YoaWNIGmGthG`rV54C%iq{@~`4Bb$G7?PLmhyEftblR0nnUX6|z=v_msoi7W-^u zm|f^C$sd#SF29Dm=}? z41{Admd%~8^5Du_6;H#QtB-YC?)8tqsk)$*_uk)fadj5WI?ml2Qa1lieYk|rzP4KZ z`?=k8fAXJvK>DwPZ;o8{-^7}+Rey@v6-T(jq diff --git a/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00000.png b/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00000.png new file mode 100644 index 0000000000000000000000000000000000000000..b82f82a69119261310141622ef5e217b1633e6fe GIT binary patch literal 452 zcmV;#0XzPQP)7y+8~Tb8SQ7Og#oN$})H^q7?=WHJ}v2(!Kf}S>|uU~Xgb5Q3HZUt<#a@19)?n-07 zR!+xyu~jJC3Rs1vUGHw#gt7&FHP%J92ZT$&+u|^`Kc}^58g%y!ri*?ODnX~Qv-QV4 z?#(!paIc}=6uXQ2O?Y}S;3fEzvZ9-WFX87Atykw0N4&&48+axFLu72u^E{5DPa1bP+bTQB+(Ie*k*I&~S6Se4 zlUsHC(S(1eRvtH0+jz?ihz9c1gHcITSkt(c&zJ!beNcbyp7E9$u+W2Y!^jKYz-P~Z zzVs{Ssx2WYB!mz`2q7PUdf{`g)q9?5BTDiDKt6mCz-NUNEPb(x`;~hTU@DIn~MUItXTjq5&83OgW0iJ z&IY?tQF8Lz#3U(DS-5b_{Wld6C0=Bf*rJx*tEJs7Y9>uB;(ZTL-x8+6*4q~{P79K8 z$V71;Nouc)LJZ_LoZ)Q?JX`3IyK`7|mNm(^x8)j@J^3vn0nu2?it8C*!v!wT!%-7; zX}FQ9!kmShaDt7ftx{VEG8>qx-9hMNdVd>^tLgpCFa}S;Rp?0uPG$^l#kv>$Zzz(J z=-2l1cm9<;`m-CJHozN4X1LA3f8p%_pBYG6F>f}d)TSYE3(L(Gt`pNI;of11tSt|X zs8?>-T0HQjRj!e^3b#a_s%$f4U`gh3K`N=zMji)Gxw)KvlC-~(UUf7@Oij8lG^{MV zO$ESR6O$yOn^TL@`tCr!Z=D5K^I?}Q;AeofR}r~czFEBRLMHy~l|8g_?Ob9;a^ zXstE5WI5Rjx#CrZMYVP|P+z(0cj}TUdaJL^ejzP39pzht}C z#=u?q)vk+b^1r>arqA7F*?-QUxSrpDS*tbMZntLIfdlF#&)WY6J&-KOyK{4sdiJm9 z>yNMhvfA#>gP+n{Yf56;+RU>Ueobaz_#nOg?uAo#TYsHc_RaeCC!Ku~XQ$nJ>TF*t zG4*(btH_W38&b<|biG&-9iX6nhC#41;@4d+W&tT?1IcF7hI^~_SU-`Ie5Yt%9mJN` z?eq0)%#>NLq?37UF1Ffyl>6pV$q4i@5@^`}ld18y-q%F7dnO=ZPgg&ebxsLQ07O5W ATL1t6 literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00003.png b/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00003.png new file mode 100644 index 0000000000000000000000000000000000000000..4f70c62e571f6c55ac48b7b75df8c37adf336855 GIT binary patch literal 923 zcmV;M17!S(P)WI^lcDF$7fb5=?h%MNt$*Q4~e7oj>Us&-3ip z$8pG8tZ>=Hn)35Jsf>Jzmh`rC0rq&DpF~}a)c%6q9Tx6{28aCFp}o&Mp**$5-$Y#| ztVzdl$PlBF^475NvXCmDx!hE>CsvY2zy%m1%5bJ*sB3f0JVUQg$+h7q2SkwM5zym~ z1$c7?)U5lE!ZnM{>EJB#M*v-~)-=$CB&}M3n%qjJ17vj%LQwfoL%N6dSfOM(;F@!0 z>#9pnYmKU6D2ui-cH;BSv4t&qA1=@C>v_-$%fg)HcIc{ z0^T}+GAYEj;BI5eu0Y>e4`mbqJr#jeW9J42w>8ZL$@d-R-l3CSAwXS-hm2xE=K7IT zsC+?_jUCE+Z7mK9Fb1W-NtmfJM?lx3TTwLVM!`bYU8S}XBNZY2tjjavS)i)|EU^mS zeFSfP9J!!RYwCBPod*o(waW(|n~gFL!amIwcR=r4w#iYB4`qeg|HFkILg_X3K7w?C zqSFPkmP#4cCr0ePaC!loJQIsJ^5Tcy_}Gc%n;4J37smd@@(5tt%>=uz^}&9oU0pud zfYHh2gIkf^yPgfzw`KT}K9gj7M^|De9VK`4{LW=zfikltX4Xirz?&K#Ppf zx1nYvn&uat8m1N^h9c0mgYIEuew@qFN3?SI_iI$%^~l}GIFlu_(;J0M2Y@F;L69|I z16kJB9pGI#hb1Y;c;1CB>r_Ku+uQ15{w^M$;%@m1nF6OAP!vT`zywc%a4U#)nS?Oa zN)5PmU9)az9VAJT0^To5`o(K2#Q}&@XBaPHG`j%4G|`{KYx$F@iX_};R$Skb&|69Z zmCD>WE8v+$^QSH|3nG_MaJ(W(P*bg<2cLrW{jZ3bGnNB9QWMi?Fx7)6AGf}U1;5oj zexZd6E=1&!S)~sYIbv|O{XnskUlKq7o(AP%&J4-6iCTkkI9xmW1DN?;H xUL~$&7WrbeSSNSGQ^~l8DF+lqQ53~P`~#8SOa>Ij7O4OL002ovPDHLkV1gCQu?zqJ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00004.png b/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..0a938aa72c4619ce080955e0df14b7e0cc322cf0 GIT binary patch literal 626 zcmV-&0*(ENP)1Qwjh800000>3qeCyYIU`U)RN} zNa1uBbHeZYu5GHShA7^ql@P*rpscF;1F01YGijYeoaIVe&fD z|JTF`#9SDuDq}8OCBWYuXkOgSts3ukpARv~5y`lf?9FtK<&3lE5h3W!9ASXR6Ui;o zA-UDv91((CjNJCsfF>ULL-Lvc000000000000000000000020gUvKw#kZ)_PZU6uP M07*qoM6N<$g4jkA6951J literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00005.png b/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..03481eb4015b64d00e3f60fb77dfa644b5a6f927 GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|@9hba4!+nDh2#AYZcrPebCi zH)?nOd)!Io*IUB4W`)DsPxbz6X@;)ahXp{opT>nTFQ4+MuxH~tEvcy%hHjF+ZMl6x z=6Q~OF?-83m!_R+J2J8ND?guqMO4; zzG&WS$=93fPAIG@jy?GP%A>uSl|{AY)l9S2AF;k8I=}azaKQ8_jpZ*U=YLb1ow_2f zI7Rj3@w1r!Xh6gvNe04j2Y-ZiBzZv!WWVE*3__A&u zbLFm_Jz~C{_UDDmozEBTo&CO!DJI?i&q0=o=haM4p3i#lnSbrs?+t+|A3w%**QMQe zsXje**XqqnC0`Y7xq13R^8-2g#PYJqKHsKiE}8X4|Cmg~BCg0swmF?&K%s{WF4r(7 WosKlEN-S*$346NwxvXB1wW27Bq9}@@xSbyZ(R?3kbxf`V`QY~9w^#sVp^K&sj zgFAN4kLsbt!%&sbg38;(bDZKi(JgA#lE(K6+dw5y-pAi4gRcgk22I6Y~}4v+MWSl{*Dyl;LX>m86~ zNl_FG`Qq`F^E;p>;%^sJt*;mw`}ineQD=)W zmBl(?Q&%6{K&O$b54N(sd5=F#sEqg66KejMYsnvcf?#^tq1BQYsOvy|+SxRkz zE}c+>8Zv75kOQiDyDU&oW&%aWTGza;ZRD)A&u$9eEPbMS0x>e9O^m7($tZbc1MZ=y zWGSYyca=s4;BHvD8wy5E_f+BDSY|S=;9jA$4JW@0m{Y{(UA>&^UYQ5cBy5>~LfOo- zC(z{I2iWF|26!~ka39FGxAoSj(jlu>I{#%R9CpgUs_n(+%H*qr7dv%iH%Sc zMNt$*@k+v9%5U08sU4oHg^@|ro>jqJhY}`AHChzdp$|oz=CpSerIb%Hb`W#xppHE5 z9lFG!p=^lmZYq=H`6x2R9?oqcvQ#Sb8?01CSbZHjv!Th0Y)he!e#&9fltAYKury8zr%0`A zXHyw-Ia9E)cf8|OuT*ly3=5w-#RZbA22pXu~8KS+;A{R;EU!2d{0Q)3Ml zP%$#rU@FqQ3EzgUq zQD#G}maLbfLu>vrmz$hMXu08JbkkTkaU>xIo3b!Is;TXQee`QRpZJA z^HOgS9<%@wmJs#7+N2-KtKHQub0+|T+;}D3{tN(nV_i}K00000000000000000000 aDf|GwIt}6W4A~|C0000^OwHJz}X(%dTWXzTZXJwNXK{;To(Vb|j4 zz0nh17O$>6>2|B`)sOtuM>|*EDSs-ztvY#k4sQwfg(>L1VZTb$^k2gEY z9O~SkPd)g;_F0yB^~86kTYmCIRdek2|EaZ2J%0Wtsi@hCI%k)!wm4@$Yb(QJM+SzB z$@ALsdcN=3vwHv0seXc=l9#Sm`oG|b$=P?mUMck(OGG-?O0*wo?OV*h`M=ZpnZeuR zBN~=`Io!yX%TG}+q0|HJ(^mA(mt4Jr{xw-S_<5TwYxf))&?-R1c`}Al~>1ZWZbT|k*=uhU*>@Y zQ#05B&oD9G3&nNzkxl$qvUeJ#6&ventAAqv;!kl`0BBMsbYF^R--0lS4#-{qFa&^O zrglTu()L@7!d)x%@~`nvQ@;5%mh5f+U%j($+znl*+>OvS`^s&&Y~X7fZBS=`aiFyz zZk90v##Ogkk43WFJ364cR?O_k-^G{kW%&%a`>rPb!zcc;XjIhBlvmF`X(hdHoWif8 zCYkIXBcz(_A4VVC#42=CCCT)`R-_jI000000000000000007wL7jUs+BNiEr|?-^=|56;;Fjs;-NoCg3u=$0a)!SvayqYcHR|`> z7v5g%I||okzk4*-Zb#Vqt460{4;I~HxX%7E=-5B+680~G`PqMO&S;ZW+v_Q`Y+-xx zg^TVH_vYU}`zcWLU5>r`q$`;~w=-lF2A@>&-nQQRHn-K|_~v~fh2~dwq)xvXx$@+- zP2LgFu?f+or{{0#`+D-wsr;aqA}X)f{^vF;kMP!<+hiAR|2d?d`N`wy4~}1{yfQa4 zTp-4IffS8@XY0Z)*urGXvP#QV88(YBle*q@1h+WV2qsk$008H6`U9 zy2eV@0b|XP+bk70^chV{rlj2cb>~f{DtHlP}P#A zX`1GVq_UyzIn2lcX-ZsyXd6J85{%>cS;mZ$fPI=?$`}H6`T=Rijj2`xH5VkGahQ9T zP7Z|tbs-)yh6$P9N7A42p-gsmDIc}9IW52#lmc&prplcGQ;%)M&|n({3qyB})=HdI zg!Hp5&&VgyMgz1Yt9|ztqU{OffE%6<9uX|}in=HRkJj@*}w z1|7er3w`lqw>Vk_83IFR2xKpnGHgto*nQyg0(Myw%Q$lILvMWS!tzb5=idWkf3Q6R z*m1MK?qhv$J?c=m4=%tNI&+1Q}mY>AsS zvKw$7%g$yLI#0s$8hH?#wb)5(sfPx5D4OV_A~+|cej}o_DoDea&LcTdkgOq+&UBR+ z0^r3Yq%&0Lb_%q}NPQdXMxtweqt-IDkTH~j_8oK&qw@1~mOi45!@pUh@}VkcBjZe# zXm4)}Djfj6AxeVm0T)nZecu7mopV`I@)|F?(N(?Gu=n<3cQbz$w=Z#Je<9QC(GF;u zrfFc3Z-Q_uh;^BSaMel;xOH2zZD=1PNm2qnE=Kw#YOBNnh*LG32Qivm03Vv@&*ioJ z$y8+$?wXa*wbq%}x9R2tQ^t053wV00000 LNkvXXu0mjf%(A)! literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00012.png b/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00012.png new file mode 100644 index 0000000000000000000000000000000000000000..e3d5c8e7fe37ef5ccfe5a3a531bc9b9d7638d5ec GIT binary patch literal 636 zcmV-?0)zdDP)5%2fAK3~@r z7dWcAQOXvwZEKy{1w~X}hf0{{6>MKkyot!6j@%8^#g1}?miKkyHh%}(o3Md%|7;!# z7rfH+Vtd{f-a)F*38_41W=ChhjT8uaa8+ve`e{Vxmf|O=kk#v4qfE?xd5F${8ecTl zBqkP3Y4|*a)JUhIewr$Emat$3^jp~~=mxV^GOH+FXM&Ie*e#3Fzm2FJe0{} zsFXe&6=LQi{l;Wo*7OT=DoaiR5g|>2Xdlj`$S8I$$deRoV!3~o!e=NGIciD$=`_jg zMQ@>Wp`&TB==_k<-_13I8btr<5~wIDP^zXBdLEpGQZE!EJz7}iv!Q*|M2Mtzb1p=i za*o?S$n0icE;ff{4xmeIv+O2gegZSwX7-|WfK%nr4r6|((G!rmiqUG*EI)Z`I`$!L z$oZE17fsh*^aWDe2{h}$uLF`t|KXW*eocPzh$JHpHGGhOG05=2Rz7fe{g?hesj%0w zhoEPE*qTa7K5PCpF$kLrYhD#!2v-S+_W`6RHgl^=Tl4u4mmHCdi)3%6HI_5(o<}S} zPv!^*+>zLPMLHz6x|<`Gpdmb8`|5(mUiz2hH30ws00000000000000000000a5z6e WjfVv^SKvwj0000fSu}ln z?{S6Y_oL6QzwL9}fA0U>t%W92GN=D7*d2cRZqbA34SLd!ya8o`$1X+2aIOAXIB&h! z6q5zL2QrViraXz<)@yjIENR#G0=@mB3(n^+4qE+FDZslky;lC=E~c*a`?PZcYxw-M z57=GYW@J)jzxmeTxF_3r-@bFq)_?dxFaGGaH?NoHIA@o4OnZ0xYUP~#k}#%c?2HTv zw!OA(Z(nUYvi(-WTS0w)ynsRfy*xzD{mHK&pIoCZ-m)*W_?!jyQ8|*9C zX1>_1*)r>ewK9A8BCflg-#^Ud&!yX%TG}+q0|HJ(^mA(mt4Jr{xw-S_<5TwYxf))&?-R1c`}Al~>1ZWZbT|k*=uhU*>@Y zQ#05B&oD9G3&nNzkxl$qvUeJ#6&ventAAqv;!kl`0BBMsbYF^R--0lS4#-{qFa&^O zrglTu()L@7!d)x%@~`nvQ@;5%mh5f+U%j($+znl*+>OvS`^s&&Y~X7fZBS=`aiFyz zZk90v##Ogkk43WFJ364cR?O_k-^G{kW%&%a`>rPb!zcc;XjIhBlvmF`X(hdHoWif8 zCYkIXBcz(_A4VVC#42=CCCT)`R-_jI000000000000000007wL7jUs+eD12qAuR6R z^IT=twBKCZh2pa=)FEBh)tB^5bcr@1n$PfiiIo=a&5Rw9XwSY`%|mBD_$peNJ&_F{ z;Lj2(L(D;RJO^y6qsQZn)7VbX-y-0bSTh4|yg}g&B0;;<$!VmW2IVzhg<0sarUS&d zzMhE6t0wQ!B_d9D3e(i8NvT;j14JKuoQ27H^@ z_fK!bcUaqrhS<-ECB-b)i@4{v_hJSU*AyjvRPU`wUxByT-D%<5neTvAkq|-%`Ci6q zXdvSR^<%BEh8M|UZ7zqqQqM^2TKmEsRmchnVyE0b%x@czNDwbNLZo@&C-A~^YG`j1 z96@H`aO5u3Baxx%HOYGKeSrQzQIL9NMfNjaXmNY!sH5JAFwx<~F(JHM74N_Q5CND)dPPNoF6sicW9w zY$(2y1D}KX{aGL)nh&c%Nll50V=yat35ksqNePPJl=5WMg8i(8Yz(@*rm)^XQba*e z1np$IXWnBOGwC}{rUc_cXkI!*Qxx->r>dD>gUY?N#;~C`w80sGjGNpH2++%}F8LvM z_-#TP0@MMkCfeEwYpPN;7n73mj7_Sc9|n3MBW`8q^KU>0whayJnC{GmS%NTv;TgwZ zzRcKnPbP#ALJ0XUh}6)mqz68F;7>h4%|OD`CBb`qU4mTojMkeVyZc7!YY}GNpjR2u zmu7@auufbpeJ~4DG|e$`4+Kvy3k<$1ua5w|m$lx=u@DZ+il$c>|2iC%T8;&y&FY=Q zc))AdL^Gg#)bdl+w9JVtEJNd7h08f@mLk0bfh&M{dafTGTD3+DisZ(@q@=3YGUC0q zHf}ncn>e2LFR<_uq7Il>G!I~0h~}R5iA)i1LpgtVAXvC`9)ipeLI@#*5b`7V3s2=m U<9@-?xBvhE07*qoM6N<$g3a=#P5=M^ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00016.png b/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00016.png new file mode 100644 index 0000000000000000000000000000000000000000..0782386f8bcee308930e72c139d5f652069a84aa GIT binary patch literal 490 zcmVXlGsZJQ4!A=hmQeOxo1@I59 zj-c>4VAD1E1J74#$Ep3gdO{~YGvHPm5%wrc(sNEuqq5SL@?HZIQRUMCVqC{h;$oMX zo7bCasipgoxLu9r)pLT|3@F#Mv)VJ&(%^Lu<RWEuYy+UoXyFbW#xpaU_O8@}x<#rm{tG35B$NGZlvI=b&FF)Zcnh8MkYxH`Dbfo70000000000 g00000001oW3)AwBnxSYgdH?_b07*qoM6N<$f?7@HNdN!< literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00017.png b/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00017.png new file mode 100644 index 0000000000000000000000000000000000000000..518993a1e4c73387c538a2f58266982a360d8237 GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*Qg1z7978JRyuIO=cUVDyHDLKE z-yQ!a?(i0$Ca|^RvWe#5i1%v^rV0E?X8yK~k>Nne_7|Lgyw+~rud~$U?32guE(%v1 zoLZD`B>G@My>3))+g$D6UGolbXRXz}zchF(w>GZxS z@rRxl)z-&uVA!eQV8F_;jG=pG0h`5~Z+vh4W#zBiwAo4i|F!%9s|`=&ugg4(ru|FF p5;&*dAj^PbX0`eJS}y70`%8T>n^%dV>bk>TM}C(cF@9*Jbj^5WTF;gKGIO)y zjlJ*9*PE9W@jdm~5r5wEu#TD8y6+Pnh8_O<-8#^Bx$bnOy3REWn^jXeAFev)TXe-F zTky7j?62N_lL@yzwx7O!=&4O1W7^|)7fcr%%i5*3D%0_G;tl)cpW3Io3*7!cdXW1o sV&B=e%knGs=l(tai5cc!1_ryAe9w9}zGZ%X&;TUp>FVdQ&MBb@086r+N&o-= literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00019.png b/tests_zemu/snapshots/x-1-tr-in-1-spend-2-sh-out/00019.png new file mode 100644 index 0000000000000000000000000000000000000000..6958c981b015b1ea976e5ba479ae4a4b4a5473c4 GIT binary patch literal 496 zcmV?9!o00000006v+FY5tpkW$*Z zKgRe+Be{`p>jAtS3Sf+U0b-02R`ZD;>j9jn);&Mo-6T99zXH?|UIF!d*L5M`0%VHx zivAPSEl6GcAiV-~H{KfI0%U6US1g9M1^okrS3sWc>P947fQ+v687#;c3IG7`Uflg8 zIXu-|$=d4EszclzL;KFwt#-HIt^kF8DKz=>FpmC|QJ3r9%IUWNnMT?ESFrq2N@@5~ zeZ^rsuqbGmF2=wj|IrgEoJ^jiMgL~XF+A!Hrr`7@p><< zpZ0GLA{GPy008h#Y|_x%4~vsrXqfJ;#7fU#&<9L(x%kN|ZSb(aGyH3XeIwR%UcDAW zb>`LD&5jH)iz51bxI_p|&U3;a7So7fFa0nE1z5~D=j!YV*SMUYyvW9a%D&g+_{}}f z4$6k7SHR)ST{oHCul8Bdr&qx6%vVR7!{D>rf6lV!p(!kJxFuZI)MQV#KZf4O>3)I* mag2u}BLDyZ00000T;&%{Hu>xcnlBXq00007y+8~Tb8SQ7Og#oN$})H^q7?=WHJ}v2(!Kf}S>|uU~Xgb5Q3HZUt<#a@19)?n-07 zR!+xyu~jJC3Rs1vUGHw#gt7&FHP%J92ZT$&+u|^`Kc}^58g%y!ri*?ODnX~Qv-QV4 z?#(!paIc}=6uXQ2O?Y}S;3fe*_F&^rf zizYiMrpY-IGvyEU2%(k7kh_R0Gr*qh8IOYlI%B_P^U8Lk3>c&Y^|SVdD>Fb`7PPzF zBE-|z{-s9aMj2onlgM&Zdh%VDG$v(JY3?)t00000;4Y989y!DLk977pr2{`u@)rJ} zQ=g7EWUij4^ALPK;|^$!quu2fhQdu{MjX%~&Om(=t}PXMzt<|7(Q%qmNWHTb7b=nV zHIlpHtTmOqG-Hz>KGlphX9g6B{8oJt%_o1ON0ao|A3WVTL$x3+&^jGnVFZ*t_*v(f zTaV)w^l0K_elc!}awD~x={{ImUR_m;n^JMK2RZMGw!X05S-am83N!=69V`?AWc8?R zp^c;MlQq$gh0V!3_gk*N@nBQ8hCi-~nrE{wg@r-@jE_O)mGl_Pe!)?7FdW~y%BrbYWCchscT3P@~@i_`1Z)mSQ2(^I!+XXZ7IX;%Up$7CPh1x^@$8YPsjZ_i>}Y vqyHyx000000000000000000000AL}%viS$@qb8%B00000NkvXXu0mjf5@$(o literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00002.png b/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00002.png new file mode 100644 index 0000000000000000000000000000000000000000..a7265ebce768769599119b7af8b7f2d39dd60048 GIT binary patch literal 362 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|=-#ba4!+nDh2Vbl+hG9*2ut zX3eem@13OocY$k1x{2bp&UZgo%@ONReDKTysEMIee{P-=hpUUryBDr5ul6OCSK54y zV^3B-ef*gd$HJL&ReMbyS8n)s=!-hnzbjRcoZnLMIdqOvy4069xwZ}KZ5B2k zS=!U>G4Joe8j*u9ndbK;>TXs~sA<1${^;J6ire@1+AwD4Zv5$z=I^lAm}i~d%R}l* zjZCEG&)MtzWI^lcDF$7fb5=?h%MNt$*Q4~e7oj>Us&-3ip z$8pG8tZ>=Hn)35Jsf>Jzmh`rC0rq&DpF~}a)c%6q9Tx6{28aCFp}o&Mp**$5-$Y#| ztVzdl$PlBF^475NvXCmDx!hE>CsvY2zy%m1%5bJ*sB3f0JVUQg$+h7q2SkwM5zym~ z1$c7?)U5lE!ZnM{>EJB#M*v-~)-=$CB&}M3n%qjJ17vj%LQwfoL%N6dSfOM(;F@!0 z>#9pnYmKU6D2ui-cH;BSv4t&qA1=@C>v_-$%fg)HcIc{ z0^T}+GAYEj;BI5eu0Y>e4`mbqJr#jeW9J42w>8ZL$@d-R-l3CSAwXS-hm2xE=K7IT zsC+?_jUCE+Z7mK9Fb1W-NtmfJM?lx3TTwLVM!`bYU8S}XBNZY2tjjavS)i)|EU^mS zeFSfP9J!!RYwCBPod*o(waW(|n~gFL!amIwcR=r4w#iYB4`qeg|HFkILg_X3K7w?C zqSFPkmP#4cCr0ePaC!loJQIsJ^5Tcy_}Gc%n;4J37smd@@(5tt%>=uz^}&9oU0pud zfYHh2gIkf^yPgfzw`KT}K9gj7M^|De9VK`4{LW=zfikltX4Xirz?&K#Ppf zx1nYvn&uat8m1N^h9c0mgYIEuew@qFN3?SI_iI$%^~l}GIFlu_(;J0M2Y@F;L69|I z16kJB9pGI#hb1Y;c;1CB>r_Ku+uQ15{w^M$;%@m1nF6OAP!vT`zywc%a4U#)nS?Oa zN)5PmU9)az9VAJT0^To5`o(K2#Q}&@XBaPHG`j%4G|`{KYx$F@iX_};R$Skb&|69Z zmCD>WE8v+$^QSH|3nG_MaJ(W(P*bg<2cLrW{jZ3bGnNB9QWMi?Fx7)6AGf}U1;5oj zexZd6E=1&!S)~sYIbv|O{XnskUlKq7o(AP%&J4-6iCTkkI9xmW1DN?;H xUL~$&7WrbeSSNSGQ^~l8DF+lqQ53~P`~#8SOa>Ij7O4OL002ovPDHLkV1gCQu?zqJ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00004.png b/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00004.png new file mode 100644 index 0000000000000000000000000000000000000000..0a938aa72c4619ce080955e0df14b7e0cc322cf0 GIT binary patch literal 626 zcmV-&0*(ENP)1Qwjh800000>3qeCyYIU`U)RN} zNa1uBbHeZYu5GHShA7^ql@P*rpscF;1F01YGijYeoaIVe&fD z|JTF`#9SDuDq}8OCBWYuXkOgSts3ukpARv~5y`lf?9FtK<&3lE5h3W!9ASXR6Ui;o zA-UDv91((CjNJCsfF>ULL-Lvc000000000000000000000020gUvKw#kZ)_PZU6uP M07*qoM6N<$g4jkA6951J literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00005.png b/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00005.png new file mode 100644 index 0000000000000000000000000000000000000000..84e36300642d3b4266fa3c504c6535cf0d05c6f3 GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|`hsba4!+nDh2#E?=_(PebDN zH)?nOd)!I2_u+WBIP9p{$=`pJRwP7SFU`9R)WlFMzrHuQCsW301;6OzLsp6{HCt+GT)`Ing4fMblBY2Ty@LEz6Ti<*OvcgG1)Klz9RPHP0JdlSCfoBaL)ainExr$ z`RT05;gd}ArcHm8@i0XC)*fM*iY;?nPpx3N6VJlX@RC!v{n_1_PtP=UZO{3%@Sei{ zu==Yv8+#@1w{czjz4pr{mNo4x7Rrq0JPxGrR5dKRt$pm&{(`5)%W|Fhuf^}@p66hG z!sWh?@)ei+5|$raL_Zz*zCryEH^{?Cz~&jh<4ljdh2QU71BrUN`njxgN@xNAewCX` literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00006.png b/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00006.png new file mode 100644 index 0000000000000000000000000000000000000000..69738ba21eef024c6a845c8982aca1d2540c9043 GIT binary patch literal 944 zcmV;h15f;kP)B1wW27Bq9}@@xSbyZ(R?3kbxf`V`QY~9w^#sVp^K&sj zgFAN4kLsbt!%&sbg38;(bDZKi(JgA#lE(K6+dw5y-pAi4gRcgk22I6Y~}4v+MWSl{*Dyl;LX>m86~ zNl_FG`Qq`F^E;p>;%^sJt*;mw`}ineQD=)W zmBl(?Q&%6{K&O$b54N(sd5=F#sEqg66KejMYsnvcf?#^tq1BQYsOvy|+SxRkz zE}c+>8Zv75kOQiDyDU&oW&%aWTGza;ZRD)A&u$9eEPbMS0x>e9O^m7($tZbc1MZ=y zWGSYyca=s4;BHvD8wy5E_f+BDSY|S=;9jA$4JW@0m{Y{(UA>&^UYQ5cBy5>~LfOo- zC(z{I2iWF|26!~ka39FGxAoSj(jlu>I{#%R9CpgUs_n(+%H*qr7dv%iH%Sc zMNt$*@k+v9%5U08sU4oHg^@|ro>jqJhY}`AHChzdp$|oz=CpSerIb%Hb`W#xppHE5 z9lFG!p=^lmZYq=H`6x2R9?oqcvQ#Sb8?01CSbZHjv!Th0Y)he!e#&9fltAYKury8zr%0`A zXHyw-Ia9E)cf8|OuT*ly3=5w-#RZbA22pXu~8KS+;A{R;EU!2d{0Q)3Ml zP%$#rU@FqQ3EzgUq zQD#G}maLbfLu>vrmz$hMXu08JbkkTkaU>xIo3b!Is;TXQee`QRpZJA z^HOgS9<%@wmJs#7+N2-KtKHQub0+|T+;}D3{tN(nV_i}K00000000000000000000 aDf|GwIt}6W4A~|C0000k_k44--OXtS&(`VA7ynmhIj~!actwhVw7y%Iqtha={_LBsA8FIJO)?A5-K!JR zJm=o_Y17v%{TkMPKU&+*Cp9Yc-;MRXPd6D};B^R}d2q&pH%T|m=3HG_VD4X7y>`hG z&Qc+9kvBuUfxP+h^mCH7>>r zY8IWIdT{04=Xw0qQ}d*6-EH0$cj$Eb&sEdX>+MseZ@p5qxx0O}#W{PcY=%B|Mur9c zRZ_c(zE!4%ZeOys-^cr#>p$O{nX}@*UUijxo{+-(HBrXM+^X;1+5atamzSSiCeNV$ zGPovaBMa{_hC4k8x6+o!-YmV=nCJPU!0VgqJ}32mBHhp4%<@$GZ8SAxa;@R91eLIb m6D#=pU8)#?!GZ)DPVZs7HOKCX{z@J_kg%t#pUXO@geCy>P^}*T literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00009.png b/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00009.png new file mode 100644 index 0000000000000000000000000000000000000000..7b0240e3ac1e6f079fec0ee251736e5be529245a GIT binary patch literal 470 zcmV;{0V)28P)!yX%TG}+q0|HJ(^mA(mt4Jr{xw-S_<5TwYxf))&?-R1c`}Al~>1ZWZbT|k*=uhU*>@Y zQ#05B&oD9G3&nNzkxl$qvUeJ#6&ventAAqv;!kl`0BBMsbYF^R--0lS4#-{qFa&^O zrglTu()L@7!d)x%@~`nvQ@;5%mh5f+U%j($+znl*+>OvS`^s&&Y~X7fZBS=`aiFyz zZk90v##Ogkk43WFJ364cR?O_k-^G{kW%&%a`>rPb!zcc;XjIhBlvmF`X(hdHoWif8 zCYkIXBcz(_A4VVC#42=CCCT)`R-_jI000000000000000007wL7jUs+BNiEr|?-^=|56;;Fjs;-NoCg3u=$0a)!SvayqYcHR|`> z7v5g%I||okzk4*-Zb#Vqt460{4;I~HxX%7E=-5B+680~G`PqMO&S;ZW+v_Q`Y+-xx zg^TVH_vYU}`zcWLU5>r`q$`;~w=-lF2A@>&-nQQRHn-K|_~v~fh2~dwq)xvXx$@+- zP2LgFu?f+or{{0#`+D-wsr;aqA}X)f{^vF;kMP!<+hiAR|2d?d`N`wy4~}1{yfQa4 zTp-4IffS8@XY0Z)*urGXvP#QV88(YBle*q@1h+WV2qsk$008H6`U9 zy2eV@0b|XP+bk70^chV{rlj2cb>~f{DtHlP}P#A zX`1GVq_UyzIn2lcX-ZsyXd6J85{%>cS;mZ$fPI=?$`}H6`T=Rijj2`xH5VkGahQ9T zP7Z|tbs-)yh6$P9N7A42p-gsmDIc}9IW52#lmc&prplcGQ;%)M&|n({3qyB})=HdI zg!Hp5&&VgyMgz1Yt9|ztqU{OffE%6<9uX|}in=HRkJj@*}w z1|7er3w`lqw>Vk_83IFR2xKpnGHgto*nQyg0(Myw%Q$lILvMWS!tzb5=idWkf3Q6R z*m1MK?qhv$J?c=m4=%tNI&+1Q}mY>AsS zvKw$7%g$yLI#0s$8hH?#wb)5(sfPx5D4OV_A~+|cej}o_DoDea&LcTdkgOq+&UBR+ z0^r3Yq%&0Lb_%q}NPQdXMxtweqt-IDkTH~j_8oK&qw@1~mOi45!@pUh@}VkcBjZe# zXm4)}Djfj6AxeVm0T)nZecu7mopV`I@)|F?(N(?Gu=n<3cQbz$w=Z#Je<9QC(GF;u zrfFc3Z-Q_uh;^BSaMel;xOH2zZD=1PNm2qnE=Kw#YOBNnh*LG32Qivm03Vv@&*ioJ z$y8+$?wXa*wbq%}x9R2tQ^t053wV00000 LNkvXXu0mjf%(A)! literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00012.png b/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00012.png new file mode 100644 index 0000000000000000000000000000000000000000..e3d5c8e7fe37ef5ccfe5a3a531bc9b9d7638d5ec GIT binary patch literal 636 zcmV-?0)zdDP)5%2fAK3~@r z7dWcAQOXvwZEKy{1w~X}hf0{{6>MKkyot!6j@%8^#g1}?miKkyHh%}(o3Md%|7;!# z7rfH+Vtd{f-a)F*38_41W=ChhjT8uaa8+ve`e{Vxmf|O=kk#v4qfE?xd5F${8ecTl zBqkP3Y4|*a)JUhIewr$Emat$3^jp~~=mxV^GOH+FXM&Ie*e#3Fzm2FJe0{} zsFXe&6=LQi{l;Wo*7OT=DoaiR5g|>2Xdlj`$S8I$$deRoV!3~o!e=NGIciD$=`_jg zMQ@>Wp`&TB==_k<-_13I8btr<5~wIDP^zXBdLEpGQZE!EJz7}iv!Q*|M2Mtzb1p=i za*o?S$n0icE;ff{4xmeIv+O2gegZSwX7-|WfK%nr4r6|((G!rmiqUG*EI)Z`I`$!L z$oZE17fsh*^aWDe2{h}$uLF`t|KXW*eocPzh$JHpHGGhOG05=2Rz7fe{g?hesj%0w zhoEPE*qTa7K5PCpF$kLrYhD#!2v-S+_W`6RHgl^=Tl4u4mmHCdi)3%6HI_5(o<}S} zPv!^*+>zLPMLHz6x|<`Gpdmb8`|5(mUiz2hH30ws00000000000000000000a5z6e WjfVv^SKvwj0000fSu}ln z?{S6Y_oL6QzwL9}fA0U>t%W92GN=D7*d2cRZqbA34SLd!ya8o`$1X+2aIOAXIB&h! z6q5zL2QrViraXz<)@yjIENR#G0=@mB3(n^+4qE+FDZslky;lC=E~c*a`?PZcYxw-M z57=GYW@J)jzxmeTxF_3r-@bFq)_?dxFaGGaH?NoHIA@o4OnZ0xYUP~#k}#%c?2HTv zw!OA(Z(nUYvi(-WTS0w)ynsRfy*xzD{mHK&pIoCZ-m)*W_?!jyQ8|*9C zX1>_1*)r>ewK9A8BCflg-#^Ud&!yX%TG}+q0|HJ(^mA(mt4Jr{xw-S_<5TwYxf))&?-R1c`}Al~>1ZWZbT|k*=uhU*>@Y zQ#05B&oD9G3&nNzkxl$qvUeJ#6&ventAAqv;!kl`0BBMsbYF^R--0lS4#-{qFa&^O zrglTu()L@7!d)x%@~`nvQ@;5%mh5f+U%j($+znl*+>OvS`^s&&Y~X7fZBS=`aiFyz zZk90v##Ogkk43WFJ364cR?O_k-^G{kW%&%a`>rPb!zcc;XjIhBlvmF`X(hdHoWif8 zCYkIXBcz(_A4VVC#42=CCCT)`R-_jI000000000000000007wL7jUs+eD12qAuR6R z^IT=twBKCZh2pa=)FEBh)tB^5bcr@1n$PfiiIo=a&5Rw9XwSY`%|mBD_$peNJ&_F{ z;Lj2(L(D;RJO^y6qsQZn)7VbX-y-0bSTh4|yg}g&B0;;<$!VmW2IVzhg<0sarUS&d zzMhE6t0wQ!B_d9D3e(i8NvT;j14JKuoQ27H^@ z_fK!bcUaqrhS<-ECB-b)i@4{v_hJSU*AyjvRPU`wUxByT-D%<5neTvAkq|-%`Ci6q zXdvSR^<%BEh8M|UZ7zqqQqM^2TKmEsRmchnVyE0b%x@czNDwbNLZo@&C-A~^YG`j1 z96@H`aO5u3Baxx%HOYGKeSrQzQIL9NMfNjaXmNY!sH5JAFwx<~F(JHM74N_Q5CND)dPPNoF6sicW9w zY$(2y1D}KX{aGL)nh&c%Nll50V=yat35ksqNePPJl=5WMg8i(8Yz(@*rm)^XQba*e z1np$IXWnBOGwC}{rUc_cXkI!*Qxx->r>dD>gUY?N#;~C`w80sGjGNpH2++%}F8LvM z_-#TP0@MMkCfeEwYpPN;7n73mj7_Sc9|n3MBW`8q^KU>0whayJnC{GmS%NTv;TgwZ zzRcKnPbP#ALJ0XUh}6)mqz68F;7>h4%|OD`CBb`qU4mTojMkeVyZc7!YY}GNpjR2u zmu7@auufbpeJ~4DG|e$`4+Kvy3k<$1ua5w|m$lx=u@DZ+il$c>|2iC%T8;&y&FY=Q zc))AdL^Gg#)bdl+w9JVtEJNd7h08f@mLk0bfh&M{dafTGTD3+DisZ(@q@=3YGUC0q zHf}ncn>e2LFR<_uq7Il>G!I~0h~}R5iA)i1LpgtVAXvC`9)ipeLI@#*5b`7V3s2=m U<9@-?xBvhE07*qoM6N<$g3a=#P5=M^ literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00016.png b/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00016.png new file mode 100644 index 0000000000000000000000000000000000000000..0782386f8bcee308930e72c139d5f652069a84aa GIT binary patch literal 490 zcmVXlGsZJQ4!A=hmQeOxo1@I59 zj-c>4VAD1E1J74#$Ep3gdO{~YGvHPm5%wrc(sNEuqq5SL@?HZIQRUMCVqC{h;$oMX zo7bCasipgoxLu9r)pLT|3@F#Mv)VJ&(%^Lu<RWEuYy+UoXyFbW#xpaU_O8@}x<#rm{tG35B$NGZlvI=b&FF)Zcnh8MkYxH`Dbfo70000000000 g00000001oW3)AwBnxSYgdH?_b07*qoM6N<$f?7@HNdN!< literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00017.png b/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00017.png new file mode 100644 index 0000000000000000000000000000000000000000..518993a1e4c73387c538a2f58266982a360d8237 GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h5*Qg1z7978JRyuIO=cUVDyHDLKE z-yQ!a?(i0$Ca|^RvWe#5i1%v^rV0E?X8yK~k>Nne_7|Lgyw+~rud~$U?32guE(%v1 zoLZD`B>G@My>3))+g$D6UGolbXRXz}zchF(w>GZxS z@rRxl)z-&uVA!eQV8F_;jG=pG0h`5~Z+vh4W#zBiwAo4i|F!%9s|`=&ugg4(ru|FF p5;&*dAj^PbX0`eJS}y70`%8T>n^%dV>bk>TM}C(cF@9*Jbj^5WTF;gKGIO)y zjlJ*9*PE9W@jdm~5r5wEu#TD8y6+Pnh8_O<-8#^Bx$bnOy3REWn^jXeAFev)TXe-F zTky7j?62N_lL@yzwx7O!=&4O1W7^|)7fcr%%i5*3D%0_G;tl)cpW3Io3*7!cdXW1o sV&B=e%knGs=l(tai5cc!1_ryAe9w9}zGZ%X&;TUp>FVdQ&MBb@086r+N&o-= literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00019.png b/tests_zemu/snapshots/x-1-tr-out-1-spend-2-sh-out/00019.png new file mode 100644 index 0000000000000000000000000000000000000000..6958c981b015b1ea976e5ba479ae4a4b4a5473c4 GIT binary patch literal 496 zcmV?9!o00000006v+FY5tpkW$*Z zKgRe+Be{`p>jAtS3Sf+U0b-02R`ZD;>j9jn);&Mo-6T99zXH?|UIF!d*L5M`0%VHx zivAPSEl6GcAiV-~H{KfI0%U6US1g9M1^okrS3sWc>P947fQ+v687#;c3IG7`Uflg8 zIXu-|$=d4EszclzL;KFwt#-HIt^kF8DKz=>FpmC|QJ3r9%IUWNnMT?ESFrq2N@@5~ zeZ^rsuqbGmF2=wj|IrgEoJ^jiMgL~XF+A!Hrr`7@p><< zpZ0GLA{GPy008h#Y|_x%4~vsrXqfJ;#7fU#&<9L(x%kN|ZSb(aGyH3XeIwR%UcDAW zb>`LD&5jH)iz51bxI_p|&U3;a7So7fFa0nE1z5~D=j!YV*SMUYyvW9a%D&g+_{}}f z4$6k7SHR)ST{oHCul8Bdr&qx6%vVR7!{D>rf6lV!p(!kJxFuZI)MQV#KZf4O>3)I* mag2u}BLDyZ00000T;&%{Hu>xcnlBXq0000m@pc^ICczva_;%+q;Y}~2#^y3000000N>>2?gku?Qabeh z7~`atT zJ?5X_Jq3C1{or~A_+5A{#{tM}_V*YJ-zn$;9M6C}-n}Ox#{tOvc6~+)x)}igfN#-m z8RQ;Gc6T+O)Y|NBRY6Kgr!hNHN}umCFZqyb+4jiIfTFarTXBmqcFWQ2i&gK7jJ`4t z)DL1+&$ds&!fT9o|7Bm%nFqGDq5-J7yW4kz4pqzhRy9@>4`S~O)zZaPFN+sP+Z9+b z0NFUDj4#w2`=NIuaw(_`+iE$@N!+f?-R;dY9uxxr06gBrTF{^fvs<633PIz8@z!(s z_8WIUty^N6daj;+4k-6f4uS3cAF9x9U4E{!w%)(&4551x&IHQ|*HRC_8r>81n%GMR zV8M^B2J6b2EF>L(HFkeuJvA8W0hl@r-<(@h)6w)@Q2S)DEo2_8HlMh>EvANzO{Z-( j^;7@=00000;5q&QSBwC@5*P^100000NkvXXu0mjf0ZZRO literal 0 HcmV?d00001 diff --git a/tests_zemu/snapshots/x-mainmenu/00004.png b/tests_zemu/snapshots/x-mainmenu/00004.png index fb347713aa626aaf6c6fd0d202f6f19360bc1ab2..07377e48ed4f4dc10b17900c3ae5d3bd48526b1d 100644 GIT binary patch delta 319 zcmV-F0l@z10@?zQB!3}EL_t(|obB0NZiOHa22k5{C+vSo?4=JTgaBfbdzJC_oHu`C zzQIv(+W-In00000fJ=O>zJONFxnG%5dhvv>oZ(VGt1qBcClx%qw+L5&qQ4dDH7TWL zakx)^Y_xL5Ex@ixwvMmflye?#s@Dvg+j2n*(8N8=ZW8E&dVkmV>I)e7%TO&5Hz^O> zPGk`au$#A(`GJoZ`@6OUHO=&XW!N^4)4hq;&(eCaKYR<>f==Rn#6A0ThapS_3a4>o!CKb@QiDB_oJ6IR&lKG|?k4?lg6 zc0N6dnMtNv5IFA?^nXD4fAaBfFF^~?>t86#%wk`yvdL>G000000002|Tfg`3!$P~l R#76)C002ovPDHLkV1laIm2dz6 delta 335 zcmcb`^onVMO1+_{i(^Q|oVPcl`3@-vv?iLoYx%3+x%bC~7N54pRa>7f`2YPH_cBZ8 z<+{sQ7!bg(aCR+YoaWNIGmGthG`rV54C%iq{@~`4Bb$G7?PLmhyEftblR0nnUX6|z=v_msoi7W-^u zm|f^C$sd#SF29Dm=}? z41{Admd%~8^5Du_6;H#QtB-YC?)8tqsk)$*_uk)fadj5WI?ml2Qa1lieYk|rzP4KZ z`?=k8fAXJvK>DwPZ;o8{-^7}+Rey@v6-T(jq diff --git a/tests_zemu/snapshots/x-mainmenu/00010.png b/tests_zemu/snapshots/x-mainmenu/00010.png index fb347713aa626aaf6c6fd0d202f6f19360bc1ab2..07377e48ed4f4dc10b17900c3ae5d3bd48526b1d 100644 GIT binary patch delta 319 zcmV-F0l@z10@?zQB!3}EL_t(|obB0NZiOHa22k5{C+vSo?4=JTgaBfbdzJC_oHu`C zzQIv(+W-In00000fJ=O>zJONFxnG%5dhvv>oZ(VGt1qBcClx%qw+L5&qQ4dDH7TWL zakx)^Y_xL5Ex@ixwvMmflye?#s@Dvg+j2n*(8N8=ZW8E&dVkmV>I)e7%TO&5Hz^O> zPGk`au$#A(`GJoZ`@6OUHO=&XW!N^4)4hq;&(eCaKYR<>f==Rn#6A0ThapS_3a4>o!CKb@QiDB_oJ6IR&lKG|?k4?lg6 zc0N6dnMtNv5IFA?^nXD4fAaBfFF^~?>t86#%wk`yvdL>G000000002|Tfg`3!$P~l R#76)C002ovPDHLkV1laIm2dz6 delta 335 zcmcb`^onVMO1+_{i(^Q|oVPcl`3@-vv?iLoYx%3+x%bC~7N54pRa>7f`2YPH_cBZ8 z<+{sQ7!bg(aCR+YoaWNIGmGthG`rV54C%iq{@~`4Bb$G7?PLmhyEftblR0nnUX6|z=v_msoi7W-^u zm|f^C$sd#SF29Dm=}? z41{Admd%~8^5Du_6;H#QtB-YC?)8tqsk)$*_uk)fadj5WI?ml2Qa1lieYk|rzP4KZ z`?=k8fAXJvK>DwPZ;o8{-^7}+Rey@v6-T(jq diff --git a/tests_zemu/tests/zcashtool.test.ts b/tests_zemu/tests/zcashtool.test.ts index deebfd2a..5cc9dbc5 100644 --- a/tests_zemu/tests/zcashtool.test.ts +++ b/tests_zemu/tests/zcashtool.test.ts @@ -267,7 +267,7 @@ describe('Zcashtool tests', function () { const req = await reqinit console.log(req) - expect(req.return_code).toEqual(0x9000) // IDA: FAILS HERE + expect(req.return_code).toEqual(0x9000) expect(req.txdata.byteLength).toEqual(32) /* @@ -481,7 +481,8 @@ describe('Zcashtool tests', function () { } }) - test.each(models)('make a transaction with 1 transparent input 1 transparent output 1 spend 2 shielded outputs', async function (m) { + + test.each(models)('make a tx with 1 transparent input 1 spend 2 shielded outputs', async function (m) { const sim = new Zemu(m.path) try { await sim.start({ ...defaultOptions, model: m.name }) @@ -509,10 +510,6 @@ describe('Zcashtool tests', function () { value: 60000, } - const tout1 = { - address: '1976a914000000000000000000000000000000000000000088ac', - value: 10000, - } const s_spend1 = { path: 1000, @@ -522,7 +519,7 @@ describe('Zcashtool tests', function () { const s_out1 = { address: '15eae700e01e24e2137d554d67bb0da64eee0bf1c2c392c5f1173a979baeb899663808cd22ed8df27566cc', - value: 55000, + value: 65000, memo_type: 0xf6, ovk: null, } @@ -536,7 +533,7 @@ describe('Zcashtool tests', function () { const tx_input_data = { t_in: [tin1], - t_out: [tout1], + t_out: [], s_spend: [s_spend1], s_output: [s_out1, s_out2], } @@ -561,15 +558,10 @@ describe('Zcashtool tests', function () { await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot()) - //we have to click several times... - // for (let i = 1; i < 1 * clicksTIN_S + 1 * clicksTOUT_S + 1 * clicksSSPEND_S + 2 * clicksSOUT_S + clicksOVKset + clicksConst; i += 1) { // 25 - // await sim.clickRight(); - // } - // await sim.clickBoth(); - const clicks = 2 * clicksSSPEND_S + 2 * clicksSOUT_S + clicksConst + clicksOVKset - 1 // 23 - const clickSchedule = m.name == 'nanos' ? [24, 0] : [20, 0] - await sim.navigateAndCompareSnapshots('.', `${m.prefix.toLowerCase()}-1-tr-in-1-tr-out-1-spend-2-sh-out`, clickSchedule) + const clicks = 1 * clicksSSPEND_S + 2 * clicksSOUT_S + clicksConst + clicksOVKset - 1 // 23 + const clickSchedule = m.name == 'nanos' ? [21, 0] : [18, 0] + await sim.navigateAndCompareSnapshots('.', `${m.prefix.toLowerCase()}-1-tr-in-1-spend-2-sh-out`, clickSchedule) const req = await reqinit @@ -606,11 +598,598 @@ describe('Zcashtool tests', function () { const bt0 = builder.add_transparent_input(t_data) console.log(bt0) + /* - To add a transparent output, the builder does not need anything other than the input to the inittx. + To add a shielded spend to the builder, we need: + - the proof generation key belonging to the spend address (proofkey) + - the randomness needed for the value commitment (rcv) + - the randomness needed for the random verification key (alpha) + All this is retrieved from the ledger using a extractspenddata call with no inputs. + The ledger already knows how much data it needs to send after the inittx call. + */ + + const req2 = await app.extractspenddata() + console.log(req2) + expect(req2.return_code).toEqual(0x9000) + const expected_proofkey_raw = + '4e005f180dab2f445ab109574fd2695e705631cd274b4f58e2b53bb3bc73ed5a3caddba8e4daddf42f11ca89e4961ae3ddc41b3bdd08c36d5a7dfcc30839d405' + expect(req2.key_raw.toString('hex')).toEqual(expected_proofkey_raw) + expect(req2.rcv_raw).not.toEqual(req2.alpha_raw) + + /* + The builder needs the data retrieved from the ledger (proofkey, rcv, alpha) + It furthermore uses the spend address and value from the UI. */ - const bt1 = builder.add_transparent_output(tout1) + const spendj1 = { + proofkey: req2.key_raw, + rcv: req2.rcv_raw, + alpha: req2.alpha_raw, + address: s_spend1.address, + value: s_spend1.value, + witness: '01305aef35a6fa9dd43af22d2557f99268fbab70a53e963fa67fc762391510406000000000', + rseed: '0000000000000000000000000000000000000000000000000000000000000000', + } + + /* + The builder adds the spend to its state. + */ + + const b1 = builder.add_sapling_spend(spendj1) + console.log(b1) + + /* + At this point we added all spends. + We cannot get more spend data from the ledger. + We now start the shielded output process. + */ + + /* + To add a shielded output to the builder, we need: + - the randomness needed for the value commitment (rcv) + - the randomness needed for the note commitment (rcm) + - the randomness needed for the random encryption key (esk) + All this is retrieved from the ledger using a extractoutputdata call with no inputs. + The ledger already knows how much data it needs to send after the inittx call. + */ + + const req4 = await app.extractoutputdata() + console.log(req4) + expect(req4.return_code).toEqual(0x9000) + + /* + The builder needs the data retrieved from the ledger (rcv, rcm, esk) + It CAN send along an outgoing viewing key (OVK), can also be all zero's. + It furthermore uses the output address, value and memo from the UI. + */ + + const outj1 = { + rcv: req4.rcv_raw, + rseed: req4.rseed_raw, + ovk: s_out1.ovk, + address: s_out1.address, + value: s_out1.value, + memo: '0000', + hash_seed: req4.hash_seed, + } + + /* + The builder adds the shielded output to its state. + */ + + const b3 = builder.add_sapling_output(outj1) + console.log(b3) + + /* + This process needs to be repeated for the second output. + Note that this output address belongs to Alice. + */ + + const req5 = await app.extractoutputdata() + console.log(req5) + expect(req5.return_code).toEqual(0x9000) + + const outj2 = { + rcv: req5.rcv_raw, + rseed: req5.rseed_raw, + ovk: s_out2.ovk, + address: s_out2.address, + value: s_out2.value, + memo: '0000', + hash_seed: req5.hash_seed, + } + + const b4 = builder.add_sapling_output(outj2) + console.log(b4) + + /* + We are now done with adding the shielded outputs to the builder. + In fact, we are done adding all inputs the builder needs for this transaction. + We now let the builder build the transaction, including the ZK proofs. + The builder returns a txdata blob. + The ledger needs this blob to validate the correctness of the tx. + */ + + const ledgerblob_txdata = builder.build(SPEND_PATH, OUTPUT_PATH) + + /* + Now the ledger will validate the txdata blob. + For this, it uses the input from inittx to verify. + If all checks are ok, the ledger signs the transaction. + */ + + const req6 = await app.checkandsign(ledgerblob_txdata) + console.log(req6) + expect(req6.return_code).toEqual(0x9000) + + /* + Check the hash of the return + */ + + hash = crypto.createHash('sha256') + hash.update(Buffer.from(ledgerblob_txdata)) + h = hash.digest('hex') + expect(req6.signdata.toString('hex')).toEqual(h) + + /* + The builder needs the spend signatures to add it to the transaction blob. + We need to do this one by one. + So we first gather all signatures we need. + */ + + const req7 = await app.extractspendsig() + console.log(req7) + expect(req7.return_code).toEqual(0x9000) + + /* + The builder also needs the transparent signature for the transparent input. + */ + + const req9 = await app.extracttranssig() + console.log(req9) + expect(req9.return_code).toEqual(0x9000) + + /* + At this point we gathered all signatures. + We now add these signaturs to the builder. + Note that for this transaction, we do not have any transparent signatures. + */ + + const signatures = { + transparent_sigs: [req9.sig_raw], + spend_sigs: [req7.sig_raw], + } + + const b5 = builder.add_signatures(signatures) + console.log(b5) + + /* + The builder is now done and the transaction is complete. + */ + + const b6 = builder.finalize() + console.log(b6) + } finally { + await sim.close() + } + }) + + test.each(models)('make a tx with 1 transparent output 1 spend 2 shielded outputs', async function (m) { + const sim = new Zemu(m.path) + try { + await sim.start({ ...defaultOptions, model: m.name }) + const app = new ZCashApp(sim.getTransport()) + + const { zcashtools } = addon + console.log(SPEND_PATH) + + const builder = new zcashtools(1000) + + /* + In this test, Alice wants to send 55000 ZEC to Bob shielded and 10000 ZEC to Charlie transparent. + For this she needs one notes of 40000 ZEC sent to her address belonging to path: 1000. + She also uses a transparent input with 60000 ZEC belonging to transparent path: 0. + The inputs to the initialization is therefore: + - one transparent input and one transparent output + - one shielded spend notes and two shielded output notes. + She takes a transaction fee of 10000 and all leftovers is sent shielded to her own address. + All this info is gathered from the UI and put in the correct jsons. + */ + + const tout1 = { + address: '1976a914000000000000000000000000000000000000000088ac', + value: 10000, + } + + const s_spend1 = { + path: 1000, + address: 'c69e979c6763c1b09238dc6bd5dcbf35360df95dcadf8c0fa25dcbedaaf6057538b812d06656726ea27667', + value: 100000, + } + + const s_out1 = { + address: '15eae700e01e24e2137d554d67bb0da64eee0bf1c2c392c5f1173a979baeb899663808cd22ed8df27566cc', + value: 55000, + memo_type: 0xf6, + ovk: null, + } + + const s_out2 = { + address: 'c69e979c6763c1b09238dc6bd5dcbf35360df95dcadf8c0fa25dcbedaaf6057538b812d06656726ea27667', + value: 100000 - 1000 - 55000 - 10000, + memo_type: 0xf6, + ovk: '6fc01eaa665e03a53c1e033ed0d77b670cf075ede4ada769997a2ed2ec225fca', + } + + const tx_input_data = { + t_in: [], + t_out: [tout1], + s_spend: [s_spend1], + s_output: [s_out1, s_out2], + } + + /* + The inputs to the get_inittx_data function are the inputs to the transaction. + The output is a blob that can be send to the ledger device. + */ + + const ledgerblob_initdata = addon.get_inittx_data(tx_input_data) + console.log(ledgerblob_initdata) + + /* + The output of the get_inittx_data can be send to the ledger. + The ledger will check this data and show the inputs on screen for verification. + If confirmed, the ledger also computes the randomness needed for : + - The shielded spends + - the shielded outputs + */ + + const reqinit = app.inittx(ledgerblob_initdata) + + await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot()) + + //we have to click several times... + // for (let i = 1; i < 1 * clicksTIN_S + 1 * clicksTOUT_S + 1 * clicksSSPEND_S + 2 * clicksSOUT_S + clicksOVKset + clicksConst; i += 1) { // 25 + // await sim.clickRight(); + // } + // await sim.clickBoth(); + + const clicks = 2 * clicksSSPEND_S + 2 * clicksSOUT_S + clicksConst + clicksOVKset - 1 // 23 + const clickSchedule = m.name == 'nanos' ? [21, 0] : [18, 0] + await sim.navigateAndCompareSnapshots('.', `${m.prefix.toLowerCase()}-1-tr-out-1-spend-2-sh-out`, clickSchedule) + + const req = await reqinit + + // const req = await app.inittx(ledgerblob_initdata); + console.log(req) + expect(req.return_code).toEqual(0x9000) + expect(req.txdata.byteLength).toEqual(32) + + /* + Check the hash of the return + */ + let hash = crypto.createHash('sha256') + hash.update(Buffer.from(ledgerblob_initdata)) + let h = hash.digest('hex') + expect(req.txdata.toString('hex')).toEqual(h) + + /* + Now we start building the transaction using the builder. + /* + + /* + To add a transparent output, the builder does not need anything other than the input to the inittx. + */ + const t_out_data = { + address: tout1.address, + value: tout1.value, + } + + const bt1 = builder.add_transparent_output(t_out_data) + console.log(bt1) + + /* + To add a shielded spend to the builder, we need: + - the proof generation key belonging to the spend address (proofkey) + - the randomness needed for the value commitment (rcv) + - the randomness needed for the random verification key (alpha) + All this is retrieved from the ledger using a extractspenddata call with no inputs. + The ledger already knows how much data it needs to send after the inittx call. + */ + + const req2 = await app.extractspenddata() + console.log(req2) + expect(req2.return_code).toEqual(0x9000) + const expected_proofkey_raw = + '4e005f180dab2f445ab109574fd2695e705631cd274b4f58e2b53bb3bc73ed5a3caddba8e4daddf42f11ca89e4961ae3ddc41b3bdd08c36d5a7dfcc30839d405' + expect(req2.key_raw.toString('hex')).toEqual(expected_proofkey_raw) + expect(req2.rcv_raw).not.toEqual(req2.alpha_raw) + + /* + The builder needs the data retrieved from the ledger (proofkey, rcv, alpha) + It furthermore uses the spend address and value from the UI. + */ + + const spendj1 = { + proofkey: req2.key_raw, + rcv: req2.rcv_raw, + alpha: req2.alpha_raw, + address: s_spend1.address, + value: s_spend1.value, + witness: '01305aef35a6fa9dd43af22d2557f99268fbab70a53e963fa67fc762391510406000000000', + rseed: '0000000000000000000000000000000000000000000000000000000000000000', + } + + /* + The builder adds the spend to its state. + */ + + const b1 = builder.add_sapling_spend(spendj1) + console.log(b1) + + /* + At this point we added all spends. + We cannot get more spend data from the ledger. + We now start the shielded output process. + */ + + /* + To add a shielded output to the builder, we need: + - the randomness needed for the value commitment (rcv) + - the randomness needed for the note commitment (rcm) + - the randomness needed for the random encryption key (esk) + All this is retrieved from the ledger using a extractoutputdata call with no inputs. + The ledger already knows how much data it needs to send after the inittx call. + */ + + const req4 = await app.extractoutputdata() + console.log(req4) + expect(req4.return_code).toEqual(0x9000) + + /* + The builder needs the data retrieved from the ledger (rcv, rcm, esk) + It CAN send along an outgoing viewing key (OVK), can also be all zero's. + It furthermore uses the output address, value and memo from the UI. + */ + + const outj1 = { + rcv: req4.rcv_raw, + rseed: req4.rseed_raw, + ovk: s_out1.ovk, + address: s_out1.address, + value: s_out1.value, + memo: '0000', + hash_seed: req4.hash_seed, + } + + /* + The builder adds the shielded output to its state. + */ + + const b3 = builder.add_sapling_output(outj1) + console.log(b3) + + /* + This process needs to be repeated for the second output. + Note that this output address belongs to Alice. + */ + + const req5 = await app.extractoutputdata() + console.log(req5) + expect(req5.return_code).toEqual(0x9000) + + const outj2 = { + rcv: req5.rcv_raw, + rseed: req5.rseed_raw, + ovk: s_out2.ovk, + address: s_out2.address, + value: s_out2.value, + memo: '0000', + hash_seed: req5.hash_seed, + } + + const b4 = builder.add_sapling_output(outj2) + console.log(b4) + + /* + We are now done with adding the shielded outputs to the builder. + In fact, we are done adding all inputs the builder needs for this transaction. + We now let the builder build the transaction, including the ZK proofs. + The builder returns a txdata blob. + The ledger needs this blob to validate the correctness of the tx. + */ + + const ledgerblob_txdata = builder.build(SPEND_PATH, OUTPUT_PATH) + + /* + Now the ledger will validate the txdata blob. + For this, it uses the input from inittx to verify. + If all checks are ok, the ledger signs the transaction. + */ + + const req6 = await app.checkandsign(ledgerblob_txdata) + console.log(req6) + expect(req6.return_code).toEqual(0x9000) + + /* + Check the hash of the return + */ + + hash = crypto.createHash('sha256') + hash.update(Buffer.from(ledgerblob_txdata)) + h = hash.digest('hex') + expect(req6.signdata.toString('hex')).toEqual(h) + + /* + The builder needs the spend signatures to add it to the transaction blob. + We need to do this one by one. + So we first gather all signatures we need. + */ + + const req7 = await app.extractspendsig() + console.log(req7) + expect(req7.return_code).toEqual(0x9000) + + /* + At this point we gathered all signatures (only for shielded inputs as there are no transparent ones) + We now add these signatures to the builder. + Note that for this transaction, we do not have any transparent signatures. + */ + + const signatures = { + transparent_sigs: [], + spend_sigs: [req7.sig_raw], + } + + const b5 = builder.add_signatures(signatures) + console.log(b5) + + /* + The builder is now done and the transaction is complete. + */ + + const b6 = builder.finalize() + console.log(b6) + } finally { + await sim.close() + } + }) + + + test.each(models)('make a transaction with 1 transparent input 1 transparent output 1 spend 2 shielded outputs', async function (m) { + const sim = new Zemu(m.path) + try { + await sim.start({ ...defaultOptions, model: m.name }) + const app = new ZCashApp(sim.getTransport()) + + const { zcashtools } = addon + console.log(SPEND_PATH) + + const builder = new zcashtools(1000) + + /* + In this test, Alice wants to send 55000 ZEC to Bob shielded and 10000 ZEC to Charlie transparent. + For this she needs one notes of 40000 ZEC sent to her address belonging to path: 1000. + She also uses a transparent input with 60000 ZEC belonging to transparent path: 0. + The inputs to the initialization is therefore: + - one transparent input and one transparent output + - one shielded spend notes and two shielded output notes. + She takes a transaction fee of 10000 and all leftovers is sent shielded to her own address. + All this info is gathered from the UI and put in the correct jsons. + */ + + const tin1 = { + path: [44 + 0x80000000, 133 + 0x80000000, 5 + 0x80000000, 0, 0], + address: '1976a9140f71709c4b828df00f93d20aa2c34ae987195b3388ac', + value: 60000, + } + + const tout1 = { + address: '1976a914000000000000000000000000000000000000000088ac', + value: 10000, + } + + const s_spend1 = { + path: 1000, + address: 'c69e979c6763c1b09238dc6bd5dcbf35360df95dcadf8c0fa25dcbedaaf6057538b812d06656726ea27667', + value: 40000, + } + + const s_out1 = { + address: '15eae700e01e24e2137d554d67bb0da64eee0bf1c2c392c5f1173a979baeb899663808cd22ed8df27566cc', + value: 55000, + memo_type: 0xf6, + ovk: null, + } + + const s_out2 = { + address: 'c69e979c6763c1b09238dc6bd5dcbf35360df95dcadf8c0fa25dcbedaaf6057538b812d06656726ea27667', + value: 100000 - 1000 - 55000 - 10000, + memo_type: 0xf6, + ovk: '6fc01eaa665e03a53c1e033ed0d77b670cf075ede4ada769997a2ed2ec225fca', + } + + const tx_input_data = { + t_in: [tin1], + t_out: [tout1], + s_spend: [s_spend1], + s_output: [s_out1, s_out2], + } + + /* + The inputs to the get_inittx_data function are the inputs to the transaction. + The output is a blob that can be send to the ledger device. + */ + + const ledgerblob_initdata = addon.get_inittx_data(tx_input_data) + console.log(ledgerblob_initdata) + + /* + The output of the get_inittx_data can be send to the ledger. + The ledger will check this data and show the inputs on screen for verification. + If confirmed, the ledger also computes the randomness needed for : + - The shielded spends + - the shielded outputs + */ + + const reqinit = app.inittx(ledgerblob_initdata) + + await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot()) + + //we have to click several times... + // for (let i = 1; i < 1 * clicksTIN_S + 1 * clicksTOUT_S + 1 * clicksSSPEND_S + 2 * clicksSOUT_S + clicksOVKset + clicksConst; i += 1) { // 25 + // await sim.clickRight(); + // } + // await sim.clickBoth(); + + const clicks = 2 * clicksSSPEND_S + 2 * clicksSOUT_S + clicksConst + clicksOVKset - 1 // 23 + const clickSchedule = m.name == 'nanos' ? [24, 0] : [20, 0] + await sim.navigateAndCompareSnapshots('.', `${m.prefix.toLowerCase()}-1-tr-in-1-tr-out-1-spend-2-sh-out`, clickSchedule) + + const req = await reqinit + + // const req = await app.inittx(ledgerblob_initdata); + console.log(req) + expect(req.return_code).toEqual(0x9000) + expect(req.txdata.byteLength).toEqual(32) + + /* + Check the hash of the return + */ + let hash = crypto.createHash('sha256') + hash.update(Buffer.from(ledgerblob_initdata)) + let h = hash.digest('hex') + expect(req.txdata.toString('hex')).toEqual(h) + + /* + Now we start building the transaction using the builder. + /* + + /* + To add transparent inputs to the builder, we dont need fresh information from the ledger. + The builder does need the secp256k1 public key belonging to the address. + The builder also need outpoint from the blockchain. + */ + + const t_data = { + outp: '000000000000000000000000000000000000000000000000000000000000000000000000', + pk: '031f6d238009787c20d5d7becb6b6ad54529fc0a3fd35088e85c2c3966bfec050e', + address: tin1.address, + value: tin1.value, + } + + const bt0 = builder.add_transparent_input(t_data) + console.log(bt0) + + /* + To add a transparent output, the builder does not need anything other than the input to the inittx. + */ + const t_out_data = { + address: tout1.address, + value: tout1.value, + } + + const bt1 = builder.add_transparent_output(t_out_data) console.log(bt1) /*