use polkadot_node_subsystem::SubsystemResult;
use polkadot_primitives::{BlockNumber, CandidateHash, CandidateIndex, Hash};
use std::collections::HashMap;
use super::{
approval_db::common::StoredBlockRange,
persisted_entries::{BlockEntry, CandidateEntry},
};
#[derive(Debug)]
pub enum BackendWriteOp {
WriteStoredBlockRange(StoredBlockRange),
WriteBlocksAtHeight(BlockNumber, Vec<Hash>),
WriteBlockEntry(BlockEntry),
WriteCandidateEntry(CandidateEntry),
DeleteStoredBlockRange,
DeleteBlocksAtHeight(BlockNumber),
DeleteBlockEntry(Hash),
DeleteCandidateEntry(CandidateHash),
}
pub trait Backend {
fn load_block_entry(&self, hash: &Hash) -> SubsystemResult<Option<BlockEntry>>;
fn load_candidate_entry(
&self,
candidate_hash: &CandidateHash,
) -> SubsystemResult<Option<CandidateEntry>>;
fn load_blocks_at_height(&self, height: &BlockNumber) -> SubsystemResult<Vec<Hash>>;
fn load_all_blocks(&self) -> SubsystemResult<Vec<Hash>>;
fn load_stored_blocks(&self) -> SubsystemResult<Option<StoredBlockRange>>;
fn write<I>(&mut self, ops: I) -> SubsystemResult<()>
where
I: IntoIterator<Item = BackendWriteOp>;
}
pub trait V1ReadBackend: Backend {
fn load_candidate_entry_v1(
&self,
candidate_hash: &CandidateHash,
candidate_index: CandidateIndex,
) -> SubsystemResult<Option<CandidateEntry>>;
fn load_block_entry_v1(&self, block_hash: &Hash) -> SubsystemResult<Option<BlockEntry>>;
}
pub trait V2ReadBackend: Backend {
fn load_candidate_entry_v2(
&self,
candidate_hash: &CandidateHash,
candidate_index: CandidateIndex,
) -> SubsystemResult<Option<CandidateEntry>>;
fn load_block_entry_v2(&self, block_hash: &Hash) -> SubsystemResult<Option<BlockEntry>>;
}
#[derive(PartialEq)]
enum BlockRangeStatus {
NotModified,
Deleted,
Inserted(StoredBlockRange),
}
pub struct OverlayedBackend<'a, B: 'a> {
inner: &'a B,
stored_block_range: BlockRangeStatus,
blocks_at_height: HashMap<BlockNumber, Option<Vec<Hash>>>,
block_entries: HashMap<Hash, Option<BlockEntry>>,
candidate_entries: HashMap<CandidateHash, Option<CandidateEntry>>,
}
impl<'a, B: 'a + Backend> OverlayedBackend<'a, B> {
pub fn new(backend: &'a B) -> Self {
OverlayedBackend {
inner: backend,
stored_block_range: BlockRangeStatus::NotModified,
blocks_at_height: HashMap::new(),
block_entries: HashMap::new(),
candidate_entries: HashMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.block_entries.is_empty() &&
self.candidate_entries.is_empty() &&
self.blocks_at_height.is_empty() &&
self.stored_block_range == BlockRangeStatus::NotModified
}
pub fn load_all_blocks(&self) -> SubsystemResult<Vec<Hash>> {
let mut hashes = Vec::new();
if let Some(stored_blocks) = self.load_stored_blocks()? {
for height in stored_blocks.0..stored_blocks.1 {
hashes.extend(self.load_blocks_at_height(&height)?);
}
}
Ok(hashes)
}
pub fn load_stored_blocks(&self) -> SubsystemResult<Option<StoredBlockRange>> {
match self.stored_block_range {
BlockRangeStatus::Inserted(ref value) => Ok(Some(value.clone())),
BlockRangeStatus::Deleted => Ok(None),
BlockRangeStatus::NotModified => self.inner.load_stored_blocks(),
}
}
pub fn load_blocks_at_height(&self, height: &BlockNumber) -> SubsystemResult<Vec<Hash>> {
if let Some(val) = self.blocks_at_height.get(&height) {
return Ok(val.clone().unwrap_or_default())
}
self.inner.load_blocks_at_height(height)
}
pub fn load_block_entry(&self, hash: &Hash) -> SubsystemResult<Option<BlockEntry>> {
if let Some(val) = self.block_entries.get(&hash) {
return Ok(val.clone())
}
self.inner.load_block_entry(hash)
}
pub fn load_candidate_entry(
&self,
candidate_hash: &CandidateHash,
) -> SubsystemResult<Option<CandidateEntry>> {
if let Some(val) = self.candidate_entries.get(&candidate_hash) {
return Ok(val.clone())
}
self.inner.load_candidate_entry(candidate_hash)
}
pub fn write_stored_block_range(&mut self, range: StoredBlockRange) {
self.stored_block_range = BlockRangeStatus::Inserted(range);
}
pub fn delete_stored_block_range(&mut self) {
self.stored_block_range = BlockRangeStatus::Deleted;
}
pub fn write_blocks_at_height(&mut self, height: BlockNumber, blocks: Vec<Hash>) {
self.blocks_at_height.insert(height, Some(blocks));
}
pub fn delete_blocks_at_height(&mut self, height: BlockNumber) {
self.blocks_at_height.insert(height, None);
}
pub fn write_block_entry(&mut self, entry: BlockEntry) {
self.block_entries.insert(entry.block_hash(), Some(entry));
}
pub fn delete_block_entry(&mut self, hash: &Hash) {
self.block_entries.insert(*hash, None);
}
pub fn write_candidate_entry(&mut self, entry: CandidateEntry) {
self.candidate_entries.insert(entry.candidate_receipt().hash(), Some(entry));
}
pub fn delete_candidate_entry(&mut self, hash: &CandidateHash) {
self.candidate_entries.insert(*hash, None);
}
pub fn into_write_ops(self) -> impl Iterator<Item = BackendWriteOp> {
let blocks_at_height_ops = self.blocks_at_height.into_iter().map(|(h, v)| match v {
Some(v) => BackendWriteOp::WriteBlocksAtHeight(h, v),
None => BackendWriteOp::DeleteBlocksAtHeight(h),
});
let block_entry_ops = self.block_entries.into_iter().map(|(h, v)| match v {
Some(v) => BackendWriteOp::WriteBlockEntry(v),
None => BackendWriteOp::DeleteBlockEntry(h),
});
let candidate_entry_ops = self.candidate_entries.into_iter().map(|(h, v)| match v {
Some(v) => BackendWriteOp::WriteCandidateEntry(v),
None => BackendWriteOp::DeleteCandidateEntry(h),
});
let stored_block_range_ops = match self.stored_block_range {
BlockRangeStatus::Inserted(val) => Some(BackendWriteOp::WriteStoredBlockRange(val)),
BlockRangeStatus::Deleted => Some(BackendWriteOp::DeleteStoredBlockRange),
BlockRangeStatus::NotModified => None,
};
stored_block_range_ops
.into_iter()
.chain(blocks_at_height_ops)
.chain(block_entry_ops)
.chain(candidate_entry_ops)
}
}