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(with_metrics)]
679pub fn init_metrics() {
680 linera_base::init_metrics();
681 linera_cache::init_metrics();
682 linera_chain::init_metrics();
683 linera_execution::init_metrics();
684 linera_views::init_metrics();
685 db_storage::metrics::init_metrics();
686}
687
688#[cfg(test)]
689mod tests {
690 use std::collections::BTreeMap;
691
692 use linera_base::{
693 crypto::{AccountPublicKey, CryptoHash},
694 data_types::{
695 Amount, ApplicationPermissions, Blob, BlockHeight, ChainDescription, ChainOrigin,
696 Epoch, InitialChainConfig, NetworkDescription, Round, Timestamp,
697 },
698 identifiers::{AccountOwner, BlobId, BlobType, ChainId, EventId, StreamId},
699 ownership::ChainOwnership,
700 };
701 use linera_chain::{
702 block::{Block, ConfirmedBlock},
703 data_types::{BlockExecutionOutcome, ProposedBlock},
704 };
705 use linera_execution::{BlobOrigin, BlobState};
706 #[cfg(feature = "scylladb")]
707 use linera_views::scylla_db::ScyllaDbDatabase;
708 use linera_views::{memory::MemoryDatabase, ViewError};
709 use test_case::test_case;
710
711 use super::*;
712 use crate::db_storage::DbStorage;
713
714 async fn test_storage_chain_exporter<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
716 where
717 S::Context: Send + Sync,
718 {
719 let _current_time = storage.clock().current_time();
721 let test_chain_id = ChainId(CryptoHash::test_hash("test_chain"));
722
723 let _chain_view = storage.load_chain(test_chain_id).await?;
725
726 let _block_exporter_context = storage.block_exporter_context(0).await?;
728 Ok(())
729 }
730
731 async fn test_storage_blob<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
732 where
733 S::Context: Send + Sync,
734 {
735 let chain_description = ChainDescription::new(
737 ChainOrigin::Root(0),
738 InitialChainConfig {
739 ownership: ChainOwnership::single(AccountPublicKey::test_key(0).into()),
740 epoch: Epoch::ZERO,
741 account: AccountOwner::CHAIN,
742 balance: Amount::ZERO,
743 application_permissions: ApplicationPermissions::default(),
744 },
745 Timestamp::from(0),
746 );
747
748 let test_blob1 = Blob::new_chain_description(&chain_description);
749 let test_blob2 = Blob::new_data(vec![10, 20, 30]);
750 let test_blob3 = Blob::new_data(vec![40, 50, 60]);
751
752 let blob_id1 = test_blob1.id();
754 let blob_id2 = test_blob2.id();
755 let blob_id3 = test_blob3.id();
756
757 assert!(!storage.contains_blob(blob_id1).await?);
759 assert!(!storage.contains_blob(blob_id2).await?);
760 assert!(!storage.contains_blob(blob_id3).await?);
761
762 storage.write_blob(&test_blob1).await?;
764 assert!(storage.contains_blob(blob_id1).await?);
765
766 storage
768 .write_blobs(&[test_blob2.clone(), test_blob3.clone()])
769 .await?;
770 assert!(storage.contains_blob(blob_id2).await?);
771 assert!(storage.contains_blob(blob_id3).await?);
772
773 let read_blob = storage.read_blob(blob_id1).await?;
775 assert_eq!(read_blob.as_deref(), Some(&test_blob1));
776
777 let blob_ids = vec![blob_id1, blob_id2, blob_id3];
779 let read_blobs = storage.read_blobs(&blob_ids).await?;
780 assert_eq!(read_blobs.len(), 3);
781
782 assert_eq!(read_blobs[0].as_deref(), Some(&test_blob1));
784 assert_eq!(read_blobs[1].as_deref(), Some(&test_blob2));
785 assert_eq!(read_blobs[2].as_deref(), Some(&test_blob3));
786
787 let missing_blob_id = BlobId::new(CryptoHash::test_hash("missing"), BlobType::Data);
789 let missing_blobs = storage.missing_blobs(&[blob_id1, missing_blob_id]).await?;
790 assert_eq!(missing_blobs, vec![missing_blob_id]);
791
792 let write_results = storage
794 .maybe_write_blobs(std::slice::from_ref(&test_blob1))
795 .await?;
796 assert_eq!(write_results, vec![false]);
797
798 let blob_state1 = BlobState {
800 origin: BlobOrigin::Published {
801 chain_id: ChainId(CryptoHash::test_hash("chain1")),
802 block_height: BlockHeight(0),
803 },
804 last_used_by: None,
805 epoch: Some(Epoch::ZERO),
806 };
807 let blob_state2 = BlobState {
808 origin: BlobOrigin::Published {
809 chain_id: ChainId(CryptoHash::test_hash("chain2")),
810 block_height: BlockHeight(1),
811 },
812 last_used_by: Some(CryptoHash::test_hash("cert")),
813 epoch: Some(Epoch::from(1)),
814 };
815
816 assert!(!storage.contains_blob_state(blob_id1).await?);
818 assert!(!storage.contains_blob_state(blob_id2).await?);
819
820 storage
822 .maybe_write_blob_states(&[blob_id1], blob_state1.clone())
823 .await?;
824 storage
825 .maybe_write_blob_states(&[blob_id2], blob_state2.clone())
826 .await?;
827
828 assert!(storage.contains_blob_state(blob_id1).await?);
830 assert!(storage.contains_blob_state(blob_id2).await?);
831
832 let read_blob_state = storage.read_blob_state(blob_id1).await?;
834 assert_eq!(read_blob_state, Some(blob_state1.clone()));
835
836 let read_blob_states = storage.read_blob_states(&[blob_id1, blob_id2]).await?;
838 assert_eq!(read_blob_states.len(), 2);
839
840 assert_eq!(read_blob_states[0], Some(blob_state1));
842 assert_eq!(read_blob_states[1], Some(blob_state2));
843
844 let write_results = storage
846 .maybe_write_blobs(std::slice::from_ref(&test_blob1))
847 .await?;
848 assert_eq!(write_results, vec![true]);
849
850 Ok(())
851 }
852
853 async fn test_storage_certificate<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
854 where
855 S::Context: Send + Sync,
856 {
857 let cert_hash = CryptoHash::test_hash("certificate");
858
859 assert!(!storage.contains_certificate(cert_hash).await?);
861
862 assert!(storage.read_certificate(cert_hash).await?.is_none());
864
865 let cert_hashes = vec![cert_hash, CryptoHash::test_hash("cert2")];
867 let certs_result = storage.read_certificates(&cert_hashes).await?;
868 assert_eq!(certs_result.len(), 2);
869 assert!(certs_result[0].is_none());
870 assert!(certs_result[1].is_none());
871
872 let raw_certs_result = storage.read_certificates_raw(&cert_hashes).await?;
874 assert!(raw_certs_result.iter().all(|cert| cert.is_none())); let block_hash = CryptoHash::test_hash("block");
878 let block_result = storage.read_confirmed_block(block_hash).await?;
879 assert!(block_result.is_none());
880
881 let test_blob1 = Blob::new_data(vec![1, 2, 3]);
884 let test_blob2 = Blob::new_data(vec![4, 5, 6]);
885 let blobs = vec![test_blob1, test_blob2];
886
887 let chain_id = ChainId(CryptoHash::test_hash("test_chain_cert"));
889
890 let proposed_block = ProposedBlock {
892 epoch: Epoch::ZERO,
893 chain_id,
894 transactions: vec![],
895 previous_block_hash: None,
896 height: BlockHeight::ZERO,
897 authenticated_owner: None,
898 timestamp: Timestamp::default(),
899 };
900
901 let outcome = BlockExecutionOutcome {
903 messages: vec![],
904 state_hash: CryptoHash::default(),
905 oracle_responses: vec![],
906 events: vec![],
907 blobs: vec![],
908 operation_results: vec![],
909 previous_event_blocks: BTreeMap::new(),
910 previous_message_blocks: BTreeMap::new(),
911 };
912
913 let block = Block::new(proposed_block, outcome);
914 let confirmed_block = ConfirmedBlock::new(block);
915 let certificate = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
916
917 storage
919 .write_blobs_and_certificate(&blobs, &certificate)
920 .await?;
921
922 let cert_hash = certificate.hash();
924 assert!(storage.contains_certificate(cert_hash).await?);
925
926 let read_certificate = storage.read_certificate(cert_hash).await?;
928 assert!(read_certificate.is_some());
929 assert_eq!(read_certificate.unwrap().hash(), cert_hash);
930
931 for blob in &blobs {
933 assert!(storage.contains_blob(blob.id()).await?);
934 }
935
936 Ok(())
937 }
938
939 async fn test_storage_event<S: Storage + Sync>(storage: &S) -> Result<(), ViewError>
940 where
941 S::Context: Send + Sync,
942 {
943 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
944 let stream_id = StreamId::system("test_stream");
945
946 let event_id1 = EventId {
948 chain_id,
949 stream_id: stream_id.clone(),
950 index: 0,
951 };
952 let event_id2 = EventId {
953 chain_id,
954 stream_id: stream_id.clone(),
955 index: 1,
956 };
957 let event_id3 = EventId {
958 chain_id,
959 stream_id: stream_id.clone(),
960 index: 2,
961 };
962
963 let event_data1 = vec![1, 2, 3];
964 let event_data2 = vec![4, 5, 6];
965 let event_data3 = vec![7, 8, 9];
966
967 assert!(!storage.contains_event(event_id1.clone()).await?);
969 assert!(!storage.contains_event(event_id2.clone()).await?);
970
971 storage
973 .write_events([
974 (event_id1.clone(), event_data1.clone()),
975 (event_id2.clone(), event_data2.clone()),
976 (event_id3.clone(), event_data3.clone()),
977 ])
978 .await?;
979
980 assert!(storage.contains_event(event_id1.clone()).await?);
982 assert!(storage.contains_event(event_id2.clone()).await?);
983 assert!(storage.contains_event(event_id3.clone()).await?);
984
985 let read_event1 = storage.read_event(event_id1).await?;
987 assert_eq!(read_event1.as_deref(), Some(&event_data1));
988
989 let read_event2 = storage.read_event(event_id2).await?;
990 assert_eq!(read_event2.as_deref(), Some(&event_data2));
991
992 let events_from_index = storage
994 .read_events_from_index(&chain_id, &stream_id, 1)
995 .await?;
996 assert!(events_from_index.len() >= 2); Ok(())
998 }
999
1000 async fn test_storage_network_description<S: Storage + Sync>(
1001 storage: &S,
1002 ) -> Result<(), ViewError>
1003 where
1004 S::Context: Send + Sync,
1005 {
1006 let admin_chain_id = ChainId(CryptoHash::test_hash("test_chain_second"));
1007
1008 let network_desc = NetworkDescription {
1009 name: "test_network".to_string(),
1010 genesis_config_hash: CryptoHash::test_hash("genesis_config"),
1011 genesis_timestamp: Timestamp::from(0),
1012 genesis_committee_blob_hash: CryptoHash::test_hash("committee"),
1013 admin_chain_id,
1014 };
1015
1016 assert!(storage.read_network_description().await?.is_none());
1018
1019 storage.write_network_description(&network_desc).await?;
1021
1022 let read_desc = storage.read_network_description().await?;
1024 assert_eq!(read_desc, Some(network_desc));
1025
1026 Ok(())
1027 }
1028
1029 #[test_case(DbStorage::<MemoryDatabase, _>::make_test_storage(None).await; "memory")]
1031 #[cfg_attr(feature = "scylladb", test_case(DbStorage::<ScyllaDbDatabase, _>::make_test_storage(None).await; "scylla_db"))]
1032 #[test_log::test(tokio::test)]
1033 async fn test_storage_features<S: Storage + Sync>(storage: S) -> Result<(), ViewError>
1034 where
1035 S::Context: Send + Sync,
1036 {
1037 test_storage_chain_exporter(&storage).await?;
1038 test_storage_blob(&storage).await?;
1039 test_storage_certificate(&storage).await?;
1040 test_storage_event(&storage).await?;
1041 test_storage_network_description(&storage).await?;
1042 Ok(())
1043 }
1044}