use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::{Sats, StoredBool, StoredU64, TxIndex};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec};
use super::Vecs;
use crate::{indexes, transactions::fees};
const FIRST_EPHEMERAL_DUST_HEIGHT: usize = 905_000;
const WRITE_INTERVAL: usize = 10_000;
impl Vecs {
pub(crate) fn compute(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
fees: &fees::Vecs,
exit: &Exit,
) -> Result<()> {
let features = &indexer.vecs.transaction_features;
let version = features.is_unconditionally_nonstandard.version()
+ features.has_dust_output.version()
+ fees.fee.tx_index.version()
+ indexer.vecs.transactions.first_tx_index.version()
+ indexes.height.tx_index_count.version();
self.is_nonstandard
.validate_computed_version_or_reset(version)?;
self.count.validate_computed_version_or_reset(version)?;
let starting_lengths = indexer.safe_lengths();
let target_tx = fees.fee.tx_index.len();
let target_height = indexes.height.tx_index_count.len();
let tx_len = self
.is_nonstandard
.len()
.min(starting_lengths.tx_index.to_usize());
let count_len = self.count.len().min(starting_lengths.height.to_usize());
let next_height = if tx_len >= target_tx {
target_height
} else {
indexes
.tx_heights
.get_shared(TxIndex::from(tx_len))
.unwrap()
.to_usize()
};
let start_height = count_len.min(next_height);
if start_height >= target_height {
return Ok(());
}
let first_tx = &indexer.vecs.transactions.first_tx_index;
let start_tx = first_tx.collect_one_at(start_height).unwrap().to_usize();
self.is_nonstandard.truncate_if_needed_at(start_tx)?;
self.count.truncate_if_needed_at(start_height)?;
let mut unconditional = features.is_unconditionally_nonstandard.cursor();
let mut has_dust = features.has_dust_output.cursor();
let mut fee = fees.fee.tx_index.cursor();
let mut tx_count = indexes.height.tx_index_count.cursor();
unconditional.advance(start_tx);
has_dust.advance(start_tx);
fee.advance(start_tx);
tx_count.advance(start_height);
let mut block_start = start_tx;
for height in start_height..target_height {
let block_end =
(block_start + u64::from(tx_count.next().unwrap()) as usize).min(target_tx);
let mut count = 0;
for _ in block_start..block_end {
let raw = unconditional.next().unwrap().is_true();
let dust = has_dust.next().unwrap().is_true();
let nonstandard = if raw {
fee.advance(1);
true
} else if dust {
dust_is_nonstandard(height, fee.next().unwrap())
} else {
fee.advance(1);
false
};
count += nonstandard as u64;
self.is_nonstandard.push(StoredBool::from(nonstandard));
}
self.count.push(StoredU64::from(count));
if (height + 1).is_multiple_of(WRITE_INTERVAL) {
let _lock = exit.lock();
self.is_nonstandard.write()?;
self.count.write()?;
}
block_start = block_end;
}
let _lock = exit.lock();
self.is_nonstandard.write()?;
self.count.write()?;
Ok(())
}
}
fn dust_is_nonstandard(height: usize, fee: Sats) -> bool {
height < FIRST_EPHEMERAL_DUST_HEIGHT || fee != Sats::ZERO
}
#[cfg(test)]
mod tests {
use brk_types::Sats;
use super::{FIRST_EPHEMERAL_DUST_HEIGHT, dust_is_nonstandard};
#[test]
fn zero_fee_ephemeral_dust_starts_at_activation() {
assert!(dust_is_nonstandard(
FIRST_EPHEMERAL_DUST_HEIGHT - 1,
Sats::ZERO
));
assert!(!dust_is_nonstandard(
FIRST_EPHEMERAL_DUST_HEIGHT,
Sats::ZERO
));
assert!(dust_is_nonstandard(
FIRST_EPHEMERAL_DUST_HEIGHT,
Sats::new(1)
));
}
}