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