Skip to content
Snippets Groups Projects
Commit 0dea8020 authored by Éloïs's avatar Éloïs
Browse files

[fix] blockchain-dal : ForksBD: too old blocks must be removed

parent 29b607d2
No related branches found
No related tags found
1 merge request!109Resolve "Fork resolution algorithm"
...@@ -116,6 +116,7 @@ pub struct ForkTree { ...@@ -116,6 +116,7 @@ pub struct ForkTree {
nodes: Vec<Option<TreeNode>>, nodes: Vec<Option<TreeNode>>,
main_branch: HashMap<BlockId, TreeNodeId>, main_branch: HashMap<BlockId, TreeNodeId>,
sheets: HashSet<TreeNodeId>, sheets: HashSet<TreeNodeId>,
removed_blockstamps: Vec<Blockstamp>,
} }
impl Default for ForkTree { impl Default for ForkTree {
...@@ -125,6 +126,7 @@ impl Default for ForkTree { ...@@ -125,6 +126,7 @@ impl Default for ForkTree {
root: None, root: None,
main_branch: HashMap::with_capacity(*crate::constants::FORK_WINDOW_SIZE + 1), main_branch: HashMap::with_capacity(*crate::constants::FORK_WINDOW_SIZE + 1),
sheets: HashSet::new(), sheets: HashSet::new(),
removed_blockstamps: Vec::with_capacity(*crate::constants::FORK_WINDOW_SIZE),
} }
} }
} }
...@@ -150,6 +152,10 @@ impl ForkTree { ...@@ -150,6 +152,10 @@ impl ForkTree {
.map(|s| (*s, self.get_ref_node(*s).data)) .map(|s| (*s, self.get_ref_node(*s).data))
.collect() .collect()
} }
///
pub fn get_removed_blockstamps(&self) -> Vec<Blockstamp> {
self.removed_blockstamps.clone()
}
/// Get specific tree node /// Get specific tree node
#[inline] #[inline]
fn get_node(&self, id: TreeNodeId) -> TreeNode { fn get_node(&self, id: TreeNodeId) -> TreeNode {
...@@ -283,6 +289,19 @@ impl ForkTree { ...@@ -283,6 +289,19 @@ impl ForkTree {
.expect("Dev error: change main branch: target branch don't exist !"); .expect("Dev error: change main branch: target branch don't exist !");
let selected_fork_branch = self.get_fork_branch_nodes_ids(target_node); let selected_fork_branch = self.get_fork_branch_nodes_ids(target_node);
// // Delete the part of the old branch at same level to the new branch
if !selected_fork_branch.is_empty() {
let selected_fork_branch_first_node_id =
selected_fork_branch.get(0).cloned().expect("safe unwrap");
let first_fork_block_id = self.nodes[selected_fork_branch_first_node_id.0]
.clone()
.expect("Dev error: node must exist !")
.data
.id;
for block_id in first_fork_block_id.0..=new_current_blockstamp.id.0 {
self.main_branch.remove(&BlockId(block_id));
}
}
// Delete the part of the old branch that exceeds the new branch // Delete the part of the old branch that exceeds the new branch
if old_current_blockstamp.id > new_current_blockstamp.id { if old_current_blockstamp.id > new_current_blockstamp.id {
for block_id in (new_current_blockstamp.id.0 + 1)..=old_current_blockstamp.id.0 { for block_id in (new_current_blockstamp.id.0 + 1)..=old_current_blockstamp.id.0 {
...@@ -291,14 +310,13 @@ impl ForkTree { ...@@ -291,14 +310,13 @@ impl ForkTree {
} }
for node_id in selected_fork_branch { for node_id in selected_fork_branch {
self.main_branch.insert( let node = self.nodes[node_id.0]
self.nodes[node_id.0]
.clone() .clone()
.expect("Dev error: node must exist !") .expect("Dev error: node must exist !");
.data self.main_branch.insert(node.data.id, node_id);
.id, if node.data.id > old_current_blockstamp.id {
node_id, self.pruning();
); }
} }
} }
/// Find node with specific blockstamp /// Find node with specific blockstamp
...@@ -313,7 +331,8 @@ impl ForkTree { ...@@ -313,7 +331,8 @@ impl ForkTree {
None None
} }
/// Insert new node with specified identifier /// Insert new node with specified identifier,
/// return blockstamps of deleted blocks
pub fn insert_new_node( pub fn insert_new_node(
&mut self, &mut self,
data: Blockstamp, data: Blockstamp,
...@@ -344,9 +363,19 @@ impl ForkTree { ...@@ -344,9 +363,19 @@ impl ForkTree {
durs_common_tools::fatal_error("Dev error: Insert root node in not empty tree !") durs_common_tools::fatal_error("Dev error: Insert root node in not empty tree !")
} }
self.removed_blockstamps.clear();
if main_branch { if main_branch {
self.main_branch.insert(data.id, new_node_id); self.main_branch.insert(data.id, new_node_id);
if self.main_branch.len() > *crate::constants::FORK_WINDOW_SIZE { if self.main_branch.len() > *crate::constants::FORK_WINDOW_SIZE {
self.pruning();
}
}
// Add new sheet
self.sheets.insert(new_node_id);
}
fn pruning(&mut self) {
// get root node infos // get root node infos
let root_node_id = self.root.expect("safe unwrap"); let root_node_id = self.root.expect("safe unwrap");
let root_node = self.get_node(root_node_id); let root_node = self.get_node(root_node_id);
...@@ -354,6 +383,7 @@ impl ForkTree { ...@@ -354,6 +383,7 @@ impl ForkTree {
// Shift the tree one step : remove root node and all his children except main child // Shift the tree one step : remove root node and all his children except main child
self.main_branch.remove(&root_node_block_id); self.main_branch.remove(&root_node_block_id);
self.removed_blockstamps.push(root_node.data);
let root_node_main_child_id = self let root_node_main_child_id = self
.main_branch .main_branch
.get(&BlockId(root_node_block_id.0 + 1)) .get(&BlockId(root_node_block_id.0 + 1))
...@@ -368,25 +398,24 @@ impl ForkTree { ...@@ -368,25 +398,24 @@ impl ForkTree {
for child_id in children_to_remove { for child_id in children_to_remove {
self.remove_node_children(child_id); self.remove_node_children(child_id);
self.removed_blockstamps
.push(self.nodes[child_id.0].clone().expect("safe unwrap").data);
self.nodes[child_id.0] = None; self.nodes[child_id.0] = None;
self.sheets.remove(&child_id);
} }
// Remove root node // Remove root node
self.nodes[root_node_id.0] = None; self.nodes[root_node_id.0] = None;
self.root = Some(root_node_main_child_id); self.root = Some(root_node_main_child_id);
} }
}
// Add new sheet
self.sheets.insert(new_node_id);
}
/// Return removed blockstamps
fn remove_node_children(&mut self, id: TreeNodeId) { fn remove_node_children(&mut self, id: TreeNodeId) {
let mut ids_to_rm: Vec<TreeNodeId> = Vec::new(); let mut ids_to_rm: Vec<TreeNodeId> = Vec::new();
let mut node = self.get_ref_node(id); let mut node = self.get_ref_node(id);
while node.children.len() <= 1 { while node.children.len() <= 1 {
if let Some(child_id) = node.children.get(1) { if let Some(child_id) = node.children.get(0) {
ids_to_rm.push(*child_id); ids_to_rm.push(*child_id);
node = self.get_ref_node(*child_id); node = self.get_ref_node(*child_id);
} else { } else {
...@@ -399,7 +428,10 @@ impl ForkTree { ...@@ -399,7 +428,10 @@ impl ForkTree {
} }
for node_id in ids_to_rm { for node_id in ids_to_rm {
self.removed_blockstamps
.push(self.nodes[node_id.0].clone().expect("safe unwrap").data);
self.nodes[node_id.0] = None; self.nodes[node_id.0] = None;
self.sheets.remove(&node_id);
} }
} }
} }
...@@ -496,7 +528,7 @@ mod tests { ...@@ -496,7 +528,7 @@ mod tests {
} }
assert_eq!(10, tree.size()); assert_eq!(10, tree.size());
// Insert fork block before block 5 // Insert fork block after block 5
let fork_blockstamp = Blockstamp { let fork_blockstamp = Blockstamp {
id: BlockId(6), id: BlockId(6),
hash: BlockHash(dup_crypto_tests_tools::mocks::hash('B')), hash: BlockHash(dup_crypto_tests_tools::mocks::hash('B')),
...@@ -582,7 +614,6 @@ mod tests { ...@@ -582,7 +614,6 @@ mod tests {
let blockstamps: Vec<Blockstamp> = dubp_documents_tests_tools::mocks::generate_blockstamps( let blockstamps: Vec<Blockstamp> = dubp_documents_tests_tools::mocks::generate_blockstamps(
*crate::constants::FORK_WINDOW_SIZE + 2, *crate::constants::FORK_WINDOW_SIZE + 2,
); );
assert_eq!(*crate::constants::FORK_WINDOW_SIZE + 2, blockstamps.len());
// Fill tree with FORK_WINDOW_SIZE nodes // Fill tree with FORK_WINDOW_SIZE nodes
tree.insert_new_node(blockstamps[0], None, true); tree.insert_new_node(blockstamps[0], None, true);
...@@ -615,4 +646,83 @@ mod tests { ...@@ -615,4 +646,83 @@ mod tests {
assert_eq!(Some(TreeNodeId(2)), tree.get_root_id()); assert_eq!(Some(TreeNodeId(2)), tree.get_root_id());
} }
#[test]
fn test_change_main_branch() {
let mut tree = ForkTree::default();
let blockstamps: Vec<Blockstamp> = dubp_documents_tests_tools::mocks::generate_blockstamps(
*crate::constants::FORK_WINDOW_SIZE + 2,
);
// Fill tree with FORK_WINDOW_SIZE nodes
tree.insert_new_node(blockstamps[0], None, true);
for i in 1..*crate::constants::FORK_WINDOW_SIZE {
tree.insert_new_node(blockstamps[i], Some(TreeNodeId(i - 1)), true);
}
// Insert 2 forks blocks after block (FORK_WINDOW_SIZE - 2)
let fork_blockstamp = Blockstamp {
id: BlockId(*crate::constants::FORK_WINDOW_SIZE as u32 - 1),
hash: BlockHash(dup_crypto_tests_tools::mocks::hash('A')),
};
tree.insert_new_node(
fork_blockstamp,
tree.get_main_branch_node_id(BlockId(*crate::constants::FORK_WINDOW_SIZE as u32 - 2)),
false,
);
let fork_blockstamp_2 = Blockstamp {
id: BlockId(*crate::constants::FORK_WINDOW_SIZE as u32),
hash: BlockHash(dup_crypto_tests_tools::mocks::hash('B')),
};
tree.insert_new_node(
fork_blockstamp_2,
Some(TreeNodeId(*crate::constants::FORK_WINDOW_SIZE)),
false,
);
// Check tree size
assert_eq!(*crate::constants::FORK_WINDOW_SIZE + 2, tree.size());
// Check that the root of the shaft has not shifted
assert_eq!(Some(TreeNodeId(0)), tree.get_root_id());
// Check that the tree is indeed 2 sheets
let sheets = tree.get_sheets();
assert_eq!(2, sheets.len());
// Check sheets content
let expected_sheets = vec![
(
TreeNodeId(*crate::constants::FORK_WINDOW_SIZE - 1),
blockstamps[*crate::constants::FORK_WINDOW_SIZE - 1],
),
(
TreeNodeId(*crate::constants::FORK_WINDOW_SIZE + 1),
fork_blockstamp_2,
),
];
println!("{:?}", sheets);
assert!(rust_tests_tools::collections::slice_same_elems(
&expected_sheets,
&sheets
));
// Switch to fork branch
tree.change_main_branch(
blockstamps[*crate::constants::FORK_WINDOW_SIZE - 1],
fork_blockstamp_2,
);
// Check that the shaft still has 2 same sheets
assert!(rust_tests_tools::collections::slice_same_elems(
&expected_sheets,
&sheets
));
// Check that tree size decrease
assert_eq!(*crate::constants::FORK_WINDOW_SIZE + 1, tree.size());
// Check that the root of the tree has shifted
assert_eq!(Some(TreeNodeId(1)), tree.get_root_id());
}
} }
...@@ -23,7 +23,7 @@ use std::collections::HashMap; ...@@ -23,7 +23,7 @@ use std::collections::HashMap;
/// Insert new head Block in databases /// Insert new head Block in databases
pub fn insert_new_head_block( pub fn insert_new_head_block(
blockchain_db: &BinDB<LocalBlockchainV10Datas>, blockchain_db: &BinDB<LocalBlockchainV10Datas>,
fork_tree_db: &BinDB<ForksTreeV10Datas>, forks_dbs: &ForksDBs,
dal_block: &DALBlock, dal_block: &DALBlock,
) -> Result<(), DALError> { ) -> Result<(), DALError> {
// Insert head block in blockchain // Insert head block in blockchain
...@@ -32,7 +32,17 @@ pub fn insert_new_head_block( ...@@ -32,7 +32,17 @@ pub fn insert_new_head_block(
})?; })?;
// Insert head block in fork tree // Insert head block in fork tree
crate::writers::fork_tree::insert_new_head_block(fork_tree_db, dal_block.blockstamp())?; let removed_blockstamps = crate::writers::fork_tree::insert_new_head_block(
&forks_dbs.fork_tree_db,
dal_block.blockstamp(),
)?;
// Remove too old blocks
forks_dbs.fork_blocks_db.write(|db| {
for blockstamp in removed_blockstamps {
db.remove(&blockstamp);
}
})?;
Ok(()) Ok(())
} }
......
...@@ -17,21 +17,23 @@ use crate::constants::MAX_FORKS; ...@@ -17,21 +17,23 @@ use crate::constants::MAX_FORKS;
use crate::*; use crate::*;
use dubp_documents::*; use dubp_documents::*;
/// Insert new head Block in fork tree /// Insert new head Block in fork tree,
/// return vector of removed blockstamps
pub fn insert_new_head_block( pub fn insert_new_head_block(
fork_tree_db: &BinDB<ForksTreeV10Datas>, fork_tree_db: &BinDB<ForksTreeV10Datas>,
blockstamp: Blockstamp, blockstamp: Blockstamp,
) -> Result<(), DALError> { ) -> Result<Vec<Blockstamp>, DALError> {
fork_tree_db.write(|fork_tree| { fork_tree_db.write(|fork_tree| {
let parent_id_opt = if blockstamp.id.0 > 0 { let parent_id_opt = if blockstamp.id.0 > 0 {
fork_tree.get_main_branch_node_id(BlockId(blockstamp.id.0 - 1)) Some(fork_tree.get_main_branch_node_id(BlockId(blockstamp.id.0 - 1))
.expect("Fatal error: fail to insert new head block : previous block not exist in main branch"))
} else { } else {
None None
}; };
fork_tree.insert_new_node(blockstamp, parent_id_opt, true); fork_tree.insert_new_node(blockstamp, parent_id_opt, true);
})?; })?;
Ok(()) Ok(fork_tree_db.read(|tree| tree.get_removed_blockstamps())?)
} }
/// Insert new fork block in fork tree only if parent exist in fork tree (orphan block not inserted) /// Insert new fork block in fork tree only if parent exist in fork tree (orphan block not inserted)
...@@ -61,14 +63,25 @@ pub fn insert_new_fork_block( ...@@ -61,14 +63,25 @@ pub fn insert_new_fork_block(
/// Modify the main branch (function to call after a successful roolback) /// Modify the main branch (function to call after a successful roolback)
pub fn change_main_branch( pub fn change_main_branch(
fork_tree_db: &BinDB<ForksTreeV10Datas>, forks_dbs: &ForksDBs,
old_current_blockstamp: Blockstamp, old_current_blockstamp: Blockstamp,
new_current_blockstamp: Blockstamp, new_current_blockstamp: Blockstamp,
) -> Result<(), DALError> { ) -> Result<(), DALError> {
fork_tree_db.write(|tree| { forks_dbs.fork_tree_db.write(|tree| {
tree.change_main_branch(old_current_blockstamp, new_current_blockstamp); tree.change_main_branch(old_current_blockstamp, new_current_blockstamp);
})?; })?;
let removed_blockstamps = forks_dbs
.fork_tree_db
.read(|tree| tree.get_removed_blockstamps())?;
// Remove too old blocks
forks_dbs.fork_blocks_db.write(|db| {
for blockstamp in removed_blockstamps {
db.remove(&blockstamp);
}
})?;
Ok(()) Ok(())
} }
...@@ -170,11 +183,16 @@ mod test { ...@@ -170,11 +183,16 @@ mod test {
#[test] #[test]
fn test_insert_new_head_block() -> Result<(), DALError> { fn test_insert_new_head_block() -> Result<(), DALError> {
// Create mock datas // Create mock datas
let blockstamps = dubp_documents_tests_tools::mocks::generate_blockstamps(4); let blockstamps = dubp_documents_tests_tools::mocks::generate_blockstamps(
*crate::constants::FORK_WINDOW_SIZE + 2,
);
let fork_tree_db = open_db::<ForksTreeV10Datas>(None, "")?; let fork_tree_db = open_db::<ForksTreeV10Datas>(None, "")?;
// Insert genesis block // Insert genesis block
assert_eq!(Ok(()), insert_new_head_block(&fork_tree_db, blockstamps[0])); assert_eq!(
Ok(vec![]),
insert_new_head_block(&fork_tree_db, blockstamps[0])
);
// Check tree state // Check tree state
assert_eq!(1, fork_tree_db.read(|tree| tree.size())?); assert_eq!(1, fork_tree_db.read(|tree| tree.size())?);
...@@ -183,32 +201,61 @@ mod test { ...@@ -183,32 +201,61 @@ mod test {
fork_tree_db.read(|tree| tree.get_sheets())? fork_tree_db.read(|tree| tree.get_sheets())?
); );
// Insert some blocks // Insert FORK_WINDOW_SIZE blocks
assert_eq!(Ok(()), insert_new_head_block(&fork_tree_db, blockstamps[1])); for i in 1..*crate::constants::FORK_WINDOW_SIZE {
assert_eq!(Ok(()), insert_new_head_block(&fork_tree_db, blockstamps[2])); assert_eq!(
assert_eq!(Ok(()), insert_new_head_block(&fork_tree_db, blockstamps[3])); Ok(vec![]),
insert_new_head_block(&fork_tree_db, blockstamps[i])
);
}
// Check tree state // Check tree state
assert_eq!(4, fork_tree_db.read(|tree| tree.size())?);
assert_eq!( assert_eq!(
vec![(TreeNodeId(3), blockstamps[3])], *crate::constants::FORK_WINDOW_SIZE,
fork_tree_db.read(|tree| tree.size())?
);
assert_eq!(
vec![(
TreeNodeId(*crate::constants::FORK_WINDOW_SIZE - 1),
blockstamps[*crate::constants::FORK_WINDOW_SIZE - 1]
)],
fork_tree_db.read(|tree| tree.get_sheets())? fork_tree_db.read(|tree| tree.get_sheets())?
); );
// Insert blocks after FORK_WINDOW_SIZE (firsts blocks must be removed)
assert_eq!(
Ok(vec![blockstamps[0]]),
insert_new_head_block(
&fork_tree_db,
blockstamps[*crate::constants::FORK_WINDOW_SIZE]
)
);
assert_eq!(
Ok(vec![blockstamps[1]]),
insert_new_head_block(
&fork_tree_db,
blockstamps[*crate::constants::FORK_WINDOW_SIZE + 1]
)
);
Ok(()) Ok(())
} }
#[test] #[test]
fn test_insert_new_fork_block() -> Result<(), DALError> { fn test_insert_new_fork_block() -> Result<(), DALError> {
// Create mock datas // Create mock datas
let blockstamps = dubp_documents_tests_tools::mocks::generate_blockstamps(5); let blockstamps = dubp_documents_tests_tools::mocks::generate_blockstamps(
*crate::constants::FORK_WINDOW_SIZE + 3,
);
let fork_tree_db = open_db::<ForksTreeV10Datas>(None, "")?; let fork_tree_db = open_db::<ForksTreeV10Datas>(None, "")?;
// Insert 3 main blocks // Insert 4 main blocks
assert_eq!(Ok(()), insert_new_head_block(&fork_tree_db, blockstamps[0])); for i in 0..4 {
assert_eq!(Ok(()), insert_new_head_block(&fork_tree_db, blockstamps[1])); assert_eq!(
assert_eq!(Ok(()), insert_new_head_block(&fork_tree_db, blockstamps[2])); Ok(vec![]),
assert_eq!(Ok(()), insert_new_head_block(&fork_tree_db, blockstamps[3])); insert_new_head_block(&fork_tree_db, blockstamps[i])
);
}
// Check tree state // Check tree state
assert_eq!(4, fork_tree_db.read(|tree| tree.size())?); assert_eq!(4, fork_tree_db.read(|tree| tree.size())?);
...@@ -257,6 +304,63 @@ mod test { ...@@ -257,6 +304,63 @@ mod test {
&fork_tree_db.read(|tree| tree.get_sheets())? &fork_tree_db.read(|tree| tree.get_sheets())?
)); ));
// Insert FORK_WINDOW_SIZE blocks
for i in 4..*crate::constants::FORK_WINDOW_SIZE {
assert_eq!(
Ok(vec![]),
insert_new_head_block(&fork_tree_db, blockstamps[i])
);
}
// Check tree state
assert_eq!(
*crate::constants::FORK_WINDOW_SIZE + 2,
fork_tree_db.read(|tree| tree.size())?
);
assert!(rust_tests_tools::collections::slice_same_elems(
&vec![
(
TreeNodeId(*crate::constants::FORK_WINDOW_SIZE + 1),
blockstamps[*crate::constants::FORK_WINDOW_SIZE - 1]
),
(TreeNodeId(5), fork_blockstamp_2)
],
&fork_tree_db.read(|tree| tree.get_sheets())?
));
// Insert 2 new main blocks (too old blocks must be removed)
for i in 0..2 {
assert_eq!(
Ok(vec![blockstamps[i]]),
insert_new_head_block(
&fork_tree_db,
blockstamps[*crate::constants::FORK_WINDOW_SIZE + i]
)
);
}
// Insert one new main block (fork branch must be removed)
assert_eq!(
Ok(vec![blockstamps[2], fork_blockstamp_2, fork_blockstamp]),
insert_new_head_block(
&fork_tree_db,
blockstamps[*crate::constants::FORK_WINDOW_SIZE + 2]
)
);
// Check tree state
assert_eq!(
*crate::constants::FORK_WINDOW_SIZE,
fork_tree_db.read(|tree| tree.size())?
);
assert_eq!(
vec![(
TreeNodeId(*crate::constants::FORK_WINDOW_SIZE + 4),
blockstamps[*crate::constants::FORK_WINDOW_SIZE + 2]
)],
fork_tree_db.read(|tree| tree.get_sheets())?
);
Ok(()) Ok(())
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment