1#![warn(missing_docs)]
30
31pub mod offchain;
32
33pub mod bench;
34
35mod children;
36mod parity_db;
37mod pinned_blocks_cache;
38mod record_stats_state;
39mod stats;
40#[cfg(any(feature = "rocksdb", test))]
41mod upgrade;
42mod utils;
43
44use linked_hash_map::LinkedHashMap;
45use log::{debug, trace, warn};
46use parking_lot::{Mutex, RwLock};
47use prometheus_endpoint::Registry;
48use std::{
49 collections::{HashMap, HashSet},
50 io,
51 path::{Path, PathBuf},
52 sync::Arc,
53};
54
55use crate::{
56 pinned_blocks_cache::PinnedBlocksCache,
57 record_stats_state::RecordStatsState,
58 stats::StateUsageStats,
59 utils::{meta_keys, read_db, read_meta, remove_from_db, DatabaseType, Meta},
60};
61use codec::{Decode, Encode};
62use hash_db::Prefix;
63use sc_client_api::{
64 backend::NewBlockState,
65 blockchain::{BlockGap, BlockGapType},
66 leaves::{FinalizationOutcome, LeafSet},
67 utils::is_descendent_of,
68 IoInfo, MemoryInfo, MemorySize, TrieCacheContext, UsageInfo,
69};
70use sc_state_db::{IsPruned, LastCanonicalized, StateDb};
71use sp_arithmetic::traits::Saturating;
72use sp_blockchain::{
73 Backend as _, CachedHeaderMetadata, DisplacedLeavesAfterFinalization, Error as ClientError,
74 HeaderBackend, HeaderMetadata, HeaderMetadataCache, Result as ClientResult,
75};
76use sp_core::{
77 offchain::OffchainOverlayedChange,
78 storage::{well_known_keys, ChildInfo},
79};
80use sp_database::Transaction;
81use sp_runtime::{
82 generic::BlockId,
83 traits::{
84 Block as BlockT, Hash, HashingFor, Header as HeaderT, NumberFor, One, SaturatedConversion,
85 Zero,
86 },
87 Justification, Justifications, StateVersion, Storage,
88};
89use sp_state_machine::{
90 backend::{AsTrieBackend, Backend as StateBackend},
91 BackendTransaction, ChildStorageCollection, DBValue, IndexOperation, IterArgs,
92 OffchainChangesCollection, StateMachineStats, StorageCollection, StorageIterator, StorageKey,
93 StorageValue, UsageInfo as StateUsageInfo,
94};
95use sp_trie::{cache::SharedTrieCache, prefixed_key, MemoryDB, MerkleValue, PrefixedMemoryDB};
96use utils::BLOCK_GAP_CURRENT_VERSION;
97
98pub use sc_state_db::PruningMode;
100pub use sp_database::Database;
101
102pub use bench::BenchmarkingState;
103
104pub trait PruningFilter: Send + Sync {
109 fn should_retain(&self, justifications: &Justifications) -> bool;
113}
114
115impl<F> PruningFilter for F
116where
117 F: Fn(&Justifications) -> bool + Send + Sync,
118{
119 fn should_retain(&self, justifications: &Justifications) -> bool {
120 (self)(justifications)
121 }
122}
123
124const CACHE_HEADERS: usize = 8;
125
126pub type DbState<H> = sp_state_machine::TrieBackend<Arc<dyn sp_state_machine::Storage<H>>, H>;
128
129pub type DbStateBuilder<Hasher> =
131 sp_state_machine::TrieBackendBuilder<Arc<dyn sp_state_machine::Storage<Hasher>>, Hasher>;
132
133const DB_HASH_LEN: usize = 32;
135
136pub type DbHash = sp_core::H256;
138
139#[derive(Debug, Encode, Decode)]
141enum DbExtrinsic<B: BlockT> {
142 Indexed {
144 hash: DbHash,
146 header: Vec<u8>,
148 },
149 Full(B::Extrinsic),
151 MultiRenew {
158 hashes: Vec<DbHash>,
160 extrinsic: Vec<u8>,
161 },
162}
163
164pub struct RefTrackingState<Block: BlockT> {
169 state: DbState<HashingFor<Block>>,
170 storage: Arc<StorageDb<Block>>,
171 parent_hash: Option<Block::Hash>,
172}
173
174impl<B: BlockT> RefTrackingState<B> {
175 fn new(
176 state: DbState<HashingFor<B>>,
177 storage: Arc<StorageDb<B>>,
178 parent_hash: Option<B::Hash>,
179 ) -> Self {
180 RefTrackingState { state, parent_hash, storage }
181 }
182}
183
184impl<B: BlockT> Drop for RefTrackingState<B> {
185 fn drop(&mut self) {
186 if let Some(hash) = &self.parent_hash {
187 self.storage.state_db.unpin(hash);
188 }
189 }
190}
191
192impl<Block: BlockT> std::fmt::Debug for RefTrackingState<Block> {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 write!(f, "Block {:?}", self.parent_hash)
195 }
196}
197
198pub struct RawIter<B: BlockT> {
200 inner: <DbState<HashingFor<B>> as StateBackend<HashingFor<B>>>::RawIter,
201}
202
203impl<B: BlockT> StorageIterator<HashingFor<B>> for RawIter<B> {
204 type Backend = RefTrackingState<B>;
205 type Error = <DbState<HashingFor<B>> as StateBackend<HashingFor<B>>>::Error;
206
207 fn next_key(&mut self, backend: &Self::Backend) -> Option<Result<StorageKey, Self::Error>> {
208 self.inner.next_key(&backend.state)
209 }
210
211 fn next_pair(
212 &mut self,
213 backend: &Self::Backend,
214 ) -> Option<Result<(StorageKey, StorageValue), Self::Error>> {
215 self.inner.next_pair(&backend.state)
216 }
217
218 fn was_complete(&self) -> bool {
219 self.inner.was_complete()
220 }
221}
222
223impl<B: BlockT> StateBackend<HashingFor<B>> for RefTrackingState<B> {
224 type Error = <DbState<HashingFor<B>> as StateBackend<HashingFor<B>>>::Error;
225 type TrieBackendStorage =
226 <DbState<HashingFor<B>> as StateBackend<HashingFor<B>>>::TrieBackendStorage;
227 type RawIter = RawIter<B>;
228
229 fn storage(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
230 self.state.storage(key)
231 }
232
233 fn storage_hash(&self, key: &[u8]) -> Result<Option<B::Hash>, Self::Error> {
234 self.state.storage_hash(key)
235 }
236
237 fn child_storage(
238 &self,
239 child_info: &ChildInfo,
240 key: &[u8],
241 ) -> Result<Option<Vec<u8>>, Self::Error> {
242 self.state.child_storage(child_info, key)
243 }
244
245 fn child_storage_hash(
246 &self,
247 child_info: &ChildInfo,
248 key: &[u8],
249 ) -> Result<Option<B::Hash>, Self::Error> {
250 self.state.child_storage_hash(child_info, key)
251 }
252
253 fn closest_merkle_value(
254 &self,
255 key: &[u8],
256 ) -> Result<Option<MerkleValue<B::Hash>>, Self::Error> {
257 self.state.closest_merkle_value(key)
258 }
259
260 fn child_closest_merkle_value(
261 &self,
262 child_info: &ChildInfo,
263 key: &[u8],
264 ) -> Result<Option<MerkleValue<B::Hash>>, Self::Error> {
265 self.state.child_closest_merkle_value(child_info, key)
266 }
267
268 fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
269 self.state.exists_storage(key)
270 }
271
272 fn exists_child_storage(
273 &self,
274 child_info: &ChildInfo,
275 key: &[u8],
276 ) -> Result<bool, Self::Error> {
277 self.state.exists_child_storage(child_info, key)
278 }
279
280 fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
281 self.state.next_storage_key(key)
282 }
283
284 fn next_child_storage_key(
285 &self,
286 child_info: &ChildInfo,
287 key: &[u8],
288 ) -> Result<Option<Vec<u8>>, Self::Error> {
289 self.state.next_child_storage_key(child_info, key)
290 }
291
292 fn storage_root<'a>(
293 &self,
294 delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
295 state_version: StateVersion,
296 ) -> (B::Hash, BackendTransaction<HashingFor<B>>) {
297 self.state.storage_root(delta, state_version)
298 }
299
300 fn child_storage_root<'a>(
301 &self,
302 child_info: &ChildInfo,
303 delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
304 state_version: StateVersion,
305 ) -> (B::Hash, bool, BackendTransaction<HashingFor<B>>) {
306 self.state.child_storage_root(child_info, delta, state_version)
307 }
308
309 fn raw_iter(&self, args: IterArgs) -> Result<Self::RawIter, Self::Error> {
310 self.state.raw_iter(args).map(|inner| RawIter { inner })
311 }
312
313 fn register_overlay_stats(&self, stats: &StateMachineStats) {
314 self.state.register_overlay_stats(stats);
315 }
316
317 fn usage_info(&self) -> StateUsageInfo {
318 self.state.usage_info()
319 }
320}
321
322impl<B: BlockT> AsTrieBackend<HashingFor<B>> for RefTrackingState<B> {
323 type TrieBackendStorage =
324 <DbState<HashingFor<B>> as StateBackend<HashingFor<B>>>::TrieBackendStorage;
325
326 fn as_trie_backend(
327 &self,
328 ) -> &sp_state_machine::TrieBackend<Self::TrieBackendStorage, HashingFor<B>> {
329 &self.state.as_trie_backend()
330 }
331}
332
333pub struct DatabaseSettings {
335 pub trie_cache_maximum_size: Option<usize>,
339 pub state_pruning: Option<PruningMode>,
341 pub source: DatabaseSource,
343 pub blocks_pruning: BlocksPruning,
347 pub pruning_filters: Vec<Arc<dyn PruningFilter>>,
353 pub metrics_registry: Option<Registry>,
355}
356
357#[derive(Debug, Clone, Copy, PartialEq)]
359pub enum BlocksPruning {
360 KeepAll,
362 KeepFinalized,
364 Some(u32),
366}
367
368impl BlocksPruning {
369 pub fn is_archive(&self) -> bool {
371 match *self {
372 BlocksPruning::KeepAll | BlocksPruning::KeepFinalized => true,
373 BlocksPruning::Some(_) => false,
374 }
375 }
376}
377
378#[derive(Debug, Clone)]
380pub enum DatabaseSource {
381 Auto {
384 paritydb_path: PathBuf,
386 rocksdb_path: PathBuf,
388 cache_size: usize,
390 },
391 #[cfg(feature = "rocksdb")]
393 RocksDb {
394 path: PathBuf,
396 cache_size: usize,
398 },
399
400 ParityDb {
402 path: PathBuf,
404 },
405
406 Custom {
408 db: Arc<dyn Database<DbHash>>,
410
411 require_create_flag: bool,
413 },
414}
415
416impl DatabaseSource {
417 pub fn path(&self) -> Option<&Path> {
419 match self {
420 DatabaseSource::Auto { paritydb_path, .. } => Some(paritydb_path),
425 #[cfg(feature = "rocksdb")]
426 DatabaseSource::RocksDb { path, .. } => Some(path),
427 DatabaseSource::ParityDb { path } => Some(path),
428 DatabaseSource::Custom { .. } => None,
429 }
430 }
431
432 pub fn set_path(&mut self, p: &Path) -> bool {
434 match self {
435 DatabaseSource::Auto { ref mut paritydb_path, .. } => {
436 *paritydb_path = p.into();
437 true
438 },
439 #[cfg(feature = "rocksdb")]
440 DatabaseSource::RocksDb { ref mut path, .. } => {
441 *path = p.into();
442 true
443 },
444 DatabaseSource::ParityDb { ref mut path } => {
445 *path = p.into();
446 true
447 },
448 DatabaseSource::Custom { .. } => false,
449 }
450 }
451}
452
453impl std::fmt::Display for DatabaseSource {
454 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
455 let name = match self {
456 DatabaseSource::Auto { .. } => "Auto",
457 #[cfg(feature = "rocksdb")]
458 DatabaseSource::RocksDb { .. } => "RocksDb",
459 DatabaseSource::ParityDb { .. } => "ParityDb",
460 DatabaseSource::Custom { .. } => "Custom",
461 };
462 write!(f, "{}", name)
463 }
464}
465
466pub(crate) mod columns {
467 pub const META: u32 = crate::utils::COLUMN_META;
468 pub const STATE: u32 = 1;
469 pub const STATE_META: u32 = 2;
470 pub const KEY_LOOKUP: u32 = 3;
472 pub const HEADER: u32 = 4;
473 pub const BODY: u32 = 5;
474 pub const JUSTIFICATIONS: u32 = 6;
475 pub const AUX: u32 = 8;
476 pub const OFFCHAIN: u32 = 9;
478 pub const TRANSACTION: u32 = 11;
480 pub const BODY_INDEX: u32 = 12;
481}
482
483struct PendingBlock<Block: BlockT> {
484 header: Block::Header,
485 justifications: Option<Justifications>,
486 body: Option<Vec<Block::Extrinsic>>,
487 indexed_body: Option<Vec<Vec<u8>>>,
488 leaf_state: NewBlockState,
489 register_as_leaf: bool,
490}
491
492#[derive(Clone)]
494struct StateMetaDb(Arc<dyn Database<DbHash>>);
495
496impl sc_state_db::MetaDb for StateMetaDb {
497 type Error = sp_database::error::DatabaseError;
498
499 fn get_meta(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
500 Ok(self.0.get(columns::STATE_META, key))
501 }
502}
503
504struct MetaUpdate<Block: BlockT> {
505 pub hash: Block::Hash,
506 pub number: NumberFor<Block>,
507 pub is_best: bool,
508 pub is_finalized: bool,
509 pub with_state: bool,
510}
511
512fn cache_header<Hash: std::cmp::Eq + std::hash::Hash, Header>(
513 cache: &mut LinkedHashMap<Hash, Option<Header>>,
514 hash: Hash,
515 header: Option<Header>,
516) {
517 cache.insert(hash, header);
518 while cache.len() > CACHE_HEADERS {
519 cache.pop_front();
520 }
521}
522
523pub struct BlockchainDb<Block: BlockT> {
525 db: Arc<dyn Database<DbHash>>,
526 meta: Arc<RwLock<Meta<NumberFor<Block>, Block::Hash>>>,
527 leaves: RwLock<LeafSet<Block::Hash, NumberFor<Block>>>,
528 header_metadata_cache: Arc<HeaderMetadataCache<Block>>,
529 header_cache: Mutex<LinkedHashMap<Block::Hash, Option<Block::Header>>>,
530 pinned_blocks_cache: Arc<RwLock<PinnedBlocksCache<Block>>>,
531}
532
533impl<Block: BlockT> BlockchainDb<Block> {
534 fn new(db: Arc<dyn Database<DbHash>>) -> ClientResult<Self> {
535 let meta = read_meta::<Block>(&*db, columns::HEADER)?;
536 let leaves = LeafSet::read_from_db(&*db, columns::META, meta_keys::LEAF_PREFIX)?;
537 Ok(BlockchainDb {
538 db,
539 leaves: RwLock::new(leaves),
540 meta: Arc::new(RwLock::new(meta)),
541 header_metadata_cache: Arc::new(HeaderMetadataCache::default()),
542 header_cache: Default::default(),
543 pinned_blocks_cache: Arc::new(RwLock::new(PinnedBlocksCache::new())),
544 })
545 }
546
547 fn update_meta(&self, update: MetaUpdate<Block>) {
548 let MetaUpdate { hash, number, is_best, is_finalized, with_state } = update;
549 let mut meta = self.meta.write();
550 if number.is_zero() {
551 meta.genesis_hash = hash;
552 }
553
554 if is_best {
555 meta.best_number = number;
556 meta.best_hash = hash;
557 }
558
559 if is_finalized {
560 if with_state {
561 meta.finalized_state = Some((hash, number));
562 }
563 meta.finalized_number = number;
564 meta.finalized_hash = hash;
565 }
566 }
567
568 fn update_block_gap(&self, gap: Option<BlockGap<NumberFor<Block>>>) {
569 let mut meta = self.meta.write();
570 meta.block_gap = gap;
571 }
572
573 fn clear_pinning_cache(&self) {
575 self.pinned_blocks_cache.write().clear();
576 }
577
578 fn insert_justifications_if_pinned(&self, hash: Block::Hash, justification: Justification) {
582 let mut cache = self.pinned_blocks_cache.write();
583 if !cache.contains(hash) {
584 return;
585 }
586
587 let justifications = Justifications::from(justification);
588 cache.insert_justifications(hash, Some(justifications));
589 }
590
591 fn insert_persisted_justifications_if_pinned(&self, hash: Block::Hash) -> ClientResult<()> {
595 let mut cache = self.pinned_blocks_cache.write();
596 if !cache.contains(hash) {
597 return Ok(());
598 }
599
600 let justifications = self.justifications_uncached(hash)?;
601 cache.insert_justifications(hash, justifications);
602 Ok(())
603 }
604
605 fn insert_persisted_body_if_pinned(&self, hash: Block::Hash) -> ClientResult<()> {
609 let mut cache = self.pinned_blocks_cache.write();
610 if !cache.contains(hash) {
611 return Ok(());
612 }
613
614 let body = self.body_uncached(hash)?;
615 cache.insert_body(hash, body);
616 Ok(())
617 }
618
619 fn bump_ref(&self, hash: Block::Hash) {
621 self.pinned_blocks_cache.write().pin(hash);
622 }
623
624 fn unpin(&self, hash: Block::Hash) {
626 self.pinned_blocks_cache.write().unpin(hash);
627 }
628
629 fn justifications_uncached(&self, hash: Block::Hash) -> ClientResult<Option<Justifications>> {
630 match read_db(
631 &*self.db,
632 columns::KEY_LOOKUP,
633 columns::JUSTIFICATIONS,
634 BlockId::<Block>::Hash(hash),
635 )? {
636 Some(justifications) => match Decode::decode(&mut &justifications[..]) {
637 Ok(justifications) => Ok(Some(justifications)),
638 Err(err) => {
639 return Err(sp_blockchain::Error::Backend(format!(
640 "Error decoding justifications: {err}"
641 )))
642 },
643 },
644 None => Ok(None),
645 }
646 }
647
648 fn body_uncached(&self, hash: Block::Hash) -> ClientResult<Option<Vec<Block::Extrinsic>>> {
649 if let Some(body) =
650 read_db(&*self.db, columns::KEY_LOOKUP, columns::BODY, BlockId::Hash::<Block>(hash))?
651 {
652 match Decode::decode(&mut &body[..]) {
654 Ok(body) => return Ok(Some(body)),
655 Err(err) => {
656 return Err(sp_blockchain::Error::Backend(format!(
657 "Error decoding body: {err}"
658 )))
659 },
660 }
661 }
662
663 if let Some(index) = read_db(
664 &*self.db,
665 columns::KEY_LOOKUP,
666 columns::BODY_INDEX,
667 BlockId::Hash::<Block>(hash),
668 )? {
669 match Vec::<DbExtrinsic<Block>>::decode(&mut &index[..]) {
670 Ok(index) => {
671 let mut body = Vec::new();
672 for ex in index {
673 match ex {
674 DbExtrinsic::Indexed { hash, header } => {
675 match self.db.get(columns::TRANSACTION, hash.as_ref()) {
676 Some(t) => {
677 let mut input =
678 utils::join_input(header.as_ref(), t.as_ref());
679 let ex = Block::Extrinsic::decode(&mut input).map_err(
680 |err| {
681 sp_blockchain::Error::Backend(format!(
682 "Error decoding indexed extrinsic: {err}"
683 ))
684 },
685 )?;
686 body.push(ex);
687 },
688 None => {
689 return Err(sp_blockchain::Error::Backend(format!(
690 "Missing indexed transaction {hash:?}"
691 )))
692 },
693 };
694 },
695 DbExtrinsic::Full(ex) => {
696 body.push(ex);
697 },
698 DbExtrinsic::MultiRenew { extrinsic, .. } => {
699 let ex = Block::Extrinsic::decode(&mut &extrinsic[..]).map_err(
702 |err| {
703 sp_blockchain::Error::Backend(format!(
704 "Error decoding multi-renew extrinsic: {err}"
705 ))
706 },
707 )?;
708 body.push(ex);
709 },
710 }
711 }
712 return Ok(Some(body));
713 },
714 Err(err) => {
715 return Err(sp_blockchain::Error::Backend(format!(
716 "Error decoding body list: {err}",
717 )))
718 },
719 }
720 }
721 Ok(None)
722 }
723
724 fn block_indexed_hashes_iter(
725 &self,
726 hash: Block::Hash,
727 ) -> ClientResult<Option<impl Iterator<Item = DbHash>>> {
728 let Some(body) = read_db(
729 &*self.db,
730 columns::KEY_LOOKUP,
731 columns::BODY_INDEX,
732 BlockId::<Block>::Hash(hash),
733 )?
734 else {
735 return Ok(None);
736 };
737 match Vec::<DbExtrinsic<Block>>::decode(&mut &body[..]) {
738 Ok(index) => Ok(Some(index.into_iter().flat_map(|ex| match ex {
739 DbExtrinsic::Indexed { hash, .. } => vec![hash],
740 DbExtrinsic::MultiRenew { hashes, .. } => hashes.into_iter().collect(),
741 _ => vec![],
742 }))),
743 Err(err) => {
744 Err(sp_blockchain::Error::Backend(format!("Error decoding body list: {err}")))
745 },
746 }
747 }
748}
749
750impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for BlockchainDb<Block> {
751 fn header(&self, hash: Block::Hash) -> ClientResult<Option<Block::Header>> {
752 let mut cache = self.header_cache.lock();
753 if let Some(result) = cache.get_refresh(&hash) {
754 return Ok(result.clone());
755 }
756 let header = utils::read_header(
757 &*self.db,
758 columns::KEY_LOOKUP,
759 columns::HEADER,
760 BlockId::<Block>::Hash(hash),
761 )?;
762 cache_header(&mut cache, hash, header.clone());
763 Ok(header)
764 }
765
766 fn info(&self) -> sc_client_api::blockchain::Info<Block> {
767 let meta = self.meta.read();
768 sc_client_api::blockchain::Info {
769 best_hash: meta.best_hash,
770 best_number: meta.best_number,
771 genesis_hash: meta.genesis_hash,
772 finalized_hash: meta.finalized_hash,
773 finalized_number: meta.finalized_number,
774 finalized_state: meta.finalized_state,
775 number_leaves: self.leaves.read().count(),
776 block_gap: meta.block_gap,
777 }
778 }
779
780 fn status(&self, hash: Block::Hash) -> ClientResult<sc_client_api::blockchain::BlockStatus> {
781 match self.header(hash)?.is_some() {
782 true => Ok(sc_client_api::blockchain::BlockStatus::InChain),
783 false => Ok(sc_client_api::blockchain::BlockStatus::Unknown),
784 }
785 }
786
787 fn number(&self, hash: Block::Hash) -> ClientResult<Option<NumberFor<Block>>> {
788 Ok(self.header_metadata(hash).ok().map(|header_metadata| header_metadata.number))
789 }
790
791 fn hash(&self, number: NumberFor<Block>) -> ClientResult<Option<Block::Hash>> {
792 Ok(utils::read_header::<Block>(
793 &*self.db,
794 columns::KEY_LOOKUP,
795 columns::HEADER,
796 BlockId::Number(number),
797 )?
798 .map(|header| header.hash()))
799 }
800}
801
802impl<Block: BlockT> sc_client_api::blockchain::Backend<Block> for BlockchainDb<Block> {
803 fn body(&self, hash: Block::Hash) -> ClientResult<Option<Vec<Block::Extrinsic>>> {
804 let cache = self.pinned_blocks_cache.read();
805 if let Some(result) = cache.body(&hash) {
806 return Ok(result.clone());
807 }
808
809 self.body_uncached(hash)
810 }
811
812 fn justifications(&self, hash: Block::Hash) -> ClientResult<Option<Justifications>> {
813 let cache = self.pinned_blocks_cache.read();
814 if let Some(result) = cache.justifications(&hash) {
815 return Ok(result.clone());
816 }
817
818 self.justifications_uncached(hash)
819 }
820
821 fn last_finalized(&self) -> ClientResult<Block::Hash> {
822 Ok(self.meta.read().finalized_hash)
823 }
824
825 fn leaves(&self) -> ClientResult<Vec<Block::Hash>> {
826 Ok(self.leaves.read().hashes())
827 }
828
829 fn children(&self, parent_hash: Block::Hash) -> ClientResult<Vec<Block::Hash>> {
830 children::read_children(&*self.db, columns::META, meta_keys::CHILDREN_PREFIX, parent_hash)
831 }
832
833 fn indexed_transaction(&self, hash: DbHash) -> ClientResult<Option<Vec<u8>>> {
834 Ok(self.db.get(columns::TRANSACTION, hash.as_ref()))
835 }
836
837 fn has_indexed_transaction(&self, hash: DbHash) -> ClientResult<bool> {
838 Ok(self.db.contains(columns::TRANSACTION, hash.as_ref()))
839 }
840
841 fn block_indexed_hashes(&self, hash: Block::Hash) -> ClientResult<Option<Vec<DbHash>>> {
842 self.block_indexed_hashes_iter(hash).map(|hashes| hashes.map(Iterator::collect))
843 }
844
845 fn block_indexed_body(&self, hash: Block::Hash) -> ClientResult<Option<Vec<Vec<u8>>>> {
846 match self.block_indexed_hashes_iter(hash) {
847 Ok(Some(hashes)) => Ok(Some(
848 hashes
849 .map(|hash| match self.db.get(columns::TRANSACTION, hash.as_ref()) {
850 Some(t) => Ok(t),
851 None => Err(sp_blockchain::Error::Backend(format!(
852 "Missing indexed transaction {hash:?}",
853 ))),
854 })
855 .collect::<Result<_, _>>()?,
856 )),
857 Ok(None) => Ok(None),
858 Err(err) => Err(err),
859 }
860 }
861}
862
863impl<Block: BlockT> HeaderMetadata<Block> for BlockchainDb<Block> {
864 type Error = sp_blockchain::Error;
865
866 fn header_metadata(
867 &self,
868 hash: Block::Hash,
869 ) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
870 self.header_metadata_cache.header_metadata(hash).map_or_else(
871 || {
872 self.header(hash)?
873 .map(|header| {
874 let header_metadata = CachedHeaderMetadata::from(&header);
875 self.header_metadata_cache
876 .insert_header_metadata(header_metadata.hash, header_metadata.clone());
877 header_metadata
878 })
879 .ok_or_else(|| {
880 ClientError::UnknownBlock(format!(
881 "Header was not found in the database: {hash:?}",
882 ))
883 })
884 },
885 Ok,
886 )
887 }
888
889 fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
890 self.header_metadata_cache.insert_header_metadata(hash, metadata)
891 }
892
893 fn remove_header_metadata(&self, hash: Block::Hash) {
894 self.header_cache.lock().remove(&hash);
895 self.header_metadata_cache.remove_header_metadata(hash);
896 }
897}
898
899pub struct BlockImportOperation<Block: BlockT> {
901 old_state: RecordStatsState<RefTrackingState<Block>, Block>,
902 db_updates: PrefixedMemoryDB<HashingFor<Block>>,
903 storage_updates: StorageCollection,
904 child_storage_updates: ChildStorageCollection,
905 offchain_storage_updates: OffchainChangesCollection,
906 pending_block: Option<PendingBlock<Block>>,
907 aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
908 finalized_blocks: Vec<(Block::Hash, Option<Justification>)>,
909 set_head: Option<Block::Hash>,
910 commit_state: bool,
911 create_gap: bool,
912 reset_storage: bool,
913 index_ops: Vec<IndexOperation>,
914 prefetched_indexed_transactions: HashMap<DbHash, Vec<u8>>,
915}
916
917impl<Block: BlockT> BlockImportOperation<Block> {
918 fn apply_offchain(&mut self, transaction: &mut Transaction<DbHash>) {
919 let mut count = 0;
920 for ((prefix, key), value_operation) in self.offchain_storage_updates.drain(..) {
921 count += 1;
922 let key = crate::offchain::concatenate_prefix_and_key(&prefix, &key);
923 match value_operation {
924 OffchainOverlayedChange::SetValue(val) => {
925 transaction.set_from_vec(columns::OFFCHAIN, &key, val)
926 },
927 OffchainOverlayedChange::Remove => transaction.remove(columns::OFFCHAIN, &key),
928 }
929 }
930
931 if count > 0 {
932 log::debug!(target: "sc_offchain", "Applied {count} offchain indexing changes.");
933 }
934 }
935
936 fn apply_aux(&mut self, transaction: &mut Transaction<DbHash>) {
937 for (key, maybe_val) in self.aux_ops.drain(..) {
938 match maybe_val {
939 Some(val) => transaction.set_from_vec(columns::AUX, &key, val),
940 None => transaction.remove(columns::AUX, &key),
941 }
942 }
943 }
944
945 fn apply_new_state(
946 &mut self,
947 storage: Storage,
948 state_version: StateVersion,
949 ) -> ClientResult<Block::Hash> {
950 if storage.top.keys().any(|k| well_known_keys::is_child_storage_key(k)) {
951 return Err(sp_blockchain::Error::InvalidState);
952 }
953
954 let child_delta = storage.children_default.values().map(|child_content| {
955 (
956 &child_content.child_info,
957 child_content.data.iter().map(|(k, v)| (&k[..], Some(&v[..]))),
958 )
959 });
960
961 let (root, transaction) = self.old_state.full_storage_root(
962 storage.top.iter().map(|(k, v)| (&k[..], Some(&v[..]))),
963 child_delta,
964 state_version,
965 );
966
967 self.db_updates = transaction;
968 Ok(root)
969 }
970}
971
972impl<Block: BlockT> sc_client_api::backend::BlockImportOperation<Block>
973 for BlockImportOperation<Block>
974{
975 type State = RecordStatsState<RefTrackingState<Block>, Block>;
976
977 fn state(&self) -> ClientResult<Option<&Self::State>> {
978 Ok(Some(&self.old_state))
979 }
980
981 fn set_block_data(
982 &mut self,
983 header: Block::Header,
984 body: Option<Vec<Block::Extrinsic>>,
985 indexed_body: Option<Vec<Vec<u8>>>,
986 justifications: Option<Justifications>,
987 leaf_state: NewBlockState,
988 register_as_leaf: bool,
989 ) -> ClientResult<()> {
990 assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
991 self.pending_block = Some(PendingBlock {
992 header,
993 body,
994 indexed_body,
995 justifications,
996 leaf_state,
997 register_as_leaf,
998 });
999 Ok(())
1000 }
1001
1002 fn update_db_storage(
1003 &mut self,
1004 update: PrefixedMemoryDB<HashingFor<Block>>,
1005 ) -> ClientResult<()> {
1006 self.db_updates = update;
1007 Ok(())
1008 }
1009
1010 fn reset_storage(
1011 &mut self,
1012 storage: Storage,
1013 state_version: StateVersion,
1014 ) -> ClientResult<Block::Hash> {
1015 let root = self.apply_new_state(storage, state_version)?;
1016 self.commit_state = true;
1017 self.reset_storage = true;
1018 Ok(root)
1019 }
1020
1021 fn set_genesis_state(
1022 &mut self,
1023 storage: Storage,
1024 commit: bool,
1025 state_version: StateVersion,
1026 ) -> ClientResult<Block::Hash> {
1027 let root = self.apply_new_state(storage, state_version)?;
1028 self.commit_state = commit;
1029 Ok(root)
1030 }
1031
1032 fn insert_aux<I>(&mut self, ops: I) -> ClientResult<()>
1033 where
1034 I: IntoIterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
1035 {
1036 self.aux_ops.append(&mut ops.into_iter().collect());
1037 Ok(())
1038 }
1039
1040 fn update_storage(
1041 &mut self,
1042 update: StorageCollection,
1043 child_update: ChildStorageCollection,
1044 ) -> ClientResult<()> {
1045 self.storage_updates = update;
1046 self.child_storage_updates = child_update;
1047 Ok(())
1048 }
1049
1050 fn update_offchain_storage(
1051 &mut self,
1052 offchain_update: OffchainChangesCollection,
1053 ) -> ClientResult<()> {
1054 self.offchain_storage_updates = offchain_update;
1055 Ok(())
1056 }
1057
1058 fn mark_finalized(
1059 &mut self,
1060 block: Block::Hash,
1061 justification: Option<Justification>,
1062 ) -> ClientResult<()> {
1063 self.finalized_blocks.push((block, justification));
1064 Ok(())
1065 }
1066
1067 fn mark_head(&mut self, hash: Block::Hash) -> ClientResult<()> {
1068 assert!(self.set_head.is_none(), "Only one set head per operation is allowed");
1069 self.set_head = Some(hash);
1070 Ok(())
1071 }
1072
1073 fn update_transaction_index(&mut self, index_ops: Vec<IndexOperation>) -> ClientResult<()> {
1074 self.index_ops = index_ops;
1075 Ok(())
1076 }
1077
1078 fn set_renew_payloads(&mut self, payloads: HashMap<DbHash, Vec<u8>>) -> ClientResult<()> {
1079 self.prefetched_indexed_transactions = payloads;
1080 Ok(())
1081 }
1082
1083 fn set_create_gap(&mut self, create_gap: bool) {
1084 self.create_gap = create_gap;
1085 }
1086}
1087
1088struct StorageDb<Block: BlockT> {
1089 pub db: Arc<dyn Database<DbHash>>,
1090 pub state_db: StateDb<Block::Hash, Vec<u8>, StateMetaDb>,
1091 prefix_keys: bool,
1092}
1093
1094impl<Block: BlockT> sp_state_machine::Storage<HashingFor<Block>> for StorageDb<Block> {
1095 fn get(&self, key: &Block::Hash, prefix: Prefix) -> Result<Option<DBValue>, String> {
1096 if self.prefix_keys {
1097 let key = prefixed_key::<HashingFor<Block>>(key, prefix);
1098 self.state_db.get(&key, self)
1099 } else {
1100 self.state_db.get(key.as_ref(), self)
1101 }
1102 .map_err(|e| format!("Database backend error: {e:?}"))
1103 }
1104}
1105
1106impl<Block: BlockT> sc_state_db::NodeDb for StorageDb<Block> {
1107 type Error = io::Error;
1108 type Key = [u8];
1109
1110 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
1111 Ok(self.db.get(columns::STATE, key))
1112 }
1113}
1114
1115struct DbGenesisStorage<Block: BlockT> {
1116 root: Block::Hash,
1117 storage: PrefixedMemoryDB<HashingFor<Block>>,
1118}
1119
1120impl<Block: BlockT> DbGenesisStorage<Block> {
1121 pub fn new(root: Block::Hash, storage: PrefixedMemoryDB<HashingFor<Block>>) -> Self {
1122 DbGenesisStorage { root, storage }
1123 }
1124}
1125
1126impl<Block: BlockT> sp_state_machine::Storage<HashingFor<Block>> for DbGenesisStorage<Block> {
1127 fn get(&self, key: &Block::Hash, prefix: Prefix) -> Result<Option<DBValue>, String> {
1128 use hash_db::HashDB;
1129 Ok(self.storage.get(key, prefix))
1130 }
1131}
1132
1133struct EmptyStorage<Block: BlockT>(pub Block::Hash);
1134
1135impl<Block: BlockT> EmptyStorage<Block> {
1136 pub fn new() -> Self {
1137 let mut root = Block::Hash::default();
1138 let mut mdb = MemoryDB::<HashingFor<Block>>::default();
1139 sp_trie::trie_types::TrieDBMutBuilderV1::<HashingFor<Block>>::new(&mut mdb, &mut root)
1141 .build();
1142 EmptyStorage(root)
1143 }
1144}
1145
1146impl<Block: BlockT> sp_state_machine::Storage<HashingFor<Block>> for EmptyStorage<Block> {
1147 fn get(&self, _key: &Block::Hash, _prefix: Prefix) -> Result<Option<DBValue>, String> {
1148 Ok(None)
1149 }
1150}
1151
1152struct Frozen<T: Clone> {
1156 at: std::time::Instant,
1157 value: Option<T>,
1158}
1159
1160pub(crate) struct FrozenForDuration<T: Clone> {
1166 duration: std::time::Duration,
1167 value: parking_lot::Mutex<Frozen<T>>,
1168}
1169
1170impl<T: Clone> FrozenForDuration<T> {
1171 fn new(duration: std::time::Duration) -> Self {
1172 Self { duration, value: Frozen { at: std::time::Instant::now(), value: None }.into() }
1173 }
1174
1175 fn take_or_else<F>(&self, f: F) -> T
1176 where
1177 F: FnOnce() -> T,
1178 {
1179 let mut lock = self.value.lock();
1180 let now = std::time::Instant::now();
1181 match lock.value.as_ref() {
1182 Some(value) if now.saturating_duration_since(lock.at) <= self.duration => value.clone(),
1183 _ => {
1184 let new_value = f();
1185 lock.at = now;
1186 lock.value = Some(new_value.clone());
1187 new_value
1188 },
1189 }
1190 }
1191}
1192
1193pub struct Backend<Block: BlockT> {
1198 storage: Arc<StorageDb<Block>>,
1199 offchain_storage: offchain::LocalStorage,
1200 blockchain: BlockchainDb<Block>,
1201 canonicalization_delay: u64,
1202 import_lock: Arc<RwLock<()>>,
1203 is_archive: bool,
1204 blocks_pruning: BlocksPruning,
1205 io_stats: FrozenForDuration<(kvdb::IoStats, StateUsageInfo)>,
1206 state_usage: Arc<StateUsageStats>,
1207 genesis_state: RwLock<Option<Arc<DbGenesisStorage<Block>>>>,
1208 shared_trie_cache: Option<sp_trie::cache::SharedTrieCache<HashingFor<Block>>>,
1209 pruning_filters: Vec<Arc<dyn PruningFilter>>,
1210}
1211
1212impl<Block: BlockT> Backend<Block> {
1213 pub fn new(db_config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult<Self> {
1217 use utils::OpenDbError;
1218
1219 let db_source = &db_config.source;
1220
1221 let (needs_init, db) =
1222 match crate::utils::open_database::<Block>(db_source, DatabaseType::Full, false) {
1223 Ok(db) => (false, db),
1224 Err(OpenDbError::DoesNotExist) => {
1225 let db =
1226 crate::utils::open_database::<Block>(db_source, DatabaseType::Full, true)?;
1227 (true, db)
1228 },
1229 Err(as_is) => return Err(as_is.into()),
1230 };
1231
1232 Self::from_database(db as Arc<_>, canonicalization_delay, &db_config, needs_init)
1233 }
1234
1235 pub fn reset_trie_cache(&self) {
1237 if let Some(cache) = &self.shared_trie_cache {
1238 cache.reset();
1239 }
1240 }
1241
1242 #[cfg(any(test, feature = "test-helpers"))]
1244 pub fn new_test(blocks_pruning: u32, canonicalization_delay: u64) -> Self {
1245 Self::new_test_with_tx_storage(BlocksPruning::Some(blocks_pruning), canonicalization_delay)
1246 }
1247
1248 #[cfg(any(test, feature = "test-helpers"))]
1250 pub fn new_test_with_pruning_filters(
1251 blocks_pruning: u32,
1252 canonicalization_delay: u64,
1253 pruning_filters: Vec<Arc<dyn PruningFilter>>,
1254 ) -> Self {
1255 Self::new_test_with_tx_storage_and_filters(
1256 BlocksPruning::Some(blocks_pruning),
1257 canonicalization_delay,
1258 pruning_filters,
1259 )
1260 }
1261
1262 #[cfg(any(test, feature = "test-helpers"))]
1264 pub fn new_test_with_tx_storage(
1265 blocks_pruning: BlocksPruning,
1266 canonicalization_delay: u64,
1267 ) -> Self {
1268 Self::new_test_with_tx_storage_and_filters(
1269 blocks_pruning,
1270 canonicalization_delay,
1271 Default::default(),
1272 )
1273 }
1274
1275 #[cfg(any(test, feature = "test-helpers"))]
1277 pub fn new_test_with_tx_storage_and_filters(
1278 blocks_pruning: BlocksPruning,
1279 canonicalization_delay: u64,
1280 pruning_filters: Vec<Arc<dyn PruningFilter>>,
1281 ) -> Self {
1282 let db = kvdb_memorydb::create(crate::utils::NUM_COLUMNS);
1283 let db = sp_database::as_database(db);
1284 Self::new_test_with_tx_storage_source(
1285 blocks_pruning,
1286 canonicalization_delay,
1287 DatabaseSource::Custom { db, require_create_flag: true },
1288 pruning_filters,
1289 )
1290 }
1291
1292 #[cfg(any(test, feature = "test-helpers"))]
1294 pub fn new_test_with_tx_storage_source(
1295 blocks_pruning: BlocksPruning,
1296 canonicalization_delay: u64,
1297 source: DatabaseSource,
1298 pruning_filters: Vec<Arc<dyn PruningFilter>>,
1299 ) -> Self {
1300 let state_pruning = match blocks_pruning {
1301 BlocksPruning::KeepAll => PruningMode::ArchiveAll,
1302 BlocksPruning::KeepFinalized => PruningMode::ArchiveCanonical,
1303 BlocksPruning::Some(n) => PruningMode::blocks_pruning(n),
1304 };
1305 let db_setting = DatabaseSettings {
1306 trie_cache_maximum_size: Some(16 * 1024 * 1024),
1307 state_pruning: Some(state_pruning),
1308 source,
1309 blocks_pruning,
1310 pruning_filters,
1311 metrics_registry: None,
1312 };
1313
1314 Self::new(db_setting, canonicalization_delay).expect("failed to create test-db")
1315 }
1316
1317 #[cfg(feature = "runtime-benchmarks")]
1322 pub fn expose_db(&self) -> (Arc<dyn sp_database::Database<DbHash>>, sp_database::ColumnId) {
1323 (self.storage.db.clone(), columns::STATE)
1324 }
1325
1326 #[cfg(feature = "runtime-benchmarks")]
1330 pub fn expose_storage(&self) -> Arc<dyn sp_state_machine::Storage<HashingFor<Block>>> {
1331 self.storage.clone()
1332 }
1333
1334 #[cfg(feature = "runtime-benchmarks")]
1338 pub fn expose_shared_trie_cache(
1339 &self,
1340 ) -> Option<sp_trie::cache::SharedTrieCache<HashingFor<Block>>> {
1341 self.shared_trie_cache.clone()
1342 }
1343
1344 fn from_database(
1345 db: Arc<dyn Database<DbHash>>,
1346 canonicalization_delay: u64,
1347 config: &DatabaseSettings,
1348 should_init: bool,
1349 ) -> ClientResult<Self> {
1350 let mut db_init_transaction = Transaction::new();
1351
1352 let requested_state_pruning = config.state_pruning.clone();
1353 let state_meta_db = StateMetaDb(db.clone());
1354 let map_e = sp_blockchain::Error::from_state_db;
1355
1356 let (state_db_init_commit_set, state_db) = StateDb::open(
1357 state_meta_db,
1358 requested_state_pruning,
1359 !db.supports_ref_counting(),
1360 should_init,
1361 )
1362 .map_err(map_e)?;
1363
1364 apply_state_commit(&mut db_init_transaction, state_db_init_commit_set);
1365
1366 let state_pruning_used = state_db.pruning_mode();
1367 let is_archive_pruning = state_pruning_used.is_archive();
1368 let blockchain = BlockchainDb::new(db.clone())?;
1369
1370 let storage_db =
1371 StorageDb { db: db.clone(), state_db, prefix_keys: !db.supports_ref_counting() };
1372
1373 let offchain_storage = offchain::LocalStorage::new(db.clone());
1374
1375 let shared_trie_cache = config.trie_cache_maximum_size.map(|maximum_size| {
1376 let system_memory = sysinfo::System::new_all();
1377 let used_memory = system_memory.used_memory();
1378 let total_memory = system_memory.total_memory();
1379
1380 debug!("Initializing shared trie cache with size {} bytes, {}% of total memory", maximum_size, (maximum_size as f64 / total_memory as f64 * 100.0));
1381 if maximum_size as u64 > total_memory - used_memory {
1382 warn!(
1383 "Not enough memory to initialize shared trie cache. Cache size: {} bytes. System memory: used {} bytes, total {} bytes",
1384 maximum_size, used_memory, total_memory,
1385 );
1386 }
1387
1388 SharedTrieCache::new(sp_trie::cache::CacheSize::new(maximum_size), config.metrics_registry.as_ref())
1389 });
1390
1391 let backend = Backend {
1392 storage: Arc::new(storage_db),
1393 offchain_storage,
1394 blockchain,
1395 canonicalization_delay,
1396 import_lock: Default::default(),
1397 is_archive: is_archive_pruning,
1398 io_stats: FrozenForDuration::new(std::time::Duration::from_secs(1)),
1399 state_usage: Arc::new(StateUsageStats::new()),
1400 blocks_pruning: config.blocks_pruning,
1401 genesis_state: RwLock::new(None),
1402 shared_trie_cache,
1403 pruning_filters: config.pruning_filters.clone(),
1404 };
1405
1406 let info = backend.blockchain.info();
1408 if info.finalized_state.is_none() &&
1409 info.finalized_hash != Default::default() &&
1410 sc_client_api::Backend::have_state_at(
1411 &backend,
1412 info.finalized_hash,
1413 info.finalized_number,
1414 ) {
1415 backend.blockchain.update_meta(MetaUpdate {
1416 hash: info.finalized_hash,
1417 number: info.finalized_number,
1418 is_best: info.finalized_hash == info.best_hash,
1419 is_finalized: true,
1420 with_state: true,
1421 });
1422 }
1423
1424 match (backend.is_archive, info.block_gap) {
1435 (false, Some(gap)) if matches!(gap.gap_type, BlockGapType::MissingBody) => {
1436 warn!(
1437 "Detected a missing body gap for non-archive nodes. Removing the gap={:?}",
1438 gap
1439 );
1440
1441 db_init_transaction.remove(columns::META, meta_keys::BLOCK_GAP);
1442 db_init_transaction.remove(columns::META, meta_keys::BLOCK_GAP_VERSION);
1443 backend.blockchain.update_block_gap(None);
1444 },
1445 _ => {},
1446 }
1447
1448 db.commit(db_init_transaction)?;
1449
1450 Ok(backend)
1451 }
1452
1453 fn set_head_with_transaction(
1461 &self,
1462 transaction: &mut Transaction<DbHash>,
1463 route_to: Block::Hash,
1464 best_to: (NumberFor<Block>, Block::Hash),
1465 ) -> ClientResult<(Vec<Block::Hash>, Vec<Block::Hash>)> {
1466 let mut enacted = Vec::default();
1467 let mut retracted = Vec::default();
1468
1469 let (best_number, best_hash) = best_to;
1470
1471 let meta = self.blockchain.meta.read();
1472
1473 if meta.best_number.saturating_sub(best_number).saturated_into::<u64>() >
1474 self.canonicalization_delay
1475 {
1476 return Err(sp_blockchain::Error::SetHeadTooOld);
1477 }
1478
1479 let parent_exists =
1480 self.blockchain.status(route_to)? == sp_blockchain::BlockStatus::InChain;
1481
1482 if meta.best_hash != Default::default() && parent_exists {
1484 let tree_route = sp_blockchain::tree_route(&self.blockchain, meta.best_hash, route_to)?;
1485
1486 for r in tree_route.retracted() {
1489 if r.hash == meta.finalized_hash {
1490 warn!(
1491 "Potential safety failure: reverting finalized block {:?}",
1492 (&r.number, &r.hash)
1493 );
1494
1495 return Err(sp_blockchain::Error::NotInFinalizedChain);
1496 }
1497
1498 retracted.push(r.hash);
1499 utils::remove_number_to_key_mapping(transaction, columns::KEY_LOOKUP, r.number)?;
1500 }
1501
1502 for e in tree_route.enacted() {
1504 enacted.push(e.hash);
1505 utils::insert_number_to_key_mapping(
1506 transaction,
1507 columns::KEY_LOOKUP,
1508 e.number,
1509 e.hash,
1510 )?;
1511 }
1512 }
1513
1514 let lookup_key = utils::number_and_hash_to_lookup_key(best_number, &best_hash)?;
1515 transaction.set_from_vec(columns::META, meta_keys::BEST_BLOCK, lookup_key);
1516 utils::insert_number_to_key_mapping(
1517 transaction,
1518 columns::KEY_LOOKUP,
1519 best_number,
1520 best_hash,
1521 )?;
1522
1523 Ok((enacted, retracted))
1524 }
1525
1526 fn ensure_sequential_finalization(
1527 &self,
1528 header: &Block::Header,
1529 last_finalized: Option<Block::Hash>,
1530 ) -> ClientResult<()> {
1531 let last_finalized =
1532 last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash);
1533 if last_finalized != self.blockchain.meta.read().genesis_hash &&
1534 *header.parent_hash() != last_finalized
1535 {
1536 return Err(sp_blockchain::Error::NonSequentialFinalization(format!(
1537 "Last finalized {last_finalized:?} not parent of {:?}",
1538 header.hash()
1539 )));
1540 }
1541 Ok(())
1542 }
1543
1544 fn finalize_block_with_transaction(
1547 &self,
1548 transaction: &mut Transaction<DbHash>,
1549 hash: Block::Hash,
1550 header: &Block::Header,
1551 last_finalized: Option<Block::Hash>,
1552 justification: Option<Justification>,
1553 current_transaction_justifications: &mut HashMap<Block::Hash, Justification>,
1554 remove_displaced: bool,
1555 ) -> ClientResult<MetaUpdate<Block>> {
1556 let number = *header.number();
1558 self.ensure_sequential_finalization(header, last_finalized)?;
1559 let with_state = sc_client_api::Backend::have_state_at(self, hash, number);
1560
1561 self.note_finalized(
1562 transaction,
1563 header,
1564 hash,
1565 with_state,
1566 current_transaction_justifications,
1567 remove_displaced,
1568 )?;
1569
1570 if let Some(justification) = justification {
1571 transaction.set_from_vec(
1572 columns::JUSTIFICATIONS,
1573 &utils::number_and_hash_to_lookup_key(number, hash)?,
1574 Justifications::from(justification.clone()).encode(),
1575 );
1576 current_transaction_justifications.insert(hash, justification);
1577 }
1578 Ok(MetaUpdate { hash, number, is_best: false, is_finalized: true, with_state })
1579 }
1580
1581 fn force_delayed_canonicalize(
1583 &self,
1584 transaction: &mut Transaction<DbHash>,
1585 ) -> ClientResult<()> {
1586 let best_canonical = match self.storage.state_db.last_canonicalized() {
1587 LastCanonicalized::None => 0,
1588 LastCanonicalized::Block(b) => b,
1589 LastCanonicalized::NotCanonicalizing => return Ok(()),
1591 };
1592
1593 let info = self.blockchain.info();
1594 let best_number: u64 = self.blockchain.info().best_number.saturated_into();
1595
1596 for to_canonicalize in
1597 best_canonical + 1..=best_number.saturating_sub(self.canonicalization_delay)
1598 {
1599 let hash_to_canonicalize = sc_client_api::blockchain::HeaderBackend::hash(
1600 &self.blockchain,
1601 to_canonicalize.saturated_into(),
1602 )?
1603 .ok_or_else(|| {
1604 let best_hash = info.best_hash;
1605
1606 sp_blockchain::Error::Backend(format!(
1607 "Can't canonicalize missing block number #{to_canonicalize} when for best block {best_hash:?} (#{best_number})",
1608 ))
1609 })?;
1610
1611 if !sc_client_api::Backend::have_state_at(
1612 self,
1613 hash_to_canonicalize,
1614 to_canonicalize.saturated_into(),
1615 ) {
1616 return Ok(());
1617 }
1618
1619 trace!(target: "db", "Canonicalize block #{to_canonicalize} ({hash_to_canonicalize:?})");
1620 let commit = self.storage.state_db.canonicalize_block(&hash_to_canonicalize).map_err(
1621 sp_blockchain::Error::from_state_db::<
1622 sc_state_db::Error<sp_database::error::DatabaseError>,
1623 >,
1624 )?;
1625 apply_state_commit(transaction, commit);
1626 }
1627
1628 Ok(())
1629 }
1630
1631 fn try_commit_operation(&self, mut operation: BlockImportOperation<Block>) -> ClientResult<()> {
1632 let mut transaction = Transaction::new();
1633
1634 operation.apply_aux(&mut transaction);
1635 operation.apply_offchain(&mut transaction);
1636
1637 let mut meta_updates = Vec::with_capacity(operation.finalized_blocks.len());
1638 let (best_num, mut last_finalized_hash, mut last_finalized_num, mut block_gap) = {
1639 let meta = self.blockchain.meta.read();
1640 (meta.best_number, meta.finalized_hash, meta.finalized_number, meta.block_gap)
1641 };
1642
1643 let mut block_gap_updated = false;
1644
1645 let mut current_transaction_justifications: HashMap<Block::Hash, Justification> =
1646 HashMap::new();
1647 let mut finalized_blocks = operation.finalized_blocks.into_iter().peekable();
1648 while let Some((block_hash, justification)) = finalized_blocks.next() {
1649 let block_header = self.blockchain.expect_header(block_hash)?;
1650 meta_updates.push(self.finalize_block_with_transaction(
1651 &mut transaction,
1652 block_hash,
1653 &block_header,
1654 Some(last_finalized_hash),
1655 justification,
1656 &mut current_transaction_justifications,
1657 finalized_blocks.peek().is_none(),
1658 )?);
1659 last_finalized_hash = block_hash;
1660 last_finalized_num = *block_header.number();
1661 }
1662
1663 let imported = if let Some(pending_block) = operation.pending_block {
1664 let hash = pending_block.header.hash();
1665
1666 let parent_hash = *pending_block.header.parent_hash();
1667 let number = *pending_block.header.number();
1668 let highest_leaf = self
1669 .blockchain
1670 .leaves
1671 .read()
1672 .highest_leaf()
1673 .map(|(n, _)| n)
1674 .unwrap_or(Zero::zero());
1675 let header_exists_in_db =
1676 number <= highest_leaf && self.blockchain.header(hash)?.is_some();
1677 let body_exists_in_db = self.blockchain.body(hash)?.is_some();
1680 let incoming_has_body = pending_block.body.is_some();
1682
1683 let lookup_key = utils::number_and_hash_to_lookup_key(number, hash)?;
1685
1686 if pending_block.leaf_state.is_best() {
1687 self.set_head_with_transaction(&mut transaction, parent_hash, (number, hash))?;
1688 };
1689
1690 utils::insert_hash_to_key_mapping(&mut transaction, columns::KEY_LOOKUP, number, hash)?;
1691
1692 transaction.set_from_vec(columns::HEADER, &lookup_key, pending_block.header.encode());
1693 if let Some(body) = pending_block.body {
1694 if operation.index_ops.is_empty() {
1697 transaction.set_from_vec(columns::BODY, &lookup_key, body.encode());
1698 } else {
1699 let body = apply_index_ops::<Block>(
1700 &mut transaction,
1701 body,
1702 operation.index_ops,
1703 operation.prefetched_indexed_transactions,
1704 );
1705 transaction.set_from_vec(columns::BODY_INDEX, &lookup_key, body);
1706 }
1707 }
1708 if let Some(body) = pending_block.indexed_body {
1709 apply_indexed_body::<Block>(&mut transaction, body);
1710 }
1711 if let Some(justifications) = pending_block.justifications {
1712 transaction.set_from_vec(
1713 columns::JUSTIFICATIONS,
1714 &lookup_key,
1715 justifications.encode(),
1716 );
1717 }
1718
1719 if number.is_zero() {
1720 transaction.set(columns::META, meta_keys::GENESIS_HASH, hash.as_ref());
1721
1722 if operation.commit_state {
1723 transaction.set_from_vec(columns::META, meta_keys::FINALIZED_STATE, lookup_key);
1724 } else {
1725 *self.genesis_state.write() = Some(Arc::new(DbGenesisStorage::new(
1729 *pending_block.header.state_root(),
1730 operation.db_updates.clone(),
1731 )));
1732 }
1733 }
1734
1735 let finalized = if operation.commit_state {
1736 let mut changeset: sc_state_db::ChangeSet<Vec<u8>> =
1737 sc_state_db::ChangeSet::default();
1738 let mut ops: u64 = 0;
1739 let mut bytes: u64 = 0;
1740 let mut removal: u64 = 0;
1741 let mut bytes_removal: u64 = 0;
1742 for (mut key, (val, rc)) in operation.db_updates.drain() {
1743 self.storage.db.sanitize_key(&mut key);
1744 if rc > 0 {
1745 ops += 1;
1746 bytes += key.len() as u64 + val.len() as u64;
1747 if rc == 1 {
1748 changeset.inserted.push((key, val.to_vec()));
1749 } else {
1750 changeset.inserted.push((key.clone(), val.to_vec()));
1751 for _ in 0..rc - 1 {
1752 changeset.inserted.push((key.clone(), Default::default()));
1753 }
1754 }
1755 } else if rc < 0 {
1756 removal += 1;
1757 bytes_removal += key.len() as u64;
1758 if rc == -1 {
1759 changeset.deleted.push(key);
1760 } else {
1761 for _ in 0..-rc {
1762 changeset.deleted.push(key.clone());
1763 }
1764 }
1765 }
1766 }
1767 self.state_usage.tally_writes_nodes(ops, bytes);
1768 self.state_usage.tally_removed_nodes(removal, bytes_removal);
1769
1770 let mut ops: u64 = 0;
1771 let mut bytes: u64 = 0;
1772 for (key, value) in operation
1773 .storage_updates
1774 .iter()
1775 .chain(operation.child_storage_updates.iter().flat_map(|(_, s)| s.iter()))
1776 {
1777 ops += 1;
1778 bytes += key.len() as u64;
1779 if let Some(v) = value.as_ref() {
1780 bytes += v.len() as u64;
1781 }
1782 }
1783 self.state_usage.tally_writes(ops, bytes);
1784 let number_u64 = number.saturated_into::<u64>();
1785 let commit = self
1786 .storage
1787 .state_db
1788 .insert_block(&hash, number_u64, pending_block.header.parent_hash(), changeset)
1789 .map_err(|e: sc_state_db::Error<sp_database::error::DatabaseError>| {
1790 sp_blockchain::Error::from_state_db(e)
1791 })?;
1792 apply_state_commit(&mut transaction, commit);
1793 if number <= last_finalized_num {
1794 let commit = self.storage.state_db.canonicalize_block(&hash).map_err(
1796 sp_blockchain::Error::from_state_db::<
1797 sc_state_db::Error<sp_database::error::DatabaseError>,
1798 >,
1799 )?;
1800 apply_state_commit(&mut transaction, commit);
1801 meta_updates.push(MetaUpdate {
1802 hash,
1803 number,
1804 is_best: false,
1805 is_finalized: true,
1806 with_state: true,
1807 });
1808 }
1809
1810 let finalized = number_u64 == 0 || pending_block.leaf_state.is_final();
1812 finalized
1813 } else {
1814 (number.is_zero() && last_finalized_num.is_zero()) ||
1815 pending_block.leaf_state.is_final()
1816 };
1817
1818 let header = &pending_block.header;
1819 let is_best = pending_block.leaf_state.is_best();
1820 trace!(
1821 target: "db",
1822 "DB Commit {hash:?} ({number}), best={is_best}, state={}, header_in_db={header_exists_in_db} body_in_db={body_exists_in_db} incoming_body={incoming_has_body}, finalized={finalized}",
1823 operation.commit_state,
1824 );
1825
1826 self.state_usage.merge_sm(operation.old_state.usage_info());
1827
1828 drop(operation.old_state);
1831
1832 if finalized {
1833 self.ensure_sequential_finalization(header, Some(last_finalized_hash))?;
1835 let mut current_transaction_justifications = HashMap::new();
1836 self.note_finalized(
1837 &mut transaction,
1838 header,
1839 hash,
1840 operation.commit_state,
1841 &mut current_transaction_justifications,
1842 true,
1843 )?;
1844 } else {
1845 self.force_delayed_canonicalize(&mut transaction)?
1847 }
1848
1849 if !header_exists_in_db {
1850 if pending_block.register_as_leaf &&
1852 (number > last_finalized_num || last_finalized_num.is_zero())
1853 {
1854 let mut leaves = self.blockchain.leaves.write();
1855 leaves.import(hash, number, parent_hash);
1856 leaves.prepare_transaction(
1857 &mut transaction,
1858 columns::META,
1859 meta_keys::LEAF_PREFIX,
1860 );
1861 }
1862
1863 let mut children = children::read_children(
1864 &*self.storage.db,
1865 columns::META,
1866 meta_keys::CHILDREN_PREFIX,
1867 parent_hash,
1868 )?;
1869 if !children.contains(&hash) {
1870 children.push(hash);
1871 children::write_children(
1872 &mut transaction,
1873 columns::META,
1874 meta_keys::CHILDREN_PREFIX,
1875 parent_hash,
1876 children,
1877 );
1878 }
1879 }
1880
1881 let should_check_block_gap = !header_exists_in_db || !body_exists_in_db;
1882 debug!(
1883 target: "db",
1884 "should_check_block_gap = {should_check_block_gap}",
1885 );
1886
1887 if should_check_block_gap {
1888 let update_gap =
1889 |transaction: &mut Transaction<DbHash>,
1890 new_gap: BlockGap<NumberFor<Block>>,
1891 block_gap: &mut Option<BlockGap<NumberFor<Block>>>| {
1892 transaction.set(columns::META, meta_keys::BLOCK_GAP, &new_gap.encode());
1893 transaction.set(
1894 columns::META,
1895 meta_keys::BLOCK_GAP_VERSION,
1896 &BLOCK_GAP_CURRENT_VERSION.encode(),
1897 );
1898 block_gap.replace(new_gap);
1899 debug!(target: "db", "Update block gap. {block_gap:?}");
1900 };
1901
1902 let remove_gap =
1903 |transaction: &mut Transaction<DbHash>,
1904 block_gap: &mut Option<BlockGap<NumberFor<Block>>>| {
1905 transaction.remove(columns::META, meta_keys::BLOCK_GAP);
1906 transaction.remove(columns::META, meta_keys::BLOCK_GAP_VERSION);
1907 *block_gap = None;
1908 debug!(target: "db", "Removed block gap.");
1909 };
1910
1911 if let Some(mut gap) = block_gap {
1912 match gap.gap_type {
1913 BlockGapType::MissingHeaderAndBody => {
1914 if number == gap.start {
1918 gap.start = number + One::one();
1919 utils::insert_number_to_key_mapping(
1920 &mut transaction,
1921 columns::KEY_LOOKUP,
1922 number,
1923 hash,
1924 )?;
1925 if gap.start > gap.end {
1926 remove_gap(&mut transaction, &mut block_gap);
1927 } else {
1928 update_gap(&mut transaction, gap, &mut block_gap);
1929 }
1930 block_gap_updated = true;
1931 }
1932 },
1933 BlockGapType::MissingBody => {
1934 if number == gap.end + One::one() && !incoming_has_body {
1936 gap.end += One::one();
1937 utils::insert_number_to_key_mapping(
1938 &mut transaction,
1939 columns::KEY_LOOKUP,
1940 number,
1941 hash,
1942 )?;
1943 update_gap(&mut transaction, gap, &mut block_gap);
1944 block_gap_updated = true;
1945 } else if number == gap.start && incoming_has_body {
1947 gap.start += One::one();
1948 if gap.start > gap.end {
1949 remove_gap(&mut transaction, &mut block_gap);
1950 } else {
1951 update_gap(&mut transaction, gap, &mut block_gap);
1952 }
1953 block_gap_updated = true;
1954 }
1955 },
1956 }
1957 } else if operation.create_gap {
1958 if number > best_num + One::one() &&
1959 self.blockchain.header(parent_hash)?.is_none()
1960 {
1961 let gap = BlockGap {
1962 start: best_num + One::one(),
1963 end: number - One::one(),
1964 gap_type: BlockGapType::MissingHeaderAndBody,
1965 };
1966 update_gap(&mut transaction, gap, &mut block_gap);
1967 block_gap_updated = true;
1968 debug!(target: "db", "Detected block gap (warp sync) {block_gap:?}");
1969 } else if number == best_num + One::one() &&
1970 self.blockchain.header(parent_hash)?.is_some() &&
1971 !incoming_has_body
1972 {
1973 let gap = BlockGap {
1974 start: number,
1975 end: number,
1976 gap_type: BlockGapType::MissingBody,
1977 };
1978 update_gap(&mut transaction, gap, &mut block_gap);
1979 block_gap_updated = true;
1980 debug!(target: "db", "Detected block gap (fast sync) {block_gap:?}");
1981 }
1982 }
1983 }
1984
1985 meta_updates.push(MetaUpdate {
1986 hash,
1987 number,
1988 is_best: pending_block.leaf_state.is_best(),
1989 is_finalized: finalized,
1990 with_state: operation.commit_state,
1991 });
1992 Some((pending_block.header, hash))
1993 } else {
1994 None
1995 };
1996
1997 if let Some(set_head) = operation.set_head {
1998 if let Some(header) =
1999 sc_client_api::blockchain::HeaderBackend::header(&self.blockchain, set_head)?
2000 {
2001 let number = header.number();
2002 let hash = header.hash();
2003
2004 self.set_head_with_transaction(&mut transaction, hash, (*number, hash))?;
2005
2006 meta_updates.push(MetaUpdate {
2007 hash,
2008 number: *number,
2009 is_best: true,
2010 is_finalized: false,
2011 with_state: false,
2012 });
2013 } else {
2014 return Err(sp_blockchain::Error::UnknownBlock(format!(
2015 "Cannot set head {set_head:?}",
2016 )));
2017 }
2018 }
2019
2020 self.storage.db.commit(transaction)?;
2021
2022 if operation.reset_storage {
2025 if let Err(e) = self.storage.db.optimize_db_col(columns::STATE) {
2026 warn!(target: "db", "Failed to optimize database after state import: {e:?}");
2027 }
2028 }
2029
2030 if let Some((header, hash)) = imported {
2034 trace!(target: "db", "DB Commit done {hash:?}");
2035 let header_metadata = CachedHeaderMetadata::from(&header);
2036 self.blockchain.insert_header_metadata(header_metadata.hash, header_metadata);
2037 cache_header(&mut self.blockchain.header_cache.lock(), hash, Some(header));
2038 }
2039
2040 for m in meta_updates {
2041 self.blockchain.update_meta(m);
2042 }
2043 if block_gap_updated {
2044 self.blockchain.update_block_gap(block_gap);
2045 }
2046
2047 Ok(())
2048 }
2049
2050 fn note_finalized(
2055 &self,
2056 transaction: &mut Transaction<DbHash>,
2057 f_header: &Block::Header,
2058 f_hash: Block::Hash,
2059 with_state: bool,
2060 current_transaction_justifications: &mut HashMap<Block::Hash, Justification>,
2061 remove_displaced: bool,
2062 ) -> ClientResult<()> {
2063 let f_num = *f_header.number();
2064
2065 let lookup_key = utils::number_and_hash_to_lookup_key(f_num, f_hash)?;
2066 if with_state {
2067 transaction.set_from_vec(columns::META, meta_keys::FINALIZED_STATE, lookup_key.clone());
2068 }
2069 transaction.set_from_vec(columns::META, meta_keys::FINALIZED_BLOCK, lookup_key);
2070
2071 let requires_canonicalization = match self.storage.state_db.last_canonicalized() {
2072 LastCanonicalized::None => true,
2073 LastCanonicalized::Block(b) => f_num.saturated_into::<u64>() > b,
2074 LastCanonicalized::NotCanonicalizing => false,
2075 };
2076
2077 if requires_canonicalization && sc_client_api::Backend::have_state_at(self, f_hash, f_num) {
2078 let commit = self.storage.state_db.canonicalize_block(&f_hash).map_err(
2079 sp_blockchain::Error::from_state_db::<
2080 sc_state_db::Error<sp_database::error::DatabaseError>,
2081 >,
2082 )?;
2083 apply_state_commit(transaction, commit);
2084 }
2085
2086 if remove_displaced {
2087 let new_displaced = self.blockchain.displaced_leaves_after_finalizing(
2088 f_hash,
2089 f_num,
2090 *f_header.parent_hash(),
2091 )?;
2092
2093 self.blockchain.leaves.write().remove_displaced_leaves(FinalizationOutcome::new(
2094 new_displaced.displaced_leaves.iter().copied(),
2095 ));
2096
2097 if !matches!(self.blocks_pruning, BlocksPruning::KeepAll) {
2098 self.prune_displaced_branches(transaction, &new_displaced)?;
2099 }
2100 }
2101
2102 self.prune_blocks(transaction, f_num, current_transaction_justifications)?;
2103
2104 Ok(())
2105 }
2106
2107 fn prune_blocks(
2108 &self,
2109 transaction: &mut Transaction<DbHash>,
2110 finalized_number: NumberFor<Block>,
2111 current_transaction_justifications: &mut HashMap<Block::Hash, Justification>,
2112 ) -> ClientResult<()> {
2113 if let BlocksPruning::Some(blocks_pruning) = self.blocks_pruning {
2114 let keep = std::cmp::max(blocks_pruning, 1);
2116 if finalized_number >= keep.into() {
2117 let number = finalized_number.saturating_sub(keep.into());
2118
2119 if let Some(hash) = self.blockchain.hash(number)? {
2121 if !self.pruning_filters.is_empty() {
2125 let justifications = match current_transaction_justifications.get(&hash) {
2126 Some(j) => Some(Justifications::from(j.clone())),
2127 None => self.blockchain.justifications(hash)?,
2128 };
2129
2130 let should_retain = justifications
2131 .map(|j| self.pruning_filters.iter().any(|f| f.should_retain(&j)))
2132 .unwrap_or(false);
2133
2134 if should_retain {
2137 debug!(
2138 target: "db",
2139 "Preserving block #{number} ({hash}) due to keep predicate match"
2140 );
2141 return Ok(());
2142 }
2143 }
2144
2145 self.blockchain.insert_persisted_body_if_pinned(hash)?;
2146
2147 if let Some(justification) = current_transaction_justifications.remove(&hash) {
2150 self.blockchain.insert_justifications_if_pinned(hash, justification);
2151 } else {
2152 self.blockchain.insert_persisted_justifications_if_pinned(hash)?;
2153 }
2154 };
2155
2156 self.prune_block(transaction, BlockId::<Block>::number(number))?;
2157 }
2158 }
2159 Ok(())
2160 }
2161
2162 fn prune_displaced_branches(
2163 &self,
2164 transaction: &mut Transaction<DbHash>,
2165 displaced: &DisplacedLeavesAfterFinalization<Block>,
2166 ) -> ClientResult<()> {
2167 for &hash in displaced.displaced_blocks.iter() {
2169 self.blockchain.insert_persisted_body_if_pinned(hash)?;
2170 self.prune_block(transaction, BlockId::<Block>::hash(hash))?;
2171 }
2172 Ok(())
2173 }
2174
2175 fn prune_block(
2176 &self,
2177 transaction: &mut Transaction<DbHash>,
2178 id: BlockId<Block>,
2179 ) -> ClientResult<()> {
2180 debug!(target: "db", "Removing block #{id}");
2181 utils::remove_from_db(
2182 transaction,
2183 &*self.storage.db,
2184 columns::KEY_LOOKUP,
2185 columns::BODY,
2186 id,
2187 )?;
2188 utils::remove_from_db(
2189 transaction,
2190 &*self.storage.db,
2191 columns::KEY_LOOKUP,
2192 columns::JUSTIFICATIONS,
2193 id,
2194 )?;
2195 if let Some(index) =
2196 read_db(&*self.storage.db, columns::KEY_LOOKUP, columns::BODY_INDEX, id)?
2197 {
2198 utils::remove_from_db(
2199 transaction,
2200 &*self.storage.db,
2201 columns::KEY_LOOKUP,
2202 columns::BODY_INDEX,
2203 id,
2204 )?;
2205 match Vec::<DbExtrinsic<Block>>::decode(&mut &index[..]) {
2206 Ok(index) => {
2207 for ex in index {
2208 match ex {
2209 DbExtrinsic::Indexed { hash, .. } => {
2210 transaction.release(columns::TRANSACTION, hash);
2211 },
2212 DbExtrinsic::MultiRenew { hashes, .. } => {
2213 for hash in hashes {
2214 transaction.release(columns::TRANSACTION, hash);
2215 }
2216 },
2217 DbExtrinsic::Full(_) => {},
2218 }
2219 }
2220 },
2221 Err(err) => {
2222 return Err(sp_blockchain::Error::Backend(format!(
2223 "Error decoding body list: {err}",
2224 )))
2225 },
2226 }
2227 }
2228 Ok(())
2229 }
2230
2231 fn empty_state(&self) -> RecordStatsState<RefTrackingState<Block>, Block> {
2232 let root = EmptyStorage::<Block>::new().0; let db_state = DbStateBuilder::<HashingFor<Block>>::new(self.storage.clone(), root)
2234 .with_optional_cache(self.shared_trie_cache.as_ref().map(|c| c.local_cache_untrusted()))
2235 .build();
2236 let state = RefTrackingState::new(db_state, self.storage.clone(), None);
2237 RecordStatsState::new(state, None, self.state_usage.clone())
2238 }
2239}
2240
2241fn apply_state_commit(
2242 transaction: &mut Transaction<DbHash>,
2243 commit: sc_state_db::CommitSet<Vec<u8>>,
2244) {
2245 for (key, val) in commit.data.inserted.into_iter() {
2246 transaction.set_from_vec(columns::STATE, &key[..], val);
2247 }
2248 for key in commit.data.deleted.into_iter() {
2249 transaction.remove(columns::STATE, &key[..]);
2250 }
2251 for (key, val) in commit.meta.inserted.into_iter() {
2252 transaction.set_from_vec(columns::STATE_META, &key[..], val);
2253 }
2254 for key in commit.meta.deleted.into_iter() {
2255 transaction.remove(columns::STATE_META, &key[..]);
2256 }
2257}
2258
2259fn apply_index_ops<Block: BlockT>(
2260 transaction: &mut Transaction<DbHash>,
2261 body: Vec<Block::Extrinsic>,
2262 ops: Vec<IndexOperation>,
2263 mut prefetched: HashMap<DbHash, Vec<u8>>,
2264) -> Vec<u8> {
2265 let mut extrinsic_index: Vec<DbExtrinsic<Block>> = Vec::with_capacity(body.len());
2266 let mut index_map = HashMap::new();
2267 let mut renewed_map: HashMap<u32, Vec<DbHash>> = HashMap::new();
2270 for op in ops {
2271 match op {
2272 IndexOperation::Insert { extrinsic, hash, size } => {
2273 index_map.insert(extrinsic, (hash, size));
2274 },
2275 IndexOperation::Renew { extrinsic, hash } => {
2276 renewed_map
2277 .entry(extrinsic)
2278 .or_default()
2279 .push(DbHash::from_slice(hash.as_ref()));
2280 },
2281 }
2282 }
2283 let mut store_or_reference = |tx: &mut Transaction<DbHash>, hash: DbHash| {
2284 if let Some(bytes) = prefetched.remove(&hash) {
2285 tx.store(columns::TRANSACTION, hash, bytes);
2286 } else {
2287 tx.reference(columns::TRANSACTION, hash);
2288 }
2289 };
2290 let mut n_inserted = 0usize;
2291 let mut n_renew_slots = 0usize;
2292 let mut n_renew_hashes = 0usize;
2293 let mut n_full = 0usize;
2294 for (index, extrinsic) in body.into_iter().enumerate() {
2295 let db_extrinsic = if let Some(hashes) = renewed_map.remove(&(index as u32)) {
2296 n_renew_slots += 1;
2297 n_renew_hashes += hashes.len();
2298 let encoded = extrinsic.encode();
2299 if hashes.len() == 1 {
2300 let hash = hashes[0];
2302 store_or_reference(transaction, hash);
2303 DbExtrinsic::Indexed { hash, header: encoded }
2304 } else {
2305 for hash in &hashes {
2307 store_or_reference(transaction, *hash);
2308 }
2309 DbExtrinsic::MultiRenew { hashes, extrinsic: encoded }
2310 }
2311 } else {
2312 match index_map.get(&(index as u32)) {
2313 Some((hash, size)) => {
2314 let encoded = extrinsic.encode();
2315 if *size as usize <= encoded.len() {
2316 n_inserted += 1;
2317 let offset = encoded.len() - *size as usize;
2318 transaction.store(
2319 columns::TRANSACTION,
2320 DbHash::from_slice(hash.as_ref()),
2321 encoded[offset..].to_vec(),
2322 );
2323 DbExtrinsic::Indexed {
2324 hash: DbHash::from_slice(hash.as_ref()),
2325 header: encoded[..offset].to_vec(),
2326 }
2327 } else {
2328 n_full += 1;
2330 DbExtrinsic::Full(extrinsic)
2331 }
2332 },
2333 _ => {
2334 n_full += 1;
2335 DbExtrinsic::Full(extrinsic)
2336 },
2337 }
2338 };
2339 extrinsic_index.push(db_extrinsic);
2340 }
2341 debug!(
2342 target: "db",
2343 "DB transaction index: {} inserted, {} slots renewed ({} hashes), {} full",
2344 n_inserted,
2345 n_renew_slots,
2346 n_renew_hashes,
2347 n_full,
2348 );
2349 extrinsic_index.encode()
2350}
2351
2352fn apply_indexed_body<Block: BlockT>(transaction: &mut Transaction<DbHash>, body: Vec<Vec<u8>>) {
2353 for extrinsic in body {
2354 let hash = sp_runtime::traits::BlakeTwo256::hash(&extrinsic);
2355 transaction.store(columns::TRANSACTION, DbHash::from_slice(hash.as_ref()), extrinsic);
2356 }
2357}
2358
2359impl<Block> sc_client_api::backend::AuxStore for Backend<Block>
2360where
2361 Block: BlockT,
2362{
2363 fn insert_aux<
2364 'a,
2365 'b: 'a,
2366 'c: 'a,
2367 I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
2368 D: IntoIterator<Item = &'a &'b [u8]>,
2369 >(
2370 &self,
2371 insert: I,
2372 delete: D,
2373 ) -> ClientResult<()> {
2374 let mut transaction = Transaction::new();
2375 for (k, v) in insert {
2376 transaction.set(columns::AUX, k, v);
2377 }
2378 for k in delete {
2379 transaction.remove(columns::AUX, k);
2380 }
2381 self.storage.db.commit(transaction)?;
2382 Ok(())
2383 }
2384
2385 fn get_aux(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
2386 Ok(self.storage.db.get(columns::AUX, key))
2387 }
2388}
2389
2390impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
2391 type BlockImportOperation = BlockImportOperation<Block>;
2392 type Blockchain = BlockchainDb<Block>;
2393 type State = RecordStatsState<RefTrackingState<Block>, Block>;
2394 type OffchainStorage = offchain::LocalStorage;
2395
2396 fn begin_operation(&self) -> ClientResult<Self::BlockImportOperation> {
2397 Ok(BlockImportOperation {
2398 pending_block: None,
2399 old_state: self.empty_state(),
2400 db_updates: PrefixedMemoryDB::default(),
2401 storage_updates: Default::default(),
2402 child_storage_updates: Default::default(),
2403 offchain_storage_updates: Default::default(),
2404 aux_ops: Vec::new(),
2405 finalized_blocks: Vec::new(),
2406 set_head: None,
2407 commit_state: false,
2408 create_gap: true,
2409 reset_storage: false,
2410 index_ops: Default::default(),
2411 prefetched_indexed_transactions: Default::default(),
2412 })
2413 }
2414
2415 fn begin_state_operation(
2416 &self,
2417 operation: &mut Self::BlockImportOperation,
2418 block: Block::Hash,
2419 ) -> ClientResult<()> {
2420 if block == Default::default() {
2421 operation.old_state = self.empty_state();
2422 } else {
2423 operation.old_state = self.state_at(block, TrieCacheContext::Untrusted)?;
2424 }
2425
2426 operation.commit_state = true;
2427 Ok(())
2428 }
2429
2430 fn commit_operation(&self, operation: Self::BlockImportOperation) -> ClientResult<()> {
2431 let usage = operation.old_state.usage_info();
2432 self.state_usage.merge_sm(usage);
2433
2434 if let Err(e) = self.try_commit_operation(operation) {
2435 let state_meta_db = StateMetaDb(self.storage.db.clone());
2436 self.storage
2437 .state_db
2438 .reset(state_meta_db)
2439 .map_err(sp_blockchain::Error::from_state_db)?;
2440 self.blockchain.clear_pinning_cache();
2441 Err(e)
2442 } else {
2443 self.storage.state_db.sync();
2444 Ok(())
2445 }
2446 }
2447
2448 fn finalize_block(
2449 &self,
2450 hash: Block::Hash,
2451 justification: Option<Justification>,
2452 ) -> ClientResult<()> {
2453 let mut transaction = Transaction::new();
2454 let header = self.blockchain.expect_header(hash)?;
2455
2456 let mut current_transaction_justifications = HashMap::new();
2457 let m = self.finalize_block_with_transaction(
2458 &mut transaction,
2459 hash,
2460 &header,
2461 None,
2462 justification,
2463 &mut current_transaction_justifications,
2464 true,
2465 )?;
2466
2467 self.storage.db.commit(transaction)?;
2468 self.blockchain.update_meta(m);
2469 Ok(())
2470 }
2471
2472 fn append_justification(
2473 &self,
2474 hash: Block::Hash,
2475 justification: Justification,
2476 ) -> ClientResult<()> {
2477 let mut transaction: Transaction<DbHash> = Transaction::new();
2478 let header = self.blockchain.expect_header(hash)?;
2479 let number = *header.number();
2480
2481 let is_descendent_of = is_descendent_of(&self.blockchain, None);
2483 let last_finalized = self.blockchain.last_finalized()?;
2484
2485 if number > self.blockchain.info().finalized_number ||
2487 (hash != last_finalized && !is_descendent_of(&hash, &last_finalized)?)
2488 {
2489 return Err(ClientError::NotInFinalizedChain);
2490 }
2491
2492 let justifications = if let Some(mut stored_justifications) =
2493 self.blockchain.justifications(hash)?
2494 {
2495 if !stored_justifications.append(justification) {
2496 return Err(ClientError::BadJustification("Duplicate consensus engine ID".into()));
2497 }
2498 stored_justifications
2499 } else {
2500 Justifications::from(justification)
2501 };
2502
2503 transaction.set_from_vec(
2504 columns::JUSTIFICATIONS,
2505 &utils::number_and_hash_to_lookup_key(number, hash)?,
2506 justifications.encode(),
2507 );
2508
2509 self.storage.db.commit(transaction)?;
2510
2511 Ok(())
2512 }
2513
2514 fn offchain_storage(&self) -> Option<Self::OffchainStorage> {
2515 Some(self.offchain_storage.clone())
2516 }
2517
2518 fn usage_info(&self) -> Option<UsageInfo> {
2519 let (io_stats, state_stats) = self.io_stats.take_or_else(|| {
2520 (
2521 kvdb::IoStats::empty(),
2523 self.state_usage.take(),
2524 )
2525 });
2526 let database_cache = MemorySize::from_bytes(0);
2527 let state_cache = MemorySize::from_bytes(
2528 self.shared_trie_cache.as_ref().map_or(0, |c| c.used_memory_size()),
2529 );
2530
2531 Some(UsageInfo {
2532 memory: MemoryInfo { state_cache, database_cache },
2533 io: IoInfo {
2534 transactions: io_stats.transactions,
2535 bytes_read: io_stats.bytes_read,
2536 bytes_written: io_stats.bytes_written,
2537 writes: io_stats.writes,
2538 reads: io_stats.reads,
2539 average_transaction_size: io_stats.avg_transaction_size() as u64,
2540 state_reads: state_stats.reads.ops,
2541 state_writes: state_stats.writes.ops,
2542 state_writes_cache: state_stats.overlay_writes.ops,
2543 state_reads_cache: state_stats.cache_reads.ops,
2544 state_writes_nodes: state_stats.nodes_writes.ops,
2545 },
2546 })
2547 }
2548
2549 fn revert(
2550 &self,
2551 n: NumberFor<Block>,
2552 revert_finalized: bool,
2553 ) -> ClientResult<(NumberFor<Block>, HashSet<Block::Hash>)> {
2554 let mut reverted_finalized = HashSet::new();
2555
2556 let info = self.blockchain.info();
2557
2558 let highest_leaf = self
2559 .blockchain
2560 .leaves
2561 .read()
2562 .highest_leaf()
2563 .and_then(|(n, h)| h.last().map(|h| (n, *h)));
2564
2565 let best_number = info.best_number;
2566 let best_hash = info.best_hash;
2567
2568 let finalized = info.finalized_number;
2569
2570 let revertible = best_number - finalized;
2571 let n = if !revert_finalized && revertible < n { revertible } else { n };
2572
2573 let (n, mut number_to_revert, mut hash_to_revert) = match highest_leaf {
2574 Some((l_n, l_h)) => (n + (l_n - best_number), l_n, l_h),
2575 None => (n, best_number, best_hash),
2576 };
2577
2578 let mut revert_blocks = || -> ClientResult<NumberFor<Block>> {
2579 for c in 0..n.saturated_into::<u64>() {
2580 if number_to_revert.is_zero() {
2581 return Ok(c.saturated_into::<NumberFor<Block>>());
2582 }
2583 let mut transaction = Transaction::new();
2584 let removed = self.blockchain.header(hash_to_revert)?.ok_or_else(|| {
2585 sp_blockchain::Error::UnknownBlock(format!(
2586 "Error reverting to {hash_to_revert}. Block header not found.",
2587 ))
2588 })?;
2589 let removed_hash = hash_to_revert;
2590
2591 let prev_number = number_to_revert.saturating_sub(One::one());
2592 let prev_hash =
2593 if prev_number == best_number { best_hash } else { *removed.parent_hash() };
2594
2595 if !self.have_state_at(prev_hash, prev_number) {
2596 return Ok(c.saturated_into::<NumberFor<Block>>());
2597 }
2598
2599 match self.storage.state_db.revert_one() {
2600 Some(commit) => {
2601 apply_state_commit(&mut transaction, commit);
2602
2603 number_to_revert = prev_number;
2604 hash_to_revert = prev_hash;
2605
2606 let update_finalized = number_to_revert < finalized;
2607
2608 let key = utils::number_and_hash_to_lookup_key(
2609 number_to_revert,
2610 &hash_to_revert,
2611 )?;
2612 if update_finalized {
2613 transaction.set_from_vec(
2614 columns::META,
2615 meta_keys::FINALIZED_BLOCK,
2616 key.clone(),
2617 );
2618
2619 reverted_finalized.insert(removed_hash);
2620 if let Some((hash, _)) = self.blockchain.info().finalized_state {
2621 if hash == hash_to_revert {
2622 if !number_to_revert.is_zero() &&
2623 self.have_state_at(prev_hash, prev_number)
2624 {
2625 let lookup_key = utils::number_and_hash_to_lookup_key(
2626 prev_number,
2627 prev_hash,
2628 )?;
2629 transaction.set_from_vec(
2630 columns::META,
2631 meta_keys::FINALIZED_STATE,
2632 lookup_key,
2633 );
2634 } else {
2635 transaction
2636 .remove(columns::META, meta_keys::FINALIZED_STATE);
2637 }
2638 }
2639 }
2640 }
2641
2642 transaction.set_from_vec(columns::META, meta_keys::BEST_BLOCK, key);
2643 transaction.remove(columns::KEY_LOOKUP, removed_hash.as_ref());
2644 children::remove_children(
2645 &mut transaction,
2646 columns::META,
2647 meta_keys::CHILDREN_PREFIX,
2648 hash_to_revert,
2649 );
2650 self.prune_block(&mut transaction, BlockId::Hash(removed_hash))?;
2651 remove_from_db::<Block>(
2652 &mut transaction,
2653 &*self.storage.db,
2654 columns::KEY_LOOKUP,
2655 columns::HEADER,
2656 BlockId::Hash(removed_hash),
2657 )?;
2658
2659 self.storage.db.commit(transaction)?;
2660
2661 self.blockchain.remove_header_metadata(removed_hash);
2663
2664 let is_best = number_to_revert < best_number;
2665
2666 self.blockchain.update_meta(MetaUpdate {
2667 hash: hash_to_revert,
2668 number: number_to_revert,
2669 is_best,
2670 is_finalized: update_finalized,
2671 with_state: false,
2672 });
2673 },
2674 None => return Ok(c.saturated_into::<NumberFor<Block>>()),
2675 }
2676 }
2677
2678 Ok(n)
2679 };
2680
2681 let reverted = revert_blocks()?;
2682
2683 let revert_leaves = || -> ClientResult<()> {
2684 let mut transaction = Transaction::new();
2685 let mut leaves = self.blockchain.leaves.write();
2686
2687 leaves.revert(hash_to_revert, number_to_revert).into_iter().try_for_each(
2688 |(h, _)| {
2689 self.blockchain.remove_header_metadata(h);
2690 transaction.remove(columns::KEY_LOOKUP, h.as_ref());
2691
2692 self.prune_block(&mut transaction, BlockId::Hash(h))?;
2693 remove_from_db::<Block>(
2694 &mut transaction,
2695 &*self.storage.db,
2696 columns::KEY_LOOKUP,
2697 columns::HEADER,
2698 BlockId::Hash(h),
2699 )?;
2700
2701 Ok::<_, ClientError>(())
2702 },
2703 )?;
2704 leaves.prepare_transaction(&mut transaction, columns::META, meta_keys::LEAF_PREFIX);
2705 self.storage.db.commit(transaction)?;
2706
2707 Ok(())
2708 };
2709
2710 revert_leaves()?;
2711
2712 Ok((reverted, reverted_finalized))
2713 }
2714
2715 fn remove_leaf_block(&self, hash: Block::Hash) -> ClientResult<()> {
2716 let best_hash = self.blockchain.info().best_hash;
2717
2718 if best_hash == hash {
2719 return Err(sp_blockchain::Error::Backend(format!("Can't remove best block {hash:?}")));
2720 }
2721
2722 let hdr = self.blockchain.header_metadata(hash)?;
2723 if !self.have_state_at(hash, hdr.number) {
2724 return Err(sp_blockchain::Error::UnknownBlock(format!(
2725 "State already discarded for {hash:?}",
2726 )));
2727 }
2728
2729 let mut leaves = self.blockchain.leaves.write();
2730 if !leaves.contains(hdr.number, hash) {
2731 return Err(sp_blockchain::Error::Backend(format!(
2732 "Can't remove non-leaf block {hash:?}",
2733 )));
2734 }
2735
2736 let mut transaction = Transaction::new();
2737 if let Some(commit) = self.storage.state_db.remove(&hash) {
2738 apply_state_commit(&mut transaction, commit);
2739 }
2740 transaction.remove(columns::KEY_LOOKUP, hash.as_ref());
2741
2742 let children: Vec<_> = self
2743 .blockchain()
2744 .children(hdr.parent)?
2745 .into_iter()
2746 .filter(|child_hash| *child_hash != hash)
2747 .collect();
2748 let parent_leaf = if children.is_empty() {
2749 children::remove_children(
2750 &mut transaction,
2751 columns::META,
2752 meta_keys::CHILDREN_PREFIX,
2753 hdr.parent,
2754 );
2755 Some(hdr.parent)
2756 } else {
2757 children::write_children(
2758 &mut transaction,
2759 columns::META,
2760 meta_keys::CHILDREN_PREFIX,
2761 hdr.parent,
2762 children,
2763 );
2764 None
2765 };
2766
2767 let remove_outcome = leaves.remove(hash, hdr.number, parent_leaf);
2768 leaves.prepare_transaction(&mut transaction, columns::META, meta_keys::LEAF_PREFIX);
2769 if let Err(e) = self.storage.db.commit(transaction) {
2770 if let Some(outcome) = remove_outcome {
2771 leaves.undo().undo_remove(outcome);
2772 }
2773 return Err(e.into());
2774 }
2775 self.blockchain().remove_header_metadata(hash);
2776 Ok(())
2777 }
2778
2779 fn blockchain(&self) -> &BlockchainDb<Block> {
2780 &self.blockchain
2781 }
2782
2783 fn state_at(
2784 &self,
2785 hash: Block::Hash,
2786 trie_cache_context: TrieCacheContext,
2787 ) -> ClientResult<Self::State> {
2788 if hash == self.blockchain.meta.read().genesis_hash {
2789 if let Some(genesis_state) = &*self.genesis_state.read() {
2790 let root = genesis_state.root;
2791 let db_state =
2792 DbStateBuilder::<HashingFor<Block>>::new(genesis_state.clone(), root)
2793 .with_optional_cache(self.shared_trie_cache.as_ref().map(|c| {
2794 if matches!(trie_cache_context, TrieCacheContext::Trusted) {
2795 c.local_cache_trusted()
2796 } else {
2797 c.local_cache_untrusted()
2798 }
2799 }))
2800 .build();
2801
2802 let state = RefTrackingState::new(db_state, self.storage.clone(), None);
2803 return Ok(RecordStatsState::new(state, None, self.state_usage.clone()));
2804 }
2805 }
2806
2807 match self.blockchain.header_metadata(hash) {
2808 Ok(ref hdr) => {
2809 let hint = || {
2810 sc_state_db::NodeDb::get(self.storage.as_ref(), hdr.state_root.as_ref())
2811 .unwrap_or(None)
2812 .is_some()
2813 };
2814
2815 if let Ok(()) =
2816 self.storage.state_db.pin(&hash, hdr.number.saturated_into::<u64>(), hint)
2817 {
2818 let root = hdr.state_root;
2819 let db_state =
2820 DbStateBuilder::<HashingFor<Block>>::new(self.storage.clone(), root)
2821 .with_optional_cache(self.shared_trie_cache.as_ref().map(|c| {
2822 if matches!(trie_cache_context, TrieCacheContext::Trusted) {
2823 c.local_cache_trusted()
2824 } else {
2825 c.local_cache_untrusted()
2826 }
2827 }))
2828 .build();
2829 let state = RefTrackingState::new(db_state, self.storage.clone(), Some(hash));
2830 Ok(RecordStatsState::new(state, Some(hash), self.state_usage.clone()))
2831 } else {
2832 Err(sp_blockchain::Error::UnknownBlock(format!(
2833 "State already discarded for {hash:?}",
2834 )))
2835 }
2836 },
2837 Err(e) => Err(e),
2838 }
2839 }
2840
2841 fn have_state_at(&self, hash: Block::Hash, number: NumberFor<Block>) -> bool {
2842 if self.is_archive {
2843 match self.blockchain.header_metadata(hash) {
2844 Ok(header) => sp_state_machine::Storage::get(
2845 self.storage.as_ref(),
2846 &header.state_root,
2847 (&[], None),
2848 )
2849 .unwrap_or(None)
2850 .is_some(),
2851 _ => false,
2852 }
2853 } else {
2854 match self.storage.state_db.is_pruned(&hash, number.saturated_into::<u64>()) {
2855 IsPruned::Pruned => false,
2856 IsPruned::NotPruned => true,
2857 IsPruned::MaybePruned => match self.blockchain.header_metadata(hash) {
2858 Ok(header) => sp_state_machine::Storage::get(
2859 self.storage.as_ref(),
2860 &header.state_root,
2861 (&[], None),
2862 )
2863 .unwrap_or(None)
2864 .is_some(),
2865 _ => false,
2866 },
2867 }
2868 }
2869 }
2870
2871 fn get_import_lock(&self) -> &RwLock<()> {
2872 &self.import_lock
2873 }
2874
2875 fn requires_full_sync(&self) -> bool {
2876 matches!(
2877 self.storage.state_db.pruning_mode(),
2878 PruningMode::ArchiveAll | PruningMode::ArchiveCanonical
2879 )
2880 }
2881
2882 fn pin_block(&self, hash: <Block as BlockT>::Hash) -> sp_blockchain::Result<()> {
2883 let hint = || {
2884 let header_metadata = self.blockchain.header_metadata(hash);
2885 header_metadata
2886 .map(|hdr| {
2887 sc_state_db::NodeDb::get(self.storage.as_ref(), hdr.state_root.as_ref())
2888 .unwrap_or(None)
2889 .is_some()
2890 })
2891 .unwrap_or(false)
2892 };
2893
2894 if let Some(number) = self.blockchain.number(hash)? {
2895 self.storage.state_db.pin(&hash, number.saturated_into::<u64>(), hint).map_err(
2896 |_| {
2897 sp_blockchain::Error::UnknownBlock(format!(
2898 "Unable to pin: state already discarded for `{hash:?}`",
2899 ))
2900 },
2901 )?;
2902 } else {
2903 return Err(ClientError::UnknownBlock(format!(
2904 "Can not pin block with hash `{hash:?}`. Block not found.",
2905 )));
2906 }
2907
2908 if self.blocks_pruning != BlocksPruning::KeepAll {
2909 self.blockchain.bump_ref(hash);
2911 }
2912 Ok(())
2913 }
2914
2915 fn unpin_block(&self, hash: <Block as BlockT>::Hash) {
2916 self.storage.state_db.unpin(&hash);
2917
2918 if self.blocks_pruning != BlocksPruning::KeepAll {
2919 self.blockchain.unpin(hash);
2920 }
2921 }
2922}
2923
2924impl<Block: BlockT> sc_client_api::backend::LocalBackend<Block> for Backend<Block> {}
2925
2926#[cfg(test)]
2927pub(crate) mod tests {
2928 use super::*;
2929 use crate::{columns, utils::number_and_hash_to_lookup_key};
2930 use hash_db::{HashDB, EMPTY_PREFIX};
2931 use sc_client_api::{
2932 backend::{Backend as BTrait, BlockImportOperation as Op},
2933 blockchain::Backend as BLBTrait,
2934 };
2935 use sp_blockchain::{lowest_common_ancestor, tree_route};
2936 use sp_core::H256;
2937 use sp_runtime::{
2938 testing::{Block as RawBlock, Header, MockCallU64, TestXt},
2939 traits::{BlakeTwo256, Hash},
2940 ConsensusEngineId, StateVersion,
2941 };
2942
2943 const CONS0_ENGINE_ID: ConsensusEngineId = *b"CON0";
2944 const CONS1_ENGINE_ID: ConsensusEngineId = *b"CON1";
2945
2946 type UncheckedXt = TestXt<MockCallU64, ()>;
2947 pub(crate) type Block = RawBlock<UncheckedXt>;
2948
2949 pub fn insert_header(
2950 backend: &Backend<Block>,
2951 number: u64,
2952 parent_hash: H256,
2953 changes: Option<Vec<(Vec<u8>, Vec<u8>)>>,
2954 extrinsics_root: H256,
2955 ) -> H256 {
2956 insert_block(backend, number, parent_hash, changes, extrinsics_root, Vec::new(), None)
2957 .unwrap()
2958 }
2959
2960 pub fn insert_block(
2961 backend: &Backend<Block>,
2962 number: u64,
2963 parent_hash: H256,
2964 _changes: Option<Vec<(Vec<u8>, Vec<u8>)>>,
2965 extrinsics_root: H256,
2966 body: Vec<UncheckedXt>,
2967 transaction_index: Option<Vec<IndexOperation>>,
2968 ) -> Result<H256, sp_blockchain::Error> {
2969 insert_block_with_prefetched(
2970 backend,
2971 number,
2972 parent_hash,
2973 extrinsics_root,
2974 body,
2975 transaction_index,
2976 HashMap::new(),
2977 )
2978 }
2979
2980 pub fn insert_block_with_prefetched(
2981 backend: &Backend<Block>,
2982 number: u64,
2983 parent_hash: H256,
2984 extrinsics_root: H256,
2985 body: Vec<UncheckedXt>,
2986 transaction_index: Option<Vec<IndexOperation>>,
2987 prefetched: HashMap<H256, Vec<u8>>,
2988 ) -> Result<H256, sp_blockchain::Error> {
2989 use sp_runtime::testing::Digest;
2990
2991 let digest = Digest::default();
2992 let mut header =
2993 Header { number, parent_hash, state_root: Default::default(), digest, extrinsics_root };
2994
2995 let block_hash = if number == 0 { Default::default() } else { parent_hash };
2996 let mut op = backend.begin_operation().unwrap();
2997 backend.begin_state_operation(&mut op, block_hash).unwrap();
2998 if !prefetched.is_empty() {
2999 op.set_renew_payloads(prefetched).unwrap();
3000 }
3001 if let Some(index) = transaction_index {
3002 op.update_transaction_index(index).unwrap();
3003 }
3004
3005 let (root, overlay) = op.old_state.storage_root(
3006 vec![(block_hash.as_ref(), Some(block_hash.as_ref()))].into_iter(),
3007 StateVersion::V1,
3008 );
3009 op.update_db_storage(overlay).unwrap();
3010 header.state_root = root.into();
3011
3012 op.set_block_data(header.clone(), Some(body), None, None, NewBlockState::Best, true)
3013 .unwrap();
3014
3015 backend.commit_operation(op)?;
3016
3017 Ok(header.hash())
3018 }
3019
3020 pub fn insert_block_with_synthetic_ops(
3022 backend: &Backend<Block>,
3023 number: u64,
3024 parent_hash: H256,
3025 extrinsics_root: H256,
3026 body: Vec<UncheckedXt>,
3027 runtime_index_ops: Vec<IndexOperation>,
3028 synthetic_index_ops: Vec<IndexOperation>,
3029 renew_payloads: HashMap<H256, Vec<u8>>,
3030 ) -> Result<H256, sp_blockchain::Error> {
3031 use sp_runtime::testing::Digest;
3032
3033 let digest = Digest::default();
3034 let mut header =
3035 Header { number, parent_hash, state_root: Default::default(), digest, extrinsics_root };
3036
3037 let block_hash = if number == 0 { Default::default() } else { parent_hash };
3038 let mut op = backend.begin_operation().unwrap();
3039 backend.begin_state_operation(&mut op, block_hash).unwrap();
3040 op.set_renew_payloads(renew_payloads).unwrap();
3041 op.update_transaction_index(synthetic_index_ops).unwrap();
3042 if !runtime_index_ops.is_empty() {
3043 op.update_transaction_index(runtime_index_ops).unwrap();
3044 }
3045
3046 let (root, overlay) = op.old_state.storage_root(
3047 vec![(block_hash.as_ref(), Some(block_hash.as_ref()))].into_iter(),
3048 StateVersion::V1,
3049 );
3050 op.update_db_storage(overlay).unwrap();
3051 header.state_root = root.into();
3052
3053 op.set_block_data(header.clone(), Some(body), None, None, NewBlockState::Best, true)
3054 .unwrap();
3055
3056 backend.commit_operation(op)?;
3057
3058 Ok(header.hash())
3059 }
3060
3061 pub fn insert_disconnected_header(
3062 backend: &Backend<Block>,
3063 number: u64,
3064 parent_hash: H256,
3065 extrinsics_root: H256,
3066 best: bool,
3067 ) -> H256 {
3068 use sp_runtime::testing::Digest;
3069
3070 let digest = Digest::default();
3071 let header =
3072 Header { number, parent_hash, state_root: Default::default(), digest, extrinsics_root };
3073
3074 let mut op = backend.begin_operation().unwrap();
3075
3076 op.set_block_data(
3077 header.clone(),
3078 Some(vec![]),
3079 None,
3080 None,
3081 if best { NewBlockState::Best } else { NewBlockState::Normal },
3082 true,
3083 )
3084 .unwrap();
3085
3086 backend.commit_operation(op).unwrap();
3087
3088 header.hash()
3089 }
3090
3091 pub fn insert_header_no_head(
3092 backend: &Backend<Block>,
3093 number: u64,
3094 parent_hash: H256,
3095 extrinsics_root: H256,
3096 ) -> H256 {
3097 use sp_runtime::testing::Digest;
3098
3099 let digest = Digest::default();
3100 let mut header =
3101 Header { number, parent_hash, state_root: Default::default(), digest, extrinsics_root };
3102 let mut op = backend.begin_operation().unwrap();
3103
3104 let root = backend
3105 .state_at(parent_hash, TrieCacheContext::Untrusted)
3106 .unwrap_or_else(|_| {
3107 if parent_hash == Default::default() {
3108 backend.empty_state()
3109 } else {
3110 panic!("Unknown block: {parent_hash:?}")
3111 }
3112 })
3113 .storage_root(
3114 vec![(parent_hash.as_ref(), Some(parent_hash.as_ref()))].into_iter(),
3115 StateVersion::V1,
3116 )
3117 .0;
3118 header.state_root = root.into();
3119
3120 op.set_block_data(header.clone(), None, None, None, NewBlockState::Normal, true)
3121 .unwrap();
3122 backend.commit_operation(op).unwrap();
3123
3124 header.hash()
3125 }
3126
3127 #[test]
3128 fn block_hash_inserted_correctly() {
3129 let backing = {
3130 let db = Backend::<Block>::new_test(1, 0);
3131 for i in 0..10 {
3132 assert!(db.blockchain().hash(i).unwrap().is_none());
3133
3134 {
3135 let hash = if i == 0 {
3136 Default::default()
3137 } else {
3138 db.blockchain.hash(i - 1).unwrap().unwrap()
3139 };
3140
3141 let mut op = db.begin_operation().unwrap();
3142 db.begin_state_operation(&mut op, hash).unwrap();
3143 let header = Header {
3144 number: i,
3145 parent_hash: hash,
3146 state_root: Default::default(),
3147 digest: Default::default(),
3148 extrinsics_root: Default::default(),
3149 };
3150
3151 op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best, true)
3152 .unwrap();
3153 db.commit_operation(op).unwrap();
3154 }
3155
3156 assert!(db.blockchain().hash(i).unwrap().is_some())
3157 }
3158 db.storage.db.clone()
3159 };
3160
3161 let backend = Backend::<Block>::new(
3162 DatabaseSettings {
3163 trie_cache_maximum_size: Some(16 * 1024 * 1024),
3164 state_pruning: Some(PruningMode::blocks_pruning(1)),
3165 source: DatabaseSource::Custom { db: backing, require_create_flag: false },
3166 blocks_pruning: BlocksPruning::KeepFinalized,
3167 pruning_filters: Default::default(),
3168 metrics_registry: None,
3169 },
3170 0,
3171 )
3172 .unwrap();
3173 assert_eq!(backend.blockchain().info().best_number, 9);
3174 for i in 0..10 {
3175 assert!(backend.blockchain().hash(i).unwrap().is_some())
3176 }
3177 }
3178
3179 #[test]
3180 fn set_state_data() {
3181 set_state_data_inner(StateVersion::V0);
3182 set_state_data_inner(StateVersion::V1);
3183 }
3184 fn set_state_data_inner(state_version: StateVersion) {
3185 let db = Backend::<Block>::new_test(2, 0);
3186 let hash = {
3187 let mut op = db.begin_operation().unwrap();
3188 let mut header = Header {
3189 number: 0,
3190 parent_hash: Default::default(),
3191 state_root: Default::default(),
3192 digest: Default::default(),
3193 extrinsics_root: Default::default(),
3194 };
3195
3196 let storage = vec![(vec![1, 3, 5], vec![2, 4, 6]), (vec![1, 2, 3], vec![9, 9, 9])];
3197
3198 header.state_root = op
3199 .old_state
3200 .storage_root(storage.iter().map(|(x, y)| (&x[..], Some(&y[..]))), state_version)
3201 .0
3202 .into();
3203 let hash = header.hash();
3204
3205 op.reset_storage(
3206 Storage {
3207 top: storage.into_iter().collect(),
3208 children_default: Default::default(),
3209 },
3210 state_version,
3211 )
3212 .unwrap();
3213 op.set_block_data(header.clone(), Some(vec![]), None, None, NewBlockState::Best, true)
3214 .unwrap();
3215
3216 db.commit_operation(op).unwrap();
3217
3218 let state = db.state_at(hash, TrieCacheContext::Untrusted).unwrap();
3219
3220 assert_eq!(state.storage(&[1, 3, 5]).unwrap(), Some(vec![2, 4, 6]));
3221 assert_eq!(state.storage(&[1, 2, 3]).unwrap(), Some(vec![9, 9, 9]));
3222 assert_eq!(state.storage(&[5, 5, 5]).unwrap(), None);
3223
3224 hash
3225 };
3226
3227 {
3228 let mut op = db.begin_operation().unwrap();
3229 db.begin_state_operation(&mut op, hash).unwrap();
3230 let mut header = Header {
3231 number: 1,
3232 parent_hash: hash,
3233 state_root: Default::default(),
3234 digest: Default::default(),
3235 extrinsics_root: Default::default(),
3236 };
3237
3238 let storage = vec![(vec![1, 3, 5], None), (vec![5, 5, 5], Some(vec![4, 5, 6]))];
3239
3240 let (root, overlay) = op.old_state.storage_root(
3241 storage.iter().map(|(k, v)| (k.as_slice(), v.as_ref().map(|v| &v[..]))),
3242 state_version,
3243 );
3244 op.update_db_storage(overlay).unwrap();
3245 header.state_root = root.into();
3246
3247 op.update_storage(storage, Vec::new()).unwrap();
3248 op.set_block_data(header.clone(), Some(vec![]), None, None, NewBlockState::Best, true)
3249 .unwrap();
3250
3251 db.commit_operation(op).unwrap();
3252
3253 let state = db.state_at(header.hash(), TrieCacheContext::Untrusted).unwrap();
3254
3255 assert_eq!(state.storage(&[1, 3, 5]).unwrap(), None);
3256 assert_eq!(state.storage(&[1, 2, 3]).unwrap(), Some(vec![9, 9, 9]));
3257 assert_eq!(state.storage(&[5, 5, 5]).unwrap(), Some(vec![4, 5, 6]));
3258 }
3259 }
3260
3261 #[test]
3262 fn delete_only_when_negative_rc() {
3263 sp_tracing::try_init_simple();
3264 let state_version = StateVersion::default();
3265 let key;
3266 let backend = Backend::<Block>::new_test(1, 0);
3267
3268 let hash = {
3269 let mut op = backend.begin_operation().unwrap();
3270 backend.begin_state_operation(&mut op, Default::default()).unwrap();
3271 let mut header = Header {
3272 number: 0,
3273 parent_hash: Default::default(),
3274 state_root: Default::default(),
3275 digest: Default::default(),
3276 extrinsics_root: Default::default(),
3277 };
3278
3279 header.state_root =
3280 op.old_state.storage_root(std::iter::empty(), state_version).0.into();
3281 let hash = header.hash();
3282
3283 op.reset_storage(
3284 Storage { top: Default::default(), children_default: Default::default() },
3285 state_version,
3286 )
3287 .unwrap();
3288
3289 key = op.db_updates.insert(EMPTY_PREFIX, b"hello");
3290 op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best, true)
3291 .unwrap();
3292
3293 backend.commit_operation(op).unwrap();
3294 assert_eq!(
3295 backend
3296 .storage
3297 .db
3298 .get(columns::STATE, &sp_trie::prefixed_key::<BlakeTwo256>(&key, EMPTY_PREFIX))
3299 .unwrap(),
3300 &b"hello"[..]
3301 );
3302 hash
3303 };
3304
3305 let hashof1 = {
3306 let mut op = backend.begin_operation().unwrap();
3307 backend.begin_state_operation(&mut op, hash).unwrap();
3308 let mut header = Header {
3309 number: 1,
3310 parent_hash: hash,
3311 state_root: Default::default(),
3312 digest: Default::default(),
3313 extrinsics_root: Default::default(),
3314 };
3315
3316 let storage: Vec<(_, _)> = vec![];
3317
3318 header.state_root = op
3319 .old_state
3320 .storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y))), state_version)
3321 .0
3322 .into();
3323 let hash = header.hash();
3324
3325 op.db_updates.insert(EMPTY_PREFIX, b"hello");
3326 op.db_updates.remove(&key, EMPTY_PREFIX);
3327 op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best, true)
3328 .unwrap();
3329
3330 backend.commit_operation(op).unwrap();
3331 assert_eq!(
3332 backend
3333 .storage
3334 .db
3335 .get(columns::STATE, &sp_trie::prefixed_key::<BlakeTwo256>(&key, EMPTY_PREFIX))
3336 .unwrap(),
3337 &b"hello"[..]
3338 );
3339 hash
3340 };
3341
3342 let hashof2 = {
3343 let mut op = backend.begin_operation().unwrap();
3344 backend.begin_state_operation(&mut op, hashof1).unwrap();
3345 let mut header = Header {
3346 number: 2,
3347 parent_hash: hashof1,
3348 state_root: Default::default(),
3349 digest: Default::default(),
3350 extrinsics_root: Default::default(),
3351 };
3352
3353 let storage: Vec<(_, _)> = vec![];
3354
3355 header.state_root = op
3356 .old_state
3357 .storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y))), state_version)
3358 .0
3359 .into();
3360 let hash = header.hash();
3361
3362 op.db_updates.remove(&key, EMPTY_PREFIX);
3363 op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best, true)
3364 .unwrap();
3365
3366 backend.commit_operation(op).unwrap();
3367
3368 assert!(backend
3369 .storage
3370 .db
3371 .get(columns::STATE, &sp_trie::prefixed_key::<BlakeTwo256>(&key, EMPTY_PREFIX))
3372 .is_some());
3373 hash
3374 };
3375
3376 let hashof3 = {
3377 let mut op = backend.begin_operation().unwrap();
3378 backend.begin_state_operation(&mut op, hashof2).unwrap();
3379 let mut header = Header {
3380 number: 3,
3381 parent_hash: hashof2,
3382 state_root: Default::default(),
3383 digest: Default::default(),
3384 extrinsics_root: Default::default(),
3385 };
3386
3387 let storage: Vec<(_, _)> = vec![];
3388
3389 header.state_root = op
3390 .old_state
3391 .storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y))), state_version)
3392 .0
3393 .into();
3394 let hash = header.hash();
3395
3396 op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best, true)
3397 .unwrap();
3398
3399 backend.commit_operation(op).unwrap();
3400 hash
3401 };
3402
3403 let hashof4 = {
3404 let mut op = backend.begin_operation().unwrap();
3405 backend.begin_state_operation(&mut op, hashof3).unwrap();
3406 let mut header = Header {
3407 number: 4,
3408 parent_hash: hashof3,
3409 state_root: Default::default(),
3410 digest: Default::default(),
3411 extrinsics_root: Default::default(),
3412 };
3413
3414 let storage: Vec<(_, _)> = vec![];
3415
3416 header.state_root = op
3417 .old_state
3418 .storage_root(storage.iter().cloned().map(|(x, y)| (x, Some(y))), state_version)
3419 .0
3420 .into();
3421 let hash = header.hash();
3422
3423 op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Best, true)
3424 .unwrap();
3425
3426 backend.commit_operation(op).unwrap();
3427 assert!(backend
3428 .storage
3429 .db
3430 .get(columns::STATE, &sp_trie::prefixed_key::<BlakeTwo256>(&key, EMPTY_PREFIX))
3431 .is_none());
3432 hash
3433 };
3434
3435 backend.finalize_block(hashof1, None).unwrap();
3436 backend.finalize_block(hashof2, None).unwrap();
3437 backend.finalize_block(hashof3, None).unwrap();
3438 backend.finalize_block(hashof4, None).unwrap();
3439 assert!(backend
3440 .storage
3441 .db
3442 .get(columns::STATE, &sp_trie::prefixed_key::<BlakeTwo256>(&key, EMPTY_PREFIX))
3443 .is_none());
3444 }
3445
3446 #[test]
3447 fn tree_route_works() {
3448 let backend = Backend::<Block>::new_test(1000, 100);
3449 let blockchain = backend.blockchain();
3450 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
3451
3452 let a1 = insert_header(&backend, 1, block0, None, Default::default());
3454 let a2 = insert_header(&backend, 2, a1, None, Default::default());
3455 let a3 = insert_header(&backend, 3, a2, None, Default::default());
3456
3457 let b1 = insert_header(&backend, 1, block0, None, H256::from([1; 32]));
3459 let b2 = insert_header(&backend, 2, b1, None, Default::default());
3460
3461 {
3462 let tree_route = tree_route(blockchain, a1, a1).unwrap();
3463
3464 assert_eq!(tree_route.common_block().hash, a1);
3465 assert!(tree_route.retracted().is_empty());
3466 assert!(tree_route.enacted().is_empty());
3467 }
3468
3469 {
3470 let tree_route = tree_route(blockchain, a3, b2).unwrap();
3471
3472 assert_eq!(tree_route.common_block().hash, block0);
3473 assert_eq!(
3474 tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(),
3475 vec![a3, a2, a1]
3476 );
3477 assert_eq!(
3478 tree_route.enacted().iter().map(|r| r.hash).collect::<Vec<_>>(),
3479 vec![b1, b2]
3480 );
3481 }
3482
3483 {
3484 let tree_route = tree_route(blockchain, a1, a3).unwrap();
3485
3486 assert_eq!(tree_route.common_block().hash, a1);
3487 assert!(tree_route.retracted().is_empty());
3488 assert_eq!(
3489 tree_route.enacted().iter().map(|r| r.hash).collect::<Vec<_>>(),
3490 vec![a2, a3]
3491 );
3492 }
3493
3494 {
3495 let tree_route = tree_route(blockchain, a3, a1).unwrap();
3496
3497 assert_eq!(tree_route.common_block().hash, a1);
3498 assert_eq!(
3499 tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(),
3500 vec![a3, a2]
3501 );
3502 assert!(tree_route.enacted().is_empty());
3503 }
3504
3505 {
3506 let tree_route = tree_route(blockchain, a2, a2).unwrap();
3507
3508 assert_eq!(tree_route.common_block().hash, a2);
3509 assert!(tree_route.retracted().is_empty());
3510 assert!(tree_route.enacted().is_empty());
3511 }
3512 }
3513
3514 #[test]
3515 fn tree_route_child() {
3516 let backend = Backend::<Block>::new_test(1000, 100);
3517 let blockchain = backend.blockchain();
3518
3519 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
3520 let block1 = insert_header(&backend, 1, block0, None, Default::default());
3521
3522 {
3523 let tree_route = tree_route(blockchain, block0, block1).unwrap();
3524
3525 assert_eq!(tree_route.common_block().hash, block0);
3526 assert!(tree_route.retracted().is_empty());
3527 assert_eq!(
3528 tree_route.enacted().iter().map(|r| r.hash).collect::<Vec<_>>(),
3529 vec![block1]
3530 );
3531 }
3532 }
3533
3534 #[test]
3535 fn lowest_common_ancestor_works() {
3536 let backend = Backend::<Block>::new_test(1000, 100);
3537 let blockchain = backend.blockchain();
3538 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
3539
3540 let a1 = insert_header(&backend, 1, block0, None, Default::default());
3542 let a2 = insert_header(&backend, 2, a1, None, Default::default());
3543 let a3 = insert_header(&backend, 3, a2, None, Default::default());
3544
3545 let b1 = insert_header(&backend, 1, block0, None, H256::from([1; 32]));
3547 let b2 = insert_header(&backend, 2, b1, None, Default::default());
3548
3549 {
3550 let lca = lowest_common_ancestor(blockchain, a3, b2).unwrap();
3551
3552 assert_eq!(lca.hash, block0);
3553 assert_eq!(lca.number, 0);
3554 }
3555
3556 {
3557 let lca = lowest_common_ancestor(blockchain, a1, a3).unwrap();
3558
3559 assert_eq!(lca.hash, a1);
3560 assert_eq!(lca.number, 1);
3561 }
3562
3563 {
3564 let lca = lowest_common_ancestor(blockchain, a3, a1).unwrap();
3565
3566 assert_eq!(lca.hash, a1);
3567 assert_eq!(lca.number, 1);
3568 }
3569
3570 {
3571 let lca = lowest_common_ancestor(blockchain, a2, a3).unwrap();
3572
3573 assert_eq!(lca.hash, a2);
3574 assert_eq!(lca.number, 2);
3575 }
3576
3577 {
3578 let lca = lowest_common_ancestor(blockchain, a2, a1).unwrap();
3579
3580 assert_eq!(lca.hash, a1);
3581 assert_eq!(lca.number, 1);
3582 }
3583
3584 {
3585 let lca = lowest_common_ancestor(blockchain, a2, a2).unwrap();
3586
3587 assert_eq!(lca.hash, a2);
3588 assert_eq!(lca.number, 2);
3589 }
3590 }
3591
3592 #[test]
3593 fn displaced_leaves_after_finalizing_works_with_disconnect() {
3594 let backend = Backend::<Block>::new_test(1000, 100);
3599 let blockchain = backend.blockchain();
3600 let genesis_number = 0;
3601 let genesis_hash =
3602 insert_header(&backend, genesis_number, Default::default(), None, Default::default());
3603
3604 let a3_number = 3;
3605 let a3_hash = insert_disconnected_header(
3606 &backend,
3607 a3_number,
3608 H256::from([200; 32]),
3609 H256::from([1; 32]),
3610 true,
3611 );
3612
3613 let a4_number = 4;
3614 let a4_hash =
3615 insert_disconnected_header(&backend, a4_number, a3_hash, H256::from([2; 32]), true);
3616 {
3617 let displaced = blockchain
3618 .displaced_leaves_after_finalizing(a3_hash, a3_number, H256::from([200; 32]))
3619 .unwrap();
3620 assert_eq!(blockchain.leaves().unwrap(), vec![a4_hash, genesis_hash]);
3621 assert_eq!(displaced.displaced_leaves, vec![(genesis_number, genesis_hash)]);
3622 assert_eq!(displaced.displaced_blocks, vec![]);
3623 }
3624
3625 {
3626 let displaced = blockchain
3627 .displaced_leaves_after_finalizing(a4_hash, a4_number, a3_hash)
3628 .unwrap();
3629 assert_eq!(blockchain.leaves().unwrap(), vec![a4_hash, genesis_hash]);
3630 assert_eq!(displaced.displaced_leaves, vec![(genesis_number, genesis_hash)]);
3631 assert_eq!(displaced.displaced_blocks, vec![]);
3632 }
3633
3634 let a1_number = 1;
3637 let a1_hash = insert_disconnected_header(
3638 &backend,
3639 a1_number,
3640 genesis_hash,
3641 H256::from([123; 32]),
3642 false,
3643 );
3644 {
3645 let displaced = blockchain
3646 .displaced_leaves_after_finalizing(a3_hash, a3_number, H256::from([2; 32]))
3647 .unwrap();
3648 assert_eq!(blockchain.leaves().unwrap(), vec![a4_hash, a1_hash]);
3649 assert_eq!(displaced.displaced_leaves, vec![]);
3650 assert_eq!(displaced.displaced_blocks, vec![]);
3651 }
3652
3653 let b1_number = 1;
3657 let b1_hash = insert_disconnected_header(
3658 &backend,
3659 b1_number,
3660 genesis_hash,
3661 H256::from([124; 32]),
3662 false,
3663 );
3664 {
3665 let displaced = blockchain
3666 .displaced_leaves_after_finalizing(a3_hash, a3_number, H256::from([2; 32]))
3667 .unwrap();
3668 assert_eq!(blockchain.leaves().unwrap(), vec![a4_hash, a1_hash, b1_hash]);
3669 assert_eq!(displaced.displaced_leaves, vec![]);
3670 assert_eq!(displaced.displaced_blocks, vec![]);
3671 }
3672
3673 let b2_number = 2;
3678 let b2_hash =
3679 insert_disconnected_header(&backend, b2_number, b1_hash, H256::from([40; 32]), false);
3680 let b3_number = 3;
3681 let b3_hash =
3682 insert_disconnected_header(&backend, b3_number, b2_hash, H256::from([41; 32]), false);
3683 let b4_number = 4;
3684 let b4_hash =
3685 insert_disconnected_header(&backend, b4_number, b3_hash, H256::from([42; 32]), false);
3686 let b5_number = 5;
3687 let b5_hash =
3688 insert_disconnected_header(&backend, b5_number, b4_hash, H256::from([43; 32]), false);
3689 {
3690 let displaced = blockchain
3691 .displaced_leaves_after_finalizing(a3_hash, a3_number, H256::from([2; 32]))
3692 .unwrap();
3693 assert_eq!(blockchain.leaves().unwrap(), vec![b5_hash, a4_hash, a1_hash]);
3694 assert_eq!(displaced.displaced_leaves, vec![]);
3695 assert_eq!(displaced.displaced_blocks, vec![]);
3696 }
3697
3698 let c4_number = 4;
3704 let c4_hash =
3705 insert_disconnected_header(&backend, c4_number, a3_hash, H256::from([44; 32]), false);
3706 {
3707 let displaced = blockchain
3708 .displaced_leaves_after_finalizing(a4_hash, a4_number, a3_hash)
3709 .unwrap();
3710 assert_eq!(blockchain.leaves().unwrap(), vec![b5_hash, a4_hash, c4_hash, a1_hash]);
3711 assert_eq!(displaced.displaced_leaves, vec![(c4_number, c4_hash)]);
3712 assert_eq!(displaced.displaced_blocks, vec![c4_hash]);
3713 }
3714 }
3715
3716 #[test]
3717 fn disconnected_blocks_do_not_become_leaves_and_warp_sync_scenario() {
3718 let backend = Backend::<Block>::new_test(1000, 100);
3732 let blockchain = backend.blockchain();
3733
3734 let insert_block_raw = |number: u64,
3735 parent_hash: H256,
3736 ext_root: H256,
3737 state: NewBlockState,
3738 register_as_leaf: bool|
3739 -> H256 {
3740 use sp_runtime::testing::Digest;
3741 let digest = Digest::default();
3742 let header = Header {
3743 number,
3744 parent_hash,
3745 state_root: Default::default(),
3746 digest,
3747 extrinsics_root: ext_root,
3748 };
3749 let mut op = backend.begin_operation().unwrap();
3750 op.set_block_data(header.clone(), Some(vec![]), None, None, state, register_as_leaf)
3751 .unwrap();
3752 backend.commit_operation(op).unwrap();
3753 header.hash()
3754 };
3755
3756 let genesis_hash = insert_header(&backend, 0, Default::default(), None, Default::default());
3758 assert_eq!(blockchain.leaves().unwrap(), vec![genesis_hash]);
3759
3760 let _proof5_hash = insert_block_raw(
3764 5,
3765 H256::from([5; 32]),
3766 H256::from([50; 32]),
3767 NewBlockState::Normal,
3768 false,
3769 );
3770 let _proof10_hash = insert_block_raw(
3771 10,
3772 H256::from([10; 32]),
3773 H256::from([100; 32]),
3774 NewBlockState::Normal,
3775 false,
3776 );
3777 let _proof15_hash = insert_block_raw(
3778 15,
3779 H256::from([15; 32]),
3780 H256::from([150; 32]),
3781 NewBlockState::Normal,
3782 false,
3783 );
3784
3785 assert_eq!(blockchain.leaves().unwrap(), vec![genesis_hash]);
3787
3788 assert!(blockchain.header(_proof5_hash).unwrap().is_some());
3790 assert!(blockchain.header(_proof10_hash).unwrap().is_some());
3791 assert!(blockchain.header(_proof15_hash).unwrap().is_some());
3792
3793 let block20_hash = insert_block_raw(
3797 20,
3798 H256::from([19; 32]),
3799 H256::from([200; 32]),
3800 NewBlockState::Final,
3801 true,
3802 );
3803
3804 let leaves = blockchain.leaves().unwrap();
3806 assert!(leaves.contains(&block20_hash));
3807 assert_eq!(blockchain.info().finalized_number, 20);
3809 assert_eq!(blockchain.info().finalized_hash, block20_hash);
3810 assert!(!leaves.contains(&_proof5_hash));
3812 assert!(!leaves.contains(&_proof10_hash));
3813 assert!(!leaves.contains(&_proof15_hash));
3814
3815 let mut prev_hash = genesis_hash;
3820 let mut gap_hashes = Vec::new();
3821 for n in 1..=19 {
3822 let h = insert_disconnected_header(&backend, n, prev_hash, Default::default(), false);
3823 gap_hashes.push(h);
3824 prev_hash = h;
3825 }
3826
3827 let leaves = blockchain.leaves().unwrap();
3829 for (i, gap_hash) in gap_hashes.iter().enumerate() {
3830 assert!(
3831 !leaves.contains(gap_hash),
3832 "Gap sync block #{} should not be a leaf, but it is",
3833 i + 1,
3834 );
3835 }
3836 assert!(leaves.contains(&block20_hash));
3838 assert!(!leaves.contains(&_proof5_hash));
3840 assert!(!leaves.contains(&_proof10_hash));
3841 assert!(!leaves.contains(&_proof15_hash));
3842
3843 {
3847 let displaced = blockchain
3848 .displaced_leaves_after_finalizing(
3849 block20_hash,
3850 20,
3851 H256::from([19; 32]), )
3853 .unwrap();
3854 assert!(!displaced.displaced_leaves.iter().any(|(_, h)| *h == _proof5_hash),);
3857 assert!(!displaced.displaced_leaves.iter().any(|(_, h)| *h == _proof10_hash),);
3858 assert!(!displaced.displaced_leaves.iter().any(|(_, h)| *h == _proof15_hash),);
3859 for gap_hash in &gap_hashes {
3862 assert!(!displaced.displaced_leaves.iter().any(|(_, h)| h == gap_hash),);
3863 }
3864 }
3865 }
3866
3867 #[test]
3868 fn displaced_leaves_after_finalizing_works() {
3869 let backend = Backend::<Block>::new_test(1000, 100);
3870 let blockchain = backend.blockchain();
3871 let genesis_number = 0;
3872 let genesis_hash =
3873 insert_header(&backend, genesis_number, Default::default(), None, Default::default());
3874
3875 let a1_number = 1;
3882 let a1_hash = insert_header(&backend, a1_number, genesis_hash, None, Default::default());
3883 let a2_number = 2;
3884 let a2_hash = insert_header(&backend, a2_number, a1_hash, None, Default::default());
3885 let a3_number = 3;
3886 let a3_hash = insert_header(&backend, a3_number, a2_hash, None, Default::default());
3887
3888 {
3889 let displaced = blockchain
3890 .displaced_leaves_after_finalizing(genesis_hash, genesis_number, Default::default())
3891 .unwrap();
3892 assert_eq!(displaced.displaced_leaves, vec![]);
3893 assert_eq!(displaced.displaced_blocks, vec![]);
3894 }
3895 {
3896 let displaced_a1 = blockchain
3897 .displaced_leaves_after_finalizing(a1_hash, a1_number, genesis_hash)
3898 .unwrap();
3899 assert_eq!(displaced_a1.displaced_leaves, vec![]);
3900 assert_eq!(displaced_a1.displaced_blocks, vec![]);
3901
3902 let displaced_a2 = blockchain
3903 .displaced_leaves_after_finalizing(a2_hash, a2_number, a1_hash)
3904 .unwrap();
3905 assert_eq!(displaced_a2.displaced_leaves, vec![]);
3906 assert_eq!(displaced_a2.displaced_blocks, vec![]);
3907
3908 let displaced_a3 = blockchain
3909 .displaced_leaves_after_finalizing(a3_hash, a3_number, a2_hash)
3910 .unwrap();
3911 assert_eq!(displaced_a3.displaced_leaves, vec![]);
3912 assert_eq!(displaced_a3.displaced_blocks, vec![]);
3913 }
3914 {
3915 let displaced = blockchain
3919 .displaced_leaves_after_finalizing(H256::from([57; 32]), 10, H256::from([56; 32]))
3920 .unwrap();
3921 assert_eq!(displaced.displaced_leaves, vec![]);
3922 assert_eq!(displaced.displaced_blocks, vec![]);
3923 }
3924
3925 let b1_number = 1;
3927 let b1_hash = insert_header(&backend, b1_number, genesis_hash, None, H256::from([1; 32]));
3928 let b2_number = 2;
3929 let b2_hash = insert_header(&backend, b2_number, b1_hash, None, Default::default());
3930
3931 let c1_number = 3;
3933 let c1_hash = insert_header(&backend, c1_number, b2_hash, None, H256::from([2; 32]));
3934 let c2_number = 4;
3935 let c2_hash = insert_header(&backend, c2_number, c1_hash, None, Default::default());
3936
3937 let d1_number = 2;
3939 let d1_hash = insert_header(&backend, d1_number, b1_hash, None, H256::from([3; 32]));
3940 let d2_number = 3;
3941 let d2_hash = insert_header(&backend, d2_number, d1_hash, None, Default::default());
3942
3943 {
3944 let displaced_a1 = blockchain
3945 .displaced_leaves_after_finalizing(a1_hash, a1_number, genesis_hash)
3946 .unwrap();
3947 assert_eq!(
3948 displaced_a1.displaced_leaves,
3949 vec![(c2_number, c2_hash), (d2_number, d2_hash)]
3950 );
3951 let mut displaced_blocks = vec![b1_hash, b2_hash, c1_hash, c2_hash, d1_hash, d2_hash];
3952 displaced_blocks.sort();
3953 assert_eq!(displaced_a1.displaced_blocks, displaced_blocks);
3954
3955 let displaced_a2 = blockchain
3956 .displaced_leaves_after_finalizing(a2_hash, a2_number, a1_hash)
3957 .unwrap();
3958 assert_eq!(displaced_a1.displaced_leaves, displaced_a2.displaced_leaves);
3959 assert_eq!(displaced_a1.displaced_blocks, displaced_a2.displaced_blocks);
3960
3961 let displaced_a3 = blockchain
3962 .displaced_leaves_after_finalizing(a3_hash, a3_number, a2_hash)
3963 .unwrap();
3964 assert_eq!(displaced_a1.displaced_leaves, displaced_a3.displaced_leaves);
3965 assert_eq!(displaced_a1.displaced_blocks, displaced_a3.displaced_blocks);
3966 }
3967 {
3968 let displaced = blockchain
3969 .displaced_leaves_after_finalizing(b1_hash, b1_number, genesis_hash)
3970 .unwrap();
3971 assert_eq!(displaced.displaced_leaves, vec![(a3_number, a3_hash)]);
3972 let mut displaced_blocks = vec![a1_hash, a2_hash, a3_hash];
3973 displaced_blocks.sort();
3974 assert_eq!(displaced.displaced_blocks, displaced_blocks);
3975 }
3976 {
3977 let displaced = blockchain
3978 .displaced_leaves_after_finalizing(b2_hash, b2_number, b1_hash)
3979 .unwrap();
3980 assert_eq!(
3981 displaced.displaced_leaves,
3982 vec![(a3_number, a3_hash), (d2_number, d2_hash)]
3983 );
3984 let mut displaced_blocks = vec![a1_hash, a2_hash, a3_hash, d1_hash, d2_hash];
3985 displaced_blocks.sort();
3986 assert_eq!(displaced.displaced_blocks, displaced_blocks);
3987 }
3988 {
3989 let displaced = blockchain
3990 .displaced_leaves_after_finalizing(c2_hash, c2_number, c1_hash)
3991 .unwrap();
3992 assert_eq!(
3993 displaced.displaced_leaves,
3994 vec![(a3_number, a3_hash), (d2_number, d2_hash)]
3995 );
3996 let mut displaced_blocks = vec![a1_hash, a2_hash, a3_hash, d1_hash, d2_hash];
3997 displaced_blocks.sort();
3998 assert_eq!(displaced.displaced_blocks, displaced_blocks);
3999 }
4000 }
4001
4002 #[test]
4003 fn test_tree_route_regression() {
4004 let backend = Backend::<Block>::new_test(10000, 10000);
4012 let blockchain = backend.blockchain();
4013
4014 let genesis = insert_header(&backend, 0, Default::default(), None, Default::default());
4015
4016 let block100 = (1..=100).fold(genesis, |parent, n| {
4017 insert_header(&backend, n, parent, None, Default::default())
4018 });
4019
4020 let block7000 = (101..=7000).fold(block100, |parent, n| {
4021 insert_header(&backend, n, parent, None, Default::default())
4022 });
4023
4024 lowest_common_ancestor(blockchain, genesis, block100).unwrap();
4026
4027 let tree_route = tree_route(blockchain, block100, block7000).unwrap();
4032
4033 assert!(tree_route.retracted().is_empty());
4034 }
4035
4036 #[test]
4037 fn test_leaves_with_complex_block_tree() {
4038 let backend: Arc<Backend<substrate_test_runtime_client::runtime::Block>> =
4039 Arc::new(Backend::new_test(20, 20));
4040 substrate_test_runtime_client::trait_tests::test_leaves_for_backend(backend);
4041 }
4042
4043 #[test]
4044 fn test_children_with_complex_block_tree() {
4045 let backend: Arc<Backend<substrate_test_runtime_client::runtime::Block>> =
4046 Arc::new(Backend::new_test(20, 20));
4047 substrate_test_runtime_client::trait_tests::test_children_for_backend(backend);
4048 }
4049
4050 #[test]
4051 fn test_blockchain_query_by_number_gets_canonical() {
4052 let backend: Arc<Backend<substrate_test_runtime_client::runtime::Block>> =
4053 Arc::new(Backend::new_test(20, 20));
4054 substrate_test_runtime_client::trait_tests::test_blockchain_query_by_number_gets_canonical(
4055 backend,
4056 );
4057 }
4058
4059 #[test]
4060 fn test_leaves_pruned_on_finality() {
4061 let backend: Backend<Block> = Backend::new_test(10, 10);
4065 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
4066
4067 let block1_a = insert_header(&backend, 1, block0, None, Default::default());
4068 let block1_b = insert_header(&backend, 1, block0, None, [1; 32].into());
4069 let block1_c = insert_header(&backend, 1, block0, None, [2; 32].into());
4070
4071 assert_eq!(backend.blockchain().leaves().unwrap(), vec![block1_a, block1_b, block1_c]);
4072
4073 let block2_a = insert_header(&backend, 2, block1_a, None, Default::default());
4074 let block2_b = insert_header(&backend, 2, block1_b, None, Default::default());
4075
4076 let block3_b = insert_header(&backend, 3, block2_b, None, [3; 32].into());
4077
4078 assert_eq!(backend.blockchain().leaves().unwrap(), vec![block3_b, block2_a, block1_c]);
4079
4080 backend.finalize_block(block1_a, None).unwrap();
4081 backend.finalize_block(block2_a, None).unwrap();
4082
4083 assert_eq!(backend.blockchain().leaves().unwrap(), vec![block2_a]);
4085 }
4086
4087 #[test]
4088 fn test_aux() {
4089 let backend: Backend<substrate_test_runtime_client::runtime::Block> =
4090 Backend::new_test(0, 0);
4091 assert!(backend.get_aux(b"test").unwrap().is_none());
4092 backend.insert_aux(&[(&b"test"[..], &b"hello"[..])], &[]).unwrap();
4093 assert_eq!(b"hello", &backend.get_aux(b"test").unwrap().unwrap()[..]);
4094 backend.insert_aux(&[], &[&b"test"[..]]).unwrap();
4095 assert!(backend.get_aux(b"test").unwrap().is_none());
4096 }
4097
4098 #[test]
4099 fn test_finalize_block_with_justification() {
4100 use sc_client_api::blockchain::Backend as BlockChainBackend;
4101
4102 let backend = Backend::<Block>::new_test(10, 10);
4103
4104 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
4105 let block1 = insert_header(&backend, 1, block0, None, Default::default());
4106
4107 let justification = Some((CONS0_ENGINE_ID, vec![1, 2, 3]));
4108 backend.finalize_block(block1, justification.clone()).unwrap();
4109
4110 assert_eq!(
4111 backend.blockchain().justifications(block1).unwrap(),
4112 justification.map(Justifications::from),
4113 );
4114 }
4115
4116 #[test]
4117 fn test_append_justification_to_finalized_block() {
4118 use sc_client_api::blockchain::Backend as BlockChainBackend;
4119
4120 let backend = Backend::<Block>::new_test(10, 10);
4121
4122 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
4123 let block1 = insert_header(&backend, 1, block0, None, Default::default());
4124
4125 let just0 = (CONS0_ENGINE_ID, vec![1, 2, 3]);
4126 backend.finalize_block(block1, Some(just0.clone().into())).unwrap();
4127
4128 let just1 = (CONS1_ENGINE_ID, vec![4, 5]);
4129 backend.append_justification(block1, just1.clone()).unwrap();
4130
4131 let just2 = (CONS1_ENGINE_ID, vec![6, 7]);
4132 assert!(matches!(
4133 backend.append_justification(block1, just2),
4134 Err(ClientError::BadJustification(_))
4135 ));
4136
4137 let justifications = {
4138 let mut just = Justifications::from(just0);
4139 just.append(just1);
4140 just
4141 };
4142 assert_eq!(backend.blockchain().justifications(block1).unwrap(), Some(justifications),);
4143 }
4144
4145 #[test]
4146 fn test_finalize_multiple_blocks_in_single_op() {
4147 let backend = Backend::<Block>::new_test(10, 10);
4148
4149 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
4150 let block1 = insert_header(&backend, 1, block0, None, Default::default());
4151 let block2 = insert_header(&backend, 2, block1, None, Default::default());
4152 let block3 = insert_header(&backend, 3, block2, None, Default::default());
4153 let block4 = insert_header(&backend, 4, block3, None, Default::default());
4154 {
4155 let mut op = backend.begin_operation().unwrap();
4156 backend.begin_state_operation(&mut op, block0).unwrap();
4157 op.mark_finalized(block1, None).unwrap();
4158 op.mark_finalized(block2, None).unwrap();
4159 backend.commit_operation(op).unwrap();
4160 }
4161 {
4162 let mut op = backend.begin_operation().unwrap();
4163 backend.begin_state_operation(&mut op, block2).unwrap();
4164 op.mark_finalized(block3, None).unwrap();
4165 op.mark_finalized(block4, None).unwrap();
4166 backend.commit_operation(op).unwrap();
4167 }
4168 }
4169
4170 #[test]
4171 fn storage_hash_is_cached_correctly() {
4172 let state_version = StateVersion::default();
4173 let backend = Backend::<Block>::new_test(10, 10);
4174
4175 let hash0 = {
4176 let mut op = backend.begin_operation().unwrap();
4177 backend.begin_state_operation(&mut op, Default::default()).unwrap();
4178 let mut header = Header {
4179 number: 0,
4180 parent_hash: Default::default(),
4181 state_root: Default::default(),
4182 digest: Default::default(),
4183 extrinsics_root: Default::default(),
4184 };
4185
4186 let storage = vec![(b"test".to_vec(), b"test".to_vec())];
4187
4188 header.state_root = op
4189 .old_state
4190 .storage_root(storage.iter().map(|(x, y)| (&x[..], Some(&y[..]))), state_version)
4191 .0
4192 .into();
4193 let hash = header.hash();
4194
4195 op.reset_storage(
4196 Storage {
4197 top: storage.into_iter().collect(),
4198 children_default: Default::default(),
4199 },
4200 state_version,
4201 )
4202 .unwrap();
4203 op.set_block_data(header.clone(), Some(vec![]), None, None, NewBlockState::Best, true)
4204 .unwrap();
4205
4206 backend.commit_operation(op).unwrap();
4207
4208 hash
4209 };
4210
4211 let block0_hash = backend
4212 .state_at(hash0, TrieCacheContext::Untrusted)
4213 .unwrap()
4214 .storage_hash(&b"test"[..])
4215 .unwrap();
4216
4217 let hash1 = {
4218 let mut op = backend.begin_operation().unwrap();
4219 backend.begin_state_operation(&mut op, hash0).unwrap();
4220 let mut header = Header {
4221 number: 1,
4222 parent_hash: hash0,
4223 state_root: Default::default(),
4224 digest: Default::default(),
4225 extrinsics_root: Default::default(),
4226 };
4227
4228 let storage = vec![(b"test".to_vec(), Some(b"test2".to_vec()))];
4229
4230 let (root, overlay) = op.old_state.storage_root(
4231 storage.iter().map(|(k, v)| (k.as_slice(), v.as_ref().map(|v| &v[..]))),
4232 state_version,
4233 );
4234 op.update_db_storage(overlay).unwrap();
4235 header.state_root = root.into();
4236 let hash = header.hash();
4237
4238 op.update_storage(storage, Vec::new()).unwrap();
4239 op.set_block_data(header, Some(vec![]), None, None, NewBlockState::Normal, true)
4240 .unwrap();
4241
4242 backend.commit_operation(op).unwrap();
4243
4244 hash
4245 };
4246
4247 {
4248 let header = backend.blockchain().header(hash1).unwrap().unwrap();
4249 let mut op = backend.begin_operation().unwrap();
4250 op.set_block_data(header, None, None, None, NewBlockState::Best, true).unwrap();
4251 backend.commit_operation(op).unwrap();
4252 }
4253
4254 let block1_hash = backend
4255 .state_at(hash1, TrieCacheContext::Untrusted)
4256 .unwrap()
4257 .storage_hash(&b"test"[..])
4258 .unwrap();
4259
4260 assert_ne!(block0_hash, block1_hash);
4261 }
4262
4263 #[test]
4264 fn test_finalize_non_sequential() {
4265 let backend = Backend::<Block>::new_test(10, 10);
4266
4267 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
4268 let block1 = insert_header(&backend, 1, block0, None, Default::default());
4269 let block2 = insert_header(&backend, 2, block1, None, Default::default());
4270 {
4271 let mut op = backend.begin_operation().unwrap();
4272 backend.begin_state_operation(&mut op, block0).unwrap();
4273 op.mark_finalized(block2, None).unwrap();
4274 backend.commit_operation(op).unwrap_err();
4275 }
4276 }
4277
4278 #[test]
4279 fn prune_blocks_on_finalize() {
4280 let pruning_modes =
4281 vec![BlocksPruning::Some(2), BlocksPruning::KeepFinalized, BlocksPruning::KeepAll];
4282
4283 for pruning_mode in pruning_modes {
4284 let backend = Backend::<Block>::new_test_with_tx_storage(pruning_mode, 0);
4285 let mut blocks = Vec::new();
4286 let mut prev_hash = Default::default();
4287 for i in 0..5 {
4288 let hash = insert_block(
4289 &backend,
4290 i,
4291 prev_hash,
4292 None,
4293 Default::default(),
4294 vec![UncheckedXt::new_transaction(i.into(), ())],
4295 None,
4296 )
4297 .unwrap();
4298 blocks.push(hash);
4299 prev_hash = hash;
4300 }
4301
4302 {
4303 let mut op = backend.begin_operation().unwrap();
4304 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
4305 for i in 1..5 {
4306 op.mark_finalized(blocks[i], None).unwrap();
4307 }
4308 backend.commit_operation(op).unwrap();
4309 }
4310 let bc = backend.blockchain();
4311
4312 if matches!(pruning_mode, BlocksPruning::Some(_)) {
4313 assert_eq!(None, bc.body(blocks[0]).unwrap());
4314 assert_eq!(None, bc.body(blocks[1]).unwrap());
4315 assert_eq!(None, bc.body(blocks[2]).unwrap());
4316 assert_eq!(
4317 Some(vec![UncheckedXt::new_transaction(3.into(), ())]),
4318 bc.body(blocks[3]).unwrap()
4319 );
4320 assert_eq!(
4321 Some(vec![UncheckedXt::new_transaction(4.into(), ())]),
4322 bc.body(blocks[4]).unwrap()
4323 );
4324 } else {
4325 for i in 0..5 {
4326 assert_eq!(
4327 Some(vec![UncheckedXt::new_transaction((i as u64).into(), ())]),
4328 bc.body(blocks[i]).unwrap()
4329 );
4330 }
4331 }
4332 }
4333 }
4334
4335 #[test]
4336 fn prune_blocks_on_finalize_with_fork() {
4337 sp_tracing::try_init_simple();
4338
4339 let pruning_modes =
4340 vec![BlocksPruning::Some(2), BlocksPruning::KeepFinalized, BlocksPruning::KeepAll];
4341
4342 for pruning in pruning_modes {
4343 let backend = Backend::<Block>::new_test_with_tx_storage(pruning, 10);
4344 let mut blocks = Vec::new();
4345 let mut prev_hash = Default::default();
4346 for i in 0..5 {
4347 let hash = insert_block(
4348 &backend,
4349 i,
4350 prev_hash,
4351 None,
4352 Default::default(),
4353 vec![UncheckedXt::new_transaction(i.into(), ())],
4354 None,
4355 )
4356 .unwrap();
4357 blocks.push(hash);
4358 prev_hash = hash;
4359 }
4360
4361 let fork_hash_root = insert_block(
4363 &backend,
4364 2,
4365 blocks[1],
4366 None,
4367 H256::random(),
4368 vec![UncheckedXt::new_transaction(2.into(), ())],
4369 None,
4370 )
4371 .unwrap();
4372 insert_block(
4373 &backend,
4374 3,
4375 fork_hash_root,
4376 None,
4377 H256::random(),
4378 vec![
4379 UncheckedXt::new_transaction(3.into(), ()),
4380 UncheckedXt::new_transaction(11.into(), ()),
4381 ],
4382 None,
4383 )
4384 .unwrap();
4385 let mut op = backend.begin_operation().unwrap();
4386 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
4387 op.mark_head(blocks[4]).unwrap();
4388 backend.commit_operation(op).unwrap();
4389
4390 let bc = backend.blockchain();
4391 assert_eq!(
4392 Some(vec![UncheckedXt::new_transaction(2.into(), ())]),
4393 bc.body(fork_hash_root).unwrap()
4394 );
4395
4396 for i in 1..5 {
4397 let mut op = backend.begin_operation().unwrap();
4398 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
4399 op.mark_finalized(blocks[i], None).unwrap();
4400 backend.commit_operation(op).unwrap();
4401 }
4402
4403 if matches!(pruning, BlocksPruning::Some(_)) {
4404 assert_eq!(None, bc.body(blocks[0]).unwrap());
4405 assert_eq!(None, bc.body(blocks[1]).unwrap());
4406 assert_eq!(None, bc.body(blocks[2]).unwrap());
4407
4408 assert_eq!(
4409 Some(vec![UncheckedXt::new_transaction(3.into(), ())]),
4410 bc.body(blocks[3]).unwrap()
4411 );
4412 assert_eq!(
4413 Some(vec![UncheckedXt::new_transaction(4.into(), ())]),
4414 bc.body(blocks[4]).unwrap()
4415 );
4416 } else {
4417 for i in 0..5 {
4418 assert_eq!(
4419 Some(vec![UncheckedXt::new_transaction((i as u64).into(), ())]),
4420 bc.body(blocks[i]).unwrap()
4421 );
4422 }
4423 }
4424
4425 if matches!(pruning, BlocksPruning::KeepAll) {
4426 assert_eq!(
4427 Some(vec![UncheckedXt::new_transaction(2.into(), ())]),
4428 bc.body(fork_hash_root).unwrap()
4429 );
4430 } else {
4431 assert_eq!(None, bc.body(fork_hash_root).unwrap());
4432 }
4433
4434 assert_eq!(bc.info().best_number, 4);
4435 for i in 0..5 {
4436 assert!(bc.hash(i).unwrap().is_some());
4437 }
4438 }
4439 }
4440
4441 #[test]
4442 fn prune_blocks_on_finalize_and_reorg() {
4443 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(10), 10);
4448
4449 let make_block = |index, parent, val: u64| {
4450 insert_block(
4451 &backend,
4452 index,
4453 parent,
4454 None,
4455 H256::random(),
4456 vec![UncheckedXt::new_transaction(val.into(), ())],
4457 None,
4458 )
4459 .unwrap()
4460 };
4461
4462 let block_0 = make_block(0, Default::default(), 0x00);
4463 let block_1a = make_block(1, block_0, 0x1a);
4464 let block_1b = make_block(1, block_0, 0x1b);
4465 let block_2a = make_block(2, block_1a, 0x2a);
4466 let block_2b = make_block(2, block_1a, 0x2b);
4467 let block_3a = make_block(3, block_2a, 0x3a);
4468
4469 let mut op = backend.begin_operation().unwrap();
4471 backend.begin_state_operation(&mut op, block_0).unwrap();
4472 op.mark_head(block_1b).unwrap();
4473 backend.commit_operation(op).unwrap();
4474
4475 let mut op = backend.begin_operation().unwrap();
4477 backend.begin_state_operation(&mut op, block_0).unwrap();
4478 op.mark_head(block_3a).unwrap();
4479 op.mark_finalized(block_1a, None).unwrap();
4480 op.mark_finalized(block_2a, None).unwrap();
4481 op.mark_finalized(block_3a, None).unwrap();
4482 backend.commit_operation(op).unwrap();
4483
4484 let bc = backend.blockchain();
4485 assert_eq!(None, bc.body(block_1b).unwrap());
4486 assert_eq!(None, bc.body(block_2b).unwrap());
4487 assert_eq!(
4488 Some(vec![UncheckedXt::new_transaction(0x00.into(), ())]),
4489 bc.body(block_0).unwrap()
4490 );
4491 assert_eq!(
4492 Some(vec![UncheckedXt::new_transaction(0x1a.into(), ())]),
4493 bc.body(block_1a).unwrap()
4494 );
4495 assert_eq!(
4496 Some(vec![UncheckedXt::new_transaction(0x2a.into(), ())]),
4497 bc.body(block_2a).unwrap()
4498 );
4499 assert_eq!(
4500 Some(vec![UncheckedXt::new_transaction(0x3a.into(), ())]),
4501 bc.body(block_3a).unwrap()
4502 );
4503 }
4504
4505 #[test]
4506 fn indexed_data_block_body() {
4507 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(1), 10);
4508
4509 let x0 = UncheckedXt::new_transaction(0.into(), ()).encode();
4510 let x1 = UncheckedXt::new_transaction(1.into(), ()).encode();
4511 let x0_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x0[1..]);
4512 let x1_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x1[1..]);
4513 let index = vec![
4514 IndexOperation::Insert {
4515 extrinsic: 0,
4516 hash: x0_hash.as_ref().to_vec(),
4517 size: (x0.len() - 1) as u32,
4518 },
4519 IndexOperation::Insert {
4520 extrinsic: 1,
4521 hash: x1_hash.as_ref().to_vec(),
4522 size: (x1.len() - 1) as u32,
4523 },
4524 ];
4525 let hash = insert_block(
4526 &backend,
4527 0,
4528 Default::default(),
4529 None,
4530 Default::default(),
4531 vec![
4532 UncheckedXt::new_transaction(0.into(), ()),
4533 UncheckedXt::new_transaction(1.into(), ()),
4534 ],
4535 Some(index),
4536 )
4537 .unwrap();
4538 let bc = backend.blockchain();
4539 assert_eq!(bc.indexed_transaction(x0_hash).unwrap().unwrap(), &x0[1..]);
4540 assert_eq!(bc.indexed_transaction(x1_hash).unwrap().unwrap(), &x1[1..]);
4541
4542 let hashof0 = bc.info().genesis_hash;
4543 let block1 =
4545 insert_block(&backend, 1, hash, None, Default::default(), vec![], None).unwrap();
4546 backend.finalize_block(block1, None).unwrap();
4547 assert_eq!(bc.body(hashof0).unwrap(), None);
4548 assert_eq!(bc.indexed_transaction(x0_hash).unwrap(), None);
4549 assert_eq!(bc.indexed_transaction(x1_hash).unwrap(), None);
4550 }
4551
4552 #[test]
4553 fn index_invalid_size() {
4554 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(1), 10);
4555
4556 let x0 = UncheckedXt::new_transaction(0.into(), ()).encode();
4557 let x1 = UncheckedXt::new_transaction(1.into(), ()).encode();
4558
4559 let x0_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x0[..]);
4560 let x1_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x1[..]);
4561 let index = vec![
4562 IndexOperation::Insert {
4563 extrinsic: 0,
4564 hash: x0_hash.as_ref().to_vec(),
4565 size: (x0.len()) as u32,
4566 },
4567 IndexOperation::Insert {
4568 extrinsic: 1,
4569 hash: x1_hash.as_ref().to_vec(),
4570 size: (x1.len() + 1) as u32,
4571 },
4572 ];
4573 insert_block(
4574 &backend,
4575 0,
4576 Default::default(),
4577 None,
4578 Default::default(),
4579 vec![
4580 UncheckedXt::new_transaction(0.into(), ()),
4581 UncheckedXt::new_transaction(1.into(), ()),
4582 ],
4583 Some(index),
4584 )
4585 .unwrap();
4586 let bc = backend.blockchain();
4587 assert_eq!(bc.indexed_transaction(x0_hash).unwrap().unwrap(), &x0[..]);
4588 assert_eq!(bc.indexed_transaction(x1_hash).unwrap(), None);
4589 }
4590
4591 #[test]
4592 fn renew_transaction_storage() {
4593 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(2), 10);
4594 let mut blocks = Vec::new();
4595 let mut prev_hash = Default::default();
4596 let x1 = UncheckedXt::new_transaction(0.into(), ()).encode();
4597 let x1_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x1[1..]);
4598 for i in 0..10 {
4599 let mut index = Vec::new();
4600 if i == 0 {
4601 index.push(IndexOperation::Insert {
4602 extrinsic: 0,
4603 hash: x1_hash.as_ref().to_vec(),
4604 size: (x1.len() - 1) as u32,
4605 });
4606 } else if i < 5 {
4607 index.push(IndexOperation::Renew { extrinsic: 0, hash: x1_hash.as_ref().to_vec() });
4609 } let hash = insert_block(
4611 &backend,
4612 i,
4613 prev_hash,
4614 None,
4615 Default::default(),
4616 vec![UncheckedXt::new_transaction(i.into(), ())],
4617 Some(index),
4618 )
4619 .unwrap();
4620 blocks.push(hash);
4621 prev_hash = hash;
4622 }
4623
4624 for i in 1..10 {
4625 let mut op = backend.begin_operation().unwrap();
4626 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
4627 op.mark_finalized(blocks[i], None).unwrap();
4628 backend.commit_operation(op).unwrap();
4629 let bc = backend.blockchain();
4630 if i < 6 {
4631 assert!(bc.indexed_transaction(x1_hash).unwrap().is_some());
4632 } else {
4633 assert!(bc.indexed_transaction(x1_hash).unwrap().is_none());
4634 }
4635 }
4636 }
4637
4638 #[test]
4639 fn multi_renew_transaction_storage() {
4640 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(2), 10);
4643 let mut blocks = Vec::new();
4644 let mut prev_hash = Default::default();
4645
4646 let x1 = UncheckedXt::new_transaction(0.into(), ()).encode();
4648 let x2 = UncheckedXt::new_transaction(1.into(), ()).encode();
4649 let x1_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x1[1..]);
4650 let x2_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x2[1..]);
4651
4652 for i in 0..10 {
4653 let mut index = Vec::new();
4654 if i == 0 {
4655 index.push(IndexOperation::Insert {
4657 extrinsic: 0,
4658 hash: x1_hash.as_ref().to_vec(),
4659 size: (x1.len() - 1) as u32,
4660 });
4661 index.push(IndexOperation::Insert {
4662 extrinsic: 1,
4663 hash: x2_hash.as_ref().to_vec(),
4664 size: (x2.len() - 1) as u32,
4665 });
4666 } else if i < 5 {
4667 index.push(IndexOperation::Renew { extrinsic: 0, hash: x1_hash.as_ref().to_vec() });
4669 index.push(IndexOperation::Renew { extrinsic: 0, hash: x2_hash.as_ref().to_vec() });
4670 }
4671 let body = if i == 0 {
4674 vec![
4675 UncheckedXt::new_transaction(0.into(), ()),
4676 UncheckedXt::new_transaction(1.into(), ()),
4677 ]
4678 } else {
4679 vec![UncheckedXt::new_transaction(i.into(), ())]
4680 };
4681 let hash =
4682 insert_block(&backend, i, prev_hash, None, Default::default(), body, Some(index))
4683 .unwrap();
4684 blocks.push(hash);
4685 prev_hash = hash;
4686 }
4687
4688 for i in 1..10 {
4690 let mut op = backend.begin_operation().unwrap();
4691 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
4692 op.mark_finalized(blocks[i], None).unwrap();
4693 backend.commit_operation(op).unwrap();
4694 let bc = backend.blockchain();
4695 if i < 6 {
4696 assert!(
4697 bc.indexed_transaction(x1_hash).unwrap().is_some(),
4698 "x1 should exist at finalization step {i}"
4699 );
4700 assert!(
4701 bc.indexed_transaction(x2_hash).unwrap().is_some(),
4702 "x2 should exist at finalization step {i}"
4703 );
4704 } else {
4705 assert!(
4706 bc.indexed_transaction(x1_hash).unwrap().is_none(),
4707 "x1 should be pruned at finalization step {i}"
4708 );
4709 assert!(
4710 bc.indexed_transaction(x2_hash).unwrap().is_none(),
4711 "x2 should be pruned at finalization step {i}"
4712 );
4713 }
4714 }
4715 }
4716
4717 #[test]
4718 fn multi_renew_block_indexed_body() {
4719 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(10), 10);
4721
4722 let x1 = UncheckedXt::new_transaction(0.into(), ()).encode();
4723 let x2 = UncheckedXt::new_transaction(1.into(), ()).encode();
4724 let x1_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x1[1..]);
4725 let x2_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x2[1..]);
4726
4727 let block0 = insert_block(
4729 &backend,
4730 0,
4731 Default::default(),
4732 None,
4733 Default::default(),
4734 vec![
4735 UncheckedXt::new_transaction(0.into(), ()),
4736 UncheckedXt::new_transaction(1.into(), ()),
4737 ],
4738 Some(vec![
4739 IndexOperation::Insert {
4740 extrinsic: 0,
4741 hash: x1_hash.as_ref().to_vec(),
4742 size: (x1.len() - 1) as u32,
4743 },
4744 IndexOperation::Insert {
4745 extrinsic: 1,
4746 hash: x2_hash.as_ref().to_vec(),
4747 size: (x2.len() - 1) as u32,
4748 },
4749 ]),
4750 )
4751 .unwrap();
4752
4753 let block1 = insert_block(
4755 &backend,
4756 1,
4757 block0,
4758 None,
4759 Default::default(),
4760 vec![UncheckedXt::new_transaction(10.into(), ())],
4761 Some(vec![
4762 IndexOperation::Renew { extrinsic: 0, hash: x1_hash.as_ref().to_vec() },
4763 IndexOperation::Renew { extrinsic: 0, hash: x2_hash.as_ref().to_vec() },
4764 ]),
4765 )
4766 .unwrap();
4767
4768 let bc = backend.blockchain();
4769 let indexed_body = bc.block_indexed_body(block1).unwrap().unwrap();
4770 assert_eq!(indexed_body.len(), 2, "Should have 2 indexed data blobs");
4771 assert_eq!(&indexed_body[0][..], &x1[1..]);
4772 assert_eq!(&indexed_body[1][..], &x2[1..]);
4773 }
4774
4775 #[test]
4776 fn multi_renew_prune_releases_all() {
4777 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(2), 10);
4781 let mut blocks = Vec::new();
4782 let mut prev_hash = Default::default();
4783
4784 let x1 = UncheckedXt::new_transaction(0.into(), ()).encode();
4785 let x2 = UncheckedXt::new_transaction(1.into(), ()).encode();
4786 let x1_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x1[1..]);
4787 let x2_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x2[1..]);
4788
4789 for i in 0..6 {
4790 let mut index = Vec::new();
4791 let body = if i == 0 {
4792 index.push(IndexOperation::Insert {
4794 extrinsic: 0,
4795 hash: x1_hash.as_ref().to_vec(),
4796 size: (x1.len() - 1) as u32,
4797 });
4798 index.push(IndexOperation::Insert {
4799 extrinsic: 1,
4800 hash: x2_hash.as_ref().to_vec(),
4801 size: (x2.len() - 1) as u32,
4802 });
4803 vec![
4804 UncheckedXt::new_transaction(0.into(), ()),
4805 UncheckedXt::new_transaction(1.into(), ()),
4806 ]
4807 } else if i == 1 {
4808 index.push(IndexOperation::Renew { extrinsic: 0, hash: x1_hash.as_ref().to_vec() });
4810 index.push(IndexOperation::Renew { extrinsic: 0, hash: x2_hash.as_ref().to_vec() });
4811 vec![UncheckedXt::new_transaction(10.into(), ())]
4812 } else {
4813 vec![UncheckedXt::new_transaction(i.into(), ())]
4815 };
4816 let hash =
4817 insert_block(&backend, i, prev_hash, None, Default::default(), body, Some(index))
4818 .unwrap();
4819 blocks.push(hash);
4820 prev_hash = hash;
4821 }
4822
4823 let bc = backend.blockchain();
4824 assert!(bc.indexed_transaction(x1_hash).unwrap().is_some());
4826 assert!(bc.indexed_transaction(x2_hash).unwrap().is_some());
4827
4828 for i in 1..6 {
4830 let mut op = backend.begin_operation().unwrap();
4831 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
4832 op.mark_finalized(blocks[i], None).unwrap();
4833 backend.commit_operation(op).unwrap();
4834 }
4835
4836 assert!(
4839 bc.indexed_transaction(x1_hash).unwrap().is_none(),
4840 "x1 should be gone after all referring blocks are pruned"
4841 );
4842 assert!(
4843 bc.indexed_transaction(x2_hash).unwrap().is_none(),
4844 "x2 should be gone after all referring blocks are pruned"
4845 );
4846 }
4847
4848 #[test]
4849 fn multi_renew_body_reconstruction() {
4850 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(10), 10);
4852
4853 let x1 = UncheckedXt::new_transaction(0.into(), ()).encode();
4854 let x2 = UncheckedXt::new_transaction(1.into(), ()).encode();
4855 let x1_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x1[1..]);
4856 let x2_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x2[1..]);
4857
4858 let block0 = insert_block(
4860 &backend,
4861 0,
4862 Default::default(),
4863 None,
4864 Default::default(),
4865 vec![
4866 UncheckedXt::new_transaction(0.into(), ()),
4867 UncheckedXt::new_transaction(1.into(), ()),
4868 ],
4869 Some(vec![
4870 IndexOperation::Insert {
4871 extrinsic: 0,
4872 hash: x1_hash.as_ref().to_vec(),
4873 size: (x1.len() - 1) as u32,
4874 },
4875 IndexOperation::Insert {
4876 extrinsic: 1,
4877 hash: x2_hash.as_ref().to_vec(),
4878 size: (x2.len() - 1) as u32,
4879 },
4880 ]),
4881 )
4882 .unwrap();
4883
4884 let renew_xt = UncheckedXt::new_transaction(10.into(), ());
4886 let block1 = insert_block(
4887 &backend,
4888 1,
4889 block0,
4890 None,
4891 Default::default(),
4892 vec![renew_xt.clone()],
4893 Some(vec![
4894 IndexOperation::Renew { extrinsic: 0, hash: x1_hash.as_ref().to_vec() },
4895 IndexOperation::Renew { extrinsic: 0, hash: x2_hash.as_ref().to_vec() },
4896 ]),
4897 )
4898 .unwrap();
4899
4900 let bc = backend.blockchain();
4902 let body = bc.body(block1).unwrap().unwrap();
4903 assert_eq!(body.len(), 1, "Block 1 has one extrinsic");
4904 assert_eq!(body[0], renew_xt, "Extrinsic should be reconstructed correctly");
4905 }
4906
4907 #[test]
4908 fn single_renew_backwards_compatible() {
4909 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(2), 10);
4912 let mut prev_hash = Default::default();
4913
4914 let x1 = UncheckedXt::new_transaction(0.into(), ()).encode();
4915 let x1_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x1[1..]);
4916
4917 let block0 = insert_block(
4919 &backend,
4920 0,
4921 prev_hash,
4922 None,
4923 Default::default(),
4924 vec![UncheckedXt::new_transaction(0.into(), ())],
4925 Some(vec![IndexOperation::Insert {
4926 extrinsic: 0,
4927 hash: x1_hash.as_ref().to_vec(),
4928 size: (x1.len() - 1) as u32,
4929 }]),
4930 )
4931 .unwrap();
4932 prev_hash = block0;
4933
4934 let block1 = insert_block(
4936 &backend,
4937 1,
4938 prev_hash,
4939 None,
4940 Default::default(),
4941 vec![UncheckedXt::new_transaction(1.into(), ())],
4942 Some(vec![IndexOperation::Renew { extrinsic: 0, hash: x1_hash.as_ref().to_vec() }]),
4943 )
4944 .unwrap();
4945
4946 let bc = backend.blockchain();
4948 assert!(bc.indexed_transaction(x1_hash).unwrap().is_some());
4949
4950 let body = bc.body(block1).unwrap().unwrap();
4952 assert_eq!(body.len(), 1);
4953 assert_eq!(body[0], UncheckedXt::new_transaction(1.into(), ()));
4954
4955 let indexed = bc.block_indexed_body(block1).unwrap().unwrap();
4957 assert_eq!(indexed.len(), 1);
4958 assert_eq!(&indexed[0][..], &x1[1..]);
4959 }
4960
4961 #[test]
4962 fn multi_renew_duplicate_hash_balanced_lifecycle() {
4963 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(2), 10);
4964 let mut blocks = Vec::new();
4965 let mut prev_hash = Default::default();
4966
4967 let x1 = UncheckedXt::new_transaction(0.into(), ()).encode();
4968 let x1_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x1[1..]);
4969
4970 for i in 0..6 {
4971 let mut index = Vec::new();
4972 let body = if i == 0 {
4973 index.push(IndexOperation::Insert {
4974 extrinsic: 0,
4975 hash: x1_hash.as_ref().to_vec(),
4976 size: (x1.len() - 1) as u32,
4977 });
4978 vec![UncheckedXt::new_transaction(0.into(), ())]
4979 } else if i == 1 {
4980 index.push(IndexOperation::Renew { extrinsic: 0, hash: x1_hash.as_ref().to_vec() });
4981 index.push(IndexOperation::Renew { extrinsic: 0, hash: x1_hash.as_ref().to_vec() });
4982 vec![UncheckedXt::new_transaction(10.into(), ())]
4983 } else {
4984 vec![UncheckedXt::new_transaction(i.into(), ())]
4985 };
4986 let hash =
4987 insert_block(&backend, i, prev_hash, None, Default::default(), body, Some(index))
4988 .unwrap();
4989 blocks.push(hash);
4990 prev_hash = hash;
4991 }
4992
4993 let bc = backend.blockchain();
4994 assert!(bc.indexed_transaction(x1_hash).unwrap().is_some());
4995
4996 for i in 1..6 {
4997 let mut op = backend.begin_operation().unwrap();
4998 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
4999 op.mark_finalized(blocks[i], None).unwrap();
5000 backend.commit_operation(op).unwrap();
5001 }
5002
5003 assert!(bc.indexed_transaction(x1_hash).unwrap().is_none());
5004 }
5005
5006 #[test]
5007 fn multi_renew_mixed_duplicates_and_uniques() {
5008 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(2), 10);
5010 let mut blocks = Vec::new();
5011 let mut prev_hash = Default::default();
5012
5013 let w = UncheckedXt::new_transaction(0.into(), ()).encode();
5014 let x = UncheckedXt::new_transaction(1.into(), ()).encode();
5015 let y = UncheckedXt::new_transaction(2.into(), ()).encode();
5016 let z = UncheckedXt::new_transaction(3.into(), ()).encode();
5017 let w_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&w[1..]);
5018 let x_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x[1..]);
5019 let y_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&y[1..]);
5020 let z_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&z[1..]);
5021
5022 for i in 0..6 {
5023 let mut index = Vec::new();
5024 let body = if i == 0 {
5025 index.push(IndexOperation::Insert {
5026 extrinsic: 0,
5027 hash: w_hash.as_ref().to_vec(),
5028 size: (w.len() - 1) as u32,
5029 });
5030 index.push(IndexOperation::Insert {
5031 extrinsic: 1,
5032 hash: x_hash.as_ref().to_vec(),
5033 size: (x.len() - 1) as u32,
5034 });
5035 index.push(IndexOperation::Insert {
5036 extrinsic: 2,
5037 hash: y_hash.as_ref().to_vec(),
5038 size: (y.len() - 1) as u32,
5039 });
5040 index.push(IndexOperation::Insert {
5041 extrinsic: 3,
5042 hash: z_hash.as_ref().to_vec(),
5043 size: (z.len() - 1) as u32,
5044 });
5045 vec![
5046 UncheckedXt::new_transaction(0.into(), ()),
5047 UncheckedXt::new_transaction(1.into(), ()),
5048 UncheckedXt::new_transaction(2.into(), ()),
5049 UncheckedXt::new_transaction(3.into(), ()),
5050 ]
5051 } else if i == 1 {
5052 index.push(IndexOperation::Renew { extrinsic: 0, hash: w_hash.as_ref().to_vec() });
5054 index.push(IndexOperation::Renew { extrinsic: 0, hash: x_hash.as_ref().to_vec() });
5055 index.push(IndexOperation::Renew { extrinsic: 0, hash: y_hash.as_ref().to_vec() });
5056 index.push(IndexOperation::Renew { extrinsic: 0, hash: w_hash.as_ref().to_vec() });
5057 index.push(IndexOperation::Renew { extrinsic: 0, hash: z_hash.as_ref().to_vec() });
5058 vec![UncheckedXt::new_transaction(10.into(), ())]
5059 } else {
5060 vec![UncheckedXt::new_transaction(i.into(), ())]
5061 };
5062 let hash =
5063 insert_block(&backend, i, prev_hash, None, Default::default(), body, Some(index))
5064 .unwrap();
5065 blocks.push(hash);
5066 prev_hash = hash;
5067 }
5068
5069 let bc = backend.blockchain();
5070
5071 let indexed_body = bc.block_indexed_body(blocks[1]).unwrap().unwrap();
5072 assert_eq!(indexed_body.len(), 5);
5073 assert_eq!(&indexed_body[0][..], &w[1..]);
5074 assert_eq!(&indexed_body[1][..], &x[1..]);
5075 assert_eq!(&indexed_body[2][..], &y[1..]);
5076 assert_eq!(&indexed_body[3][..], &w[1..]);
5077 assert_eq!(&indexed_body[4][..], &z[1..]);
5078
5079 for i in 1..6 {
5080 let mut op = backend.begin_operation().unwrap();
5081 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
5082 op.mark_finalized(blocks[i], None).unwrap();
5083 backend.commit_operation(op).unwrap();
5084 }
5085
5086 assert!(bc.indexed_transaction(w_hash).unwrap().is_none(), "W deleted");
5087 assert!(bc.indexed_transaction(x_hash).unwrap().is_none(), "X deleted");
5088 assert!(bc.indexed_transaction(y_hash).unwrap().is_none(), "Y deleted");
5089 assert!(bc.indexed_transaction(z_hash).unwrap().is_none(), "Z deleted");
5090 }
5091
5092 #[test]
5093 fn block_indexed_body_preserves_renew_op_submission_order() {
5094 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::KeepAll, 10);
5098
5099 let payloads: Vec<Vec<u8>> = (0..5)
5100 .map(|i: u64| UncheckedXt::new_transaction(i.into(), ()).encode())
5101 .collect();
5102 let hashes: Vec<<HashingFor<Block> as sp_core::Hasher>::Out> = payloads
5103 .iter()
5104 .map(|p| <HashingFor<Block> as sp_core::Hasher>::hash(&p[1..]))
5105 .collect();
5106
5107 let mut prev_hash = Default::default();
5108 let insert_ops: Vec<IndexOperation> = (0..5)
5109 .map(|i| IndexOperation::Insert {
5110 extrinsic: i as u32,
5111 hash: hashes[i].as_ref().to_vec(),
5112 size: (payloads[i].len() - 1) as u32,
5113 })
5114 .collect();
5115 let body0: Vec<UncheckedXt> =
5116 (0..5).map(|i| UncheckedXt::new_transaction((i as u64).into(), ())).collect();
5117 prev_hash =
5118 insert_block(&backend, 0, prev_hash, None, Default::default(), body0, Some(insert_ops))
5119 .unwrap();
5120
5121 let submission_order = [4usize, 1, 0, 3, 2];
5123 let renew_ops: Vec<IndexOperation> = submission_order
5124 .iter()
5125 .map(|&i| IndexOperation::Renew { extrinsic: 0, hash: hashes[i].as_ref().to_vec() })
5126 .collect();
5127 let block1 = insert_block(
5128 &backend,
5129 1,
5130 prev_hash,
5131 None,
5132 Default::default(),
5133 vec![UncheckedXt::new_transaction(100.into(), ())],
5134 Some(renew_ops),
5135 )
5136 .unwrap();
5137
5138 let bc = backend.blockchain();
5139 let body_index_bytes = read_db(
5140 &*backend.storage.db,
5141 columns::KEY_LOOKUP,
5142 columns::BODY_INDEX,
5143 BlockId::<Block>::Hash(block1),
5144 )
5145 .unwrap()
5146 .expect("block 1 must have a BODY_INDEX entry");
5147 let decoded: Vec<DbExtrinsic<Block>> =
5148 Decode::decode(&mut &body_index_bytes[..]).expect("must decode");
5149 assert_eq!(decoded.len(), 1);
5150 match &decoded[0] {
5151 DbExtrinsic::MultiRenew { hashes: stored_hashes, .. } => {
5152 assert_eq!(stored_hashes.len(), 5);
5153 for (i, &order_idx) in submission_order.iter().enumerate() {
5154 assert_eq!(stored_hashes[i].as_ref(), hashes[order_idx].as_ref());
5155 }
5156 },
5157 other => panic!("expected MultiRenew; got {other:?}"),
5158 }
5159
5160 let blobs = bc.block_indexed_body(block1).unwrap().unwrap();
5161 assert_eq!(blobs.len(), 5);
5162 for (i, &order_idx) in submission_order.iter().enumerate() {
5163 assert_eq!(blobs[i].as_slice(), &payloads[order_idx][1..]);
5164 }
5165 }
5166
5167 #[test]
5168 fn insert_and_renew_same_index_renew_wins() {
5169 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(10), 10);
5174
5175 let x = UncheckedXt::new_transaction(0.into(), ()).encode();
5176 let y = UncheckedXt::new_transaction(1.into(), ()).encode();
5177 let x_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x[1..]);
5178 let y_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&y[1..]);
5179
5180 let block0 = insert_block(
5182 &backend,
5183 0,
5184 Default::default(),
5185 None,
5186 Default::default(),
5187 vec![UncheckedXt::new_transaction(0.into(), ())],
5188 Some(vec![IndexOperation::Insert {
5189 extrinsic: 0,
5190 hash: x_hash.as_ref().to_vec(),
5191 size: (x.len() - 1) as u32,
5192 }]),
5193 )
5194 .unwrap();
5195
5196 let block1 = insert_block(
5199 &backend,
5200 1,
5201 block0,
5202 None,
5203 Default::default(),
5204 vec![UncheckedXt::new_transaction(99.into(), ())],
5205 Some(vec![
5206 IndexOperation::Insert {
5207 extrinsic: 0,
5208 hash: y_hash.as_ref().to_vec(),
5209 size: (y.len() - 1) as u32,
5210 },
5211 IndexOperation::Renew { extrinsic: 0, hash: x_hash.as_ref().to_vec() },
5212 ]),
5213 )
5214 .unwrap();
5215
5216 let bc = backend.blockchain();
5217
5218 assert!(bc.indexed_transaction(x_hash).unwrap().is_some());
5219 assert!(
5220 bc.indexed_transaction(y_hash).unwrap().is_none(),
5221 "Insert at the same extrinsic index as a Renew is silently dropped",
5222 );
5223
5224 let indexed = bc.block_indexed_body(block1).unwrap().unwrap();
5225 assert_eq!(indexed.len(), 1);
5226 assert_eq!(&indexed[0][..], &x[1..]);
5227 }
5228
5229 #[test]
5230 fn db_extrinsic_encoding_round_trip() {
5231 let entries: Vec<DbExtrinsic<Block>> = vec![
5232 DbExtrinsic::Indexed { hash: H256::repeat_byte(0xAA), header: vec![0x01, 0x02, 0x03] },
5233 DbExtrinsic::Full(UncheckedXt::new_transaction(42.into(), ())),
5234 DbExtrinsic::MultiRenew {
5235 hashes: vec![H256::repeat_byte(0xBB), H256::repeat_byte(0xCC)],
5236 extrinsic: vec![0x04, 0x05, 0x06, 0x07],
5237 },
5238 ];
5239
5240 let encoded = entries.encode();
5241 let decoded: Vec<DbExtrinsic<Block>> =
5242 Decode::decode(&mut &encoded[..]).expect("encoded DbExtrinsic vec must decode");
5243 assert_eq!(encoded, decoded.encode());
5244 }
5245
5246 #[test]
5247 fn apply_index_ops_deterministic() {
5248 let body = vec![
5249 UncheckedXt::new_transaction(0.into(), ()),
5250 UncheckedXt::new_transaction(1.into(), ()),
5251 ];
5252 let h1 = H256::repeat_byte(0x11).as_ref().to_vec();
5253 let h2 = H256::repeat_byte(0x22).as_ref().to_vec();
5254 let h3 = H256::repeat_byte(0x33).as_ref().to_vec();
5255
5256 let ops = vec![
5257 IndexOperation::Renew { extrinsic: 0, hash: h1.clone() },
5258 IndexOperation::Renew { extrinsic: 0, hash: h2.clone() },
5259 IndexOperation::Renew { extrinsic: 0, hash: h1.clone() },
5260 IndexOperation::Renew { extrinsic: 1, hash: h3.clone() },
5261 ];
5262
5263 let mut tx1: Transaction<DbHash> = Transaction::new();
5264 let bytes1 = apply_index_ops::<Block>(&mut tx1, body.clone(), ops.clone(), HashMap::new());
5265
5266 let mut tx2: Transaction<DbHash> = Transaction::new();
5267 let bytes2 = apply_index_ops::<Block>(&mut tx2, body, ops, HashMap::new());
5268
5269 assert_eq!(bytes1, bytes2);
5270
5271 let decoded: Vec<DbExtrinsic<Block>> =
5272 Decode::decode(&mut &bytes1[..]).expect("apply_index_ops output must decode");
5273 assert_eq!(decoded.len(), 2);
5274 match &decoded[0] {
5275 DbExtrinsic::MultiRenew { hashes, .. } => {
5276 assert_eq!(hashes.len(), 3);
5277 assert_eq!(hashes[0].as_ref(), h1.as_slice());
5278 assert_eq!(hashes[1].as_ref(), h2.as_slice());
5279 assert_eq!(hashes[2].as_ref(), h1.as_slice());
5280 },
5281 other => panic!("expected MultiRenew, got {other:?}"),
5282 }
5283 assert!(matches!(decoded[1], DbExtrinsic::Indexed { .. }));
5284 }
5285
5286 #[test]
5287 fn multi_renew_in_one_block_indexed_in_another() {
5288 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(2), 10);
5290 let mut blocks = Vec::new();
5291 let mut prev_hash = Default::default();
5292
5293 let x = UncheckedXt::new_transaction(0.into(), ()).encode();
5294 let x_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x[1..]);
5295
5296 for i in 0..6 {
5297 let mut index = Vec::new();
5298 let body = if i == 0 {
5299 index.push(IndexOperation::Insert {
5300 extrinsic: 0,
5301 hash: x_hash.as_ref().to_vec(),
5302 size: (x.len() - 1) as u32,
5303 });
5304 vec![UncheckedXt::new_transaction(0.into(), ())]
5305 } else if i == 1 {
5306 index.push(IndexOperation::Renew { extrinsic: 0, hash: x_hash.as_ref().to_vec() });
5307 vec![UncheckedXt::new_transaction(10.into(), ())]
5308 } else if i == 2 {
5309 index.push(IndexOperation::Renew { extrinsic: 0, hash: x_hash.as_ref().to_vec() });
5310 index.push(IndexOperation::Renew { extrinsic: 0, hash: x_hash.as_ref().to_vec() });
5311 vec![UncheckedXt::new_transaction(20.into(), ())]
5312 } else {
5313 vec![UncheckedXt::new_transaction(i.into(), ())]
5314 };
5315 let hash =
5316 insert_block(&backend, i, prev_hash, None, Default::default(), body, Some(index))
5317 .unwrap();
5318 blocks.push(hash);
5319 prev_hash = hash;
5320 }
5321
5322 let bc = backend.blockchain();
5323 assert!(bc.indexed_transaction(x_hash).unwrap().is_some());
5324
5325 for i in 1..6 {
5326 let mut op = backend.begin_operation().unwrap();
5327 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
5328 op.mark_finalized(blocks[i], None).unwrap();
5329 backend.commit_operation(op).unwrap();
5330 }
5331
5332 assert!(bc.indexed_transaction(x_hash).unwrap().is_none());
5333 }
5334
5335 #[test]
5336 fn remove_leaf_block_works() {
5337 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(2), 10);
5338 let mut blocks = Vec::new();
5339 let mut prev_hash = Default::default();
5340 for i in 0..2 {
5341 let hash = insert_block(
5342 &backend,
5343 i,
5344 prev_hash,
5345 None,
5346 Default::default(),
5347 vec![UncheckedXt::new_transaction(i.into(), ())],
5348 None,
5349 )
5350 .unwrap();
5351 blocks.push(hash);
5352 prev_hash = hash;
5353 }
5354
5355 for i in 0..2 {
5356 let hash = insert_block(
5357 &backend,
5358 2,
5359 blocks[1],
5360 None,
5361 sp_core::H256::random(),
5362 vec![UncheckedXt::new_transaction(i.into(), ())],
5363 None,
5364 )
5365 .unwrap();
5366 blocks.push(hash);
5367 }
5368
5369 let best_hash = insert_block(
5371 &backend,
5372 1,
5373 blocks[0],
5374 None,
5375 sp_core::H256::random(),
5376 vec![UncheckedXt::new_transaction(42.into(), ())],
5377 None,
5378 )
5379 .unwrap();
5380
5381 assert_eq!(backend.blockchain().info().best_hash, best_hash);
5382 assert!(backend.remove_leaf_block(best_hash).is_err());
5383
5384 assert_eq!(backend.blockchain().leaves().unwrap(), vec![blocks[2], blocks[3], best_hash]);
5385 assert_eq!(backend.blockchain().children(blocks[1]).unwrap(), vec![blocks[2], blocks[3]]);
5386
5387 assert!(backend.have_state_at(blocks[3], 2));
5388 assert!(backend.blockchain().header(blocks[3]).unwrap().is_some());
5389 backend.remove_leaf_block(blocks[3]).unwrap();
5390 assert!(!backend.have_state_at(blocks[3], 2));
5391 assert!(backend.blockchain().header(blocks[3]).unwrap().is_none());
5392 assert_eq!(backend.blockchain().leaves().unwrap(), vec![blocks[2], best_hash]);
5393 assert_eq!(backend.blockchain().children(blocks[1]).unwrap(), vec![blocks[2]]);
5394
5395 assert!(backend.have_state_at(blocks[2], 2));
5396 assert!(backend.blockchain().header(blocks[2]).unwrap().is_some());
5397 backend.remove_leaf_block(blocks[2]).unwrap();
5398 assert!(!backend.have_state_at(blocks[2], 2));
5399 assert!(backend.blockchain().header(blocks[2]).unwrap().is_none());
5400 assert_eq!(backend.blockchain().leaves().unwrap(), vec![best_hash, blocks[1]]);
5401 assert_eq!(backend.blockchain().children(blocks[1]).unwrap(), vec![]);
5402
5403 assert!(backend.have_state_at(blocks[1], 1));
5404 assert!(backend.blockchain().header(blocks[1]).unwrap().is_some());
5405 backend.remove_leaf_block(blocks[1]).unwrap();
5406 assert!(!backend.have_state_at(blocks[1], 1));
5407 assert!(backend.blockchain().header(blocks[1]).unwrap().is_none());
5408 assert_eq!(backend.blockchain().leaves().unwrap(), vec![best_hash]);
5409 assert_eq!(backend.blockchain().children(blocks[0]).unwrap(), vec![best_hash]);
5410 }
5411
5412 #[test]
5413 fn test_import_existing_block_as_new_head() {
5414 let backend: Backend<Block> = Backend::new_test(10, 3);
5415 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
5416 let block1 = insert_header(&backend, 1, block0, None, Default::default());
5417 let block2 = insert_header(&backend, 2, block1, None, Default::default());
5418 let block3 = insert_header(&backend, 3, block2, None, Default::default());
5419 let block4 = insert_header(&backend, 4, block3, None, Default::default());
5420 let block5 = insert_header(&backend, 5, block4, None, Default::default());
5421 assert_eq!(backend.blockchain().info().best_hash, block5);
5422
5423 let header = Header {
5426 number: 1,
5427 parent_hash: block0,
5428 state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1),
5429 digest: Default::default(),
5430 extrinsics_root: Default::default(),
5431 };
5432 let mut op = backend.begin_operation().unwrap();
5433 op.set_block_data(header, None, None, None, NewBlockState::Best, true).unwrap();
5434 assert!(matches!(backend.commit_operation(op), Err(sp_blockchain::Error::SetHeadTooOld)));
5435
5436 let header = backend.blockchain().header(block2).unwrap().unwrap();
5438 let mut op = backend.begin_operation().unwrap();
5439 op.set_block_data(header, None, None, None, NewBlockState::Best, true).unwrap();
5440 backend.commit_operation(op).unwrap();
5441 assert_eq!(backend.blockchain().info().best_hash, block2);
5442 }
5443
5444 #[test]
5445 fn test_import_existing_block_as_final() {
5446 let backend: Backend<Block> = Backend::new_test(10, 10);
5447 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
5448 let block1 = insert_header(&backend, 1, block0, None, Default::default());
5449 let _block2 = insert_header(&backend, 2, block1, None, Default::default());
5450 assert_eq!(backend.blockchain().info().finalized_hash, block0);
5452
5453 let header = backend.blockchain().header(block1).unwrap().unwrap();
5455
5456 let mut op = backend.begin_operation().unwrap();
5457 op.set_block_data(header, None, None, None, NewBlockState::Final, true).unwrap();
5458 backend.commit_operation(op).unwrap();
5459
5460 assert_eq!(backend.blockchain().info().finalized_hash, block1);
5461 }
5462
5463 #[test]
5464 fn test_import_existing_state_fails() {
5465 let backend: Backend<Block> = Backend::new_test(10, 10);
5466 let genesis =
5467 insert_block(&backend, 0, Default::default(), None, Default::default(), vec![], None)
5468 .unwrap();
5469
5470 insert_block(&backend, 1, genesis, None, Default::default(), vec![], None).unwrap();
5471 let err = insert_block(&backend, 1, genesis, None, Default::default(), vec![], None)
5472 .err()
5473 .unwrap();
5474 match err {
5475 sp_blockchain::Error::StateDatabase(m) if m == "Block already exists" => (),
5476 e @ _ => panic!("Unexpected error {:?}", e),
5477 }
5478 }
5479
5480 #[test]
5481 fn test_leaves_not_created_for_ancient_blocks() {
5482 let backend: Backend<Block> = Backend::new_test(10, 10);
5483 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
5484
5485 let block1_a = insert_header(&backend, 1, block0, None, Default::default());
5486 let block2_a = insert_header(&backend, 2, block1_a, None, Default::default());
5487 backend.finalize_block(block1_a, None).unwrap();
5488 assert_eq!(backend.blockchain().leaves().unwrap(), vec![block2_a]);
5489
5490 insert_header_no_head(&backend, 1, block0, [1; 32].into());
5492 assert_eq!(backend.blockchain().leaves().unwrap(), vec![block2_a]);
5493 }
5494
5495 #[test]
5496 fn revert_non_best_blocks() {
5497 let backend = Backend::<Block>::new_test(10, 10);
5498
5499 let genesis =
5500 insert_block(&backend, 0, Default::default(), None, Default::default(), vec![], None)
5501 .unwrap();
5502
5503 let block1 =
5504 insert_block(&backend, 1, genesis, None, Default::default(), vec![], None).unwrap();
5505
5506 let block2 =
5507 insert_block(&backend, 2, block1, None, Default::default(), vec![], None).unwrap();
5508
5509 let block3 = {
5510 let mut op = backend.begin_operation().unwrap();
5511 backend.begin_state_operation(&mut op, block1).unwrap();
5512 let header = Header {
5513 number: 3,
5514 parent_hash: block2,
5515 state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1),
5516 digest: Default::default(),
5517 extrinsics_root: Default::default(),
5518 };
5519
5520 op.set_block_data(
5521 header.clone(),
5522 Some(Vec::new()),
5523 None,
5524 None,
5525 NewBlockState::Normal,
5526 true,
5527 )
5528 .unwrap();
5529
5530 backend.commit_operation(op).unwrap();
5531
5532 header.hash()
5533 };
5534
5535 let block4 = {
5536 let mut op = backend.begin_operation().unwrap();
5537 backend.begin_state_operation(&mut op, block2).unwrap();
5538 let header = Header {
5539 number: 4,
5540 parent_hash: block3,
5541 state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1),
5542 digest: Default::default(),
5543 extrinsics_root: Default::default(),
5544 };
5545
5546 op.set_block_data(
5547 header.clone(),
5548 Some(Vec::new()),
5549 None,
5550 None,
5551 NewBlockState::Normal,
5552 true,
5553 )
5554 .unwrap();
5555
5556 backend.commit_operation(op).unwrap();
5557
5558 header.hash()
5559 };
5560
5561 let block3_fork = {
5562 let mut op = backend.begin_operation().unwrap();
5563 backend.begin_state_operation(&mut op, block2).unwrap();
5564 let header = Header {
5565 number: 3,
5566 parent_hash: block2,
5567 state_root: BlakeTwo256::trie_root(Vec::new(), StateVersion::V1),
5568 digest: Default::default(),
5569 extrinsics_root: H256::from_low_u64_le(42),
5570 };
5571
5572 op.set_block_data(
5573 header.clone(),
5574 Some(Vec::new()),
5575 None,
5576 None,
5577 NewBlockState::Normal,
5578 true,
5579 )
5580 .unwrap();
5581
5582 backend.commit_operation(op).unwrap();
5583
5584 header.hash()
5585 };
5586
5587 assert!(backend.have_state_at(block1, 1));
5588 assert!(backend.have_state_at(block2, 2));
5589 assert!(backend.have_state_at(block3, 3));
5590 assert!(backend.have_state_at(block4, 4));
5591 assert!(backend.have_state_at(block3_fork, 3));
5592
5593 assert_eq!(backend.blockchain.leaves().unwrap(), vec![block4, block3_fork]);
5594 assert_eq!(4, backend.blockchain.leaves.read().highest_leaf().unwrap().0);
5595
5596 assert_eq!(3, backend.revert(1, false).unwrap().0);
5597
5598 assert!(backend.have_state_at(block1, 1));
5599
5600 let ensure_pruned = |hash, number: u32| {
5601 assert_eq!(
5602 backend.blockchain.status(hash).unwrap(),
5603 sc_client_api::blockchain::BlockStatus::Unknown
5604 );
5605 assert!(
5606 backend
5607 .blockchain
5608 .db
5609 .get(columns::BODY, &number_and_hash_to_lookup_key(number, hash).unwrap())
5610 .is_none(),
5611 "{number}"
5612 );
5613 assert!(
5614 backend
5615 .blockchain
5616 .db
5617 .get(columns::HEADER, &number_and_hash_to_lookup_key(number, hash).unwrap())
5618 .is_none(),
5619 "{number}"
5620 );
5621 };
5622
5623 ensure_pruned(block2, 2);
5624 ensure_pruned(block3, 3);
5625 ensure_pruned(block4, 4);
5626 ensure_pruned(block3_fork, 3);
5627
5628 assert_eq!(backend.blockchain.leaves().unwrap(), vec![block1]);
5629 assert_eq!(1, backend.blockchain.leaves.read().highest_leaf().unwrap().0);
5630 }
5631
5632 #[test]
5633 fn revert_finalized_blocks() {
5634 let pruning_modes = [BlocksPruning::Some(10), BlocksPruning::KeepAll];
5635
5636 for pruning_mode in pruning_modes {
5639 let backend = Backend::<Block>::new_test_with_tx_storage(pruning_mode, 1);
5640
5641 let mut parent = Default::default();
5642 for i in 0..=10 {
5643 parent = insert_block(&backend, i, parent, None, Default::default(), vec![], None)
5644 .unwrap();
5645 }
5646
5647 assert_eq!(backend.blockchain().info().best_number, 10);
5648
5649 let block8 = backend.blockchain().hash(8).unwrap().unwrap();
5650 backend.finalize_block(block8, None).unwrap();
5651 backend.revert(5, true).unwrap();
5652
5653 match pruning_mode {
5654 BlocksPruning::Some(_) => {
5657 assert_eq!(backend.blockchain().info().finalized_number, 8)
5658 },
5659 _ => assert_eq!(backend.blockchain().info().finalized_number, 5),
5661 }
5662 }
5663 }
5664
5665 #[test]
5666 fn test_no_duplicated_leaves_allowed() {
5667 let backend: Backend<Block> = Backend::new_test(10, 10);
5668 let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
5669 let block1 = insert_header(&backend, 1, block0, None, Default::default());
5670 let block2 = insert_header_no_head(&backend, 2, block1, Default::default());
5672 assert_eq!(backend.blockchain().leaves().unwrap(), vec![block2]);
5673 assert_eq!(backend.blockchain().info().best_hash, block1);
5674
5675 let block2 = insert_header(&backend, 2, block1, None, Default::default());
5677 assert_eq!(backend.blockchain().leaves().unwrap(), vec![block2]);
5678 assert_eq!(backend.blockchain().info().best_hash, block2);
5679 }
5680
5681 #[test]
5682 fn force_delayed_canonicalize_waiting_for_blocks_to_be_finalized() {
5683 let pruning_modes =
5684 [BlocksPruning::Some(10), BlocksPruning::KeepAll, BlocksPruning::KeepFinalized];
5685
5686 for pruning_mode in pruning_modes {
5687 eprintln!("Running with pruning mode: {:?}", pruning_mode);
5688
5689 let backend = Backend::<Block>::new_test_with_tx_storage(pruning_mode, 1);
5690
5691 let genesis = insert_block(
5692 &backend,
5693 0,
5694 Default::default(),
5695 None,
5696 Default::default(),
5697 vec![],
5698 None,
5699 )
5700 .unwrap();
5701
5702 let block1 = {
5703 let mut op = backend.begin_operation().unwrap();
5704 backend.begin_state_operation(&mut op, genesis).unwrap();
5705 let mut header = Header {
5706 number: 1,
5707 parent_hash: genesis,
5708 state_root: Default::default(),
5709 digest: Default::default(),
5710 extrinsics_root: Default::default(),
5711 };
5712
5713 let storage = vec![(vec![1, 3, 5], None), (vec![5, 5, 5], Some(vec![4, 5, 6]))];
5714
5715 let (root, overlay) = op.old_state.storage_root(
5716 storage.iter().map(|(k, v)| (k.as_slice(), v.as_ref().map(|v| &v[..]))),
5717 StateVersion::V1,
5718 );
5719 op.update_db_storage(overlay).unwrap();
5720 header.state_root = root.into();
5721
5722 op.update_storage(storage, Vec::new()).unwrap();
5723
5724 op.set_block_data(
5725 header.clone(),
5726 Some(Vec::new()),
5727 None,
5728 None,
5729 NewBlockState::Normal,
5730 true,
5731 )
5732 .unwrap();
5733
5734 backend.commit_operation(op).unwrap();
5735
5736 header.hash()
5737 };
5738
5739 if matches!(pruning_mode, BlocksPruning::Some(_)) {
5740 assert_eq!(
5741 LastCanonicalized::Block(0),
5742 backend.storage.state_db.last_canonicalized()
5743 );
5744 }
5745
5746 let block2 = {
5749 let mut op = backend.begin_operation().unwrap();
5750 backend.begin_state_operation(&mut op, block1).unwrap();
5751 let mut header = Header {
5752 number: 2,
5753 parent_hash: block1,
5754 state_root: Default::default(),
5755 digest: Default::default(),
5756 extrinsics_root: Default::default(),
5757 };
5758
5759 let storage = vec![(vec![5, 5, 5], Some(vec![4, 5, 6, 2]))];
5760
5761 let (root, overlay) = op.old_state.storage_root(
5762 storage.iter().map(|(k, v)| (k.as_slice(), v.as_ref().map(|v| &v[..]))),
5763 StateVersion::V1,
5764 );
5765 op.update_db_storage(overlay).unwrap();
5766 header.state_root = root.into();
5767
5768 op.update_storage(storage, Vec::new()).unwrap();
5769
5770 op.set_block_data(
5771 header.clone(),
5772 Some(Vec::new()),
5773 None,
5774 None,
5775 NewBlockState::Normal,
5776 true,
5777 )
5778 .unwrap();
5779
5780 backend.commit_operation(op).unwrap();
5781
5782 header.hash()
5783 };
5784
5785 if matches!(pruning_mode, BlocksPruning::Some(_)) {
5786 assert_eq!(
5787 LastCanonicalized::Block(0),
5788 backend.storage.state_db.last_canonicalized()
5789 );
5790 }
5791
5792 let block3 = {
5795 let mut op = backend.begin_operation().unwrap();
5796 backend.begin_state_operation(&mut op, block2).unwrap();
5797 let mut header = Header {
5798 number: 3,
5799 parent_hash: block2,
5800 state_root: Default::default(),
5801 digest: Default::default(),
5802 extrinsics_root: Default::default(),
5803 };
5804
5805 let storage = vec![(vec![5, 5, 5], Some(vec![4, 5, 6, 3]))];
5806
5807 let (root, overlay) = op.old_state.storage_root(
5808 storage.iter().map(|(k, v)| (k.as_slice(), v.as_ref().map(|v| &v[..]))),
5809 StateVersion::V1,
5810 );
5811 op.update_db_storage(overlay).unwrap();
5812 header.state_root = root.into();
5813
5814 op.update_storage(storage, Vec::new()).unwrap();
5815
5816 op.set_block_data(
5817 header.clone(),
5818 Some(Vec::new()),
5819 None,
5820 None,
5821 NewBlockState::Best,
5822 true,
5823 )
5824 .unwrap();
5825
5826 backend.commit_operation(op).unwrap();
5827
5828 header.hash()
5829 };
5830
5831 let block4 = {
5833 let mut op = backend.begin_operation().unwrap();
5834 backend.begin_state_operation(&mut op, block3).unwrap();
5835 let mut header = Header {
5836 number: 4,
5837 parent_hash: block3,
5838 state_root: Default::default(),
5839 digest: Default::default(),
5840 extrinsics_root: Default::default(),
5841 };
5842
5843 let storage = vec![(vec![5, 5, 5], Some(vec![4, 5, 6, 4]))];
5844
5845 let (root, overlay) = op.old_state.storage_root(
5846 storage.iter().map(|(k, v)| (k.as_slice(), v.as_ref().map(|v| &v[..]))),
5847 StateVersion::V1,
5848 );
5849 op.update_db_storage(overlay).unwrap();
5850 header.state_root = root.into();
5851
5852 op.update_storage(storage, Vec::new()).unwrap();
5853
5854 op.set_block_data(
5855 header.clone(),
5856 Some(Vec::new()),
5857 None,
5858 None,
5859 NewBlockState::Best,
5860 true,
5861 )
5862 .unwrap();
5863
5864 backend.commit_operation(op).unwrap();
5865
5866 header.hash()
5867 };
5868
5869 if matches!(pruning_mode, BlocksPruning::Some(_)) {
5870 assert_eq!(
5871 LastCanonicalized::Block(2),
5872 backend.storage.state_db.last_canonicalized()
5873 );
5874 }
5875
5876 assert_eq!(block1, backend.blockchain().hash(1).unwrap().unwrap());
5877 assert_eq!(block2, backend.blockchain().hash(2).unwrap().unwrap());
5878 assert_eq!(block3, backend.blockchain().hash(3).unwrap().unwrap());
5879 assert_eq!(block4, backend.blockchain().hash(4).unwrap().unwrap());
5880 }
5881 }
5882
5883 #[test]
5884 fn test_pinned_blocks_on_finalize() {
5885 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(1), 10);
5886 let mut blocks = Vec::new();
5887 let mut prev_hash = Default::default();
5888
5889 let build_justification = |i: u64| ([0, 0, 0, 0], vec![i.try_into().unwrap()]);
5890 for i in 0..5 {
5893 let hash = insert_block(
5894 &backend,
5895 i,
5896 prev_hash,
5897 None,
5898 Default::default(),
5899 vec![UncheckedXt::new_transaction(i.into(), ())],
5900 None,
5901 )
5902 .unwrap();
5903 blocks.push(hash);
5904 backend.pin_block(blocks[i as usize]).unwrap();
5906
5907 prev_hash = hash;
5908 }
5909
5910 let bc = backend.blockchain();
5911
5912 assert_eq!(
5915 Some(vec![UncheckedXt::new_transaction(1.into(), ())]),
5916 bc.body(blocks[1]).unwrap()
5917 );
5918
5919 backend.pin_block(blocks[1]).unwrap();
5921 backend.pin_block(blocks[1]).unwrap();
5922
5923 let mut op = backend.begin_operation().unwrap();
5925 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
5926 for i in 1..5 {
5927 op.mark_finalized(blocks[i], Some(build_justification(i.try_into().unwrap())))
5928 .unwrap();
5929 }
5930 backend.commit_operation(op).unwrap();
5931
5932 assert_eq!(
5935 Some(vec![UncheckedXt::new_transaction(0.into(), ())]),
5936 bc.body(blocks[0]).unwrap()
5937 );
5938
5939 assert_eq!(
5940 Some(vec![UncheckedXt::new_transaction(1.into(), ())]),
5941 bc.body(blocks[1]).unwrap()
5942 );
5943 assert_eq!(
5944 Some(Justifications::from(build_justification(1))),
5945 bc.justifications(blocks[1]).unwrap()
5946 );
5947
5948 assert_eq!(
5949 Some(vec![UncheckedXt::new_transaction(2.into(), ())]),
5950 bc.body(blocks[2]).unwrap()
5951 );
5952 assert_eq!(
5953 Some(Justifications::from(build_justification(2))),
5954 bc.justifications(blocks[2]).unwrap()
5955 );
5956
5957 assert_eq!(
5958 Some(vec![UncheckedXt::new_transaction(3.into(), ())]),
5959 bc.body(blocks[3]).unwrap()
5960 );
5961 assert_eq!(
5962 Some(Justifications::from(build_justification(3))),
5963 bc.justifications(blocks[3]).unwrap()
5964 );
5965
5966 assert_eq!(
5967 Some(vec![UncheckedXt::new_transaction(4.into(), ())]),
5968 bc.body(blocks[4]).unwrap()
5969 );
5970 assert_eq!(
5971 Some(Justifications::from(build_justification(4))),
5972 bc.justifications(blocks[4]).unwrap()
5973 );
5974
5975 for block in &blocks {
5977 backend.unpin_block(*block);
5978 }
5979
5980 assert!(bc.body(blocks[0]).unwrap().is_none());
5981 assert!(bc.body(blocks[1]).unwrap().is_some());
5983 assert!(bc.justifications(blocks[1]).unwrap().is_some());
5984 assert!(bc.header(blocks[1]).ok().flatten().is_some());
5986 assert!(bc.body(blocks[2]).unwrap().is_none());
5987 assert!(bc.justifications(blocks[2]).unwrap().is_none());
5988 assert!(bc.body(blocks[3]).unwrap().is_none());
5989 assert!(bc.justifications(blocks[3]).unwrap().is_none());
5990
5991 backend.unpin_block(blocks[1]);
5993 assert!(bc.body(blocks[1]).unwrap().is_some());
5994 assert!(bc.justifications(blocks[1]).unwrap().is_some());
5995 backend.unpin_block(blocks[1]);
5996 assert!(bc.body(blocks[1]).unwrap().is_none());
5997 assert!(bc.justifications(blocks[1]).unwrap().is_none());
5998
5999 assert_eq!(
6001 Some(vec![UncheckedXt::new_transaction(4.into(), ())]),
6002 bc.body(blocks[4]).unwrap()
6003 );
6004 assert_eq!(
6005 Some(Justifications::from(build_justification(4))),
6006 bc.justifications(blocks[4]).unwrap()
6007 );
6008
6009 let hash = insert_block(
6012 &backend,
6013 5,
6014 prev_hash,
6015 None,
6016 Default::default(),
6017 vec![UncheckedXt::new_transaction(5.into(), ())],
6018 None,
6019 )
6020 .unwrap();
6021 blocks.push(hash);
6022
6023 backend.pin_block(blocks[4]).unwrap();
6024 let mut op = backend.begin_operation().unwrap();
6026 backend.begin_state_operation(&mut op, blocks[5]).unwrap();
6027 op.mark_finalized(blocks[5], Some(build_justification(5))).unwrap();
6028 backend.commit_operation(op).unwrap();
6029
6030 assert!(bc.body(blocks[0]).unwrap().is_none());
6031 assert!(bc.body(blocks[1]).unwrap().is_none());
6032 assert!(bc.body(blocks[2]).unwrap().is_none());
6033 assert!(bc.body(blocks[3]).unwrap().is_none());
6034
6035 assert_eq!(
6036 Some(vec![UncheckedXt::new_transaction(4.into(), ())]),
6037 bc.body(blocks[4]).unwrap()
6038 );
6039 assert_eq!(
6040 Some(Justifications::from(build_justification(4))),
6041 bc.justifications(blocks[4]).unwrap()
6042 );
6043 assert_eq!(
6044 Some(vec![UncheckedXt::new_transaction(5.into(), ())]),
6045 bc.body(blocks[5]).unwrap()
6046 );
6047 assert!(bc.header(blocks[5]).ok().flatten().is_some());
6048
6049 backend.unpin_block(blocks[4]);
6050 assert!(bc.body(blocks[4]).unwrap().is_none());
6051 assert!(bc.justifications(blocks[4]).unwrap().is_none());
6052
6053 backend.append_justification(blocks[5], ([0, 0, 0, 1], vec![42])).unwrap();
6055
6056 let hash = insert_block(
6057 &backend,
6058 6,
6059 blocks[5],
6060 None,
6061 Default::default(),
6062 vec![UncheckedXt::new_transaction(6.into(), ())],
6063 None,
6064 )
6065 .unwrap();
6066 blocks.push(hash);
6067
6068 backend.pin_block(blocks[5]).unwrap();
6070
6071 let mut op = backend.begin_operation().unwrap();
6074 backend.begin_state_operation(&mut op, blocks[6]).unwrap();
6075 op.mark_finalized(blocks[6], None).unwrap();
6076 backend.commit_operation(op).unwrap();
6077
6078 assert_eq!(
6079 Some(vec![UncheckedXt::new_transaction(5.into(), ())]),
6080 bc.body(blocks[5]).unwrap()
6081 );
6082 assert!(bc.header(blocks[5]).ok().flatten().is_some());
6083 let mut expected = Justifications::from(build_justification(5));
6084 expected.append(([0, 0, 0, 1], vec![42]));
6085 assert_eq!(Some(expected), bc.justifications(blocks[5]).unwrap());
6086 }
6087
6088 #[test]
6089 fn test_pinned_blocks_on_finalize_with_fork() {
6090 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(1), 10);
6091 let mut blocks = Vec::new();
6092 let mut prev_hash = Default::default();
6093
6094 for i in 0..5 {
6097 let hash = insert_block(
6098 &backend,
6099 i,
6100 prev_hash,
6101 None,
6102 Default::default(),
6103 vec![UncheckedXt::new_transaction(i.into(), ())],
6104 None,
6105 )
6106 .unwrap();
6107 blocks.push(hash);
6108
6109 backend.pin_block(blocks[i as usize]).unwrap();
6111
6112 prev_hash = hash;
6113 }
6114
6115 let fork_hash_root = insert_block(
6120 &backend,
6121 2,
6122 blocks[1],
6123 None,
6124 H256::random(),
6125 vec![UncheckedXt::new_transaction(2.into(), ())],
6126 None,
6127 )
6128 .unwrap();
6129 let fork_hash_3 = insert_block(
6130 &backend,
6131 3,
6132 fork_hash_root,
6133 None,
6134 H256::random(),
6135 vec![
6136 UncheckedXt::new_transaction(3.into(), ()),
6137 UncheckedXt::new_transaction(11.into(), ()),
6138 ],
6139 None,
6140 )
6141 .unwrap();
6142
6143 backend.pin_block(fork_hash_3).unwrap();
6145
6146 let mut op = backend.begin_operation().unwrap();
6147 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
6148 op.mark_head(blocks[4]).unwrap();
6149 backend.commit_operation(op).unwrap();
6150
6151 for i in 1..5 {
6152 let mut op = backend.begin_operation().unwrap();
6153 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
6154 op.mark_finalized(blocks[i], None).unwrap();
6155 backend.commit_operation(op).unwrap();
6156 }
6157
6158 let bc = backend.blockchain();
6159 assert_eq!(
6160 Some(vec![UncheckedXt::new_transaction(0.into(), ())]),
6161 bc.body(blocks[0]).unwrap()
6162 );
6163 assert_eq!(
6164 Some(vec![UncheckedXt::new_transaction(1.into(), ())]),
6165 bc.body(blocks[1]).unwrap()
6166 );
6167 assert_eq!(
6168 Some(vec![UncheckedXt::new_transaction(2.into(), ())]),
6169 bc.body(blocks[2]).unwrap()
6170 );
6171 assert_eq!(
6172 Some(vec![UncheckedXt::new_transaction(3.into(), ())]),
6173 bc.body(blocks[3]).unwrap()
6174 );
6175 assert_eq!(
6176 Some(vec![UncheckedXt::new_transaction(4.into(), ())]),
6177 bc.body(blocks[4]).unwrap()
6178 );
6179 assert_eq!(None, bc.body(fork_hash_root).unwrap());
6181 assert_eq!(
6182 Some(vec![
6183 UncheckedXt::new_transaction(3.into(), ()),
6184 UncheckedXt::new_transaction(11.into(), ())
6185 ]),
6186 bc.body(fork_hash_3).unwrap()
6187 );
6188
6189 for block in &blocks {
6191 backend.unpin_block(*block);
6192 }
6193 assert!(bc.body(blocks[0]).unwrap().is_none());
6194 assert!(bc.body(blocks[1]).unwrap().is_none());
6195 assert!(bc.body(blocks[2]).unwrap().is_none());
6196 assert!(bc.body(blocks[3]).unwrap().is_none());
6197
6198 assert!(bc.body(fork_hash_3).unwrap().is_some());
6199 backend.unpin_block(fork_hash_3);
6200 assert!(bc.body(fork_hash_3).unwrap().is_none());
6201 }
6202
6203 #[test]
6204 fn prune_blocks_with_empty_predicates_prunes_all() {
6205 let backend = Backend::<Block>::new_test_with_tx_storage_and_filters(
6207 BlocksPruning::Some(2),
6208 0,
6209 vec![], );
6211
6212 let mut blocks = Vec::new();
6213 let mut prev_hash = Default::default();
6214
6215 for i in 0..5 {
6217 let hash = insert_block(
6218 &backend,
6219 i,
6220 prev_hash,
6221 None,
6222 Default::default(),
6223 vec![UncheckedXt::new_transaction(i.into(), ())],
6224 None,
6225 )
6226 .unwrap();
6227 blocks.push(hash);
6228 prev_hash = hash;
6229 }
6230
6231 let justification = (CONS0_ENGINE_ID, vec![1, 2, 3]);
6233
6234 {
6236 let mut op = backend.begin_operation().unwrap();
6237 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
6238 op.mark_finalized(blocks[1], Some(justification.clone())).unwrap();
6239 op.mark_finalized(blocks[2], None).unwrap();
6240 op.mark_finalized(blocks[3], None).unwrap();
6241 op.mark_finalized(blocks[4], None).unwrap();
6242 backend.commit_operation(op).unwrap();
6243 }
6244
6245 let bc = backend.blockchain();
6246
6247 assert_eq!(None, bc.body(blocks[0]).unwrap());
6249 assert_eq!(None, bc.body(blocks[1]).unwrap()); assert_eq!(None, bc.body(blocks[2]).unwrap());
6251
6252 assert!(bc.body(blocks[3]).unwrap().is_some());
6254 assert!(bc.body(blocks[4]).unwrap().is_some());
6255 }
6256
6257 #[test]
6258 fn prune_blocks_multiple_filters_or_logic() {
6259 let backend = Backend::<Block>::new_test_with_tx_storage_and_filters(
6261 BlocksPruning::Some(2),
6262 0,
6263 vec![
6264 Arc::new(|j: &Justifications| j.get(CONS0_ENGINE_ID).is_some()),
6265 Arc::new(|j: &Justifications| j.get(CONS1_ENGINE_ID).is_some()),
6266 ],
6267 );
6268
6269 let mut blocks = Vec::new();
6270 let mut prev_hash = Default::default();
6271
6272 for i in 0..7 {
6274 let hash = insert_block(
6275 &backend,
6276 i,
6277 prev_hash,
6278 None,
6279 Default::default(),
6280 vec![UncheckedXt::new_transaction(i.into(), ())],
6281 None,
6282 )
6283 .unwrap();
6284 blocks.push(hash);
6285 prev_hash = hash;
6286 }
6287
6288 let cons0_justification = (CONS0_ENGINE_ID, vec![1, 2, 3]);
6289 let cons1_justification = (CONS1_ENGINE_ID, vec![4, 5, 6]);
6290
6291 {
6293 let mut op = backend.begin_operation().unwrap();
6294 backend.begin_state_operation(&mut op, blocks[6]).unwrap();
6295 op.mark_finalized(blocks[1], Some(cons0_justification.clone())).unwrap();
6297 op.mark_finalized(blocks[2], Some(cons1_justification.clone())).unwrap();
6299 op.mark_finalized(blocks[3], None).unwrap();
6301 op.mark_finalized(blocks[4], Some(([9, 9, 9, 9], vec![7, 8, 9]))).unwrap();
6303 op.mark_finalized(blocks[5], None).unwrap();
6304 op.mark_finalized(blocks[6], None).unwrap();
6305 backend.commit_operation(op).unwrap();
6306 }
6307
6308 let bc = backend.blockchain();
6309
6310 assert_eq!(None, bc.body(blocks[0]).unwrap());
6312
6313 assert!(bc.body(blocks[1]).unwrap().is_some());
6315
6316 assert!(bc.body(blocks[2]).unwrap().is_some());
6318
6319 assert_eq!(None, bc.body(blocks[3]).unwrap());
6321
6322 assert_eq!(None, bc.body(blocks[4]).unwrap());
6324
6325 assert!(bc.body(blocks[5]).unwrap().is_some());
6327 assert!(bc.body(blocks[6]).unwrap().is_some());
6328 }
6329
6330 #[test]
6331 fn prune_blocks_filter_only_matches_specific_engine() {
6332 let backend = Backend::<Block>::new_test_with_tx_storage_and_filters(
6334 BlocksPruning::Some(2),
6335 0,
6336 vec![Arc::new(|j: &Justifications| j.get(CONS0_ENGINE_ID).is_some())],
6337 );
6338
6339 let mut blocks = Vec::new();
6340 let mut prev_hash = Default::default();
6341
6342 for i in 0..5 {
6344 let hash = insert_block(
6345 &backend,
6346 i,
6347 prev_hash,
6348 None,
6349 Default::default(),
6350 vec![UncheckedXt::new_transaction(i.into(), ())],
6351 None,
6352 )
6353 .unwrap();
6354 blocks.push(hash);
6355 prev_hash = hash;
6356 }
6357
6358 let cons1_justification = (CONS1_ENGINE_ID, vec![4, 5, 6]);
6359
6360 {
6362 let mut op = backend.begin_operation().unwrap();
6363 backend.begin_state_operation(&mut op, blocks[4]).unwrap();
6364 op.mark_finalized(blocks[1], Some(cons1_justification.clone())).unwrap();
6366 op.mark_finalized(blocks[2], None).unwrap();
6367 op.mark_finalized(blocks[3], None).unwrap();
6368 op.mark_finalized(blocks[4], None).unwrap();
6369 backend.commit_operation(op).unwrap();
6370 }
6371
6372 let bc = backend.blockchain();
6373
6374 assert_eq!(None, bc.body(blocks[0]).unwrap());
6376
6377 assert_eq!(None, bc.body(blocks[1]).unwrap());
6379
6380 assert_eq!(None, bc.body(blocks[2]).unwrap());
6382
6383 assert!(bc.body(blocks[3]).unwrap().is_some());
6385 assert!(bc.body(blocks[4]).unwrap().is_some());
6386 }
6387
6388 fn insert_header_no_body_as_best(
6391 backend: &Backend<Block>,
6392 number: u64,
6393 parent_hash: H256,
6394 ) -> H256 {
6395 use sp_runtime::testing::Digest;
6396
6397 let digest = Digest::default();
6398 let header = Header {
6399 number,
6400 parent_hash,
6401 state_root: Default::default(),
6402 digest,
6403 extrinsics_root: Default::default(),
6404 };
6405
6406 let mut op = backend.begin_operation().unwrap();
6407 op.set_block_data(header.clone(), None, None, None, NewBlockState::Best, true)
6409 .unwrap();
6410 backend.commit_operation(op).unwrap();
6411
6412 header.hash()
6413 }
6414
6415 fn reopen_backend(
6417 db: Arc<dyn sp_database::Database<DbHash>>,
6418 blocks_pruning: BlocksPruning,
6419 ) -> Backend<Block> {
6420 let state_pruning = match blocks_pruning {
6421 BlocksPruning::KeepAll => PruningMode::ArchiveAll,
6422 BlocksPruning::KeepFinalized => PruningMode::ArchiveCanonical,
6423 BlocksPruning::Some(n) => PruningMode::blocks_pruning(n),
6424 };
6425 Backend::<Block>::new(
6426 DatabaseSettings {
6427 trie_cache_maximum_size: Some(16 * 1024 * 1024),
6428 state_pruning: Some(state_pruning),
6429 source: DatabaseSource::Custom { db, require_create_flag: false },
6430 blocks_pruning,
6431 pruning_filters: Default::default(),
6432 metrics_registry: None,
6433 },
6434 0,
6435 )
6436 .unwrap()
6437 }
6438
6439 #[test]
6440 fn missing_body_gap_is_removed_for_non_archive_node() {
6441 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(100), 0);
6443 assert!(!backend.is_archive);
6444
6445 let genesis_hash = insert_header(&backend, 0, Default::default(), None, Default::default());
6446
6447 let hash_1 = insert_header_no_body_as_best(&backend, 1, genesis_hash);
6449 let hash_2 = insert_header_no_body_as_best(&backend, 2, hash_1);
6450 insert_header_no_body_as_best(&backend, 3, hash_2);
6451
6452 let info = backend.blockchain().info();
6453 assert!(info.block_gap.is_some(), "MissingBody gap should have been created");
6454 let gap = info.block_gap.unwrap();
6455 assert!(matches!(gap.gap_type, BlockGapType::MissingBody));
6456 assert_eq!(gap.start, 1);
6457 assert_eq!(gap.end, 3);
6458
6459 let db = backend.storage.db.clone();
6461 let backend = reopen_backend(db, BlocksPruning::Some(100));
6462 assert!(!backend.is_archive);
6463
6464 let info = backend.blockchain().info();
6466 assert!(
6467 info.block_gap.is_none(),
6468 "MissingBody gap should be removed for non-archive nodes, got: {:?}",
6469 info.block_gap,
6470 );
6471 }
6472
6473 #[test]
6474 fn missing_body_gap_is_preserved_for_archive_node() {
6475 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::KeepAll, 0);
6477 assert!(backend.is_archive);
6478
6479 let genesis_hash = insert_header(&backend, 0, Default::default(), None, Default::default());
6480
6481 let hash_1 = insert_header_no_body_as_best(&backend, 1, genesis_hash);
6483 let hash_2 = insert_header_no_body_as_best(&backend, 2, hash_1);
6484 insert_header_no_body_as_best(&backend, 3, hash_2);
6485
6486 let info = backend.blockchain().info();
6487 assert!(info.block_gap.is_some(), "MissingBody gap should have been created");
6488 let gap = info.block_gap.unwrap();
6489 assert!(matches!(gap.gap_type, BlockGapType::MissingBody));
6490 assert_eq!(gap.start, 1);
6491 assert_eq!(gap.end, 3);
6492
6493 let db = backend.storage.db.clone();
6495 let backend = reopen_backend(db, BlocksPruning::KeepAll);
6496 assert!(backend.is_archive);
6497
6498 let info = backend.blockchain().info();
6500 assert!(info.block_gap.is_some(), "MissingBody gap should be preserved for archive nodes",);
6501 let gap = info.block_gap.unwrap();
6502 assert!(matches!(gap.gap_type, BlockGapType::MissingBody));
6503 assert_eq!(gap.start, 1);
6504 assert_eq!(gap.end, 3);
6505 }
6506
6507 #[test]
6508 fn missing_header_and_body_gap_is_preserved_for_non_archive_node() {
6509 let backend = Backend::<Block>::new_test_with_tx_storage(BlocksPruning::Some(100), 0);
6511 assert!(!backend.is_archive);
6512
6513 let _genesis_hash =
6514 insert_header(&backend, 0, Default::default(), None, Default::default());
6515
6516 insert_disconnected_header(&backend, 3, H256::from([200; 32]), Default::default(), true);
6519
6520 let info = backend.blockchain().info();
6521 assert!(info.block_gap.is_some(), "Gap should have been created");
6522 let gap = info.block_gap.unwrap();
6523 assert!(matches!(gap.gap_type, BlockGapType::MissingHeaderAndBody));
6524 assert_eq!(gap.start, 1);
6525 assert_eq!(gap.end, 2);
6526
6527 let db = backend.storage.db.clone();
6529 let backend = reopen_backend(db, BlocksPruning::Some(100));
6530 assert!(!backend.is_archive);
6531
6532 let info = backend.blockchain().info();
6534 assert!(
6535 info.block_gap.is_some(),
6536 "MissingHeaderAndBody gap should be preserved for non-archive nodes",
6537 );
6538 let gap = info.block_gap.unwrap();
6539 assert!(matches!(gap.gap_type, BlockGapType::MissingHeaderAndBody));
6540 assert_eq!(gap.start, 1);
6541 assert_eq!(gap.end, 2);
6542 }
6543
6544 mod indexed_transaction_tests {
6545 use super::*;
6546 use crate::utils::NUM_COLUMNS;
6547 use rstest::rstest;
6548 use sp_database::Transaction as DbTransaction;
6549 use std::{path::PathBuf, sync::Arc};
6550 use tempfile::TempDir;
6551
6552 #[derive(Debug, Clone, Copy)]
6553 enum BackendKind {
6554 KvdbMemdb,
6555 ParityDb,
6556 RocksDb,
6557 }
6558
6559 enum DbFactory {
6560 Persistent(Arc<dyn Database<DbHash>>),
6561 OnDisk { path: PathBuf, kind: BackendKind, _tmp: TempDir },
6562 }
6563
6564 impl DbFactory {
6565 fn new(kind: BackendKind) -> Self {
6566 match kind {
6567 BackendKind::KvdbMemdb => Self::Persistent(sp_database::as_database(
6568 kvdb_memorydb::create(NUM_COLUMNS),
6569 )),
6570 BackendKind::ParityDb | BackendKind::RocksDb => {
6571 let tmp = TempDir::new().unwrap();
6572 let path = tmp.path().to_path_buf();
6573 Self::OnDisk { path, kind, _tmp: tmp }
6574 },
6575 }
6576 }
6577
6578 fn open(&self) -> Arc<dyn Database<DbHash>> {
6579 match self {
6580 Self::Persistent(arc) => arc.clone(),
6581 Self::OnDisk { path, kind: BackendKind::ParityDb, .. } => {
6582 crate::parity_db::open::<DbHash>(path, DatabaseType::Full, true, false)
6583 .expect("parity-db open succeeds in test")
6584 },
6585 Self::OnDisk { path, kind: BackendKind::RocksDb, .. } => {
6586 let mut cfg = kvdb_rocksdb::DatabaseConfig::with_columns(NUM_COLUMNS);
6587 cfg.create_if_missing = true;
6588 let db = kvdb_rocksdb::Database::open(&cfg, path)
6589 .expect("kvdb-rocksdb open succeeds in test");
6590 sp_database::as_database(db)
6591 },
6592 Self::OnDisk { kind: BackendKind::KvdbMemdb, .. } => unreachable!(),
6593 }
6594 }
6595 }
6596
6597 const TEST_COL: u32 = columns::TRANSACTION;
6598
6599 fn hash(seed: u8) -> DbHash {
6600 DbHash::repeat_byte(seed)
6601 }
6602
6603 fn commit_store(factory: &DbFactory, h: DbHash, bytes: Vec<u8>) {
6604 let db = factory.open();
6605 let mut tx = DbTransaction::new();
6606 tx.store(TEST_COL, h, bytes);
6607 db.commit(tx).unwrap();
6608 }
6609
6610 fn commit_reference(factory: &DbFactory, h: DbHash) {
6611 let db = factory.open();
6612 let mut tx = DbTransaction::new();
6613 tx.reference(TEST_COL, h);
6614 db.commit(tx).unwrap();
6615 }
6616
6617 fn commit_release(factory: &DbFactory, h: DbHash) {
6618 let db = factory.open();
6619 let mut tx = DbTransaction::new();
6620 tx.release(TEST_COL, h);
6621 db.commit(tx).unwrap();
6622 }
6623
6624 fn get_value(factory: &DbFactory, h: DbHash) -> Option<Vec<u8>> {
6625 factory.open().get(TEST_COL, h.as_ref())
6626 }
6627
6628 #[rstest]
6629 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
6630 #[case::paritydb(BackendKind::ParityDb)]
6631 #[case::rocksdb(BackendKind::RocksDb)]
6632 fn store_then_get(#[case] kind: BackendKind) {
6633 let factory = DbFactory::new(kind);
6634 let h = hash(0xA1);
6635 let bytes = b"a1-bytes".to_vec();
6636 commit_store(&factory, h, bytes.clone());
6637 assert_eq!(get_value(&factory, h).as_deref(), Some(bytes.as_slice()));
6638 }
6639
6640 #[rstest]
6641 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
6642 #[case::paritydb(BackendKind::ParityDb)]
6643 #[case::rocksdb(BackendKind::RocksDb)]
6644 fn store_release_separate_commits(#[case] kind: BackendKind) {
6645 let factory = DbFactory::new(kind);
6646 let h = hash(0xA2);
6647 let bytes = b"a2-bytes".to_vec();
6648 commit_store(&factory, h, bytes);
6649 assert!(get_value(&factory, h).is_some(), "present after store");
6650 commit_release(&factory, h);
6651 assert!(get_value(&factory, h).is_none(), "gone after release");
6652 }
6653
6654 #[rstest]
6655 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
6656 #[case::paritydb(BackendKind::ParityDb)]
6657 #[case::rocksdb(BackendKind::RocksDb)]
6658 fn store_reference_release_release_separate_commits(#[case] kind: BackendKind) {
6659 let factory = DbFactory::new(kind);
6660 let h = hash(0xA3);
6661 let bytes = b"a3-bytes".to_vec();
6662 commit_store(&factory, h, bytes);
6663 commit_reference(&factory, h);
6664 assert!(get_value(&factory, h).is_some(), "rc=2 after reference");
6665 commit_release(&factory, h);
6666 assert!(get_value(&factory, h).is_some(), "rc=1 still present");
6667 commit_release(&factory, h);
6668 assert!(get_value(&factory, h).is_none(), "rc=0 removed");
6669 }
6670
6671 #[rstest]
6672 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
6673 #[case::paritydb(BackendKind::ParityDb)]
6674 #[case::rocksdb(BackendKind::RocksDb)]
6675 fn store_then_reference_same_commit_keeps_value(#[case] kind: BackendKind) {
6676 let factory = DbFactory::new(kind);
6677 let h = hash(0xA4);
6678 let bytes = b"a4-bytes".to_vec();
6679 {
6680 let db = factory.open();
6681 let mut tx = DbTransaction::new();
6682 tx.store(TEST_COL, h, bytes.clone());
6683 tx.reference(TEST_COL, h);
6684 db.commit(tx).unwrap();
6685 }
6686 assert_eq!(
6687 get_value(&factory, h).as_deref(),
6688 Some(bytes.as_slice()),
6689 "Store + Reference on fresh hash in a single commit must keep the value \
6690 (observed via fresh DB handle so overlay caching is bypassed)",
6691 );
6692 }
6693
6694 #[rstest]
6695 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
6696 #[case::paritydb(BackendKind::ParityDb)]
6697 #[case::rocksdb(BackendKind::RocksDb)]
6698 fn store_then_two_references_same_commit_keeps_value(#[case] kind: BackendKind) {
6699 let factory = DbFactory::new(kind);
6700 let h = hash(0xA5);
6701 let bytes = b"a5-bytes".to_vec();
6702 {
6703 let db = factory.open();
6704 let mut tx = DbTransaction::new();
6705 tx.store(TEST_COL, h, bytes.clone());
6706 tx.reference(TEST_COL, h);
6707 tx.reference(TEST_COL, h);
6708 db.commit(tx).unwrap();
6709 }
6710 assert_eq!(
6711 get_value(&factory, h).as_deref(),
6712 Some(bytes.as_slice()),
6713 "Store + 2x Reference on fresh hash must keep the value (post-sync observation)",
6714 );
6715 }
6716
6717 #[rstest]
6718 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
6719 #[case::paritydb(BackendKind::ParityDb)]
6720 #[case::rocksdb(BackendKind::RocksDb)]
6721 fn reference_on_missing_hash_is_noop(#[case] kind: BackendKind) {
6722 let factory = DbFactory::new(kind);
6723 let h = hash(0xA6);
6724 commit_reference(&factory, h);
6725 assert!(get_value(&factory, h).is_none(), "reference on missing key is a no-op");
6726 let bytes = b"a6-bytes".to_vec();
6727 commit_store(&factory, h, bytes.clone());
6728 assert_eq!(get_value(&factory, h).as_deref(), Some(bytes.as_slice()));
6729 commit_release(&factory, h);
6730 assert!(get_value(&factory, h).is_none(), "single release balances the store");
6731 }
6732
6733 #[rstest]
6734 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
6735 #[case::paritydb(BackendKind::ParityDb)]
6736 #[case::rocksdb(BackendKind::RocksDb)]
6737 fn release_on_missing_hash_is_noop(#[case] kind: BackendKind) {
6738 let factory = DbFactory::new(kind);
6739 let h = hash(0xA7);
6740 commit_release(&factory, h);
6741 assert!(get_value(&factory, h).is_none(), "release on missing key is a no-op");
6742 let bytes = b"a7-bytes".to_vec();
6743 commit_store(&factory, h, bytes.clone());
6744 assert_eq!(get_value(&factory, h).as_deref(), Some(bytes.as_slice()));
6745 }
6746
6747 struct BackendFactory {
6748 backend: Option<Backend<Block>>,
6749 kind: BackendKind,
6750 blocks_pruning: BlocksPruning,
6751 tmp_path: Option<PathBuf>,
6752 _tmp: Option<TempDir>,
6753 }
6754
6755 impl BackendFactory {
6756 fn new(kind: BackendKind, blocks_pruning: BlocksPruning) -> Self {
6757 match kind {
6758 BackendKind::KvdbMemdb => Self {
6759 backend: Some(Backend::new_test_with_tx_storage(blocks_pruning, 10)),
6760 kind,
6761 blocks_pruning,
6762 tmp_path: None,
6763 _tmp: None,
6764 },
6765 BackendKind::ParityDb => {
6766 let tmp = TempDir::new().unwrap();
6767 let tmp_path = tmp.path().to_path_buf();
6768 let backend = Backend::new_test_with_tx_storage_source(
6769 blocks_pruning,
6770 10,
6771 DatabaseSource::ParityDb { path: tmp_path.clone() },
6772 Default::default(),
6773 );
6774 Self {
6775 backend: Some(backend),
6776 kind,
6777 blocks_pruning,
6778 tmp_path: Some(tmp_path),
6779 _tmp: Some(tmp),
6780 }
6781 },
6782 BackendKind::RocksDb => {
6783 let tmp = TempDir::new().unwrap();
6784 let tmp_path = tmp.path().to_path_buf();
6785 let mut cfg = kvdb_rocksdb::DatabaseConfig::with_columns(NUM_COLUMNS);
6786 cfg.create_if_missing = true;
6787 let db = kvdb_rocksdb::Database::open(&cfg, &tmp_path)
6788 .expect("kvdb-rocksdb open succeeds in test");
6789 let db = sp_database::as_database(db);
6790 let backend = Backend::new_test_with_tx_storage_source(
6791 blocks_pruning,
6792 10,
6793 DatabaseSource::Custom { db, require_create_flag: true },
6794 Default::default(),
6795 );
6796 Self {
6797 backend: Some(backend),
6798 kind,
6799 blocks_pruning,
6800 tmp_path: Some(tmp_path),
6801 _tmp: Some(tmp),
6802 }
6803 },
6804 }
6805 }
6806
6807 fn backend(&self) -> &Backend<Block> {
6808 self.backend.as_ref().expect("backend present")
6809 }
6810
6811 fn refresh_for_assertion(&mut self) {
6814 if !matches!(self.kind, BackendKind::ParityDb) {
6815 return;
6816 }
6817 let path = self.tmp_path.clone().expect("paritydb has tmp_path");
6818 self.backend = None;
6819 self.backend = Some(Backend::new_test_with_tx_storage_source(
6820 self.blocks_pruning,
6821 10,
6822 DatabaseSource::ParityDb { path },
6823 Default::default(),
6824 ));
6825 }
6826 }
6827
6828 #[rstest]
6829 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
6830 #[case::paritydb(BackendKind::ParityDb)]
6831 #[case::rocksdb(BackendKind::RocksDb)]
6832 fn prefetched_multi_renew_same_hash_balanced_lifecycle(#[case] kind: BackendKind) {
6833 let mut factory = BackendFactory::new(kind, BlocksPruning::Some(2));
6834 let payload = b"prefetched-blob".to_vec();
6835 let payload_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&payload);
6836 let payload_hash_arr: [u8; 32] = payload_hash.into();
6837
6838 let mut blocks = Vec::new();
6839 let block0 = insert_block_with_prefetched(
6840 factory.backend(),
6841 0,
6842 Default::default(),
6843 Default::default(),
6844 vec![UncheckedXt::new_transaction(0.into(), ())],
6845 Some(vec![
6846 IndexOperation::Renew { extrinsic: 0, hash: payload_hash_arr.into() },
6847 IndexOperation::Renew { extrinsic: 0, hash: payload_hash_arr.into() },
6848 ]),
6849 HashMap::from([(payload_hash, payload.clone())]),
6850 )
6851 .unwrap();
6852 blocks.push(block0);
6853
6854 assert!(factory
6855 .backend()
6856 .blockchain()
6857 .indexed_transaction(payload_hash)
6858 .unwrap()
6859 .is_some());
6860
6861 let mut prev = block0;
6862 for i in 1..6u64 {
6863 prev = insert_block(
6864 factory.backend(),
6865 i,
6866 prev,
6867 None,
6868 Default::default(),
6869 vec![UncheckedXt::new_transaction(i.into(), ())],
6870 None,
6871 )
6872 .unwrap();
6873 blocks.push(prev);
6874 }
6875
6876 for i in 1..6 {
6877 let mut op = factory.backend().begin_operation().unwrap();
6878 factory.backend().begin_state_operation(&mut op, blocks[4]).unwrap();
6879 op.mark_finalized(blocks[i], None).unwrap();
6880 factory.backend().commit_operation(op).unwrap();
6881 }
6882
6883 factory.refresh_for_assertion();
6884 assert!(factory
6885 .backend()
6886 .blockchain()
6887 .indexed_transaction(payload_hash)
6888 .unwrap()
6889 .is_none());
6890 }
6891
6892 #[rstest]
6893 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
6894 #[case::paritydb(BackendKind::ParityDb)]
6895 #[case::rocksdb(BackendKind::RocksDb)]
6896 fn prefetched_single_renew_full_lifecycle(#[case] kind: BackendKind) {
6897 let mut factory = BackendFactory::new(kind, BlocksPruning::Some(2));
6898 let payload = b"prefetched-blob".to_vec();
6899 let payload_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&payload);
6900 let payload_hash_arr: [u8; 32] = payload_hash.into();
6901
6902 let mut blocks = Vec::new();
6903 let block0 = insert_block_with_prefetched(
6904 factory.backend(),
6905 0,
6906 Default::default(),
6907 Default::default(),
6908 vec![UncheckedXt::new_transaction(0.into(), ())],
6909 Some(vec![IndexOperation::Renew { extrinsic: 0, hash: payload_hash_arr.into() }]),
6910 HashMap::from([(payload_hash, payload.clone())]),
6911 )
6912 .unwrap();
6913 blocks.push(block0);
6914
6915 assert_eq!(
6916 factory
6917 .backend()
6918 .blockchain()
6919 .indexed_transaction(payload_hash)
6920 .unwrap()
6921 .as_deref(),
6922 Some(payload.as_slice()),
6923 );
6924
6925 let mut prev = block0;
6926 for i in 1..6u64 {
6927 prev = insert_block(
6928 factory.backend(),
6929 i,
6930 prev,
6931 None,
6932 Default::default(),
6933 vec![UncheckedXt::new_transaction(i.into(), ())],
6934 None,
6935 )
6936 .unwrap();
6937 blocks.push(prev);
6938 }
6939
6940 for i in 1..6 {
6941 let mut op = factory.backend().begin_operation().unwrap();
6942 factory.backend().begin_state_operation(&mut op, blocks[4]).unwrap();
6943 op.mark_finalized(blocks[i], None).unwrap();
6944 factory.backend().commit_operation(op).unwrap();
6945 }
6946
6947 factory.refresh_for_assertion();
6948 assert!(factory
6949 .backend()
6950 .blockchain()
6951 .indexed_transaction(payload_hash)
6952 .unwrap()
6953 .is_none());
6954 }
6955
6956 #[rstest]
6957 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
6958 #[case::paritydb(BackendKind::ParityDb)]
6959 #[case::rocksdb(BackendKind::RocksDb)]
6960 fn redundant_prefetch_on_local_data_balanced_lifecycle(#[case] kind: BackendKind) {
6961 let mut factory = BackendFactory::new(kind, BlocksPruning::Some(2));
6962 let payload_xt = UncheckedXt::new_transaction(5.into(), ()).encode();
6963 let payload = payload_xt[1..].to_vec();
6964 let payload_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&payload);
6965 let payload_hash_arr: [u8; 32] = payload_hash.into();
6966
6967 let mut blocks = Vec::new();
6968 let block0 = insert_block(
6969 factory.backend(),
6970 0,
6971 Default::default(),
6972 None,
6973 Default::default(),
6974 vec![UncheckedXt::new_transaction(5.into(), ())],
6975 Some(vec![IndexOperation::Insert {
6976 extrinsic: 0,
6977 hash: payload_hash_arr.into(),
6978 size: payload.len() as u32,
6979 }]),
6980 )
6981 .unwrap();
6982 blocks.push(block0);
6983
6984 let block1 = insert_block_with_prefetched(
6985 factory.backend(),
6986 1,
6987 block0,
6988 Default::default(),
6989 vec![UncheckedXt::new_transaction(99.into(), ())],
6990 Some(vec![IndexOperation::Renew { extrinsic: 0, hash: payload_hash_arr.into() }]),
6991 HashMap::from([(payload_hash, payload.clone())]),
6992 )
6993 .unwrap();
6994 blocks.push(block1);
6995
6996 assert!(factory
6997 .backend()
6998 .blockchain()
6999 .indexed_transaction(payload_hash)
7000 .unwrap()
7001 .is_some());
7002
7003 let mut prev = block1;
7004 for i in 2..7u64 {
7005 prev = insert_block(
7006 factory.backend(),
7007 i,
7008 prev,
7009 None,
7010 Default::default(),
7011 vec![UncheckedXt::new_transaction(i.into(), ())],
7012 None,
7013 )
7014 .unwrap();
7015 blocks.push(prev);
7016 }
7017
7018 for i in 1..7 {
7019 let mut op = factory.backend().begin_operation().unwrap();
7020 factory.backend().begin_state_operation(&mut op, blocks[5]).unwrap();
7021 op.mark_finalized(blocks[i], None).unwrap();
7022 factory.backend().commit_operation(op).unwrap();
7023 }
7024
7025 factory.refresh_for_assertion();
7026 assert!(
7027 factory
7028 .backend()
7029 .blockchain()
7030 .indexed_transaction(payload_hash)
7031 .unwrap()
7032 .is_none(),
7033 "redundant prefetch must not leak refcount through prune",
7034 );
7035 }
7036
7037 #[rstest]
7038 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
7039 #[case::paritydb(BackendKind::ParityDb)]
7040 #[case::rocksdb(BackendKind::RocksDb)]
7041 fn same_block_insert_and_renew_different_indices_with_prefetch(#[case] kind: BackendKind) {
7042 let mut factory = BackendFactory::new(kind, BlocksPruning::Some(2));
7043 let x_xt = UncheckedXt::new_transaction(0.into(), ()).encode();
7044 let x = x_xt[1..].to_vec();
7045 let x_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&x);
7046 let x_hash_arr: [u8; 32] = x_hash.into();
7047
7048 let mut blocks = Vec::new();
7049
7050 let block0 = insert_block(
7051 factory.backend(),
7052 0,
7053 Default::default(),
7054 None,
7055 Default::default(),
7056 vec![UncheckedXt::new_transaction(0.into(), ())],
7057 Some(vec![IndexOperation::Insert {
7058 extrinsic: 0,
7059 hash: x_hash_arr.into(),
7060 size: x.len() as u32,
7061 }]),
7062 )
7063 .unwrap();
7064 blocks.push(block0);
7065
7066 let block1 = insert_block_with_prefetched(
7067 factory.backend(),
7068 1,
7069 block0,
7070 Default::default(),
7071 vec![
7072 UncheckedXt::new_transaction(0.into(), ()),
7073 UncheckedXt::new_transaction(99.into(), ()),
7074 ],
7075 Some(vec![
7076 IndexOperation::Insert {
7077 extrinsic: 0,
7078 hash: x_hash_arr.into(),
7079 size: x.len() as u32,
7080 },
7081 IndexOperation::Renew { extrinsic: 1, hash: x_hash_arr.into() },
7082 ]),
7083 HashMap::from([(x_hash, x.clone())]),
7084 )
7085 .unwrap();
7086 blocks.push(block1);
7087
7088 assert!(factory.backend().blockchain().indexed_transaction(x_hash).unwrap().is_some());
7089
7090 let mut prev = block1;
7091 for i in 2..8u64 {
7092 prev = insert_block(
7093 factory.backend(),
7094 i,
7095 prev,
7096 None,
7097 Default::default(),
7098 vec![UncheckedXt::new_transaction(i.into(), ())],
7099 None,
7100 )
7101 .unwrap();
7102 blocks.push(prev);
7103 }
7104
7105 for i in 1..8 {
7106 let mut op = factory.backend().begin_operation().unwrap();
7107 factory.backend().begin_state_operation(&mut op, blocks[6]).unwrap();
7108 op.mark_finalized(blocks[i], None).unwrap();
7109 factory.backend().commit_operation(op).unwrap();
7110 }
7111
7112 factory.refresh_for_assertion();
7113 assert!(
7114 factory.backend().blockchain().indexed_transaction(x_hash).unwrap().is_none(),
7115 "same-block Insert+Renew with prefetch must balance refcount through prune",
7116 );
7117 }
7118
7119 #[rstest]
7120 #[case::kvdb_memdb(BackendKind::KvdbMemdb)]
7121 #[case::paritydb(BackendKind::ParityDb)]
7122 #[case::rocksdb(BackendKind::RocksDb)]
7123 fn sequential_renew_blocks_all_prefetched_eventually_pruned(#[case] kind: BackendKind) {
7124 let mut factory = BackendFactory::new(kind, BlocksPruning::Some(2));
7125 let payload = b"prefetched-blob".to_vec();
7126 let payload_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&payload);
7127 let payload_hash_arr: [u8; 32] = payload_hash.into();
7128
7129 let mut blocks = Vec::new();
7130 let mut prev = Default::default();
7131 for i in 0..4u64 {
7132 let block = insert_block_with_prefetched(
7133 factory.backend(),
7134 i,
7135 prev,
7136 Default::default(),
7137 vec![UncheckedXt::new_transaction(i.into(), ())],
7138 Some(vec![IndexOperation::Renew {
7139 extrinsic: 0,
7140 hash: payload_hash_arr.into(),
7141 }]),
7142 HashMap::from([(payload_hash, payload.clone())]),
7143 )
7144 .unwrap();
7145 blocks.push(block);
7146 prev = block;
7147 }
7148
7149 assert!(factory
7150 .backend()
7151 .blockchain()
7152 .indexed_transaction(payload_hash)
7153 .unwrap()
7154 .is_some());
7155
7156 for i in 4..10u64 {
7157 prev = insert_block(
7158 factory.backend(),
7159 i,
7160 prev,
7161 None,
7162 Default::default(),
7163 vec![UncheckedXt::new_transaction(i.into(), ())],
7164 None,
7165 )
7166 .unwrap();
7167 blocks.push(prev);
7168 }
7169
7170 for i in 1..10 {
7171 let mut op = factory.backend().begin_operation().unwrap();
7172 factory.backend().begin_state_operation(&mut op, blocks[8]).unwrap();
7173 op.mark_finalized(blocks[i], None).unwrap();
7174 factory.backend().commit_operation(op).unwrap();
7175 }
7176
7177 factory.refresh_for_assertion();
7178 assert!(
7179 factory
7180 .backend()
7181 .blockchain()
7182 .indexed_transaction(payload_hash)
7183 .unwrap()
7184 .is_none(),
7185 "sequential prefetched renews must all release through prune",
7186 );
7187 }
7188
7189 #[test]
7192 fn runtime_index_ops_win_over_synthetic() {
7193 let factory = BackendFactory::new(BackendKind::KvdbMemdb, BlocksPruning::KeepAll);
7194 let payload_xt = UncheckedXt::new_transaction(11.into(), ()).encode();
7195 let payload = payload_xt[1..].to_vec();
7196 let payload_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&payload);
7197 let payload_hash_arr: [u8; 32] = payload_hash.into();
7198
7199 let bogus_hash_arr = [0xAAu8; 32];
7200
7201 insert_block_with_synthetic_ops(
7202 factory.backend(),
7203 0,
7204 Default::default(),
7205 Default::default(),
7206 vec![UncheckedXt::new_transaction(11.into(), ())],
7207 vec![IndexOperation::Insert {
7208 extrinsic: 0,
7209 hash: payload_hash_arr.into(),
7210 size: payload.len() as u32,
7211 }],
7212 vec![IndexOperation::Insert {
7213 extrinsic: 0,
7214 hash: bogus_hash_arr.into(),
7215 size: payload.len() as u32,
7216 }],
7217 HashMap::new(),
7218 )
7219 .unwrap();
7220
7221 assert_eq!(
7222 factory
7223 .backend()
7224 .blockchain()
7225 .indexed_transaction(payload_hash)
7226 .unwrap()
7227 .as_deref(),
7228 Some(payload.as_slice()),
7229 "runtime ops win",
7230 );
7231 assert!(
7232 factory
7233 .backend()
7234 .blockchain()
7235 .indexed_transaction(bogus_hash_arr.into())
7236 .unwrap()
7237 .is_none(),
7238 "synthetic dropped",
7239 );
7240 }
7241
7242 #[test]
7243 fn empty_both_falls_back_to_plain_body() {
7244 let factory = BackendFactory::new(BackendKind::KvdbMemdb, BlocksPruning::KeepAll);
7245 let body = vec![UncheckedXt::new_transaction(42.into(), ())];
7246
7247 let block_hash = insert_block_with_synthetic_ops(
7248 factory.backend(),
7249 0,
7250 Default::default(),
7251 Default::default(),
7252 body.clone(),
7253 Vec::new(),
7254 Vec::new(),
7255 HashMap::new(),
7256 )
7257 .unwrap();
7258
7259 let stored_body = factory.backend().blockchain().body(block_hash).unwrap();
7260 assert_eq!(stored_body, Some(body));
7261 }
7262
7263 #[test]
7264 fn synthetic_renew_uses_prefetched_payload() {
7265 let factory = BackendFactory::new(BackendKind::KvdbMemdb, BlocksPruning::KeepAll);
7266 let payload = b"prefetched-blob".to_vec();
7267 let payload_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&payload);
7268 let payload_hash_arr: [u8; 32] = payload_hash.into();
7269
7270 insert_block_with_synthetic_ops(
7271 factory.backend(),
7272 0,
7273 Default::default(),
7274 Default::default(),
7275 vec![UncheckedXt::new_transaction(1.into(), ())],
7276 Vec::new(),
7277 vec![IndexOperation::Renew { extrinsic: 0, hash: payload_hash_arr.into() }],
7278 HashMap::from([(payload_hash, payload.clone())]),
7279 )
7280 .unwrap();
7281
7282 assert_eq!(
7283 factory
7284 .backend()
7285 .blockchain()
7286 .indexed_transaction(payload_hash)
7287 .unwrap()
7288 .as_deref(),
7289 Some(payload.as_slice()),
7290 );
7291 }
7292
7293 #[test]
7294 fn synthetic_renew_without_prefetched_references_existing() {
7295 let factory = BackendFactory::new(BackendKind::KvdbMemdb, BlocksPruning::KeepAll);
7296 let payload_xt = UncheckedXt::new_transaction(5.into(), ()).encode();
7297 let payload = payload_xt[1..].to_vec();
7298 let payload_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&payload);
7299 let payload_hash_arr: [u8; 32] = payload_hash.into();
7300
7301 let block0 = insert_block(
7302 factory.backend(),
7303 0,
7304 Default::default(),
7305 None,
7306 Default::default(),
7307 vec![UncheckedXt::new_transaction(5.into(), ())],
7308 Some(vec![IndexOperation::Insert {
7309 extrinsic: 0,
7310 hash: payload_hash_arr.into(),
7311 size: payload.len() as u32,
7312 }]),
7313 )
7314 .unwrap();
7315
7316 insert_block_with_synthetic_ops(
7317 factory.backend(),
7318 1,
7319 block0,
7320 Default::default(),
7321 vec![UncheckedXt::new_transaction(6.into(), ())],
7322 Vec::new(),
7323 vec![IndexOperation::Renew { extrinsic: 0, hash: payload_hash_arr.into() }],
7324 HashMap::new(),
7325 )
7326 .unwrap();
7327
7328 assert_eq!(
7329 factory
7330 .backend()
7331 .blockchain()
7332 .indexed_transaction(payload_hash)
7333 .unwrap()
7334 .as_deref(),
7335 Some(payload.as_slice()),
7336 );
7337 }
7338
7339 #[test]
7340 fn synthetic_insert_extracts_tail_from_body() {
7341 let factory = BackendFactory::new(BackendKind::KvdbMemdb, BlocksPruning::KeepAll);
7342 let payload_xt = UncheckedXt::new_transaction(13.into(), ()).encode();
7343 let tail_size = 4u32;
7344 let tail_start = payload_xt.len() - tail_size as usize;
7345 let expected_tail = payload_xt[tail_start..].to_vec();
7346 let tail_hash = <HashingFor<Block> as sp_core::Hasher>::hash(&expected_tail);
7347 let tail_hash_arr: [u8; 32] = tail_hash.into();
7348
7349 insert_block_with_synthetic_ops(
7350 factory.backend(),
7351 0,
7352 Default::default(),
7353 Default::default(),
7354 vec![UncheckedXt::new_transaction(13.into(), ())],
7355 Vec::new(),
7356 vec![IndexOperation::Insert {
7357 extrinsic: 0,
7358 hash: tail_hash_arr.into(),
7359 size: tail_size,
7360 }],
7361 HashMap::new(),
7362 )
7363 .unwrap();
7364
7365 assert_eq!(
7366 factory
7367 .backend()
7368 .blockchain()
7369 .indexed_transaction(tail_hash)
7370 .unwrap()
7371 .as_deref(),
7372 Some(expected_tail.as_slice()),
7373 );
7374 }
7375
7376 #[test]
7377 fn synthetic_insert_oversized_size_falls_back_to_full_extrinsic() {
7378 let factory = BackendFactory::new(BackendKind::KvdbMemdb, BlocksPruning::KeepAll);
7379 let payload_xt = UncheckedXt::new_transaction(17.into(), ()).encode();
7380 let bogus_hash_arr = [0xBBu8; 32];
7381 let oversized = (payload_xt.len() + 1) as u32;
7382
7383 let block_hash = insert_block_with_synthetic_ops(
7384 factory.backend(),
7385 0,
7386 Default::default(),
7387 Default::default(),
7388 vec![UncheckedXt::new_transaction(17.into(), ())],
7389 Vec::new(),
7390 vec![IndexOperation::Insert {
7391 extrinsic: 0,
7392 hash: bogus_hash_arr.into(),
7393 size: oversized,
7394 }],
7395 HashMap::new(),
7396 )
7397 .unwrap();
7398
7399 assert!(factory
7400 .backend()
7401 .blockchain()
7402 .indexed_transaction(bogus_hash_arr.into())
7403 .unwrap()
7404 .is_none());
7405 let stored_body = factory.backend().blockchain().body(block_hash).unwrap();
7406 assert_eq!(stored_body, Some(vec![UncheckedXt::new_transaction(17.into(), ())]));
7407 }
7408
7409 #[test]
7410 fn multiple_synthetic_ops_per_block_apply_in_order() {
7411 let factory = BackendFactory::new(BackendKind::KvdbMemdb, BlocksPruning::KeepAll);
7412 let xt_a = UncheckedXt::new_transaction(21.into(), ()).encode();
7413 let xt_b = UncheckedXt::new_transaction(22.into(), ()).encode();
7414 let payload_a = xt_a[1..].to_vec();
7415 let payload_b = xt_b[1..].to_vec();
7416 let hash_a = <HashingFor<Block> as sp_core::Hasher>::hash(&payload_a);
7417 let hash_b = <HashingFor<Block> as sp_core::Hasher>::hash(&payload_b);
7418 let hash_a_arr: [u8; 32] = hash_a.into();
7419 let hash_b_arr: [u8; 32] = hash_b.into();
7420
7421 insert_block_with_synthetic_ops(
7422 factory.backend(),
7423 0,
7424 Default::default(),
7425 Default::default(),
7426 vec![
7427 UncheckedXt::new_transaction(21.into(), ()),
7428 UncheckedXt::new_transaction(22.into(), ()),
7429 ],
7430 Vec::new(),
7431 vec![
7432 IndexOperation::Insert {
7433 extrinsic: 0,
7434 hash: hash_a_arr.into(),
7435 size: payload_a.len() as u32,
7436 },
7437 IndexOperation::Insert {
7438 extrinsic: 1,
7439 hash: hash_b_arr.into(),
7440 size: payload_b.len() as u32,
7441 },
7442 ],
7443 HashMap::new(),
7444 )
7445 .unwrap();
7446
7447 assert_eq!(
7448 factory.backend().blockchain().indexed_transaction(hash_a).unwrap().as_deref(),
7449 Some(payload_a.as_slice()),
7450 "first op",
7451 );
7452 assert_eq!(
7453 factory.backend().blockchain().indexed_transaction(hash_b).unwrap().as_deref(),
7454 Some(payload_b.as_slice()),
7455 "second op",
7456 );
7457 }
7458 }
7459}