diff --git a/crates/view/src/storage.rs b/crates/view/src/storage.rs index 9796149a61..4b9e9db95d 100644 --- a/crates/view/src/storage.rs +++ b/crates/view/src/storage.rs @@ -1225,6 +1225,8 @@ impl Storage { let scanned_nullifiers_tx = self.scanned_nullifiers_tx.clone(); let scanned_swaps_tx = self.scanned_swaps_tx.clone(); + let fvk = self.full_viewing_key().await?; + // Cloning the SCT is cheap because it's a copy-on-write structure, so we move an owned copy // into the spawned thread. This means that if for any reason the thread panics or throws an // error, the changes to the SCT will be discarded, just like any changes to the database, @@ -1378,12 +1380,13 @@ impl Storage { let tx_hash_owned = sha2::Sha256::digest(&tx_bytes); let tx_hash = tx_hash_owned.as_slice(); let tx_block_height = filtered_block.height as i64; + let return_address = transaction.decrypt_memo(&fvk).map_or(None, |x| Some(x.sender.to_vec())); tracing::debug!(tx_hash = ?hex::encode(tx_hash), "recording extended transaction"); dbtx.execute( - "INSERT INTO tx (tx_hash, tx_bytes, block_height) VALUES (?1, ?2, ?3)", - (&tx_hash, &tx_bytes, tx_block_height), + "INSERT INTO tx (tx_hash, tx_bytes, block_height, return_address) VALUES (?1, ?2, ?3, ?4)", + (&tx_hash, &tx_bytes, tx_block_height, return_address), )?; // Associate all of the spent nullifiers with the transaction by hash. @@ -1493,4 +1496,39 @@ impl Storage { }) .await? } + + pub async fn notes_by_sender( + &self, + return_address: &Address, + ) -> anyhow::Result> { + let pool = self.pool.clone(); + + let query = "SELECT notes.note_commitment, + spendable_notes.height_created, + notes.address, + notes.amount, + notes.asset_id, + notes.rseed, + spendable_notes.address_index, + spendable_notes.source, + spendable_notes.height_spent, + spendable_notes.nullifier, + spendable_notes.position + FROM notes + JOIN spendable_notes ON notes.note_commitment = spendable_notes.note_commitment + JOIN tx ON spendable_notes.source = tx.tx_hash + WHERE tx.return_address = ?1"; + + let return_address = return_address.to_vec(); + + let records = spawn_blocking(move || { + pool.get()? + .prepare(query)? + .query_and_then([return_address], |record| record.try_into())? + .collect::>>() + }) + .await??; + + Ok(records) + } } diff --git a/crates/view/src/storage/schema.sql b/crates/view/src/storage/schema.sql index e1b6d16a13..506bd4e451 100644 --- a/crates/view/src/storage/schema.sql +++ b/crates/view/src/storage/schema.sql @@ -50,7 +50,8 @@ CREATE TABLE tx_by_nullifier ( CREATE TABLE tx ( tx_hash BLOB PRIMARY KEY NOT NULL, tx_bytes BLOB NOT NULL, - block_height BIGINT NOT NULL + block_height BIGINT NOT NULL, + return_address BLOB ); -- This table just records the mapping from note commitments to note plaintexts.