1#![deny(missing_docs)]
7
8mod db_storage;
9
10use std::sync::Arc as StdArc;
11
12use async_trait::async_trait;
13use itertools::Itertools;
14use linera_base::{
15 crypto::CryptoHash,
16 data_types::{
17 ApplicationDescription, Blob, BlockHeight, ChainDescription, CompressedBytecode, Epoch,
18 NetworkDescription, TimeDelta, Timestamp,
19 },
20 identifiers::{ApplicationId, BlobId, BlobType, ChainId, EventId, IndexAndEvent, StreamId},
21 time::Duration,
22 vm::VmRuntime,
23};
24pub use linera_cache::{Arc, DEFAULT_CLEANUP_INTERVAL_SECS};
25use linera_chain::{
26 types::{ConfirmedBlock, ConfirmedBlockCertificate},
27 ChainError, ChainStateView,
28};
29use linera_execution::{
30 committee::Committee, BlobState, ExecutionError, ExecutionRuntimeConfig,
31 ExecutionRuntimeContext, SharedCommittees, TransactionTracker, UserContractCode,
32 UserServiceCode, WasmRuntime,
33};
34#[cfg(with_revm)]
35use linera_execution::{
36 evm::revm::{EvmContractModule, EvmServiceModule},
37 EvmRuntime,
38};
39#[cfg(with_wasm_runtime)]
40use linera_execution::{WasmContractModule, WasmServiceModule};
41use linera_views::{context::Context, views::RootView, ViewError};
42
43#[cfg(with_metrics)]
44pub use crate::db_storage::metrics;
45pub use crate::db_storage::{
46 ChainStatesFirstAssignment, DbStorage, RootKey, StorageCacheConfig, StorageCaches, WallClock,
47};
48#[cfg(with_testing)]
49pub use crate::db_storage::{TestClock, DEFAULT_STORAGE_CACHE_CONFIG};
50
51pub const DEFAULT_NAMESPACE: &str = "default";
53
54#[cfg_attr(not(web), async_trait)]
56#[cfg_attr(web, async_trait(?Send))]
57pub trait Storage: linera_base::util::traits::AutoTraits + Sized {
58 type Context: Context<Extra = ChainRuntimeContext<Self>> + Clone + 'static;
60
61 type Clock: Clock + Clone + Send + Sync;
63
64 type BlockExporterContext: Context<Extra = u32> + Clone;
66
67 fn clock(&self) -> &Self::Clock;
69
70 fn thread_pool(&self) -> &StdArc<linera_execution::ThreadPool>;
72
73 async fn load_chain(&self, id: ChainId) -> Result<ChainStateView<Self::Context>, ViewError>;
81
82 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError>;
84
85 async fn missing_blobs(&self, blob_ids: &[BlobId]) -> Result<Vec<BlobId>, ViewError>;
87
88 async fn contains_blob_state(&self, blob_id: BlobId) -> Result<bool, ViewError>;
90
91 async fn read_confirmed_block(
93 &self,
94 hash: CryptoHash,
95 ) -> Result<Option<Arc<ConfirmedBlock>>, ViewError>;
96
97 async fn read_confirmed_blocks<I: IntoIterator<Item = CryptoHash> + Send>(
99 &self,
100 hashes: I,
101 ) -> Result<Vec<Option<Arc<ConfirmedBlock>>>, ViewError>;
102
103 async fn read_blob(&self, blob_id: BlobId) -> Result<Option<Arc<Blob>>, ViewError>;
105
106 async fn read_blobs(&self, blob_ids: &[BlobId]) -> Result<Vec<Option<Arc<Blob>>>, ViewError>;
108
109 async fn read_blob_state(&self, blob_id: BlobId) -> Result<Option<BlobState>, ViewError>;
111
112 async fn read_blob_states(
114 &self,
115 blob_ids: &[BlobId],
116 ) -> Result<Vec<Option<BlobState>>, ViewError>;
117
118 async fn write_blob(&self, blob: &Blob) -> Result<(), ViewError>;
120
121 async fn write_blobs_and_certificate(
123 &self,
124 blobs: &[Blob],
125 certificate: &ConfirmedBlockCertificate,
126 ) -> Result<(), ViewError>;
127
128 async fn maybe_write_blobs(&self, blobs: &[Blob]) -> Result<Vec<bool>, ViewError>;
131
132 async fn maybe_write_blob_states(
134 &self,
135 blob_ids: &[BlobId],
136 blob_state: BlobState,
137 ) -> Result<(), ViewError>;
138
139 async fn write_blobs(&self, blobs: &[Blob]) -> Result<(), ViewError>;
141
142 async fn contains_certificate(&self, hash: CryptoHash) -> Result<bool, ViewError>;
144
145 fn cache_certificate(
152 &self,
153 certificate: ConfirmedBlockCertificate,
154 ) -> Arc<ConfirmedBlockCertificate>;
155
156 fn cache_blob(&self, blob: Blob) -> Arc<Blob>;
163
164 fn cache_confirmed_block(&self, block: ConfirmedBlock) -> Arc<ConfirmedBlock>;
171
172 async fn read_certificate(
174 &self,
175 hash: CryptoHash,
176 ) -> Result<Option<Arc<ConfirmedBlockCertificate>>, ViewError>;
177
178 async fn read_certificates(
180 &self,
181 hashes: &[CryptoHash],
182 ) -> Result<Vec<Option<Arc<ConfirmedBlockCertificate>>>, ViewError>;
183
184 async fn read_certificates_raw(
190 &self,
191 hashes: &[CryptoHash],
192 ) -> Result<Vec<Option<Arc<(Vec<u8>, Vec<u8>)>>>, ViewError>;
193
194 async fn read_certificates_by_heights(
198 &self,
199 chain_id: ChainId,
200 heights: &[BlockHeight],
201 ) -> Result<Vec<Option<Arc<ConfirmedBlockCertificate>>>, ViewError>;
202
203 async fn read_certificates_by_heights_raw(
208 &self,
209 chain_id: ChainId,
210 heights: &[BlockHeight],
211 ) -> Result<Vec<Option<Arc<(Vec<u8>, Vec<u8>)>>>, ViewError>;
212
213 async fn read_certificate_hashes_by_heights(
217 &self,
218 chain_id: ChainId,
219 heights: &[BlockHeight],
220 ) -> Result<Vec<Option<CryptoHash>>, ViewError>;
221
222 async fn read_event_block_heights(
225 &self,
226 event_ids: &[EventId],
227 ) -> Result<Vec<Option<BlockHeight>>, ViewError>;
228
229 async fn read_event(&self, id: EventId) -> Result<Option<Arc<Vec<u8>>>, ViewError>;
231
232 async fn contains_event(&self, id: EventId) -> Result<bool, ViewError>;
234
235 async fn read_events_from_index(
237 &self,
238 chain_id: &ChainId,
239 stream_id: &StreamId,
240 start_index: u32,
241 ) -> Result<Vec<IndexAndEvent>, ViewError>;
242
243 async fn write_events(
245 &self,
246 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
247 ) -> Result<(), ViewError>;
248
249 async fn read_network_description(&self) -> Result<Option<NetworkDescription>, ViewError>;
251
252 async fn write_network_description(
254 &self,
255 information: &NetworkDescription,
256 ) -> Result<(), ViewError>;
257
258 async fn create_chain(&self, description: ChainDescription) -> Result<(), ChainError>
266 where
267 ChainRuntimeContext<Self>: ExecutionRuntimeContext,
268 {
269 let id = description.id();
270 let description_blob = Blob::new_chain_description(&description);
274 let description_blob_id = description_blob.id();
275 self.write_blob(&description_blob).await?;
276 self.maybe_write_blob_states(&[description_blob_id], BlobState::GENESIS)
277 .await?;
278 let mut chain = self.load_chain(id).await?;
279 assert!(
280 !chain.is_active().await?,
281 "Attempting to create a chain twice"
282 );
283 let current_time = self.clock().current_time();
284 chain.initialize_if_needed(current_time).await?;
285 chain.save().await?;
286 Ok(())
287 }
288
289 fn wasm_runtime(&self) -> Option<WasmRuntime>;
291
292 async fn load_contract(
295 &self,
296 application_description: &ApplicationDescription,
297 txn_tracker: &TransactionTracker,
298 ) -> Result<UserContractCode, ExecutionError> {
299 let contract_bytecode_blob_id = application_description.contract_bytecode_blob_id();
300 let content = match txn_tracker.get_blob_content(&contract_bytecode_blob_id) {
301 Some(content) => content.clone(),
302 None => self
303 .read_blob(contract_bytecode_blob_id)
304 .await?
305 .ok_or(ExecutionError::BlobsNotFound(vec![
306 contract_bytecode_blob_id,
307 ]))?
308 .content()
309 .clone(),
310 };
311 let compressed_contract_bytecode = CompressedBytecode {
312 compressed_bytes: content.into_arc_bytes(),
313 };
314 #[cfg_attr(not(any(with_wasm_runtime, with_revm)), allow(unused_variables))]
315 let contract_bytecode = self
316 .thread_pool()
317 .run_send((), move |()| async move {
318 compressed_contract_bytecode.decompress()
319 })
320 .await
321 .await??;
322 match application_description.module_id.vm_runtime {
323 VmRuntime::Wasm => {
324 cfg_if::cfg_if! {
325 if #[cfg(with_wasm_runtime)] {
326 let Some(wasm_runtime) = self.wasm_runtime() else {
327 panic!("A Wasm runtime is required to load user applications.");
328 };
329 Ok(WasmContractModule::new(contract_bytecode, wasm_runtime)
330 .await?
331 .into())
332 } else {
333 panic!(
334 "A Wasm runtime is required to load user applications. \
335 Please enable the `wasmer` or the `wasmtime` feature flags \
336 when compiling `linera-storage`."
337 );
338 }
339 }
340 }
341 VmRuntime::Evm => {
342 cfg_if::cfg_if! {
343 if #[cfg(with_revm)] {
344 let evm_runtime = EvmRuntime::Revm;
345 Ok(EvmContractModule::new(contract_bytecode, evm_runtime)?
346 .into())
347 } else {
348 panic!(
349 "An Evm runtime is required to load user applications. \
350 Please enable the `revm` feature flag \
351 when compiling `linera-storage`."
352 );
353 }
354 }
355 }
356 }
357 }
358
359 async fn load_service(
362 &self,
363 application_description: &ApplicationDescription,
364 txn_tracker: &TransactionTracker,
365 ) -> Result<UserServiceCode, ExecutionError> {
366 let service_bytecode_blob_id = application_description.service_bytecode_blob_id();
367 let content = match txn_tracker.get_blob_content(&service_bytecode_blob_id) {
368 Some(content) => content.clone(),
369 None => self
370 .read_blob(service_bytecode_blob_id)
371 .await?
372 .ok_or(ExecutionError::BlobsNotFound(vec![
373 service_bytecode_blob_id,
374 ]))?
375 .content()
376 .clone(),
377 };
378 let compressed_service_bytecode = CompressedBytecode {
379 compressed_bytes: content.into_arc_bytes(),
380 };
381 #[cfg_attr(not(any(with_wasm_runtime, with_revm)), allow(unused_variables))]
382 let service_bytecode = self
383 .thread_pool()
384 .run_send((), move |()| async move {
385 compressed_service_bytecode.decompress()
386 })
387 .await
388 .await??;
389 match application_description.module_id.vm_runtime {
390 VmRuntime::Wasm => {
391 cfg_if::cfg_if! {
392 if #[cfg(with_wasm_runtime)] {
393 let Some(wasm_runtime) = self.wasm_runtime() else {
394 panic!("A Wasm runtime is required to load user applications.");
395 };
396 Ok(WasmServiceModule::new(service_bytecode, wasm_runtime)
397 .await?
398 .into())
399 } else {
400 panic!(
401 "A Wasm runtime is required to load user applications. \
402 Please enable the `wasmer` or the `wasmtime` feature flags \
403 when compiling `linera-storage`."
404 );
405 }
406 }
407 }
408 VmRuntime::Evm => {
409 cfg_if::cfg_if! {
410 if #[cfg(with_revm)] {
411 let evm_runtime = EvmRuntime::Revm;
412 Ok(EvmServiceModule::new(service_bytecode, evm_runtime)?
413 .into())
414 } else {
415 panic!(
416 "An Evm runtime is required to load user applications. \
417 Please enable the `revm` feature flag \
418 when compiling `linera-storage`."
419 );
420 }
421 }
422 }
423 }
424 }
425
426 async fn block_exporter_context(
428 &self,
429 block_exporter_id: u32,
430 ) -> Result<Self::BlockExporterContext, ViewError>;
431
432 fn shared_committees(&self) -> &SharedCommittees;
434
435 async fn get_or_load_committee_by_hash(
438 &self,
439 hash: CryptoHash,
440 ) -> Result<StdArc<Committee>, ExecutionError> {
441 if let Some(committee) = self.shared_committees().get(hash) {
442 return Ok(committee);
443 }
444 let blob_id = BlobId::new(hash, BlobType::Committee);
445 let blob = self
446 .read_blob(blob_id)
447 .await?
448 .ok_or(ExecutionError::BlobsNotFound(vec![blob_id]))?;
449 let committee = bcs::from_bytes(blob.bytes())?;
450 Ok(self
451 .shared_committees()
452 .insert(hash, StdArc::new(committee)))
453 }
454
455 async fn is_epoch_revoked(&self, epoch: Epoch) -> Result<bool, ExecutionError> {
458 let net_desc = self
459 .read_network_description()
460 .await?
461 .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
462 let event_id = EventId {
463 chain_id: net_desc.admin_chain_id,
464 stream_id: StreamId::system(linera_execution::system::REMOVED_EPOCH_STREAM_NAME),
465 index: epoch.0,
466 };
467 Ok(self.contains_event(event_id).await?)
468 }
469
470 async fn committee_for_epoch(
475 &self,
476 epoch: Epoch,
477 ) -> Result<Option<StdArc<Committee>>, ExecutionError> {
478 let blob_hash = if epoch == Epoch::ZERO {
479 self.read_network_description()
480 .await?
481 .ok_or(ExecutionError::NoNetworkDescriptionFound)?
482 .genesis_committee_blob_hash
483 } else {
484 let net_desc = self
485 .read_network_description()
486 .await?
487 .ok_or(ExecutionError::NoNetworkDescriptionFound)?;
488 let event_id = EventId {
489 chain_id: net_desc.admin_chain_id,
490 stream_id: StreamId::system(linera_execution::system::EPOCH_STREAM_NAME),
491 index: epoch.0,
492 };
493 let Some(bytes) = self.read_event(event_id).await? else {
494 return Ok(None);
495 };
496 let event_data: linera_execution::system::EpochEventData = bcs::from_bytes(&bytes)?;
497 event_data.blob_hash
498 };
499 Ok(Some(self.get_or_load_committee_by_hash(blob_hash).await?))
500 }
501
502 async fn list_blob_ids(&self) -> Result<Vec<BlobId>, ViewError>;
504
505 async fn list_chain_ids(&self) -> Result<Vec<ChainId>, ViewError>;
507
508 async fn list_event_ids(&self) -> Result<Vec<EventId>, ViewError>;
510}
511
512pub enum ResultReadCertificates {
514 Certificates(Vec<ConfirmedBlockCertificate>),
516 InvalidHashes(Vec<CryptoHash>),
518}
519
520impl ResultReadCertificates {
521 pub fn new(
523 certificates: Vec<Option<Arc<ConfirmedBlockCertificate>>>,
524 hashes: Vec<CryptoHash>,
525 ) -> Self {
526 let (certificates, invalid_hashes) = certificates
527 .into_iter()
528 .zip(hashes)
529 .partition_map::<Vec<_>, Vec<_>, _, _, _>(|(certificate, hash)| match certificate {
530 Some(cert) => itertools::Either::Left(Arc::unwrap_or_clone(cert)),
531 None => itertools::Either::Right(hash),
532 });
533 if invalid_hashes.is_empty() {
534 Self::Certificates(certificates)
535 } else {
536 Self::InvalidHashes(invalid_hashes)
537 }
538 }
539}
540
541#[derive(Clone)]
543pub struct ChainRuntimeContext<S> {
544 storage: S,
545 chain_id: ChainId,
546 thread_pool: StdArc<linera_execution::ThreadPool>,
547 execution_runtime_config: ExecutionRuntimeConfig,
548 user_contracts: StdArc<papaya::HashMap<ApplicationId, UserContractCode>>,
549 user_services: StdArc<papaya::HashMap<ApplicationId, UserServiceCode>>,
550}
551
552#[cfg_attr(not(web), async_trait)]
553#[cfg_attr(web, async_trait(?Send))]
554impl<S: Storage> ExecutionRuntimeContext for ChainRuntimeContext<S> {
555 fn chain_id(&self) -> ChainId {
556 self.chain_id
557 }
558
559 fn thread_pool(&self) -> &StdArc<linera_execution::ThreadPool> {
560 &self.thread_pool
561 }
562
563 fn execution_runtime_config(&self) -> linera_execution::ExecutionRuntimeConfig {
564 self.execution_runtime_config
565 }
566
567 fn user_contracts(&self) -> &StdArc<papaya::HashMap<ApplicationId, UserContractCode>> {
568 &self.user_contracts
569 }
570
571 fn user_services(&self) -> &StdArc<papaya::HashMap<ApplicationId, UserServiceCode>> {
572 &self.user_services
573 }
574
575 async fn get_user_contract(
576 &self,
577 description: &ApplicationDescription,
578 txn_tracker: &TransactionTracker,
579 ) -> Result<UserContractCode, ExecutionError> {
580 let application_id = description.into();
581 let pinned = self.user_contracts.pin_owned();
582 if let Some(contract) = pinned.get(&application_id) {
583 return Ok(contract.clone());
584 }
585 let contract = self.storage.load_contract(description, txn_tracker).await?;
586 pinned.insert(application_id, contract.clone());
587 Ok(contract)
588 }
589
590 async fn get_user_service(
591 &self,
592 description: &ApplicationDescription,
593 txn_tracker: &TransactionTracker,
594 ) -> Result<UserServiceCode, ExecutionError> {
595 let application_id = description.into();
596 let pinned = self.user_services.pin_owned();
597 if let Some(service) = pinned.get(&application_id) {
598 return Ok(service.clone());
599 }
600 let service = self.storage.load_service(description, txn_tracker).await?;
601 pinned.insert(application_id, service.clone());
602 Ok(service)
603 }
604
605 async fn get_blob(&self, blob_id: BlobId) -> Result<Option<StdArc<Blob>>, ViewError> {
606 Ok(self.storage.read_blob(blob_id).await?.map(Arc::into_std))
607 }
608
609 async fn get_event(&self, event_id: EventId) -> Result<Option<StdArc<Vec<u8>>>, ViewError> {
610 Ok(self.storage.read_event(event_id).await?.map(Arc::into_std))
611 }
612
613 async fn get_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
614 self.storage.read_network_description().await
615 }
616
617 async fn get_or_load_committee_by_hash(
618 &self,
619 hash: CryptoHash,
620 ) -> Result<StdArc<Committee>, ExecutionError> {
621 self.storage.get_or_load_committee_by_hash(hash).await
622 }
623
624 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
625 self.storage.contains_blob(blob_id).await
626 }
627
628 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
629 self.storage.contains_event(event_id).await
630 }
631
632 #[cfg(with_testing)]
633 async fn add_blobs(
634 &self,
635 blobs: impl IntoIterator<Item = Blob> + Send,
636 ) -> Result<(), ViewError> {
637 let blobs = Vec::from_iter(blobs);
638 self.storage.write_blobs(&blobs).await
639 }
640
641 #[cfg(with_testing)]
642 async fn add_events(
643 &self,
644 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
645 ) -> Result<(), ViewError> {
646 self.storage.write_events(events).await
647 }
648}
649
650#[cfg_attr(not(web), async_trait)]
652#[cfg_attr(web, async_trait(?Send))]
653pub trait Clock {
654 fn current_time(&self) -> Timestamp;
656
657 async fn sleep_until(&self, timestamp: Timestamp);
659
660 async fn sleep_for(&self, duration: Duration) {
665 self.sleep_until(
666 self.current_time()
667 .saturating_add(TimeDelta::from_duration(duration)),
668 )
669 .await
670 }
671}
672
673#[cfg(test)]
674mod tests {
675 use std::collections::BTreeMap;
676
677 use linera_base::{
678 crypto::{AccountPublicKey, CryptoHash},
679 data_types::{
680 Amount, ApplicationPermissions, Blob, BlockHeight, ChainDescription, ChainOrigin,
681 Epoch, InitialChainConfig, NetworkDescription, Round, Timestamp,
682 },
683 identifiers::{BlobId, BlobType, ChainId, EventId, StreamId},
684 ownership::ChainOwnership,
685 };
686 use linera_chain::{
687 block::{Block, ConfirmedBlock},
688 data_types::{BlockExecutionOutcome, ProposedBlock},
689 };
690 use linera_execution::{BlobOrigin, BlobState};
691 #[cfg(feature = "scylladb")]
692 use linera_views::scylla_db::ScyllaDbDatabase;
693 use linera_views::{memory::MemoryDatabase, ViewError};
694 use test_case::test_case;
695
696 use super::*;
697 use crate::db_storage::DbStorage;
698
699 async fn test_storage_chain_exporter<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
701 where
702 S::Context: Send + Sync,
703 {
704 let _current_time = storage.clock().current_time();
706 let test_chain_id = ChainId(CryptoHash::test_hash("test_chain"));
707
708 let _chain_view = storage.load_chain(test_chain_id).await?;
710
711 let _block_exporter_context = storage.block_exporter_context(0).await?;
713 Ok(())
714 }
715
716 async fn test_storage_blob<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
717 where
718 S::Context: Send + Sync,
719 {
720 let chain_description = ChainDescription::new(
722 ChainOrigin::Root(0),
723 InitialChainConfig {
724 ownership: ChainOwnership::single(AccountPublicKey::test_key(0).into()),
725 epoch: Epoch::ZERO,
726 balance: Amount::ZERO,
727 application_permissions: ApplicationPermissions::default(),
728 },
729 Timestamp::from(0),
730 );
731
732 let test_blob1 = Blob::new_chain_description(&chain_description);
733 let test_blob2 = Blob::new_data(vec![10, 20, 30]);
734 let test_blob3 = Blob::new_data(vec![40, 50, 60]);
735
736 let blob_id1 = test_blob1.id();
738 let blob_id2 = test_blob2.id();
739 let blob_id3 = test_blob3.id();
740
741 assert!(!storage.contains_blob(blob_id1).await?);
743 assert!(!storage.contains_blob(blob_id2).await?);
744 assert!(!storage.contains_blob(blob_id3).await?);
745
746 storage.write_blob(&test_blob1).await?;
748 assert!(storage.contains_blob(blob_id1).await?);
749
750 storage
752 .write_blobs(&[test_blob2.clone(), test_blob3.clone()])
753 .await?;
754 assert!(storage.contains_blob(blob_id2).await?);
755 assert!(storage.contains_blob(blob_id3).await?);
756
757 let read_blob = storage.read_blob(blob_id1).await?;
759 assert_eq!(read_blob.as_deref(), Some(&test_blob1));
760
761 let blob_ids = vec![blob_id1, blob_id2, blob_id3];
763 let read_blobs = storage.read_blobs(&blob_ids).await?;
764 assert_eq!(read_blobs.len(), 3);
765
766 assert_eq!(read_blobs[0].as_deref(), Some(&test_blob1));
768 assert_eq!(read_blobs[1].as_deref(), Some(&test_blob2));
769 assert_eq!(read_blobs[2].as_deref(), Some(&test_blob3));
770
771 let missing_blob_id = BlobId::new(CryptoHash::test_hash("missing"), BlobType::Data);
773 let missing_blobs = storage.missing_blobs(&[blob_id1, missing_blob_id]).await?;
774 assert_eq!(missing_blobs, vec![missing_blob_id]);
775
776 let write_results = storage
778 .maybe_write_blobs(std::slice::from_ref(&test_blob1))
779 .await?;
780 assert_eq!(write_results, vec![false]);
781
782 let blob_state1 = BlobState {
784 origin: BlobOrigin::Published {
785 chain_id: ChainId(CryptoHash::test_hash("chain1")),
786 block_height: BlockHeight(0),
787 },
788 last_used_by: None,
789 epoch: Some(Epoch::ZERO),
790 };
791 let blob_state2 = BlobState {
792 origin: BlobOrigin::Published {
793 chain_id: ChainId(CryptoHash::test_hash("chain2")),
794 block_height: BlockHeight(1),
795 },
796 last_used_by: Some(CryptoHash::test_hash("cert")),
797 epoch: Some(Epoch::from(1)),
798 };
799
800 assert!(!storage.contains_blob_state(blob_id1).await?);
802 assert!(!storage.contains_blob_state(blob_id2).await?);
803
804 storage
806 .maybe_write_blob_states(&[blob_id1], blob_state1.clone())
807 .await?;
808 storage
809 .maybe_write_blob_states(&[blob_id2], blob_state2.clone())
810 .await?;
811
812 assert!(storage.contains_blob_state(blob_id1).await?);
814 assert!(storage.contains_blob_state(blob_id2).await?);
815
816 let read_blob_state = storage.read_blob_state(blob_id1).await?;
818 assert_eq!(read_blob_state, Some(blob_state1.clone()));
819
820 let read_blob_states = storage.read_blob_states(&[blob_id1, blob_id2]).await?;
822 assert_eq!(read_blob_states.len(), 2);
823
824 assert_eq!(read_blob_states[0], Some(blob_state1));
826 assert_eq!(read_blob_states[1], Some(blob_state2));
827
828 let write_results = storage
830 .maybe_write_blobs(std::slice::from_ref(&test_blob1))
831 .await?;
832 assert_eq!(write_results, vec![true]);
833
834 Ok(())
835 }
836
837 async fn test_storage_certificate<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
838 where
839 S::Context: Send + Sync,
840 {
841 let cert_hash = CryptoHash::test_hash("certificate");
842
843 assert!(!storage.contains_certificate(cert_hash).await?);
845
846 assert!(storage.read_certificate(cert_hash).await?.is_none());
848
849 let cert_hashes = vec![cert_hash, CryptoHash::test_hash("cert2")];
851 let certs_result = storage.read_certificates(&cert_hashes).await?;
852 assert_eq!(certs_result.len(), 2);
853 assert!(certs_result[0].is_none());
854 assert!(certs_result[1].is_none());
855
856 let raw_certs_result = storage.read_certificates_raw(&cert_hashes).await?;
858 assert!(raw_certs_result.iter().all(|cert| cert.is_none())); let block_hash = CryptoHash::test_hash("block");
862 let block_result = storage.read_confirmed_block(block_hash).await?;
863 assert!(block_result.is_none());
864
865 let test_blob1 = Blob::new_data(vec![1, 2, 3]);
868 let test_blob2 = Blob::new_data(vec![4, 5, 6]);
869 let blobs = vec![test_blob1, test_blob2];
870
871 let chain_id = ChainId(CryptoHash::test_hash("test_chain_cert"));
873
874 let proposed_block = ProposedBlock {
876 epoch: Epoch::ZERO,
877 chain_id,
878 transactions: vec![],
879 previous_block_hash: None,
880 height: BlockHeight::ZERO,
881 authenticated_owner: None,
882 timestamp: Timestamp::default(),
883 };
884
885 let outcome = BlockExecutionOutcome {
887 messages: vec![],
888 state_hash: CryptoHash::default(),
889 oracle_responses: vec![],
890 events: vec![],
891 blobs: vec![],
892 operation_results: vec![],
893 previous_event_blocks: BTreeMap::new(),
894 previous_message_blocks: BTreeMap::new(),
895 };
896
897 let block = Block::new(proposed_block, outcome);
898 let confirmed_block = ConfirmedBlock::new(block);
899 let certificate = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
900
901 storage
903 .write_blobs_and_certificate(&blobs, &certificate)
904 .await?;
905
906 let cert_hash = certificate.hash();
908 assert!(storage.contains_certificate(cert_hash).await?);
909
910 let read_certificate = storage.read_certificate(cert_hash).await?;
912 assert!(read_certificate.is_some());
913 assert_eq!(read_certificate.unwrap().hash(), cert_hash);
914
915 for blob in &blobs {
917 assert!(storage.contains_blob(blob.id()).await?);
918 }
919
920 Ok(())
921 }
922
923 async fn test_storage_event<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
924 where
925 S::Context: Send + Sync,
926 {
927 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
928 let stream_id = StreamId::system("test_stream");
929
930 let event_id1 = EventId {
932 chain_id,
933 stream_id: stream_id.clone(),
934 index: 0,
935 };
936 let event_id2 = EventId {
937 chain_id,
938 stream_id: stream_id.clone(),
939 index: 1,
940 };
941 let event_id3 = EventId {
942 chain_id,
943 stream_id: stream_id.clone(),
944 index: 2,
945 };
946
947 let event_data1 = vec![1, 2, 3];
948 let event_data2 = vec![4, 5, 6];
949 let event_data3 = vec![7, 8, 9];
950
951 assert!(!storage.contains_event(event_id1.clone()).await?);
953 assert!(!storage.contains_event(event_id2.clone()).await?);
954
955 storage
957 .write_events([
958 (event_id1.clone(), event_data1.clone()),
959 (event_id2.clone(), event_data2.clone()),
960 (event_id3.clone(), event_data3.clone()),
961 ])
962 .await?;
963
964 assert!(storage.contains_event(event_id1.clone()).await?);
966 assert!(storage.contains_event(event_id2.clone()).await?);
967 assert!(storage.contains_event(event_id3.clone()).await?);
968
969 let read_event1 = storage.read_event(event_id1).await?;
971 assert_eq!(read_event1.as_deref(), Some(&event_data1));
972
973 let read_event2 = storage.read_event(event_id2).await?;
974 assert_eq!(read_event2.as_deref(), Some(&event_data2));
975
976 let events_from_index = storage
978 .read_events_from_index(&chain_id, &stream_id, 1)
979 .await?;
980 assert!(events_from_index.len() >= 2); Ok(())
982 }
983
984 async fn test_storage_network_description<S: Storage + Sync>(
985 storage: &S,
986 ) -> Result<(), ViewError>
987 where
988 S::Context: Send + Sync,
989 {
990 let admin_chain_id = ChainId(CryptoHash::test_hash("test_chain_second"));
991
992 let network_desc = NetworkDescription {
993 name: "test_network".to_string(),
994 genesis_config_hash: CryptoHash::test_hash("genesis_config"),
995 genesis_timestamp: Timestamp::from(0),
996 genesis_committee_blob_hash: CryptoHash::test_hash("committee"),
997 admin_chain_id,
998 };
999
1000 assert!(storage.read_network_description().await?.is_none());
1002
1003 storage.write_network_description(&network_desc).await?;
1005
1006 let read_desc = storage.read_network_description().await?;
1008 assert_eq!(read_desc, Some(network_desc));
1009
1010 Ok(())
1011 }
1012
1013 #[test_case(DbStorage::<MemoryDatabase, _>::make_test_storage(None).await; "memory")]
1015 #[cfg_attr(feature = "scylladb", test_case(DbStorage::<ScyllaDbDatabase, _>::make_test_storage(None).await; "scylla_db"))]
1016 #[test_log::test(tokio::test)]
1017 async fn test_storage_features<S: Storage + Sync>(storage: S) -> Result<(), ViewError>
1018 where
1019 S::Context: Send + Sync,
1020 {
1021 test_storage_chain_exporter(&storage).await?;
1022 test_storage_blob(&storage).await?;
1023 test_storage_certificate(&storage).await?;
1024 test_storage_event(&storage).await?;
1025 test_storage_network_description(&storage).await?;
1026 Ok(())
1027 }
1028}