From 52815657135ef61b9180957ff58ee97416988dbd Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 26 Sep 2024 21:37:37 -0400 Subject: [PATCH] add spending tx ids for spent outpoints to non-finalized chains --- zebra-state/src/service/check/tests/utxo.rs | 10 +++++----- zebra-state/src/service/check/utxo.rs | 10 +++++----- .../src/service/non_finalized_state/chain.rs | 20 +++++++++---------- zebra-state/src/service/read/block.rs | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/zebra-state/src/service/check/tests/utxo.rs b/zebra-state/src/service/check/tests/utxo.rs index acdc2d399..0d63359d8 100644 --- a/zebra-state/src/service/check/tests/utxo.rs +++ b/zebra-state/src/service/check/tests/utxo.rs @@ -221,7 +221,7 @@ proptest! { .unwrap(); prop_assert!(!chain.unspent_utxos().contains_key(&expected_outpoint)); prop_assert!(chain.created_utxos.contains_key(&expected_outpoint)); - prop_assert!(chain.spent_utxos.contains(&expected_outpoint)); + prop_assert!(chain.spent_utxos.contains_key(&expected_outpoint)); // the finalized state does not have the UTXO prop_assert!(finalized_state.utxo(&expected_outpoint).is_none()); @@ -310,14 +310,14 @@ proptest! { if use_finalized_state_output { // the chain has spent the UTXO from the finalized state prop_assert!(!chain.created_utxos.contains_key(&expected_outpoint)); - prop_assert!(chain.spent_utxos.contains(&expected_outpoint)); + prop_assert!(chain.spent_utxos.contains_key(&expected_outpoint)); // the finalized state has the UTXO, but it will get deleted on commit prop_assert!(finalized_state.utxo(&expected_outpoint).is_some()); } else { // the chain has spent its own UTXO prop_assert!(!chain.unspent_utxos().contains_key(&expected_outpoint)); prop_assert!(chain.created_utxos.contains_key(&expected_outpoint)); - prop_assert!(chain.spent_utxos.contains(&expected_outpoint)); + prop_assert!(chain.spent_utxos.contains_key(&expected_outpoint)); // the finalized state does not have the UTXO prop_assert!(finalized_state.utxo(&expected_outpoint).is_none()); } @@ -650,12 +650,12 @@ proptest! { // the finalized state has the unspent UTXO prop_assert!(finalized_state.utxo(&expected_outpoint).is_some()); // the non-finalized state has spent the UTXO - prop_assert!(chain.spent_utxos.contains(&expected_outpoint)); + prop_assert!(chain.spent_utxos.contains_key(&expected_outpoint)); } else { // the non-finalized state has created and spent the UTXO prop_assert!(!chain.unspent_utxos().contains_key(&expected_outpoint)); prop_assert!(chain.created_utxos.contains_key(&expected_outpoint)); - prop_assert!(chain.spent_utxos.contains(&expected_outpoint)); + prop_assert!(chain.spent_utxos.contains_key(&expected_outpoint)); // the finalized state does not have the UTXO prop_assert!(finalized_state.utxo(&expected_outpoint).is_none()); } diff --git a/zebra-state/src/service/check/utxo.rs b/zebra-state/src/service/check/utxo.rs index 324efa3c0..27035629e 100644 --- a/zebra-state/src/service/check/utxo.rs +++ b/zebra-state/src/service/check/utxo.rs @@ -1,9 +1,9 @@ //! Consensus rule checks for the finalized state. -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use zebra_chain::{ - amount, + amount, transaction, transparent::{self, utxos_from_ordered_utxos, CoinbaseSpendRestriction::*}, }; @@ -38,7 +38,7 @@ use crate::{ pub fn transparent_spend( semantically_verified: &SemanticallyVerifiedBlock, non_finalized_chain_unspent_utxos: &HashMap, - non_finalized_chain_spent_utxos: &HashSet, + non_finalized_chain_spent_utxos: &HashMap, finalized_state: &ZebraDb, ) -> Result, ValidateContextError> { let mut block_spends = HashMap::new(); @@ -126,7 +126,7 @@ fn transparent_spend_chain_order( spend_tx_index_in_block: usize, block_new_outputs: &HashMap, non_finalized_chain_unspent_utxos: &HashMap, - non_finalized_chain_spent_utxos: &HashSet, + non_finalized_chain_spent_utxos: &HashMap, finalized_state: &ZebraDb, ) -> Result { if let Some(output) = block_new_outputs.get(&spend) { @@ -146,7 +146,7 @@ fn transparent_spend_chain_order( } } - if non_finalized_chain_spent_utxos.contains(&spend) { + if non_finalized_chain_spent_utxos.contains_key(&spend) { // reject the spend if its UTXO is already spent in the // non-finalized parent chain return Err(DuplicateTransparentSpend { diff --git a/zebra-state/src/service/non_finalized_state/chain.rs b/zebra-state/src/service/non_finalized_state/chain.rs index a390d6c98..351a7b7cc 100644 --- a/zebra-state/src/service/non_finalized_state/chain.rs +++ b/zebra-state/src/service/non_finalized_state/chain.rs @@ -90,18 +90,15 @@ pub struct ChainInner { // // TODO: replace OutPoint with OutputLocation? pub(crate) created_utxos: HashMap, - /// The [`transparent::OutPoint`]s spent by `blocks`, + /// The spending transaction ids by [`transparent::OutPoint`]s spent by `blocks`, /// including those created by earlier transactions or blocks in the chain. - pub(crate) spent_utxos: HashSet, - // 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 + pub(crate) spent_utxos: HashMap, // Note commitment trees // @@ -1249,7 +1246,7 @@ impl Chain { /// and removed from the relevant chain(s). pub fn unspent_utxos(&self) -> HashMap { let mut unspent_utxos = self.created_utxos.clone(); - unspent_utxos.retain(|outpoint, _utxo| !self.spent_utxos.contains(outpoint)); + unspent_utxos.retain(|outpoint, _utxo| !self.spent_utxos.contains_key(outpoint)); unspent_utxos } @@ -1854,9 +1851,12 @@ impl }; // Index the spent outpoint in the chain - let first_spend = self.spent_utxos.insert(spent_outpoint); + let was_spend_already_present = self + .spent_utxos + .insert(spent_outpoint, *spending_tx_hash) + .is_some(); assert!( - first_spend, + was_spend_already_present, "unexpected duplicate spent output: should be checked earlier" ); @@ -1904,9 +1904,9 @@ impl }; // Revert the spent outpoint in the chain - let spent_outpoint_was_removed = self.spent_utxos.remove(&spent_outpoint); + let was_spent_outpoint_removed = self.spent_utxos.remove(&spent_outpoint).is_some(); assert!( - spent_outpoint_was_removed, + was_spent_outpoint_removed, "spent_utxos must be present if block was added to chain" ); diff --git a/zebra-state/src/service/read/block.rs b/zebra-state/src/service/read/block.rs index 283fe9ddc..ba527f74a 100644 --- a/zebra-state/src/service/read/block.rs +++ b/zebra-state/src/service/read/block.rs @@ -176,7 +176,7 @@ where C: AsRef, { match chain { - Some(chain) if chain.as_ref().spent_utxos.contains(&outpoint) => None, + Some(chain) if chain.as_ref().spent_utxos.contains_key(&outpoint) => None, chain => utxo(chain, db, outpoint), } }