Adds new column family for [spent_out_loc] -> [spending_tx_loc] with a read method and an update to `prepare_spending_transparent_tx_ids_batch()` for maintaining it when committing blocks to the finalized state.

Adds TODOs for remaining production changes needed for issue #8837.
This commit is contained in:
Arya 2024-09-24 23:44:38 -04:00
parent 4f0746a613
commit e9b39306c9
4 changed files with 72 additions and 7 deletions

View File

@ -20,6 +20,7 @@ use std::{
};
use zebra_chain::{block, parallel::tree::NoteCommitmentTrees, parameters::Network};
use zebra_db::transparent::TX_LOC_BY_SPENT_OUT_LOC;
use crate::{
constants::{state_database_format_version_in_code, STATE_DATABASE_KIND},
@ -77,6 +78,7 @@ pub const STATE_COLUMN_FAMILIES_IN_CODE: &[&str] = &[
"tx_loc_by_transparent_addr_loc",
"utxo_by_out_loc",
"utxo_loc_by_transparent_addr_loc",
TX_LOC_BY_SPENT_OUT_LOC,
// Sprout
"sprout_nullifiers",
"sprout_anchors",

View File

@ -355,6 +355,10 @@ impl ZebraDb {
.iter()
.map(|(outpoint, _output_loc, utxo)| (*outpoint, utxo.clone()))
.collect();
let out_loc_by_outpoint: HashMap<transparent::OutPoint, OutputLocation> = spent_utxos
.iter()
.map(|(outpoint, out_loc, _utxo)| (*outpoint, *out_loc))
.collect();
let spent_utxos_by_out_loc: BTreeMap<OutputLocation, transparent::Utxo> = spent_utxos
.into_iter()
.map(|(_outpoint, out_loc, utxo)| (out_loc, utxo))
@ -392,6 +396,7 @@ impl ZebraDb {
new_outputs_by_out_loc,
spent_utxos_by_outpoint,
spent_utxos_by_out_loc,
out_loc_by_outpoint,
address_balances,
self.finalized_value_pool(),
prev_note_commitment_trees,
@ -448,6 +453,7 @@ impl DiskWriteBatch {
new_outputs_by_out_loc: BTreeMap<OutputLocation, transparent::Utxo>,
spent_utxos_by_outpoint: HashMap<transparent::OutPoint, transparent::Utxo>,
spent_utxos_by_out_loc: BTreeMap<OutputLocation, transparent::Utxo>,
out_loc_by_outpoint: HashMap<transparent::OutPoint, OutputLocation>,
address_balances: HashMap<transparent::Address, AddressBalanceLocation>,
value_pool: ValueBalance<NonNegative>,
prev_note_commitment_trees: Option<NoteCommitmentTrees>,
@ -479,12 +485,13 @@ impl DiskWriteBatch {
if !finalized.height.is_min() {
// Commit transaction indexes
self.prepare_transparent_transaction_batch(
db,
zebra_db,
network,
finalized,
&new_outputs_by_out_loc,
&spent_utxos_by_outpoint,
&spent_utxos_by_out_loc,
&out_loc_by_outpoint,
address_balances,
)?;

View File

@ -1,5 +1,6 @@
//! Provides high-level access to database:
//! - unspent [`transparent::Output`]s (UTXOs), and
//! - unspent [`transparent::Output`]s (UTXOs),
//! - spent [`transparent::Output`]s, and
//! - transparent address indexes.
//!
//! This module makes sure that:
@ -37,12 +38,44 @@ use crate::{
},
zebra_db::ZebraDb,
},
BoxError,
BoxError, TypedColumnFamily,
};
/// The name of the transaction hash by spent outpoints column family.
///
/// This constant should be used so the compiler can detect typos.
pub const TX_LOC_BY_SPENT_OUT_LOC: &str = "tx_loc_by_spent_out_loc";
/// The type for reading value pools from the database.
///
/// This constant should be used so the compiler can detect incorrectly typed accesses to the
/// column family.
pub type TransactionLocationBySpentOutputLocationCf<'cf> =
TypedColumnFamily<'cf, OutputLocation, TransactionLocation>;
impl ZebraDb {
// Column family convenience methods
/// Returns a typed handle to the transaction location by spent output location column family.
pub(crate) fn tx_loc_by_spent_output_loc_cf(
&self,
) -> TransactionLocationBySpentOutputLocationCf {
TransactionLocationBySpentOutputLocationCf::new(&self.db, TX_LOC_BY_SPENT_OUT_LOC)
.expect("column family was created when database was created")
}
// Read transparent methods
/// Returns the [`TransactionLocation`] for a transaction that spent the output
/// at the provided [`OutputLocation`], if it is in the finalized state.
#[allow(clippy::unwrap_in_result)]
pub fn tx_loc_by_spent_output_loc(
&self,
output_location: &OutputLocation,
) -> Option<TransactionLocation> {
self.tx_loc_by_spent_output_loc_cf().zs_get(output_location)
}
/// Returns the [`AddressBalanceLocation`] for a [`transparent::Address`],
/// if it is in the finalized state.
#[allow(clippy::unwrap_in_result)]
@ -342,14 +375,16 @@ impl DiskWriteBatch {
#[allow(clippy::too_many_arguments)]
pub fn prepare_transparent_transaction_batch(
&mut self,
db: &DiskDb,
zebra_db: &ZebraDb,
network: &Network,
finalized: &FinalizedBlock,
new_outputs_by_out_loc: &BTreeMap<OutputLocation, transparent::Utxo>,
spent_utxos_by_outpoint: &HashMap<transparent::OutPoint, transparent::Utxo>,
spent_utxos_by_out_loc: &BTreeMap<OutputLocation, transparent::Utxo>,
out_loc_by_outpoint: &HashMap<transparent::OutPoint, OutputLocation>,
mut address_balances: HashMap<transparent::Address, AddressBalanceLocation>,
) -> Result<(), BoxError> {
let db = &zebra_db.db;
let FinalizedBlock { block, height, .. } = finalized;
// Update created and spent transparent outputs
@ -371,11 +406,12 @@ impl DiskWriteBatch {
let spending_tx_location = TransactionLocation::from_usize(*height, tx_index);
self.prepare_spending_transparent_tx_ids_batch(
db,
zebra_db,
network,
spending_tx_location,
transaction,
spent_utxos_by_outpoint,
out_loc_by_outpoint,
&address_balances,
)?;
}
@ -531,16 +567,18 @@ impl DiskWriteBatch {
/// # Errors
///
/// - This method doesn't currently return any errors, but it might in future
#[allow(clippy::unwrap_in_result)]
#[allow(clippy::unwrap_in_result, clippy::too_many_arguments)]
pub fn prepare_spending_transparent_tx_ids_batch(
&mut self,
db: &DiskDb,
zebra_db: &ZebraDb,
network: &Network,
spending_tx_location: TransactionLocation,
transaction: &Transaction,
spent_utxos_by_outpoint: &HashMap<transparent::OutPoint, transparent::Utxo>,
out_loc_by_outpoint: &HashMap<transparent::OutPoint, OutputLocation>,
address_balances: &HashMap<transparent::Address, AddressBalanceLocation>,
) -> Result<(), BoxError> {
let db = &zebra_db.db;
let tx_loc_by_transparent_addr_loc =
db.cf_handle("tx_loc_by_transparent_addr_loc").unwrap();
@ -569,6 +607,15 @@ impl DiskWriteBatch {
AddressTransaction::new(sending_address_location, spending_tx_location);
self.zs_insert(&tx_loc_by_transparent_addr_loc, address_transaction, ());
}
let spent_output_location = out_loc_by_outpoint
.get(&spent_outpoint)
.expect("spent outpoints must already have output locations");
let _ = zebra_db
.tx_loc_by_spent_output_loc_cf()
.with_batch_for_writing(self)
.zs_insert(spent_output_location, &spending_tx_location);
}
Ok(())

View File

@ -94,6 +94,15 @@ pub struct ChainInner {
/// including those created by earlier transactions or blocks in the chain.
pub(crate) spent_utxos: HashSet<transparent::OutPoint>,
// TODO:
// - Add a field for tracking spending tx ids by spent outpoint
// - Update the field when committing blocks to non-finalized chain
// - Add a read fn for querying tx ids by spent outpoint
// - Add a db format upgrade for indexing spending tx ids (transaction locations) by
// spent outpoints (output locations) in the finalized state
// - Add ReadRequest & ReadResponse variants for querying spending tx ids by
// spent outpoints and handle them in the ReadStateService
// Note commitment trees
//
/// The Sprout note commitment tree for each anchor.