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,
};
use zcash_protocol::local_consensus::LocalNetwork;
use zcash_protocol::value::{ZatBalance, Zatoshis};
use crate::{
chain::init::init_cache_database,
@ -958,6 +959,105 @@ impl<Cache> TestState<Cache> {
)
.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.

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)),
Ok(None)
);
let tx_history = st.get_tx_history().unwrap();
assert_eq!(tx_history.len(), 2);
}
#[cfg(feature = "transparent-inputs")]

View File

@ -552,14 +552,14 @@ mod tests {
// v_received_notes
"CREATE VIEW v_received_notes AS
SELECT
id,
tx,
sapling_received_notes.id AS id_within_pool_table,
sapling_received_notes.tx,
2 AS pool,
sapling_received_notes.output_index AS output_index,
account_id,
value,
sapling_received_notes.value,
is_change,
memo,
sapling_received_notes.memo,
spent,
sent_notes.id AS sent_note_id
FROM sapling_received_notes
@ -568,14 +568,14 @@ mod tests {
(sapling_received_notes.tx, 2, sapling_received_notes.output_index)
UNION
SELECT
id,
tx,
orchard_received_notes.id AS id_within_pool_table,
orchard_received_notes.tx,
3 AS pool,
orchard_received_notes.action_index AS output_index,
account_id,
value,
orchard_received_notes.value,
is_change,
memo,
orchard_received_notes.memo,
spent,
sent_notes.id AS sent_note_id
FROM orchard_received_notes
@ -650,11 +650,12 @@ mod tests {
"CREATE VIEW v_transactions AS
WITH
notes AS (
SELECT v_received_notes.id AS id,
v_received_notes.account_id AS account_id,
-- Shielded notes received in this transaction
SELECT v_received_notes.account_id AS account_id,
transactions.block AS block,
transactions.txid AS txid,
v_received_notes.pool AS pool,
id_within_pool_table,
v_received_notes.value AS value,
CASE
WHEN v_received_notes.is_change THEN 1
@ -673,22 +674,24 @@ mod tests {
JOIN transactions
ON transactions.id_tx = v_received_notes.tx
UNION
SELECT utxos.id AS id,
utxos.received_by_account_id AS account_id,
-- Transparent TXOs received in this transaction
SELECT utxos.received_by_account_id AS account_id,
utxos.height AS block,
utxos.prevout_txid AS txid,
0 AS pool,
utxos.id AS id_within_pool_table,
utxos.value_zat AS value,
0 AS is_change,
1 AS received_count,
0 AS memo_present
FROM utxos
UNION
SELECT v_received_notes.id AS id,
v_received_notes.account_id AS account_id,
-- Shielded notes spent in this transaction
SELECT v_received_notes.account_id AS account_id,
transactions.block AS block,
transactions.txid AS txid,
v_received_notes.pool AS pool,
id_within_pool_table,
-v_received_notes.value AS value,
0 AS is_change,
0 AS received_count,
@ -697,11 +700,12 @@ mod tests {
JOIN transactions
ON transactions.id_tx = v_received_notes.spent
UNION
SELECT utxos.id AS id,
utxos.received_by_account_id AS account_id,
-- Transparent TXOs spent in this transaction
SELECT utxos.received_by_account_id AS account_id,
transactions.block AS block,
transactions.txid AS txid,
0 AS pool,
utxos.id AS id_within_pool_table,
-utxos.value_zat AS value,
0 AS is_change,
0 AS received_count,
@ -710,6 +714,8 @@ mod tests {
JOIN transactions
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 (
SELECT sent_notes.from_account_id AS account_id,
transactions.txid AS txid,

View File

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