referrerpolicy=no-referrer-when-downgrade

polkadot_node_core_approval_voting/
backend.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! An abstraction over storage used by the chain selection subsystem.
18//!
19//! This provides both a [`Backend`] trait and an [`OverlayedBackend`]
20//! struct which allows in-memory changes to be applied on top of a
21//! [`Backend`], maintaining consistency between queries and temporary writes,
22//! before any commit to the underlying storage is made.
23
24use 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
46/// An abstraction over backend storage for the logic of this subsystem.
47/// Implementation must always target latest storage version.
48pub trait Backend {
49	/// Load a block entry from the DB.
50	fn load_block_entry(&self, hash: &Hash) -> SubsystemResult<Option<BlockEntry>>;
51	/// Load a candidate entry from the DB.
52	fn load_candidate_entry(
53		&self,
54		candidate_hash: &CandidateHash,
55	) -> SubsystemResult<Option<CandidateEntry>>;
56
57	/// Load all blocks at a specific height.
58	fn load_blocks_at_height(&self, height: &BlockNumber) -> SubsystemResult<Vec<Hash>>;
59	/// Load all block from the DB.
60	fn load_all_blocks(&self) -> SubsystemResult<Vec<Hash>>;
61	/// Load stored block range form the DB.
62	fn load_stored_blocks(&self) -> SubsystemResult<Option<StoredBlockRange>>;
63	/// Atomically write the list of operations, with later operations taking precedence over prior.
64	fn write<I>(&mut self, ops: I) -> SubsystemResult<()>
65	where
66		I: IntoIterator<Item = BackendWriteOp>;
67}
68
69/// A read only backend to enable db migration from version 1 of DB.
70pub trait V1ReadBackend: Backend {
71	/// Load a candidate entry from the DB with scheme version 1.
72	fn load_candidate_entry_v1(
73		&self,
74		candidate_hash: &CandidateHash,
75		candidate_index: CandidateIndex,
76	) -> SubsystemResult<Option<CandidateEntry>>;
77
78	/// Load a block entry from the DB with scheme version 1.
79	fn load_block_entry_v1(&self, block_hash: &Hash) -> SubsystemResult<Option<BlockEntry>>;
80}
81
82/// A read only backend to enable db migration from version 2 of DB.
83pub trait V2ReadBackend: Backend {
84	/// Load a candidate entry from the DB with scheme version 1.
85	fn load_candidate_entry_v2(
86		&self,
87		candidate_hash: &CandidateHash,
88		candidate_index: CandidateIndex,
89	) -> SubsystemResult<Option<CandidateEntry>>;
90
91	/// Load a block entry from the DB with scheme version 1.
92	fn load_block_entry_v2(&self, block_hash: &Hash) -> SubsystemResult<Option<BlockEntry>>;
93}
94
95// Status of block range in the `OverlayedBackend`.
96#[derive(PartialEq)]
97enum BlockRangeStatus {
98	// Value has not been modified.
99	NotModified,
100	// Value has been deleted
101	Deleted,
102	// Value has been updated.
103	Inserted(StoredBlockRange),
104}
105
106/// An in-memory overlay over the backend.
107///
108/// This maintains read-only access to the underlying backend, but can be
109/// converted into a set of write operations which will, when written to
110/// the underlying backend, give the same view as the state of the overlay.
111pub struct OverlayedBackend<'a, B: 'a> {
112	inner: &'a B,
113	// `Some(None)` means deleted. Missing (`None`) means query inner.
114	stored_block_range: BlockRangeStatus,
115	// `None` means 'deleted', missing means query inner.
116	blocks_at_height: HashMap<BlockNumber, Option<Vec<Hash>>>,
117	// `None` means 'deleted', missing means query inner.
118	block_entries: HashMap<Hash, Option<BlockEntry>>,
119	// `None` means 'deleted', missing means query inner.
120	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	/// Transform this backend into a set of write-ops to be written to the
220	/// inner backend.
221	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}