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