zcash_client_sqlite: Fix ambiguities in transaction views
Co-authored-by: Kris Nuttycombe <kris@nutty.land>
This commit is contained in:
parent
1775f6525b
commit
8b8757ce65
|
@ -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.
|
||||||
|
|
|
@ -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")]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue