zcash_client_sqlite: Fix ambiguities in transaction views

Co-authored-by: Kris Nuttycombe <kris@nutty.land>
This commit is contained in:
Jack Grigg 2024-03-15 16:50:53 +00:00 committed by Kris Nuttycombe
parent 1775f6525b
commit 8b8757ce65
4 changed files with 147 additions and 32 deletions

View File

@ -63,6 +63,7 @@ use zcash_primitives::{
zip32::DiversifierIndex, zip32::DiversifierIndex,
}; };
use zcash_protocol::local_consensus::LocalNetwork; use zcash_protocol::local_consensus::LocalNetwork;
use zcash_protocol::value::{ZatBalance, Zatoshis};
use crate::{ use crate::{
chain::init::init_cache_database, chain::init::init_cache_database,
@ -958,6 +959,105 @@ impl<Cache> TestState<Cache> {
) )
.unwrap() .unwrap()
} }
/// Returns a vector of transaction summaries
pub(crate) fn get_tx_history(
&self,
) -> Result<Vec<TransactionSummary<AccountId>>, SqliteClientError> {
let mut stmt = self.wallet().conn.prepare_cached(
"SELECT *
FROM v_transactions
ORDER BY mined_height DESC, tx_index DESC",
)?;
let results = stmt
.query_and_then::<TransactionSummary<AccountId>, SqliteClientError, _, _>([], |row| {
Ok(TransactionSummary {
account_id: AccountId(row.get("account_id")?),
txid: TxId::from_bytes(row.get("txid")?),
expiry_height: row
.get::<_, Option<u32>>("expiry_height")?
.map(BlockHeight::from),
mined_height: row
.get::<_, Option<u32>>("mined_height")?
.map(BlockHeight::from),
account_value_delta: ZatBalance::from_i64(row.get("account_balance_delta")?)?,
fee_paid: row
.get::<_, Option<i64>>("fee_paid")?
.map(Zatoshis::from_nonnegative_i64)
.transpose()?,
has_change: row.get("has_change")?,
sent_note_count: row.get("sent_note_count")?,
received_note_count: row.get("received_note_count")?,
memo_count: row.get("memo_count")?,
expired_unmined: row.get("expired_unmined")?,
})
})?
.collect::<Result<Vec<_>, _>>()?;
Ok(results)
}
}
pub(crate) struct TransactionSummary<AccountId> {
account_id: AccountId,
txid: TxId,
expiry_height: Option<BlockHeight>,
mined_height: Option<BlockHeight>,
account_value_delta: ZatBalance,
fee_paid: Option<Zatoshis>,
has_change: bool,
sent_note_count: usize,
received_note_count: usize,
memo_count: usize,
expired_unmined: bool,
}
#[allow(dead_code)]
impl<AccountId> TransactionSummary<AccountId> {
pub(crate) fn account_id(&self) -> &AccountId {
&self.account_id
}
pub(crate) fn txid(&self) -> TxId {
self.txid
}
pub(crate) fn expiry_height(&self) -> Option<BlockHeight> {
self.expiry_height
}
pub(crate) fn mined_height(&self) -> Option<BlockHeight> {
self.mined_height
}
pub(crate) fn account_value_delta(&self) -> ZatBalance {
self.account_value_delta
}
pub(crate) fn fee_paid(&self) -> Option<Zatoshis> {
self.fee_paid
}
pub(crate) fn has_change(&self) -> bool {
self.has_change
}
pub(crate) fn sent_note_count(&self) -> usize {
self.sent_note_count
}
pub(crate) fn received_note_count(&self) -> usize {
self.received_note_count
}
pub(crate) fn expired_unmined(&self) -> bool {
self.expired_unmined
}
pub(crate) fn memo_count(&self) -> usize {
self.memo_count
}
} }
/// Trait used by tests that require a full viewing key. /// Trait used by tests that require a full viewing key.

View File

@ -271,6 +271,9 @@ pub(crate) fn send_single_step_proposed_transfer<T: ShieldedPoolTester>() {
.get_memo(NoteId::new(sent_tx_id, T::SHIELDED_PROTOCOL, 12345)), .get_memo(NoteId::new(sent_tx_id, T::SHIELDED_PROTOCOL, 12345)),
Ok(None) Ok(None)
); );
let tx_history = st.get_tx_history().unwrap();
assert_eq!(tx_history.len(), 2);
} }
#[cfg(feature = "transparent-inputs")] #[cfg(feature = "transparent-inputs")]

View File

@ -552,14 +552,14 @@ mod tests {
// v_received_notes // v_received_notes
"CREATE VIEW v_received_notes AS "CREATE VIEW v_received_notes AS
SELECT SELECT
id, sapling_received_notes.id AS id_within_pool_table,
tx, sapling_received_notes.tx,
2 AS pool, 2 AS pool,
sapling_received_notes.output_index AS output_index, sapling_received_notes.output_index AS output_index,
account_id, account_id,
value, sapling_received_notes.value,
is_change, is_change,
memo, sapling_received_notes.memo,
spent, spent,
sent_notes.id AS sent_note_id sent_notes.id AS sent_note_id
FROM sapling_received_notes FROM sapling_received_notes
@ -568,14 +568,14 @@ mod tests {
(sapling_received_notes.tx, 2, sapling_received_notes.output_index) (sapling_received_notes.tx, 2, sapling_received_notes.output_index)
UNION UNION
SELECT SELECT
id, orchard_received_notes.id AS id_within_pool_table,
tx, orchard_received_notes.tx,
3 AS pool, 3 AS pool,
orchard_received_notes.action_index AS output_index, orchard_received_notes.action_index AS output_index,
account_id, account_id,
value, orchard_received_notes.value,
is_change, is_change,
memo, orchard_received_notes.memo,
spent, spent,
sent_notes.id AS sent_note_id sent_notes.id AS sent_note_id
FROM orchard_received_notes FROM orchard_received_notes
@ -650,11 +650,12 @@ mod tests {
"CREATE VIEW v_transactions AS "CREATE VIEW v_transactions AS
WITH WITH
notes AS ( notes AS (
SELECT v_received_notes.id AS id, -- Shielded notes received in this transaction
v_received_notes.account_id AS account_id, SELECT v_received_notes.account_id AS account_id,
transactions.block AS block, transactions.block AS block,
transactions.txid AS txid, transactions.txid AS txid,
v_received_notes.pool AS pool, v_received_notes.pool AS pool,
id_within_pool_table,
v_received_notes.value AS value, v_received_notes.value AS value,
CASE CASE
WHEN v_received_notes.is_change THEN 1 WHEN v_received_notes.is_change THEN 1
@ -673,22 +674,24 @@ mod tests {
JOIN transactions JOIN transactions
ON transactions.id_tx = v_received_notes.tx ON transactions.id_tx = v_received_notes.tx
UNION UNION
SELECT utxos.id AS id, -- Transparent TXOs received in this transaction
utxos.received_by_account_id AS account_id, SELECT utxos.received_by_account_id AS account_id,
utxos.height AS block, utxos.height AS block,
utxos.prevout_txid AS txid, utxos.prevout_txid AS txid,
0 AS pool, 0 AS pool,
utxos.id AS id_within_pool_table,
utxos.value_zat AS value, utxos.value_zat AS value,
0 AS is_change, 0 AS is_change,
1 AS received_count, 1 AS received_count,
0 AS memo_present 0 AS memo_present
FROM utxos FROM utxos
UNION UNION
SELECT v_received_notes.id AS id, -- Shielded notes spent in this transaction
v_received_notes.account_id AS account_id, SELECT v_received_notes.account_id AS account_id,
transactions.block AS block, transactions.block AS block,
transactions.txid AS txid, transactions.txid AS txid,
v_received_notes.pool AS pool, v_received_notes.pool AS pool,
id_within_pool_table,
-v_received_notes.value AS value, -v_received_notes.value AS value,
0 AS is_change, 0 AS is_change,
0 AS received_count, 0 AS received_count,
@ -697,11 +700,12 @@ mod tests {
JOIN transactions JOIN transactions
ON transactions.id_tx = v_received_notes.spent ON transactions.id_tx = v_received_notes.spent
UNION UNION
SELECT utxos.id AS id, -- Transparent TXOs spent in this transaction
utxos.received_by_account_id AS account_id, SELECT utxos.received_by_account_id AS account_id,
transactions.block AS block, transactions.block AS block,
transactions.txid AS txid, transactions.txid AS txid,
0 AS pool, 0 AS pool,
utxos.id AS id_within_pool_table,
-utxos.value_zat AS value, -utxos.value_zat AS value,
0 AS is_change, 0 AS is_change,
0 AS received_count, 0 AS received_count,
@ -710,6 +714,8 @@ mod tests {
JOIN transactions JOIN transactions
ON transactions.id_tx = utxos.spent_in_tx ON transactions.id_tx = utxos.spent_in_tx
), ),
-- Obtain a count of the notes that the wallet created in each transaction,
-- not counting change notes.
sent_note_counts AS ( sent_note_counts AS (
SELECT sent_notes.from_account_id AS account_id, SELECT sent_notes.from_account_id AS account_id,
transactions.txid AS txid, transactions.txid AS txid,

View File

@ -70,14 +70,14 @@ impl RusqliteMigration for Migration {
&format!( &format!(
"CREATE VIEW v_received_notes AS "CREATE VIEW v_received_notes AS
SELECT SELECT
id, sapling_received_notes.id AS id_within_pool_table,
tx, sapling_received_notes.tx,
{sapling_pool_code} AS pool, {sapling_pool_code} AS pool,
sapling_received_notes.output_index AS output_index, sapling_received_notes.output_index AS output_index,
account_id, account_id,
value, sapling_received_notes.value,
is_change, is_change,
memo, sapling_received_notes.memo,
spent, spent,
sent_notes.id AS sent_note_id sent_notes.id AS sent_note_id
FROM sapling_received_notes FROM sapling_received_notes
@ -86,14 +86,14 @@ impl RusqliteMigration for Migration {
(sapling_received_notes.tx, {sapling_pool_code}, sapling_received_notes.output_index) (sapling_received_notes.tx, {sapling_pool_code}, sapling_received_notes.output_index)
UNION UNION
SELECT SELECT
id, orchard_received_notes.id AS id_within_pool_table,
tx, orchard_received_notes.tx,
{orchard_pool_code} AS pool, {orchard_pool_code} AS pool,
orchard_received_notes.action_index AS output_index, orchard_received_notes.action_index AS output_index,
account_id, account_id,
value, orchard_received_notes.value,
is_change, is_change,
memo, orchard_received_notes.memo,
spent, spent,
sent_notes.id AS sent_note_id sent_notes.id AS sent_note_id
FROM orchard_received_notes FROM orchard_received_notes
@ -110,11 +110,12 @@ impl RusqliteMigration for Migration {
CREATE VIEW v_transactions AS CREATE VIEW v_transactions AS
WITH WITH
notes AS ( notes AS (
SELECT v_received_notes.id AS id, -- Shielded notes received in this transaction
v_received_notes.account_id AS account_id, SELECT v_received_notes.account_id AS account_id,
transactions.block AS block, transactions.block AS block,
transactions.txid AS txid, transactions.txid AS txid,
v_received_notes.pool AS pool, v_received_notes.pool AS pool,
id_within_pool_table,
v_received_notes.value AS value, v_received_notes.value AS value,
CASE CASE
WHEN v_received_notes.is_change THEN 1 WHEN v_received_notes.is_change THEN 1
@ -133,22 +134,24 @@ impl RusqliteMigration for Migration {
JOIN transactions JOIN transactions
ON transactions.id_tx = v_received_notes.tx ON transactions.id_tx = v_received_notes.tx
UNION UNION
SELECT utxos.id AS id, -- Transparent TXOs received in this transaction
utxos.received_by_account_id AS account_id, SELECT utxos.received_by_account_id AS account_id,
utxos.height AS block, utxos.height AS block,
utxos.prevout_txid AS txid, utxos.prevout_txid AS txid,
{transparent_pool_code} AS pool, {transparent_pool_code} AS pool,
utxos.id AS id_within_pool_table,
utxos.value_zat AS value, utxos.value_zat AS value,
0 AS is_change, 0 AS is_change,
1 AS received_count, 1 AS received_count,
0 AS memo_present 0 AS memo_present
FROM utxos FROM utxos
UNION UNION
SELECT v_received_notes.id AS id, -- Shielded notes spent in this transaction
v_received_notes.account_id AS account_id, SELECT v_received_notes.account_id AS account_id,
transactions.block AS block, transactions.block AS block,
transactions.txid AS txid, transactions.txid AS txid,
v_received_notes.pool AS pool, v_received_notes.pool AS pool,
id_within_pool_table,
-v_received_notes.value AS value, -v_received_notes.value AS value,
0 AS is_change, 0 AS is_change,
0 AS received_count, 0 AS received_count,
@ -157,11 +160,12 @@ impl RusqliteMigration for Migration {
JOIN transactions JOIN transactions
ON transactions.id_tx = v_received_notes.spent ON transactions.id_tx = v_received_notes.spent
UNION UNION
SELECT utxos.id AS id, -- Transparent TXOs spent in this transaction
utxos.received_by_account_id AS account_id, SELECT utxos.received_by_account_id AS account_id,
transactions.block AS block, transactions.block AS block,
transactions.txid AS txid, transactions.txid AS txid,
{transparent_pool_code} AS pool, {transparent_pool_code} AS pool,
utxos.id AS id_within_pool_table,
-utxos.value_zat AS value, -utxos.value_zat AS value,
0 AS is_change, 0 AS is_change,
0 AS received_count, 0 AS received_count,
@ -170,6 +174,8 @@ impl RusqliteMigration for Migration {
JOIN transactions JOIN transactions
ON transactions.id_tx = utxos.spent_in_tx ON transactions.id_tx = utxos.spent_in_tx
), ),
-- Obtain a count of the notes that the wallet created in each transaction,
-- not counting change notes.
sent_note_counts AS ( sent_note_counts AS (
SELECT sent_notes.from_account_id AS account_id, SELECT sent_notes.from_account_id AS account_id,
transactions.txid AS txid, transactions.txid AS txid,