Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make handle_transaction explicitly return the new decrypted notes #92

Merged
merged 2 commits into from
Oct 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions js/pivx_shield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface Block {

interface TransactionResult {
decrypted_notes: [Note, string][];
decrypted_new_notes: [Note, string][];
commitment_tree: string;
nullifiers: string[];
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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({
Expand All @@ -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) {
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -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]),
Expand Down
46 changes: 33 additions & 13 deletions src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
pub commitment_tree: String,
}
Expand Down Expand Up @@ -156,17 +157,21 @@ pub fn handle_transaction(
(note, IncrementalWitness::read(wit).unwrap())
})
.collect::<Vec<_>>();
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<Node>)> = 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<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)));
}

for nullif in nullifiers.iter() {
ser_nullifiers.push(hex::encode(nullif.0));
Expand All @@ -178,19 +183,35 @@ 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<Node>)>,
) -> Result<Vec<(Note, String)>, Box<dyn Error>> {
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<Node>,
tx: &str,
key: UnifiedFullViewingKey,
is_testnet: bool,
witnesses: &mut Vec<(Note, IncrementalWitness<Node>)>,
new_witnesses: &mut Vec<(Note, IncrementalWitness<Node>)>,
) -> Result<Vec<Nullifier>, Box<dyn Error>> {
let tx = Transaction::read(
Cursor::new(hex::decode(tx)?),
Expand All @@ -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")?;
Expand All @@ -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;
}
}
Expand Down
26 changes: 17 additions & 9 deletions src/transaction/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -83,11 +85,17 @@ pub async fn test_create_transaction() -> Result<(), Box<dyn Error>> {
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) = &notes[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);
Expand Down
Loading