From e219b188dda9ee51a401c97f2a231980405c1410 Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Thu, 24 Oct 2024 18:24:20 +0200 Subject: [PATCH 1/2] Make handle_block explicitly return the new decrypted notes --- js/pivx_shield.ts | 31 ++++++++++++++++--------------- src/transaction.rs | 46 +++++++++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/js/pivx_shield.ts b/js/pivx_shield.ts index 6e5295f..18b5619 100644 --- a/js/pivx_shield.ts +++ b/js/pivx_shield.ts @@ -20,6 +20,7 @@ interface Block { interface TransactionResult { decrypted_notes: [Note, string][]; + decrypted_new_notes: [Note, string][]; commitment_tree: string; nullifiers: string[]; } @@ -316,15 +317,14 @@ export class PIVXShield { ); } for (const tx of block.txs) { - const { belongToWallet, decryptedNotes } = await this.decryptTransaction( + const { belongToWallet, decryptedNewNotes } = await this.addTransaction( tx.hex, ); - await this.addTransaction(tx.hex); if (belongToWallet) { walletTransactions.push(tx.hex); } // Add all the decryptedNotes to the Nullifier->Note map - for (const note of decryptedNotes) { + for (const note of decryptedNewNotes) { const nullifier = await this.generateNullifierFromNote(note); const simplifiedNote = { value: note[0].value, @@ -361,7 +361,7 @@ export class PIVXShield { ); } async decryptTransactionOutputs(hex: string) { - const { decryptedNotes } = await this.decryptTransaction(hex); + const decryptedNotes = await this.decryptTransaction(hex); const simplifiedNotes = []; for (const [note, _] of decryptedNotes) { simplifiedNotes.push({ @@ -381,11 +381,20 @@ export class PIVXShield { this.unspentNotes, ); this.commitmentTree = res.commitment_tree; - this.unspentNotes = res.decrypted_notes; + this.unspentNotes = res.decrypted_notes.concat(res.decrypted_new_notes); if (res.nullifiers.length > 0) { await this.removeSpentNotes(res.nullifiers); } + // Check if the transaction belongs to the wallet: + let belongToWallet = res.decrypted_new_notes.length > 0; + for (const nullifier of res.nullifiers) { + if (belongToWallet) { + break; + } + belongToWallet = belongToWallet || this.mapNullifierNote.has(nullifier); + } + return { belongToWallet, decryptedNewNotes: res.decrypted_new_notes }; } async decryptTransaction(hex: string) { @@ -397,15 +406,7 @@ export class PIVXShield { this.isTestnet, [], ); - // Check if the transaction belongs to the wallet: - let belongToWallet = res.decrypted_notes.length > 0; - for (const nullifier of res.nullifiers) { - if (belongToWallet) { - break; - } - belongToWallet = belongToWallet || this.mapNullifierNote.has(nullifier); - } - return { belongToWallet, decryptedNotes: res.decrypted_notes }; + return res.decrypted_new_notes; } /** @@ -474,7 +475,7 @@ export class PIVXShield { if (useShieldInputs) { this.pendingSpentNotes.set(txid, nullifiers); } - const { decryptedNotes } = await this.decryptTransaction(txhex); + const decryptedNotes = await this.decryptTransaction(txhex); this.pendingUnspentNotes.set( txid, decryptedNotes.map((n) => n[0]), diff --git a/src/transaction.rs b/src/transaction.rs index ec3a4cc..9a50bb8 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -126,6 +126,7 @@ pub async fn load_prover() -> bool { #[derive(Serialize, Deserialize)] pub struct JSTxSaplingData { pub decrypted_notes: Vec<(Note, String)>, + pub decrypted_new_notes: Vec<(Note, String)>, pub nullifiers: Vec, pub commitment_tree: String, } @@ -156,17 +157,21 @@ pub fn handle_transaction( (note, IncrementalWitness::read(wit).unwrap()) }) .collect::>(); - let nullifiers = handle_transaction_internal(&mut tree, tx, key, true, &mut comp_note) - .map_err(|_| "Cannot decode tx")?; - let mut ser_comp_note: Vec<(Note, String)> = vec![]; + let mut new_comp_note: Vec<(Note, IncrementalWitness)> = vec![]; + let nullifiers = handle_transaction_internal( + &mut tree, + tx, + key, + true, + &mut comp_note, + &mut new_comp_note, + ) + .map_err(|_| "Cannot decode tx")?; + let ser_comp_note: Vec<(Note, String)> = + serialize_comp_note(comp_note).map_err(|_| "Cannot serialize notes")?; + let ser_new_comp_note: Vec<(Note, String)> = + serialize_comp_note(new_comp_note).map_err(|_| "Cannot serialize notes")?; let mut ser_nullifiers: Vec = vec![]; - for (note, witness) in comp_note { - let mut buff = Vec::new(); - witness - .write(&mut buff) - .map_err(|_| "Cannot write witness to buffer")?; - ser_comp_note.push((note, hex::encode(&buff))); - } for nullif in nullifiers.iter() { ser_nullifiers.push(hex::encode(nullif.0)); @@ -178,12 +183,27 @@ pub fn handle_transaction( let res: JSTxSaplingData = JSTxSaplingData { decrypted_notes: ser_comp_note, + decrypted_new_notes: ser_new_comp_note, nullifiers: ser_nullifiers, commitment_tree: hex::encode(buff), }; Ok(serde_wasm_bindgen::to_value(&res).map_err(|_| "Cannot serialize tx output")?) } +pub fn serialize_comp_note( + comp_note: Vec<(Note, IncrementalWitness)>, +) -> Result, Box> { + let mut ser_comp_note: Vec<(Note, String)> = vec![]; + for (note, witness) in comp_note { + let mut buff = Vec::new(); + witness + .write(&mut buff) + .map_err(|_| "Cannot write witness to buffer")?; + ser_comp_note.push((note, hex::encode(&buff))); + } + Ok(ser_comp_note) +} + //add a tx to a given commitment tree and the return a witness to each output pub fn handle_transaction_internal( tree: &mut CommitmentTree, @@ -191,6 +211,7 @@ pub fn handle_transaction_internal( key: UnifiedFullViewingKey, is_testnet: bool, witnesses: &mut Vec<(Note, IncrementalWitness)>, + new_witnesses: &mut Vec<(Note, IncrementalWitness)>, ) -> Result, Box> { let tx = Transaction::read( Cursor::new(hex::decode(tx)?), @@ -210,10 +231,9 @@ pub fn handle_transaction_internal( } for (i, out) in sapling.shielded_outputs().iter().enumerate() { - println!("note found!"); tree.append(Node::from_cmu(out.cmu())) .map_err(|_| "Failed to add cmu to tree")?; - for (_, witness) in witnesses.iter_mut() { + for (_, witness) in witnesses.iter_mut().chain(new_witnesses.iter_mut()) { witness .append(Node::from_cmu(out.cmu())) .map_err(|_| "Failed to add cmu to witness")?; @@ -222,7 +242,7 @@ pub fn handle_transaction_internal( if note.index == i { // Save witness let witness = IncrementalWitness::from_tree(tree); - witnesses.push((decrypted_tx.swap_remove(index).note, witness)); + new_witnesses.push((decrypted_tx.swap_remove(index).note, witness)); break; } } From 2410f2c6d599f34efa0d06b2a725b8d7b73fac84 Mon Sep 17 00:00:00 2001 From: Alessandro Rezzi Date: Fri, 25 Oct 2024 09:14:52 +0200 Subject: [PATCH 2/2] Update broken tests --- src/transaction/test.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/transaction/test.rs b/src/transaction/test.rs index fea60d5..fb9f947 100644 --- a/src/transaction/test.rs +++ b/src/transaction/test.rs @@ -28,13 +28,15 @@ fn check_tx_decryption() { let tx = "0300000001a7d31ea039ab2a9914be2a84b6e8966758da5f8d1a64ac6fb49d2763dccc38da000000006b483045022100bb67345313edea3c7462c463ea8e03ef3b14caccfbefff9877ef246138427b6a02200b74211e1f27be080561c3985980e6b0e2e833f0751ea68dfb1e465b994afefc0121025c6802ec58464d8e65d5f01be0b7ce6e8404e4a99f28ea3bfe47efe40df9108cffffffff01e8657096050000001976a914b4f73d5c66d999699a4c38ba9fe851d7141f1afa88ac0000000001003665c4ffffffff00010b39ab5d98de6f5e3f50f3f075f61fea263b4cdd6927a53ac92c196be72911237f5041af34fed06560b8620e16652edf6d297d14a9cff2145731de6643d4bf13e189dbc4b6c4b91fe421133a2f257e5b516efd9b080814251ec0169fabdac1ce4a14575d3a42a7ca852c1ef6f6e1f3daf60e9ae4b77ef4d9a589dcbc09e8437fc28e80d6a0c4f1627b3e34ee9dd5cd587d1d57bab30e0a2eba893a6b61d7e53f5b49b4cb67a807e5db203b76744025d8395c83be2eb71009f9b82e78e7b65d9740340106ee59b22cd3628f1f10c3712c2b4f86464b627b27910cd3e0a80c5387798db4f15f751b5886beb1ab1a8c298185ed6f5d3a074689ba6e327e8dc2bd3b41790ecbe0240f909b8735b8ac98a59855b448e9f37d31d5d25b71959264c145abd15f0606ab5844391819afd4017890696272abad451dab8654d76e41c389941f0fd134d7d6e3b971b15cc63ba9bea421383639bdbeaa970636d637a1c6167154f39ded089d0f07776c58e8e86c0dac8259d22644e9d8a89456e9ccf2f66ce8633a9055f1703669c6a7b009865347ef608cb4ba8f3158e05947580ec50c32f69c0079dff58b3b53367f43490d4bcaba946ef4c42b4d366c66184f84ec442499a056b6b60eeaee94151459ac0b61eb6debfa96554bbe8ec39d2c49ee6eca48ed8dc137f84584803e2372ec35e0f9f4252beef9170419e703183fa87e7d35c2403b41700bc9f5d69da6c01c870515694f5c48372cba6bacd6a79ca1cdb85f38841f7680d0dd6853b22fc95d6e307419271edb05f2f40733c31c6f827eca592658716c5c73a9dd00a7e387250beffaa78bd1f104e031e00f014f9a50935864e11ffd655ea4d4c6c3d80b681e7581a19b2668c00528110ee5322add9dacb35b519280812050061788884cad7cc409a9261e86485cc4f2d904bdf40b3c78208a395a2488eb938b8a198b51ac418fa79e5d1d7bd8f96fe0910fe61136d8fe302f144745a988d6de83e89cd8befef8a762103aa32a14d93e3ac41b44188ab385b65c1f21cf29f19a6d2af556385dd60a994ecd1ac909488f7abce29e26690651a389d4466a9e20b7f08bfbdf4f4aa3e1577dc7debf1951688db8c75347d01e836f7816df3c7a7aaa833cbd6309d179d5dfc34045e52984cf475890f04b2ffcdf123175cc07568d08d9b8525ad9eabad231e1549a19fdce0fbb30c1fe7ec59bf8ed8e642ec6571456bdba8ade4458cffb1d65fee35242d7409de14a21514416a68e9c2d5c21eb9ca5813e1d8162a48d650ed7696b7b14b4f3d3f5eb892cf32614f62dea794e7f68e6d3d3ae6edf22e811f85e1ac7fe2a8437bdf287aa4d5ff842173039074516304042370a4e2feeb901319665ffc9b005b37c2afbea22faca316ea4f6b5f365fe46f679581966dadd029d687d2b400201"; let key = UnifiedFullViewingKey::new(Some(skey.to_diversifiable_full_viewing_key()), None) .expect("Failed to create key"); - let mut comp_note = vec![]; - let nullifiers = handle_transaction_internal(&mut tree, tx, key, true, &mut comp_note).unwrap(); + let mut new_comp_note = vec![]; + let nullifiers = + handle_transaction_internal(&mut tree, tx, key, true, &mut vec![], &mut new_comp_note) + .unwrap(); //This was a t-s tx assert_eq!(nullifiers.len(), 0); //Successfully decrypt exactly 1 note - assert_eq!(comp_note.len(), 1); - let note = &comp_note[0].0; + assert_eq!(new_comp_note.len(), 1); + let note = &new_comp_note[0].0; //Successfully decrypt the balance assert_eq!(note.value().inner(), 1000000000); //Successfully decrypt the payment address @@ -83,11 +85,17 @@ pub async fn test_create_transaction() -> Result<(), Box> { let output = "yAHuqx6mZMAiPKeV35C11Lfb3Pqxdsru5D"; let input_tx = "0300000001a347f398c8957afee7ef0fae759ff29feda25f3e72ab5052ea09729389fd48ca000000006b483045022100c332effdceaa20b3225d52d20059e443ed112d561329b81f78a9db338637e6a102204f948d70c37bfe96bbe776f8279ad5fa857c638338d8ce49f553a3ec60993d8f0121025c6802ec58464d8e65d5f01be0b7ce6e8404e4a99f28ea3bfe47efe40df9108cffffffff01e89bd55a050000001976a9147888a1affe25e5c7af03fffdbea29f13ee1be22b88ac0000000001006cca88ffffffff000150585de8e31e6c65dfa07981275f13ebb8c9c67d8c7d088622aacca6c35c67a23642ad16653acda3cf9b5230f652592197e578ea1eae78c2496a3dc274a4ba0b522216af4c44abd4e9b81964d0a801929df1cb543c4fea041d056cc493b2f8dd90db662a0a43dae4d80a8cb0bd93e22b7000c0bcdab93f94800b88268a78a4d77147f2f16bde98b2386e5ac4025260df5f63adaef13bc8d7a920dbd14fa7e8ef0c5ff29f00942341e29b15509bfa99b4b1bd0ba29c5cf2c419113c27288b3a8d8f4919a4845e47d4e5fe1d1081a98e0ee49bb0e422b339e949276a1264c236850d9beb94c7855143a4f00689d1bf8d996eee9f0ee865ca780713f5aa1990aa848d47a39ea45c926141a1ff5a5a45c2e2e78d470a180e02b3dd47e0b206a4542d4dbfc540023ee5cb35e54a086942657232c27a15c87eef6dd11587e871ea690a45002e0b60605d7c4ac7fde81a71aadde9d0cc0d5c347fbe942993bd2a69ca2ca98ea0885454e7387d609192094bea075b96f020a8ed7080b5ef0aaf13e73da67a68e377db62720724e8c0d2913487be2a3e39380b33a90f0336f07fa031345a42784460356987da3227bd40a8cf933e4b8661020cf566af785a5c9b404c84153a69d9280739cb567c6cdf41f7a1a38b4d5847b33956b4dfa847b386850eff2a3e9fe7434fb551d1c6d31fae868a2f491ebd4f382a0ac203652f4be9fb3cff3ed10e6295639af76a41e40e562862d4359e4874b565aa1bae4b68abb0a7fe66884b75250d16276521925ead4821c7f04338286c2e52e7772f980d7a228ad2b89c18c8eeaf3ee1b4d5c5a959fc93c1cda3f9340f8256a88076b96a8718efc5dcb3733e3e11f6ca1198a97a248ff4ab0a7e883e360b8495470badc7ec75f84e58d87ff83d03c594a11b9029177efa5fea026a71c2c328a6356bd447eb154ac39e43963118033fc1a72702b12e641e7dfa8f98a58e43d75f6b3350af9fc54e683c6074cfd76e86752d7f598b6816696a4f17ba5f10c983ad2f8e102f44f42b2d07b24fb599abbfd067373c4b00f9ae830fcdd79ca8fa8c90eb414f8f5bb070d1199b9e9fae7124772865e0d6f486d7f10f073a0d61bd9e8c94b7a963c831e76b5c07cef22c06877a683aca53396289b115f8b59989f3d5906c4961891ef4677ce73d752ee0ba8929056f38d7630b02db2188d512d733126fa2479217dcd5ed4061928e5ba374300d7a5fa08af2b64cbf5a2176e07b3a4a5bb4812c46c2e608d364d8589225f9b7620116e0cd6a175ab397d295ff0ee0100d2415db6c6736a0f6e2248a62c4c47b39103f67e30814cf3c9b0b82936546d4b81826cd8fdebe24ae91a81b69e7188f4b18c3422d61b367bc4ca92f8815c0fc42caf524b3337a8b9a6737557e1d471745e02a8e88a19fe730e224126d290a"; - let mut notes = vec![]; - let _nullifiers = - handle_transaction_internal(&mut commitment_tree, input_tx, key, true, &mut notes)?; - assert_eq!(notes.len(), 1); - let (note, path) = ¬es[0]; + let mut new_notes = vec![]; + let _nullifiers = handle_transaction_internal( + &mut commitment_tree, + input_tx, + key, + true, + &mut vec![], + &mut new_notes, + )?; + assert_eq!(new_notes.len(), 1); + let (note, path) = &new_notes[0]; let mut path_vec = vec![]; path.write(&mut path_vec)?; let path = hex::encode(path_vec);