Skip to main content

soil_client/client_api/
leaves.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Helper for managing the set of available leaves in the chain for DB implementations.
8
9use crate::blockchain::{Error, Result};
10use codec::{Decode, Encode};
11use std::{cmp::Reverse, collections::BTreeMap};
12use subsoil::database::{Database, Transaction};
13use subsoil::runtime::traits::AtLeast32Bit;
14
15type DbHash = subsoil::core::H256;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18struct LeafSetItem<H, N> {
19	hash: H,
20	number: Reverse<N>,
21}
22
23/// Inserted and removed leaves after an import action.
24pub struct ImportOutcome<H, N> {
25	inserted: LeafSetItem<H, N>,
26	removed: Option<H>,
27}
28
29/// Inserted and removed leaves after a remove action.
30pub struct RemoveOutcome<H, N> {
31	inserted: Option<H>,
32	removed: LeafSetItem<H, N>,
33}
34
35/// Removed leaves after a finalization action.
36pub struct FinalizationOutcome<I, H, N>
37where
38	I: Iterator<Item = (N, H)>,
39{
40	removed: I,
41}
42
43impl<I, H: Ord, N: Ord> FinalizationOutcome<I, H, N>
44where
45	I: Iterator<Item = (N, H)>,
46{
47	/// Constructor
48	pub fn new(new_displaced: I) -> Self {
49		FinalizationOutcome { removed: new_displaced }
50	}
51}
52
53/// list of leaf hashes ordered by number (descending).
54/// stored in memory for fast access.
55/// this allows very fast checking and modification of active leaves.
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct LeafSet<H, N> {
58	storage: BTreeMap<Reverse<N>, Vec<H>>,
59}
60
61impl<H, N> LeafSet<H, N>
62where
63	H: Clone + PartialEq + Decode + Encode,
64	N: std::fmt::Debug + Copy + AtLeast32Bit + Decode + Encode,
65{
66	/// Construct a new, blank leaf set.
67	pub fn new() -> Self {
68		Self { storage: BTreeMap::new() }
69	}
70
71	/// Read the leaf list from the DB, using given prefix for keys.
72	pub fn read_from_db(db: &dyn Database<DbHash>, column: u32, prefix: &[u8]) -> Result<Self> {
73		let mut storage = BTreeMap::new();
74
75		match db.get(column, prefix) {
76			Some(leaves) => {
77				let vals: Vec<_> = match Decode::decode(&mut leaves.as_ref()) {
78					Ok(vals) => vals,
79					Err(_) => return Err(Error::Backend("Error decoding leaves".into())),
80				};
81				for (number, hashes) in vals.into_iter() {
82					storage.insert(Reverse(number), hashes);
83				}
84			},
85			None => {},
86		}
87		Ok(Self { storage })
88	}
89
90	/// Update the leaf list on import.
91	pub fn import(&mut self, hash: H, number: N, parent_hash: H) -> ImportOutcome<H, N> {
92		let number = Reverse(number);
93
94		let removed = if number.0 != N::zero() {
95			let parent_number = Reverse(number.0 - N::one());
96			self.remove_leaf(&parent_number, &parent_hash).then(|| parent_hash)
97		} else {
98			None
99		};
100
101		self.insert_leaf(number, hash.clone());
102
103		ImportOutcome { inserted: LeafSetItem { hash, number }, removed }
104	}
105
106	/// Update the leaf list on removal.
107	///
108	/// Note that the leaves set structure doesn't have the information to decide if the
109	/// leaf we're removing is the last children of the parent. Follows that this method requires
110	/// the caller to check this condition and optionally pass the `parent_hash` if `hash` is
111	/// its last child.
112	///
113	/// Returns `None` if no modifications are applied.
114	pub fn remove(
115		&mut self,
116		hash: H,
117		number: N,
118		parent_hash: Option<H>,
119	) -> Option<RemoveOutcome<H, N>> {
120		let number = Reverse(number);
121
122		if !self.remove_leaf(&number, &hash) {
123			return None;
124		}
125
126		let inserted = parent_hash.and_then(|parent_hash| {
127			if number.0 != N::zero() {
128				let parent_number = Reverse(number.0 - N::one());
129				self.insert_leaf(parent_number, parent_hash.clone());
130				Some(parent_hash)
131			} else {
132				None
133			}
134		});
135
136		Some(RemoveOutcome { inserted, removed: LeafSetItem { hash, number } })
137	}
138
139	/// Remove all leaves displaced by the last block finalization.
140	pub fn remove_displaced_leaves<I>(&mut self, displaced_leaves: FinalizationOutcome<I, H, N>)
141	where
142		I: Iterator<Item = (N, H)>,
143	{
144		for (number, hash) in displaced_leaves.removed {
145			self.remove_leaf(&Reverse(number), &hash);
146		}
147	}
148
149	/// Undo all pending operations.
150	///
151	/// This returns an `Undo` struct, where any
152	/// `Displaced` objects that have returned by previous method calls
153	/// should be passed to via the appropriate methods. Otherwise,
154	/// the on-disk state may get out of sync with in-memory state.
155	pub fn undo(&mut self) -> Undo<'_, H, N> {
156		Undo { inner: self }
157	}
158
159	/// Revert to the given block height by dropping all leaves in the leaf set
160	/// with a block number higher than the target.
161	///
162	/// Returns the removed leaves.
163	pub fn revert(&mut self, best_hash: H, best_number: N) -> impl Iterator<Item = (H, N)> {
164		let items = self
165			.storage
166			.iter()
167			.flat_map(|(number, hashes)| hashes.iter().map(move |h| (h.clone(), number.0)))
168			.collect::<Vec<_>>();
169
170		for (hash, number) in &items {
171			if *number > best_number {
172				assert!(
173					self.remove_leaf(&Reverse(*number), &hash),
174					"item comes from an iterator over storage; qed",
175				);
176			}
177		}
178
179		let best_number_rev = Reverse(best_number);
180		let leaves_contains_best = self
181			.storage
182			.get(&best_number_rev)
183			.map_or(false, |hashes| hashes.contains(&best_hash));
184
185		// We need to make sure that the best block exists in the leaf set as
186		// this is an invariant of regular block import.
187		if !leaves_contains_best {
188			self.insert_leaf(best_number_rev, best_hash.clone());
189		}
190
191		items.into_iter().filter(move |(_, n)| *n > best_number)
192	}
193
194	/// Returns an iterator over all hashes in the leaf set
195	/// ordered by their block number descending.
196	pub fn hashes(&self) -> Vec<H> {
197		self.storage.iter().flat_map(|(_, hashes)| hashes.iter()).cloned().collect()
198	}
199
200	/// Number of known leaves.
201	pub fn count(&self) -> usize {
202		self.storage.values().map(|level| level.len()).sum()
203	}
204
205	/// Write the leaf list to the database transaction.
206	pub fn prepare_transaction(
207		&mut self,
208		tx: &mut Transaction<DbHash>,
209		column: u32,
210		prefix: &[u8],
211	) {
212		let leaves: Vec<_> = self.storage.iter().map(|(n, h)| (n.0, h.clone())).collect();
213		tx.set_from_vec(column, prefix, leaves.encode());
214	}
215
216	/// Check if given block is a leaf.
217	pub fn contains(&self, number: N, hash: H) -> bool {
218		self.storage
219			.get(&Reverse(number))
220			.map_or(false, |hashes| hashes.contains(&hash))
221	}
222
223	fn insert_leaf(&mut self, number: Reverse<N>, hash: H) {
224		self.storage.entry(number).or_insert_with(Vec::new).push(hash);
225	}
226
227	// Returns true if this leaf was contained, false otherwise.
228	fn remove_leaf(&mut self, number: &Reverse<N>, hash: &H) -> bool {
229		let mut empty = false;
230		let removed = self.storage.get_mut(number).map_or(false, |leaves| {
231			let mut found = false;
232			leaves.retain(|h| {
233				if h == hash {
234					found = true;
235					false
236				} else {
237					true
238				}
239			});
240
241			if leaves.is_empty() {
242				empty = true
243			}
244
245			found
246		});
247
248		if removed && empty {
249			self.storage.remove(number);
250		}
251
252		removed
253	}
254
255	/// Returns the highest leaf and all hashes associated to it.
256	pub fn highest_leaf(&self) -> Option<(N, &[H])> {
257		self.storage.iter().next().map(|(k, v)| (k.0, &v[..]))
258	}
259}
260
261/// Helper for undoing operations.
262pub struct Undo<'a, H: 'a, N: 'a> {
263	inner: &'a mut LeafSet<H, N>,
264}
265
266impl<'a, H: 'a, N: 'a> Undo<'a, H, N>
267where
268	H: Clone + PartialEq + Decode + Encode,
269	N: std::fmt::Debug + Copy + AtLeast32Bit + Decode + Encode,
270{
271	/// Undo an imported block by providing the import operation outcome.
272	/// No additional operations should be performed between import and undo.
273	pub fn undo_import(&mut self, outcome: ImportOutcome<H, N>) {
274		if let Some(removed_hash) = outcome.removed {
275			let removed_number = Reverse(outcome.inserted.number.0 - N::one());
276			self.inner.insert_leaf(removed_number, removed_hash);
277		}
278		self.inner.remove_leaf(&outcome.inserted.number, &outcome.inserted.hash);
279	}
280
281	/// Undo a removed block by providing the displaced leaf.
282	/// No additional operations should be performed between remove and undo.
283	pub fn undo_remove(&mut self, outcome: RemoveOutcome<H, N>) {
284		if let Some(inserted_hash) = outcome.inserted {
285			let inserted_number = Reverse(outcome.removed.number.0 - N::one());
286			self.inner.remove_leaf(&inserted_number, &inserted_hash);
287		}
288		self.inner.insert_leaf(outcome.removed.number, outcome.removed.hash);
289	}
290
291	/// Undo a finalization operation by providing the displaced leaves.
292	/// No additional operations should be performed between finalization and undo.
293	pub fn undo_finalization<I>(&mut self, outcome: FinalizationOutcome<I, H, N>)
294	where
295		I: Iterator<Item = (N, H)>,
296	{
297		for (number, hash) in outcome.removed {
298			self.inner.storage.entry(Reverse(number)).or_default().push(hash);
299		}
300	}
301}
302
303#[cfg(test)]
304mod tests {
305	use super::*;
306	use std::sync::Arc;
307
308	#[test]
309	fn import_works() {
310		let mut set = LeafSet::new();
311		set.import(0u32, 0u32, 0u32);
312
313		set.import(1_1, 1, 0);
314		set.import(2_1, 2, 1_1);
315		set.import(3_1, 3, 2_1);
316
317		assert_eq!(set.count(), 1);
318		assert!(set.contains(3, 3_1));
319		assert!(!set.contains(2, 2_1));
320		assert!(!set.contains(1, 1_1));
321		assert!(!set.contains(0, 0));
322
323		set.import(2_2, 2, 1_1);
324		set.import(1_2, 1, 0);
325		set.import(2_3, 2, 1_2);
326
327		assert_eq!(set.count(), 3);
328		assert!(set.contains(3, 3_1));
329		assert!(set.contains(2, 2_2));
330		assert!(set.contains(2, 2_3));
331
332		// Finally test the undo feature
333
334		let outcome = set.import(2_4, 2, 1_1);
335		assert_eq!(outcome.inserted.hash, 2_4);
336		assert_eq!(outcome.removed, None);
337		assert_eq!(set.count(), 4);
338		assert!(set.contains(2, 2_4));
339
340		set.undo().undo_import(outcome);
341		assert_eq!(set.count(), 3);
342		assert!(set.contains(3, 3_1));
343		assert!(set.contains(2, 2_2));
344		assert!(set.contains(2, 2_3));
345
346		let outcome = set.import(3_2, 3, 2_3);
347		assert_eq!(outcome.inserted.hash, 3_2);
348		assert_eq!(outcome.removed, Some(2_3));
349		assert_eq!(set.count(), 3);
350		assert!(set.contains(3, 3_2));
351
352		set.undo().undo_import(outcome);
353		assert_eq!(set.count(), 3);
354		assert!(set.contains(3, 3_1));
355		assert!(set.contains(2, 2_2));
356		assert!(set.contains(2, 2_3));
357	}
358
359	#[test]
360	fn removal_works() {
361		let mut set = LeafSet::new();
362		set.import(10_1u32, 10u32, 0u32);
363		set.import(11_1, 11, 10_1);
364		set.import(11_2, 11, 10_1);
365		set.import(12_1, 12, 11_1);
366
367		let outcome = set.remove(12_1, 12, Some(11_1)).unwrap();
368		assert_eq!(outcome.removed.hash, 12_1);
369		assert_eq!(outcome.inserted, Some(11_1));
370		assert_eq!(set.count(), 2);
371		assert!(set.contains(11, 11_1));
372		assert!(set.contains(11, 11_2));
373
374		let outcome = set.remove(11_1, 11, None).unwrap();
375		assert_eq!(outcome.removed.hash, 11_1);
376		assert_eq!(outcome.inserted, None);
377		assert_eq!(set.count(), 1);
378		assert!(set.contains(11, 11_2));
379
380		let outcome = set.remove(11_2, 11, Some(10_1)).unwrap();
381		assert_eq!(outcome.removed.hash, 11_2);
382		assert_eq!(outcome.inserted, Some(10_1));
383		assert_eq!(set.count(), 1);
384		assert!(set.contains(10, 10_1));
385
386		set.undo().undo_remove(outcome);
387		assert_eq!(set.count(), 1);
388		assert!(set.contains(11, 11_2));
389	}
390
391	#[test]
392	fn flush_to_disk() {
393		const PREFIX: &[u8] = b"abcdefg";
394		let db = Arc::new(subsoil::database::MemDb::default());
395
396		let mut set = LeafSet::new();
397		set.import(0u32, 0u32, 0u32);
398
399		set.import(1_1, 1, 0);
400		set.import(2_1, 2, 1_1);
401		set.import(3_1, 3, 2_1);
402
403		let mut tx = Transaction::new();
404
405		set.prepare_transaction(&mut tx, 0, PREFIX);
406		db.commit(tx).unwrap();
407
408		let set2 = LeafSet::read_from_db(&*db, 0, PREFIX).unwrap();
409		assert_eq!(set, set2);
410	}
411
412	#[test]
413	fn two_leaves_same_height_can_be_included() {
414		let mut set = LeafSet::new();
415
416		set.import(1_1u32, 10u32, 0u32);
417		set.import(1_2, 10, 0);
418
419		assert!(set.storage.contains_key(&Reverse(10)));
420		assert!(set.contains(10, 1_1));
421		assert!(set.contains(10, 1_2));
422		assert!(!set.contains(10, 1_3));
423	}
424}