zcash_client_sqlite: Add an internal newtype for transaction primary key values.

This commit is contained in:
Kris Nuttycombe 2024-07-29 11:41:56 -06:00
parent 7f7b685b99
commit 301e8b497c
7 changed files with 39 additions and 33 deletions

View File

@ -177,11 +177,14 @@ impl fmt::Display for ReceivedNoteId {
} }
} }
/// A newtype wrapper for sqlite primary key values for the utxos /// A newtype wrapper for sqlite primary key values for the utxos table.
/// table.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct UtxoId(pub i64); pub struct UtxoId(pub i64);
/// A newtype wrapper for sqlite primary key values for the transactions table.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct TxRef(pub i64);
/// A wrapper for the SQLite connection to the wallet database. /// A wrapper for the SQLite connection to the wallet database.
pub struct WalletDb<C, P> { pub struct WalletDb<C, P> {
conn: C, conn: C,

View File

@ -108,6 +108,7 @@ use zcash_primitives::{
}; };
use zip32::{self, DiversifierIndex, Scope}; use zip32::{self, DiversifierIndex, Scope};
use crate::TxRef;
use crate::{ use crate::{
error::SqliteClientError, error::SqliteClientError,
wallet::commitment_tree::{get_max_checkpointed_height, SqliteShardStore}, wallet::commitment_tree::{get_max_checkpointed_height, SqliteShardStore},
@ -2199,7 +2200,7 @@ pub(crate) fn put_tx_meta(
conn: &rusqlite::Connection, conn: &rusqlite::Connection,
tx: &WalletTx<AccountId>, tx: &WalletTx<AccountId>,
height: BlockHeight, height: BlockHeight,
) -> Result<i64, SqliteClientError> { ) -> Result<TxRef, SqliteClientError> {
// It isn't there, so insert our transaction into the database. // It isn't there, so insert our transaction into the database.
let mut stmt_upsert_tx_meta = conn.prepare_cached( let mut stmt_upsert_tx_meta = conn.prepare_cached(
"INSERT INTO transactions (txid, block, mined_height, tx_index) "INSERT INTO transactions (txid, block, mined_height, tx_index)
@ -2219,7 +2220,7 @@ pub(crate) fn put_tx_meta(
]; ];
stmt_upsert_tx_meta stmt_upsert_tx_meta
.query_row(tx_params, |row| row.get::<_, i64>(0)) .query_row(tx_params, |row| row.get::<_, i64>(0).map(TxRef))
.map_err(SqliteClientError::from) .map_err(SqliteClientError::from)
} }
@ -2271,7 +2272,7 @@ pub(crate) fn put_tx_data(
tx: &Transaction, tx: &Transaction,
fee: Option<NonNegativeAmount>, fee: Option<NonNegativeAmount>,
created_at: Option<time::OffsetDateTime>, created_at: Option<time::OffsetDateTime>,
) -> Result<i64, SqliteClientError> { ) -> Result<TxRef, SqliteClientError> {
let mut stmt_upsert_tx_data = conn.prepare_cached( let mut stmt_upsert_tx_data = conn.prepare_cached(
"INSERT INTO transactions (txid, created, expiry_height, raw, fee) "INSERT INTO transactions (txid, created, expiry_height, raw, fee)
VALUES (:txid, :created_at, :expiry_height, :raw, :fee) VALUES (:txid, :created_at, :expiry_height, :raw, :fee)
@ -2295,7 +2296,7 @@ pub(crate) fn put_tx_data(
]; ];
stmt_upsert_tx_data stmt_upsert_tx_data
.query_row(tx_params, |row| row.get::<_, i64>(0)) .query_row(tx_params, |row| row.get::<_, i64>(0).map(TxRef))
.map_err(SqliteClientError::from) .map_err(SqliteClientError::from)
} }
@ -2332,7 +2333,7 @@ fn recipient_params<P: consensus::Parameters>(
pub(crate) fn insert_sent_output<P: consensus::Parameters>( pub(crate) fn insert_sent_output<P: consensus::Parameters>(
conn: &rusqlite::Connection, conn: &rusqlite::Connection,
params: &P, params: &P,
tx_ref: i64, tx_ref: TxRef,
from_account: AccountId, from_account: AccountId,
output: &SentTransactionOutput<AccountId>, output: &SentTransactionOutput<AccountId>,
) -> Result<(), SqliteClientError> { ) -> Result<(), SqliteClientError> {
@ -2347,7 +2348,7 @@ pub(crate) fn insert_sent_output<P: consensus::Parameters>(
let (to_address, to_account_id, pool_type) = recipient_params(params, output.recipient()); let (to_address, to_account_id, pool_type) = recipient_params(params, output.recipient());
let sql_args = named_params![ let sql_args = named_params![
":tx": &tx_ref, ":tx": tx_ref.0,
":output_pool": &pool_code(pool_type), ":output_pool": &pool_code(pool_type),
":output_index": &i64::try_from(output.output_index()).unwrap(), ":output_index": &i64::try_from(output.output_index()).unwrap(),
":from_account_id": from_account.0, ":from_account_id": from_account.0,
@ -2378,7 +2379,7 @@ pub(crate) fn put_sent_output<P: consensus::Parameters>(
conn: &rusqlite::Connection, conn: &rusqlite::Connection,
params: &P, params: &P,
from_account: AccountId, from_account: AccountId,
tx_ref: i64, tx_ref: TxRef,
output_index: usize, output_index: usize,
recipient: &Recipient<AccountId, Note, OutPoint>, recipient: &Recipient<AccountId, Note, OutPoint>,
value: NonNegativeAmount, value: NonNegativeAmount,
@ -2401,7 +2402,7 @@ pub(crate) fn put_sent_output<P: consensus::Parameters>(
let (to_address, to_account_id, pool_type) = recipient_params(params, recipient); let (to_address, to_account_id, pool_type) = recipient_params(params, recipient);
let sql_args = named_params![ let sql_args = named_params![
":tx": &tx_ref, ":tx": tx_ref.0,
":output_pool": &pool_code(pool_type), ":output_pool": &pool_code(pool_type),
":output_index": &i64::try_from(output_index).unwrap(), ":output_index": &i64::try_from(output_index).unwrap(),
":from_account_id": from_account.0, ":from_account_id": from_account.0,
@ -2515,7 +2516,7 @@ pub(crate) fn query_nullifier_map<N: AsRef<[u8]>>(
conn: &rusqlite::Transaction<'_>, conn: &rusqlite::Transaction<'_>,
spend_pool: ShieldedProtocol, spend_pool: ShieldedProtocol,
nf: &N, nf: &N,
) -> Result<Option<i64>, SqliteClientError> { ) -> Result<Option<TxRef>, SqliteClientError> {
let mut stmt_select_locator = conn.prepare_cached( let mut stmt_select_locator = conn.prepare_cached(
"SELECT block_height, tx_index, txid "SELECT block_height, tx_index, txid
FROM nullifier_map FROM nullifier_map

View File

@ -516,7 +516,7 @@ mod tests {
// Don't need to bother with sent outputs for this test. // Don't need to bother with sent outputs for this test.
if output.transfer_type() != TransferType::Outgoing { if output.transfer_type() != TransferType::Outgoing {
put_received_note_before_migration( put_received_note_before_migration(
wdb.conn.0, output, tx_ref, None, wdb.conn.0, output, tx_ref.0, None,
) )
.unwrap(); .unwrap();
} }
@ -529,7 +529,7 @@ mod tests {
} }
} }
put_received_note_before_migration(wdb.conn.0, output, tx_ref, None) put_received_note_before_migration(wdb.conn.0, output, tx_ref.0, None)
.unwrap(); .unwrap();
} }
} }

View File

@ -21,7 +21,7 @@ use zcash_protocol::{
}; };
use zip32::Scope; use zip32::Scope;
use crate::{error::SqliteClientError, AccountId, ReceivedNoteId}; use crate::{error::SqliteClientError, AccountId, ReceivedNoteId, TxRef};
use super::{memo_repr, parse_scope, scope_code}; use super::{memo_repr, parse_scope, scope_code};
@ -227,8 +227,8 @@ pub(crate) fn select_spendable_orchard_notes<P: consensus::Parameters>(
pub(crate) fn put_received_note<T: ReceivedOrchardOutput>( pub(crate) fn put_received_note<T: ReceivedOrchardOutput>(
conn: &Transaction, conn: &Transaction,
output: &T, output: &T,
tx_ref: i64, tx_ref: TxRef,
spent_in: Option<i64>, spent_in: Option<TxRef>,
) -> Result<(), SqliteClientError> { ) -> Result<(), SqliteClientError> {
let mut stmt_upsert_received_note = conn.prepare_cached( let mut stmt_upsert_received_note = conn.prepare_cached(
"INSERT INTO orchard_received_notes "INSERT INTO orchard_received_notes
@ -263,7 +263,7 @@ pub(crate) fn put_received_note<T: ReceivedOrchardOutput>(
let diversifier = to.diversifier(); let diversifier = to.diversifier();
let sql_args = named_params![ let sql_args = named_params![
":tx": &tx_ref, ":tx": tx_ref.0,
":action_index": i64::try_from(output.index()).expect("output indices are representable as i64"), ":action_index": i64::try_from(output.index()).expect("output indices are representable as i64"),
":account_id": output.account_id().0, ":account_id": output.account_id().0,
":diversifier": diversifier.as_array(), ":diversifier": diversifier.as_array(),
@ -288,7 +288,7 @@ pub(crate) fn put_received_note<T: ReceivedOrchardOutput>(
ON CONFLICT (orchard_received_note_id, transaction_id) DO NOTHING", ON CONFLICT (orchard_received_note_id, transaction_id) DO NOTHING",
named_params![ named_params![
":orchard_received_note_id": received_note_id, ":orchard_received_note_id": received_note_id,
":transaction_id": spent_in ":transaction_id": spent_in.0
], ],
)?; )?;
} }
@ -366,7 +366,7 @@ pub(crate) fn detect_spending_accounts<'a>(
/// spending transaction has been mined. /// spending transaction has been mined.
pub(crate) fn mark_orchard_note_spent( pub(crate) fn mark_orchard_note_spent(
conn: &Connection, conn: &Connection,
tx_ref: i64, tx_ref: TxRef,
nf: &Nullifier, nf: &Nullifier,
) -> Result<bool, SqliteClientError> { ) -> Result<bool, SqliteClientError> {
let mut stmt_mark_orchard_note_spent = conn.prepare_cached( let mut stmt_mark_orchard_note_spent = conn.prepare_cached(
@ -377,7 +377,7 @@ pub(crate) fn mark_orchard_note_spent(
match stmt_mark_orchard_note_spent.execute(named_params![ match stmt_mark_orchard_note_spent.execute(named_params![
":nf": nf.to_bytes(), ":nf": nf.to_bytes(),
":transaction_id": tx_ref ":transaction_id": tx_ref.0
])? { ])? {
0 => Ok(false), 0 => Ok(false),
1 => Ok(true), 1 => Ok(true),

View File

@ -20,7 +20,7 @@ use zcash_protocol::{
}; };
use zip32::Scope; use zip32::Scope;
use crate::{error::SqliteClientError, AccountId, ReceivedNoteId}; use crate::{error::SqliteClientError, AccountId, ReceivedNoteId, TxRef};
use super::{memo_repr, parse_scope, scope_code}; use super::{memo_repr, parse_scope, scope_code};
@ -300,7 +300,7 @@ pub(crate) fn detect_spending_accounts<'a>(
/// spending transaction has been mined. /// spending transaction has been mined.
pub(crate) fn mark_sapling_note_spent( pub(crate) fn mark_sapling_note_spent(
conn: &Connection, conn: &Connection,
tx_ref: i64, tx_ref: TxRef,
nf: &sapling::Nullifier, nf: &sapling::Nullifier,
) -> Result<bool, SqliteClientError> { ) -> Result<bool, SqliteClientError> {
let mut stmt_mark_sapling_note_spent = conn.prepare_cached( let mut stmt_mark_sapling_note_spent = conn.prepare_cached(
@ -311,7 +311,7 @@ pub(crate) fn mark_sapling_note_spent(
match stmt_mark_sapling_note_spent.execute(named_params![ match stmt_mark_sapling_note_spent.execute(named_params![
":nf": &nf.0[..], ":nf": &nf.0[..],
":transaction_id": tx_ref ":transaction_id": tx_ref.0
])? { ])? {
0 => Ok(false), 0 => Ok(false),
1 => Ok(true), 1 => Ok(true),
@ -327,8 +327,8 @@ pub(crate) fn mark_sapling_note_spent(
pub(crate) fn put_received_note<T: ReceivedSaplingOutput>( pub(crate) fn put_received_note<T: ReceivedSaplingOutput>(
conn: &Transaction, conn: &Transaction,
output: &T, output: &T,
tx_ref: i64, tx_ref: TxRef,
spent_in: Option<i64>, spent_in: Option<TxRef>,
) -> Result<(), SqliteClientError> { ) -> Result<(), SqliteClientError> {
let mut stmt_upsert_received_note = conn.prepare_cached( let mut stmt_upsert_received_note = conn.prepare_cached(
"INSERT INTO sapling_received_notes "INSERT INTO sapling_received_notes
@ -366,7 +366,7 @@ pub(crate) fn put_received_note<T: ReceivedSaplingOutput>(
let diversifier = to.diversifier(); let diversifier = to.diversifier();
let sql_args = named_params![ let sql_args = named_params![
":tx": &tx_ref, ":tx": tx_ref.0,
":output_index": i64::try_from(output.index()).expect("output indices are representable as i64"), ":output_index": i64::try_from(output.index()).expect("output indices are representable as i64"),
":account_id": output.account_id().0, ":account_id": output.account_id().0,
":diversifier": &diversifier.0.as_ref(), ":diversifier": &diversifier.0.as_ref(),
@ -390,7 +390,7 @@ pub(crate) fn put_received_note<T: ReceivedSaplingOutput>(
ON CONFLICT (sapling_received_note_id, transaction_id) DO NOTHING", ON CONFLICT (sapling_received_note_id, transaction_id) DO NOTHING",
named_params![ named_params![
":sapling_received_note_id": received_note_id, ":sapling_received_note_id": received_note_id,
":transaction_id": spent_in ":transaction_id": spent_in.0
], ],
)?; )?;
} }

View File

@ -20,6 +20,7 @@ use zcash_primitives::{
}; };
use zcash_protocol::consensus::{self, BlockHeight}; use zcash_protocol::consensus::{self, BlockHeight};
use crate::TxRef;
use crate::{error::SqliteClientError, AccountId, UtxoId}; use crate::{error::SqliteClientError, AccountId, UtxoId};
use super::{chain_tip_height, get_account_ids}; use super::{chain_tip_height, get_account_ids};
@ -429,7 +430,7 @@ pub(crate) fn add_transparent_account_balances(
/// Marks the given UTXO as having been spent. /// Marks the given UTXO as having been spent.
pub(crate) fn mark_transparent_utxo_spent( pub(crate) fn mark_transparent_utxo_spent(
conn: &rusqlite::Connection, conn: &rusqlite::Connection,
tx_ref: i64, tx_ref: TxRef,
outpoint: &OutPoint, outpoint: &OutPoint,
) -> Result<(), SqliteClientError> { ) -> Result<(), SqliteClientError> {
let mut stmt_mark_transparent_utxo_spent = conn.prepare_cached( let mut stmt_mark_transparent_utxo_spent = conn.prepare_cached(
@ -443,7 +444,7 @@ pub(crate) fn mark_transparent_utxo_spent(
)?; )?;
let sql_args = named_params![ let sql_args = named_params![
":spent_in_tx": &tx_ref, ":spent_in_tx": tx_ref.0,
":prevout_txid": &outpoint.hash().to_vec(), ":prevout_txid": &outpoint.hash().to_vec(),
":prevout_idx": &outpoint.n(), ":prevout_idx": &outpoint.n(),
]; ];

View File

@ -15,6 +15,7 @@ use zcash_primitives::{
}; };
use zcash_protocol::consensus; use zcash_protocol::consensus;
use crate::TxRef;
use crate::{ use crate::{
error::SqliteClientError, error::SqliteClientError,
wallet::{get_account, GAP_LIMIT}, wallet::{get_account, GAP_LIMIT},
@ -360,7 +361,7 @@ fn ephemeral_address_reuse_check<P: consensus::Parameters>(
pub(crate) fn mark_ephemeral_address_as_used<P: consensus::Parameters>( pub(crate) fn mark_ephemeral_address_as_used<P: consensus::Parameters>(
wdb: &mut WalletDb<SqlTransaction<'_>, P>, wdb: &mut WalletDb<SqlTransaction<'_>, P>,
ephemeral_address: &TransparentAddress, ephemeral_address: &TransparentAddress,
tx_ref: i64, tx_ref: TxRef,
) -> Result<(), SqliteClientError> { ) -> Result<(), SqliteClientError> {
let address_str = ephemeral_address.encode(&wdb.params); let address_str = ephemeral_address.encode(&wdb.params);
ephemeral_address_reuse_check(wdb, &address_str)?; ephemeral_address_reuse_check(wdb, &address_str)?;
@ -377,7 +378,7 @@ pub(crate) fn mark_ephemeral_address_as_used<P: consensus::Parameters>(
SET used_in_tx = :tx_ref, seen_in_tx = :tx_ref SET used_in_tx = :tx_ref, seen_in_tx = :tx_ref
WHERE address = :address WHERE address = :address
RETURNING account_id, address_index", RETURNING account_id, address_index",
named_params![":tx_ref": &tx_ref, ":address": address_str], named_params![":tx_ref": tx_ref.0, ":address": address_str],
|row| Ok((AccountId(row.get::<_, u32>(0)?), row.get::<_, u32>(1)?)), |row| Ok((AccountId(row.get::<_, u32>(0)?), row.get::<_, u32>(1)?)),
) )
.optional()?; .optional()?;
@ -398,7 +399,7 @@ pub(crate) fn mark_ephemeral_address_as_used<P: consensus::Parameters>(
pub(crate) fn mark_ephemeral_address_as_seen<P: consensus::Parameters>( pub(crate) fn mark_ephemeral_address_as_seen<P: consensus::Parameters>(
wdb: &mut WalletDb<SqlTransaction<'_>, P>, wdb: &mut WalletDb<SqlTransaction<'_>, P>,
address: &TransparentAddress, address: &TransparentAddress,
tx_ref: i64, tx_ref: TxRef,
) -> Result<(), SqliteClientError> { ) -> Result<(), SqliteClientError> {
let address_str = address.encode(&wdb.params); let address_str = address.encode(&wdb.params);
@ -419,7 +420,7 @@ pub(crate) fn mark_ephemeral_address_as_seen<P: consensus::Parameters>(
tx_index ASC NULLS LAST, tx_index ASC NULLS LAST,
e.seen_in_tx ASC NULLS LAST e.seen_in_tx ASC NULLS LAST
LIMIT 1", LIMIT 1",
named_params![":tx_ref": &tx_ref, ":address": address_str], named_params![":tx_ref": tx_ref.0, ":address": address_str],
|row| row.get::<_, i64>(0), |row| row.get::<_, i64>(0),
)?; )?;