Files
grin-node/store/src/leaf_set.rs
T
Yeastplume 43b43d9749 croaring-rs upgrade 0.5.2 -> 1.0.1 (#3779)
* first pass compilation of croaring update

* cargo.lock

* add roaring arch flag into CI build scripts

* revert CI to use windows 2019 image

* add more debug

* more debug info

* update range arguments to bitmap remove_range function calls

* remove unnecessary cast
2024-02-06 16:33:32 +00:00

214 lines
6.1 KiB
Rust

// Copyright 2021 The Grin Developers
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The Grin leaf_set implementation.
//! Compact (roaring) bitmap representing the set of leaf positions
//! that exist and are not currently pruned in the MMR.
use std::path::{Path, PathBuf};
use croaring::{Bitmap, Portable};
use crate::grin_core::core::hash::Hashed;
use crate::grin_core::core::pmmr;
use crate::grin_core::core::BlockHeader;
use crate::prune_list::PruneList;
use crate::{read_bitmap, save_via_temp_file};
use std::fs::File;
use std::io::{self, BufWriter, Write};
/// Compact (roaring) bitmap representing the set of positions of
/// leaves that are currently unpruned in the MMR.
pub struct LeafSet {
path: PathBuf,
bitmap: Bitmap,
bitmap_bak: Bitmap,
}
impl LeafSet {
/// Open the remove log file.
/// The content of the file will be read in memory for fast checking.
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<LeafSet> {
let file_path = path.as_ref();
let bitmap = if file_path.exists() {
read_bitmap(&file_path)?
} else {
Bitmap::new()
};
if !bitmap.is_empty() {
debug!(
"bitmap {} pos ({} bytes)",
bitmap.cardinality(),
bitmap.get_serialized_size_in_bytes::<Portable>(),
);
}
Ok(LeafSet {
path: file_path.to_path_buf(),
bitmap_bak: bitmap.clone(),
bitmap,
})
}
/// Copies a snapshot of the utxo file into the primary utxo file.
pub fn copy_snapshot<P: AsRef<Path>>(path: P, cp_path: P) -> io::Result<()> {
let cp_file_path = cp_path.as_ref();
if !cp_file_path.exists() {
debug!(
"leaf_set: rewound leaf file not found: {}",
cp_file_path.display()
);
return Ok(());
}
let bitmap = read_bitmap(&cp_file_path)?;
debug!(
"leaf_set: copying rewound file {} to {}",
cp_file_path.display(),
path.as_ref().display()
);
let mut leaf_set = LeafSet {
path: path.as_ref().to_path_buf(),
bitmap_bak: bitmap.clone(),
bitmap,
};
leaf_set.flush()?;
Ok(())
}
/// Calculate the set of unpruned leaves
/// up to and including the cutoff_pos.
/// Only applicable for the output MMR.
fn unpruned_pre_cutoff(&self, cutoff_pos: u64, prune_list: &PruneList) -> Bitmap {
(1..=cutoff_pos)
.filter(|&x| pmmr::is_leaf(x - 1) && !prune_list.is_pruned(x - 1))
.map(|x| x as u32)
.collect()
}
/// Calculate the set of pruned positions
/// up to and including the cutoff_pos.
/// Uses both the leaf_set and the prune_list to determine prunedness.
pub fn removed_pre_cutoff(
&self,
cutoff_pos: u64,
rewind_rm_pos: &Bitmap,
prune_list: &PruneList,
) -> Bitmap {
let mut bitmap = self.bitmap.clone();
// First remove pos from leaf_set that were
// added after the point we are rewinding to.
let to_remove = ((cutoff_pos + 1) as u32)..=bitmap.maximum().unwrap_or(0);
bitmap.remove_range(to_remove);
// Then add back output pos to the leaf_set
// that were removed.
bitmap.or_inplace(&rewind_rm_pos);
// Invert bitmap for the leaf pos and return the resulting bitmap.
bitmap
.flip(1u32..(cutoff_pos + 1) as u32)
.and(&self.unpruned_pre_cutoff(cutoff_pos, prune_list))
}
/// Rewinds the leaf_set back to a previous state.
/// Removes all pos after the cutoff.
/// Adds back all pos in rewind_rm_pos.
pub fn rewind(&mut self, cutoff_pos: u64, rewind_rm_pos: &Bitmap) {
// First remove pos from leaf_set that were
// added after the point we are rewinding to.
let to_remove = ((cutoff_pos + 1) as u32)..=self.bitmap.maximum().unwrap_or(0);
self.bitmap.remove_range(to_remove);
// Then add back output pos to the leaf_set
// that were removed.
self.bitmap.or_inplace(&rewind_rm_pos);
}
/// Append a new position to the leaf_set.
pub fn add(&mut self, pos0: u64) {
self.bitmap.add(1 + pos0 as u32);
}
/// Remove the provided position from the leaf_set.
pub fn remove(&mut self, pos0: u64) {
self.bitmap.remove(1 + pos0 as u32);
}
/// Saves the utxo file tagged with block hash as filename suffix.
/// Needed during fast-sync as the receiving node cannot rewind
/// after receiving the txhashset zip file.
pub fn snapshot(&self, header: &BlockHeader) -> io::Result<()> {
let mut cp_bitmap = self.bitmap.clone();
cp_bitmap.run_optimize();
let cp_path = format!("{}.{}", self.path.to_str().unwrap(), header.hash());
let mut file = BufWriter::new(File::create(cp_path)?);
file.write_all(&cp_bitmap.serialize::<Portable>())?;
file.flush()?;
Ok(())
}
/// Flush the leaf_set to file.
pub fn flush(&mut self) -> io::Result<()> {
// First run the optimization step on the bitmap.
self.bitmap.run_optimize();
// Write the updated bitmap file to disk.
save_via_temp_file(&self.path, ".tmp", |file| {
file.write_all(&self.bitmap.serialize::<Portable>())
})?;
// Make sure our backup in memory is up to date.
self.bitmap_bak = self.bitmap.clone();
Ok(())
}
/// Discard any pending changes.
pub fn discard(&mut self) {
self.bitmap = self.bitmap_bak.clone();
}
/// Whether the leaf_set includes the provided position.
pub fn includes(&self, pos0: u64) -> bool {
self.bitmap.contains(1 + pos0 as u32)
}
/// Number of positions stored in the leaf_set.
pub fn len(&self) -> usize {
self.bitmap.cardinality() as usize
}
/// Number of positions up to index n in the leaf set
pub fn n_unpruned_leaves_to_index(&self, to_index: u64) -> u64 {
self.bitmap.range_cardinality(0u32..to_index as u32)
}
/// Is the leaf_set empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Iterator over positionns in the leaf_set (all leaf positions).
pub fn iter(&self) -> impl Iterator<Item = u64> + '_ {
self.bitmap.iter().map(|x| x as u64)
}
}