use std::collections::HashMap;
use crate::{Block, Auxiliary};
use crate::backend::{tree_route, Store, ChainQuery, ChainSettlement, OperationError};
pub struct BlockData<B: Block, S> {
pub block: B,
pub state: S,
pub depth: usize,
pub children: Vec<B::Identifier>,
pub is_canon: bool,
}
pub struct ImportOperation<B, S> {
pub block: B,
pub state: S,
}
pub struct Operation<B: Block, S, A: Auxiliary<B>> {
pub import_block: Vec<ImportOperation<B, S>>,
pub set_head: Option<B::Identifier>,
pub insert_auxiliaries: Vec<A>,
pub remove_auxiliaries: Vec<A::Key>,
}
impl<B: Block, S, A: Auxiliary<B>> Default for Operation<B, S, A> {
fn default() -> Self {
Self {
import_block: Vec::new(),
set_head: None,
insert_auxiliaries: Vec::new(),
remove_auxiliaries: Vec::new(),
}
}
}
impl<B: Block, S, A: Auxiliary<B>> Operation<B, S, A> {
pub fn settle<Ba>(self, backend: &mut Ba) -> Result<(), Ba::Error> where
Ba: ChainQuery + ChainSettlement + Store<Block=B, State=S, Auxiliary=A>,
Ba::Error: OperationError,
{
let mut parent_ides = HashMap::new();
let mut importing: HashMap<<Ba::Block as Block>::Identifier, BlockData<Ba::Block, Ba::State>> = HashMap::new();
let mut verifying = self.import_block;
loop {
let mut progress = false;
let mut next_verifying = Vec::new();
for op in verifying {
let parent_depth = match op.block.parent_id() {
Some(parent_id) => {
if backend.contains(&parent_id)? {
Some(backend.depth_at(&parent_id)?)
} else if importing.contains_key(&parent_id) {
importing.get(&parent_id)
.map(|data| data.depth)
} else {
None
}
},
None => return Err(Ba::Error::block_is_genesis()),
};
let depth = parent_depth.map(|d| d + 1);
if let Some(depth) = depth {
progress = true;
if let Some(parent_id) = op.block.parent_id() {
parent_ides.insert(op.block.id(), parent_id);
}
importing.insert(op.block.id(), BlockData {
block: op.block,
state: op.state,
depth,
children: Vec::new(),
is_canon: false,
});
} else {
next_verifying.push(op)
}
}
if next_verifying.len() == 0 {
break;
}
if !progress {
return Err(Ba::Error::invalid_operation());
}
verifying = next_verifying;
}
if let Some(new_head) = &self.set_head {
let head_exists = backend.contains(new_head)? ||
importing.contains_key(new_head);
if !head_exists {
return Err(Ba::Error::invalid_operation());
}
}
for aux in &self.insert_auxiliaries {
for id in aux.associated() {
if !(backend.contains(&id)? || importing.contains_key(&id)) {
return Err(Ba::Error::invalid_operation());
}
}
}
for (id, data) in importing {
backend.insert_block(
id, data.block, data.state, data.depth, data.children, data.is_canon
);
}
for (id, parent_id) in parent_ides {
backend.push_child(parent_id, id);
}
if let Some(new_head) = self.set_head {
let route = tree_route(backend, &backend.head(), &new_head)
.expect("Blocks are checked to exist or importing; qed");
for id in route.retracted() {
backend.set_canon(id.clone(), false);
let depth = backend.depth_at(id)
.expect("Block is fetched from tree_route; it must exist; qed");
backend.remove_canon_depth_mapping(&depth);
}
for id in route.enacted() {
backend.set_canon(id.clone(), true);
let depth = backend.depth_at(id)
.expect("Block is fetched from tree_route; it must exist; qed");
backend.insert_canon_depth_mapping(depth, id.clone());
}
backend.set_head(new_head);
}
for aux_key in self.remove_auxiliaries {
backend.remove_auxiliary(&aux_key);
}
for aux in self.insert_auxiliaries {
backend.insert_auxiliary(aux.key(), aux);
}
Ok(())
}
}