use super::ghostdag::mergeset::unordered_mergeset_without_selected_parent;
use crate::model::{
services::reachability::ReachabilityService,
stores::{children::ChildrenStore, relations::RelationsStore},
};
use itertools::Itertools;
use kaspa_consensus_core::{
blockhash::{BlockHashIteratorExtensions, BlockHashes, ORIGIN},
BlockHashSet,
};
use kaspa_database::prelude::{BatchDbWriter, DbWriter, DirectWriter, StoreError};
use kaspa_hashes::Hash;
use rocksdb::WriteBatch;
pub fn init<S: RelationsStore + ChildrenStore + ?Sized>(relations: &mut S) {
if !relations.has(ORIGIN).unwrap() {
relations.insert(ORIGIN, BlockHashes::new(vec![])).unwrap();
}
}
pub fn delete_level_relations<W, S>(mut writer: W, relations: &mut S, hash: Hash) -> Result<(), StoreError>
where
W: DirectWriter,
S: RelationsStore + ChildrenStore + ?Sized,
{
let children = relations.get_children(hash)?; for child in children.read().iter().copied() {
let child_parents = relations.get_parents(child).unwrap();
let replace_with: &[Hash] = if child_parents.as_slice() == [hash] { &[ORIGIN] } else { &[] };
relations.replace_parent(&mut writer, child, hash, replace_with).unwrap();
}
relations.delete(&mut writer, hash).unwrap();
Ok(())
}
pub fn delete_reachability_relations<W, S, U>(mut writer: W, relations: &mut S, reachability: &U, hash: Hash) -> BlockHashSet
where
W: DirectWriter,
S: RelationsStore + ChildrenStore + ?Sized,
U: ReachabilityService + ?Sized,
{
let selected_parent = reachability.get_chain_parent(hash);
let parents = relations.get_parents(hash).unwrap();
let children = relations.get_children(hash).unwrap();
let mergeset = unordered_mergeset_without_selected_parent(relations, reachability, selected_parent, &parents);
for child in children.read().iter().copied() {
let other_parents = relations.get_parents(child).unwrap().iter().copied().filter(|&p| p != hash).collect_vec();
let needed_grandparents = parents
.iter()
.copied()
.filter(|&grandparent| {
!reachability.is_dag_ancestor_of_any(grandparent, &mut other_parents.iter().copied())
})
.collect_vec();
relations.replace_parent(&mut writer, child, hash, &needed_grandparents).unwrap();
}
relations.delete(&mut writer, hash).unwrap();
mergeset
}
pub trait RelationsStoreExtensions: RelationsStore + ChildrenStore {
fn insert(&mut self, hash: Hash, parents: BlockHashes) -> Result<(), StoreError> {
self.insert_with_writer(self.default_writer(), hash, parents)
}
fn insert_batch(&mut self, batch: &mut WriteBatch, hash: Hash, parents: BlockHashes) -> Result<(), StoreError> {
self.insert_with_writer(BatchDbWriter::new(batch), hash, parents)
}
fn insert_with_writer<W>(&mut self, mut writer: W, hash: Hash, mut parents: BlockHashes) -> Result<(), StoreError>
where
W: DbWriter,
{
if self.has(hash)? {
return Err(StoreError::HashAlreadyExists(hash));
}
if parents.len() != parents.iter().copied().block_unique().count() {
parents = BlockHashes::new(parents.iter().copied().block_unique().collect());
}
self.set_parents(&mut writer, hash, parents.clone())?;
for parent in parents.iter().cloned() {
self.insert_child(&mut writer, parent, hash)?;
}
Ok(())
}
fn delete<W>(&mut self, mut writer: W, hash: Hash) -> Result<(), StoreError>
where
W: DbWriter,
{
let parents = self.get_parents(hash)?;
self.delete_entries(&mut writer, hash)?;
for parent in parents.iter().cloned() {
self.delete_child(&mut writer, parent, hash)?;
}
Ok(())
}
fn replace_parent<W>(&mut self, mut writer: W, hash: Hash, replaced_parent: Hash, replace_with: &[Hash]) -> Result<(), StoreError>
where
W: DbWriter,
{
let mut parents = (*self.get_parents(hash)?).clone();
let replaced_index =
parents.iter().copied().position(|h| h == replaced_parent).expect("callers must ensure replaced is a parent");
parents.swap_remove(replaced_index);
parents.extend(replace_with);
self.set_parents(&mut writer, hash, BlockHashes::new(parents))?;
for parent in replace_with.iter().cloned() {
self.insert_child(&mut writer, parent, hash)?;
}
Ok(())
}
}
impl<S: RelationsStore + ChildrenStore + ?Sized> RelationsStoreExtensions for S {}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::stores::relations::{DbRelationsStore, RelationsStoreReader, StagingRelationsStore};
use kaspa_core::assert_match;
use kaspa_database::prelude::{CachePolicy, ConnBuilder};
use kaspa_database::{create_temp_db, prelude::MemoryWriter};
use std::sync::Arc;
#[test]
fn test_delete_level_relations_zero_cache() {
let (_lifetime, db) = create_temp_db!(ConnBuilder::default().with_files_limit(10));
let mut relations = DbRelationsStore::new(db.clone(), 0, CachePolicy::Empty, CachePolicy::Empty);
relations.insert(ORIGIN, Default::default()).unwrap();
relations.insert(1.into(), Arc::new(vec![ORIGIN])).unwrap();
relations.insert(2.into(), Arc::new(vec![1.into()])).unwrap();
assert_eq!(relations.get_parents(ORIGIN).unwrap().as_slice(), []);
assert_eq!(
relations.get_children(ORIGIN).unwrap().read().iter().copied().collect::<BlockHashSet>(),
BlockHashSet::from_iter([1.into()])
);
assert_eq!(relations.get_parents(1.into()).unwrap().as_slice(), [ORIGIN]);
assert_eq!(
relations.get_children(1.into()).unwrap().read().iter().copied().collect::<BlockHashSet>(),
BlockHashSet::from_iter([2.into()])
);
assert_eq!(relations.get_parents(2.into()).unwrap().as_slice(), [1.into()]);
assert_eq!(
relations.get_children(2.into()).unwrap().read().iter().copied().collect::<BlockHashSet>(),
BlockHashSet::from_iter([])
);
let mut batch = WriteBatch::default();
let mut staging_relations = StagingRelationsStore::new(&mut relations);
delete_level_relations(MemoryWriter, &mut staging_relations, 1.into()).unwrap();
staging_relations.commit(&mut batch).unwrap();
db.write(batch).unwrap();
assert_match!(relations.get_parents(1.into()), Err(StoreError::KeyNotFound(_)));
assert_match!(relations.get_children(1.into()).unwrap_err(), StoreError::KeyNotFound(_));
assert_eq!(relations.get_parents(ORIGIN).unwrap().as_slice(), []);
assert_eq!(
relations.get_children(ORIGIN).unwrap().read().iter().copied().collect::<BlockHashSet>(),
BlockHashSet::from_iter([2.into()])
);
assert_eq!(relations.get_parents(2.into()).unwrap().as_slice(), [ORIGIN]);
assert_eq!(
relations.get_children(2.into()).unwrap().read().iter().copied().collect::<BlockHashSet>(),
BlockHashSet::from_iter([])
);
let mut batch = WriteBatch::default();
let mut staging_relations = StagingRelationsStore::new(&mut relations);
delete_level_relations(MemoryWriter, &mut staging_relations, 2.into()).unwrap();
staging_relations.commit(&mut batch).unwrap();
db.write(batch).unwrap();
assert_match!(relations.get_parents(2.into()), Err(StoreError::KeyNotFound(_)));
assert_match!(relations.get_children(2.into()), Err(StoreError::KeyNotFound(_)));
assert_eq!(relations.get_parents(ORIGIN).unwrap().as_slice(), []);
assert_eq!(
relations.get_children(ORIGIN).unwrap().read().iter().copied().collect::<BlockHashSet>(),
BlockHashSet::from_iter([])
);
}
}