polkadot_node_core_approval_voting/
backend.rs1use polkadot_node_subsystem::SubsystemResult;
25use polkadot_primitives::{BlockNumber, CandidateHash, CandidateIndex, Hash};
26
27use std::collections::HashMap;
28
29use super::{
30 approval_db::common::StoredBlockRange,
31 persisted_entries::{BlockEntry, CandidateEntry},
32};
33
34#[derive(Debug)]
35pub enum BackendWriteOp {
36 WriteStoredBlockRange(StoredBlockRange),
37 WriteBlocksAtHeight(BlockNumber, Vec<Hash>),
38 WriteBlockEntry(BlockEntry),
39 WriteCandidateEntry(CandidateEntry),
40 DeleteStoredBlockRange,
41 DeleteBlocksAtHeight(BlockNumber),
42 DeleteBlockEntry(Hash),
43 DeleteCandidateEntry(CandidateHash),
44}
45
46pub trait Backend {
49 fn load_block_entry(&self, hash: &Hash) -> SubsystemResult<Option<BlockEntry>>;
51 fn load_candidate_entry(
53 &self,
54 candidate_hash: &CandidateHash,
55 ) -> SubsystemResult<Option<CandidateEntry>>;
56
57 fn load_blocks_at_height(&self, height: &BlockNumber) -> SubsystemResult<Vec<Hash>>;
59 fn load_all_blocks(&self) -> SubsystemResult<Vec<Hash>>;
61 fn load_stored_blocks(&self) -> SubsystemResult<Option<StoredBlockRange>>;
63 fn write<I>(&mut self, ops: I) -> SubsystemResult<()>
65 where
66 I: IntoIterator<Item = BackendWriteOp>;
67}
68
69pub trait V1ReadBackend: Backend {
71 fn load_candidate_entry_v1(
73 &self,
74 candidate_hash: &CandidateHash,
75 candidate_index: CandidateIndex,
76 ) -> SubsystemResult<Option<CandidateEntry>>;
77
78 fn load_block_entry_v1(&self, block_hash: &Hash) -> SubsystemResult<Option<BlockEntry>>;
80}
81
82pub trait V2ReadBackend: Backend {
84 fn load_candidate_entry_v2(
86 &self,
87 candidate_hash: &CandidateHash,
88 candidate_index: CandidateIndex,
89 ) -> SubsystemResult<Option<CandidateEntry>>;
90
91 fn load_block_entry_v2(&self, block_hash: &Hash) -> SubsystemResult<Option<BlockEntry>>;
93}
94
95#[derive(PartialEq)]
97enum BlockRangeStatus {
98 NotModified,
100 Deleted,
102 Inserted(StoredBlockRange),
104}
105
106pub struct OverlayedBackend<'a, B: 'a> {
112 inner: &'a B,
113 stored_block_range: BlockRangeStatus,
115 blocks_at_height: HashMap<BlockNumber, Option<Vec<Hash>>>,
117 block_entries: HashMap<Hash, Option<BlockEntry>>,
119 candidate_entries: HashMap<CandidateHash, Option<CandidateEntry>>,
121}
122
123impl<'a, B: 'a + Backend> OverlayedBackend<'a, B> {
124 pub fn new(backend: &'a B) -> Self {
125 OverlayedBackend {
126 inner: backend,
127 stored_block_range: BlockRangeStatus::NotModified,
128 blocks_at_height: HashMap::new(),
129 block_entries: HashMap::new(),
130 candidate_entries: HashMap::new(),
131 }
132 }
133
134 pub fn is_empty(&self) -> bool {
135 self.block_entries.is_empty() &&
136 self.candidate_entries.is_empty() &&
137 self.blocks_at_height.is_empty() &&
138 self.stored_block_range == BlockRangeStatus::NotModified
139 }
140
141 pub fn load_all_blocks(&self) -> SubsystemResult<Vec<Hash>> {
142 let mut hashes = Vec::new();
143 if let Some(stored_blocks) = self.load_stored_blocks()? {
144 for height in stored_blocks.0..stored_blocks.1 {
145 hashes.extend(self.load_blocks_at_height(&height)?);
146 }
147 }
148
149 Ok(hashes)
150 }
151
152 pub fn load_stored_blocks(&self) -> SubsystemResult<Option<StoredBlockRange>> {
153 match self.stored_block_range {
154 BlockRangeStatus::Inserted(ref value) => Ok(Some(value.clone())),
155 BlockRangeStatus::Deleted => Ok(None),
156 BlockRangeStatus::NotModified => self.inner.load_stored_blocks(),
157 }
158 }
159
160 pub fn load_blocks_at_height(&self, height: &BlockNumber) -> SubsystemResult<Vec<Hash>> {
161 if let Some(val) = self.blocks_at_height.get(&height) {
162 return Ok(val.clone().unwrap_or_default())
163 }
164
165 self.inner.load_blocks_at_height(height)
166 }
167
168 pub fn load_block_entry(&self, hash: &Hash) -> SubsystemResult<Option<BlockEntry>> {
169 if let Some(val) = self.block_entries.get(&hash) {
170 return Ok(val.clone())
171 }
172
173 self.inner.load_block_entry(hash)
174 }
175
176 pub fn load_candidate_entry(
177 &self,
178 candidate_hash: &CandidateHash,
179 ) -> SubsystemResult<Option<CandidateEntry>> {
180 if let Some(val) = self.candidate_entries.get(&candidate_hash) {
181 return Ok(val.clone())
182 }
183
184 self.inner.load_candidate_entry(candidate_hash)
185 }
186
187 pub fn write_stored_block_range(&mut self, range: StoredBlockRange) {
188 self.stored_block_range = BlockRangeStatus::Inserted(range);
189 }
190
191 pub fn delete_stored_block_range(&mut self) {
192 self.stored_block_range = BlockRangeStatus::Deleted;
193 }
194
195 pub fn write_blocks_at_height(&mut self, height: BlockNumber, blocks: Vec<Hash>) {
196 self.blocks_at_height.insert(height, Some(blocks));
197 }
198
199 pub fn delete_blocks_at_height(&mut self, height: BlockNumber) {
200 self.blocks_at_height.insert(height, None);
201 }
202
203 pub fn write_block_entry(&mut self, entry: BlockEntry) {
204 self.block_entries.insert(entry.block_hash(), Some(entry));
205 }
206
207 pub fn delete_block_entry(&mut self, hash: &Hash) {
208 self.block_entries.insert(*hash, None);
209 }
210
211 pub fn write_candidate_entry(&mut self, entry: CandidateEntry) {
212 self.candidate_entries.insert(entry.candidate_receipt().hash(), Some(entry));
213 }
214
215 pub fn delete_candidate_entry(&mut self, hash: &CandidateHash) {
216 self.candidate_entries.insert(*hash, None);
217 }
218
219 pub fn into_write_ops(self) -> impl Iterator<Item = BackendWriteOp> {
222 let blocks_at_height_ops = self.blocks_at_height.into_iter().map(|(h, v)| match v {
223 Some(v) => BackendWriteOp::WriteBlocksAtHeight(h, v),
224 None => BackendWriteOp::DeleteBlocksAtHeight(h),
225 });
226
227 let block_entry_ops = self.block_entries.into_iter().map(|(h, v)| match v {
228 Some(v) => BackendWriteOp::WriteBlockEntry(v),
229 None => BackendWriteOp::DeleteBlockEntry(h),
230 });
231
232 let candidate_entry_ops = self.candidate_entries.into_iter().map(|(h, v)| match v {
233 Some(v) => BackendWriteOp::WriteCandidateEntry(v),
234 None => BackendWriteOp::DeleteCandidateEntry(h),
235 });
236
237 let stored_block_range_ops = match self.stored_block_range {
238 BlockRangeStatus::Inserted(val) => Some(BackendWriteOp::WriteStoredBlockRange(val)),
239 BlockRangeStatus::Deleted => Some(BackendWriteOp::DeleteStoredBlockRange),
240 BlockRangeStatus::NotModified => None,
241 };
242
243 stored_block_range_ops
244 .into_iter()
245 .chain(blocks_at_height_ops)
246 .chain(block_entry_ops)
247 .chain(candidate_entry_ops)
248 }
249}