Skip to content

Commit

Permalink
Add API to delete spent UTXOs from database
Browse files Browse the repository at this point in the history
We currently store spent UTXOs in the database
with an `is_spent` field and we don't provide
users with a way to delete these UTXOs. The
issue here is that these could easily bloat the
database or memory. This PR provides methods
that can allow users to delete spent UTXOs
from the database following certain criteria
like size of spent utxos and number of confirmations.
This PR fixes issue #573
  • Loading branch information
vladimirfomene committed Sep 1, 2022
1 parent 9f9ffd0 commit 3850be6
Showing 1 changed file with 77 additions and 0 deletions.
77 changes: 77 additions & 0 deletions src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ pub struct SyncTime {
pub block_time: BlockTime,
}

/// Structure encapsulates criteria used for deleting
/// spent UTXOs
#[derive(Clone, Debug)]
pub struct DelCriteria {
/// Number of confirmations on UTXOs to make it eligible for deletion
pub confirmations: Option<u32>,
/// Max number of allowable spent UTXOs in the database
pub threshold_size: Option<u64>,
}

/// Trait for operations that can be batched
///
/// This trait defines the list of operations that must be implemented on the [`Database`] type and
Expand Down Expand Up @@ -158,6 +168,73 @@ pub trait Database: BatchOperations {
///
/// It should insert and return `0` if not present in the database
fn increment_last_index(&mut self, keychain: KeychainKind) -> Result<u32, Error>;

/// Delete UTXOs provided as params, if `spent_utxos` is empty it deletes all spent
/// UTXOs in the database.
fn del_spent_utxos(
&mut self,
spent_outpoints: Option<Vec<OutPoint>>,
) -> Result<Vec<LocalUtxo>, Error> {
if let Some(spent_outpoints) = spent_outpoints {
let deleted_utxos = spent_outpoints
.iter()
.filter_map(|out| self.del_utxo(out).transpose())
.collect::<Result<Vec<_>, _>>()?;
Ok(deleted_utxos)
} else {
let deleted_utxos = self
.iter_utxos()?
.iter()
.filter(|utxo| utxo.is_spent)
.filter_map(|out| self.del_utxo(&out.outpoint).transpose())
.collect::<Result<Vec<_>, _>>()?;
Ok(deleted_utxos)
}
}

/// Delete UTXOs based on the number of confirmations or
/// threshold size (number of spent utxos in DB) defined in [`DelCriteria`]
fn del_spent_utxos_by_criteria(
&mut self,
criteria: DelCriteria,
) -> Result<Vec<LocalUtxo>, Error> {
let spent_utxos: Vec<LocalUtxo> = self
.iter_utxos()?
.into_iter()
.filter(|utxo| utxo.is_spent)
.collect();
if let Some(threshold_size) = criteria.threshold_size {
// if the threshold size is smaller than current size, delete everything
if threshold_size < spent_utxos.len() as u64 {
//delete all spent utxos
return self.del_spent_utxos(None);
}
}

let mut filtered_outpoints: Vec<OutPoint> = vec![];

// get utxos that matches criteria and delete them
match self.get_sync_time()? {
Some(sync_time) => {
for spent_utxo in spent_utxos {
let tx_details = self.get_tx(&spent_utxo.outpoint.txid, false)?;
let height = tx_details
.and_then(|details| details.confirmation_time)
.map(|conf| conf.height);
let confirmations = criteria.confirmations;
if height.is_some()
&& confirmations.is_some()
&& ((sync_time.block_time.height - height.unwrap())
> confirmations.unwrap())
{
filtered_outpoints.push(spent_utxo.outpoint);
}
}
Ok(self.del_spent_utxos(Some(filtered_outpoints))?)
}
None => Ok(vec![]),
}
}
}

/// Trait for a database that supports batch operations
Expand Down

0 comments on commit 3850be6

Please sign in to comment.