use std::{
collections::HashMap,
sync::{Mutex, MutexGuard},
};
use crate::chaindef::ScriptHash;
use bitcoincash::Txid;
use rayon::prelude::*;
use crate::{
chaindef::OutPointHash,
indexes::{
headerindex::HeaderRow, heightindex::HeightIndexRow, inputindex::InputIndexRow,
outputindex::OutputIndexRow, outputtokenindex::OutputTokenIndexRow,
outtoscriptindex::OutToScripthashIndex, scripthashindex::ScriptHashIndexRow,
tokenoutputindex::TokenOutputIndexRow, unspentindex::UnspentIndexRow, DBRow,
},
store::{Row, COLUMN_FAMILIES, META_CF},
};
#[derive(Clone)]
pub struct PrefilledSpentData {
pub scripthash: ScriptHash,
pub has_token: bool,
}
pub struct SpentUpdateBatch {
#[allow(clippy::type_complexity)]
spending_metadata: Mutex<Vec<(OutPointHash, Txid, u32, u32, Option<PrefilledSpentData>)>>,
erase_only: bool,
prefilled_spent_data: HashMap<OutPointHash, PrefilledSpentData>,
}
impl Default for SpentUpdateBatch {
fn default() -> Self {
Self::new()
}
}
impl SpentUpdateBatch {
pub fn new() -> Self {
Self {
spending_metadata: Default::default(),
erase_only: false,
prefilled_spent_data: Default::default(),
}
}
pub fn set_erase_only(mut self) -> Self {
self.erase_only = true;
self
}
pub fn is_erase_only(&self) -> bool {
self.erase_only
}
pub fn add_prefilled_spent_data(
mut self,
prefilled_spent_data: HashMap<OutPointHash, PrefilledSpentData>,
) -> Self {
self.prefilled_spent_data.extend(prefilled_spent_data);
self
}
pub fn insert_spending_metadata(
&self,
oph: OutPointHash,
txid: Txid,
vin: u32,
height: u32,
scripthash_and_has_token: Option<PrefilledSpentData>,
) {
let scripthash_and_has_token =
scripthash_and_has_token.or_else(|| self.prefilled_spent_data.get(&oph).cloned());
self.spending_metadata.lock().unwrap().push((
oph,
txid,
vin,
height,
scripthash_and_has_token,
));
}
pub fn take_spending_metadata(
&self,
) -> Vec<(OutPointHash, Txid, u32, u32, Option<PrefilledSpentData>)> {
std::mem::take(&mut *self.spending_metadata.lock().unwrap())
}
}
pub struct WriteBatch {
metadata: Mutex<Vec<Row>>,
header: Mutex<Vec<Row>>,
height: Mutex<Vec<Row>>,
input: Mutex<Vec<Row>>,
output: Mutex<Vec<Row>>,
output_token: Mutex<Vec<Row>>,
scripthash: Mutex<Vec<Row>>,
token_output: Mutex<Vec<Row>>,
unspent: Mutex<Vec<Row>>,
outtoscripthash: Mutex<Vec<Row>>,
}
impl WriteBatch {
pub fn new() -> Self {
WriteBatch {
metadata: Default::default(),
header: Default::default(),
height: Default::default(),
input: Default::default(),
output: Default::default(),
output_token: Default::default(),
scripthash: Default::default(),
token_output: Default::default(),
unspent: Default::default(),
outtoscripthash: Default::default(),
}
}
pub fn cf_to_vec_tuples(&self) -> Vec<(&'static str, &Mutex<Vec<Row>>)> {
let map = vec![
(META_CF, &self.metadata),
(HeaderRow::CF, &self.header),
(HeightIndexRow::CF, &self.height),
(InputIndexRow::CF, &self.input),
(OutputIndexRow::CF, &self.output),
(OutputTokenIndexRow::CF, &self.output_token),
(ScriptHashIndexRow::CF, &self.scripthash),
(TokenOutputIndexRow::CF, &self.token_output),
(UnspentIndexRow::CF, &self.unspent),
(OutToScripthashIndex::CF, &self.outtoscripthash),
];
assert!(map.len() == COLUMN_FAMILIES.len());
map
}
pub fn insert<I: IntoParallelIterator<Item = Row>>(&self, cf_name: &str, rows: I) {
let mut rows: Vec<Row> = rows.into_par_iter().collect();
let mut locked = match cf_name {
META_CF => self.metadata.lock().unwrap(),
HeaderRow::CF => self.header.lock().unwrap(),
HeightIndexRow::CF => self.height.lock().unwrap(),
InputIndexRow::CF => self.input.lock().unwrap(),
OutputIndexRow::CF => self.output.lock().unwrap(),
OutputTokenIndexRow::CF => self.output_token.lock().unwrap(),
ScriptHashIndexRow::CF => self.scripthash.lock().unwrap(),
TokenOutputIndexRow::CF => self.token_output.lock().unwrap(),
UnspentIndexRow::CF => self.unspent.lock().unwrap(),
OutToScripthashIndex::CF => self.outtoscripthash.lock().unwrap(),
_ => panic!("unknown cf family {cf_name}"),
};
locked.append(&mut rows);
}
pub fn len(&self) -> usize {
let locks: Vec<MutexGuard<Vec<Row>>> = self
.cf_to_vec_tuples()
.into_iter()
.map(|(_, column)| column.lock().unwrap())
.collect();
locks.iter().map(|rows| rows.len()).sum()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Default for WriteBatch {
fn default() -> Self {
Self::new()
}
}