1use std::{
5 collections::HashMap,
6 fmt::Debug,
7 sync::{Arc, OnceLock},
8};
9
10use async_trait::async_trait;
11#[cfg(with_metrics)]
12use linera_base::prometheus_util::MeasureLatency as _;
13use linera_base::{
14 crypto::CryptoHash,
15 data_types::{Blob, BlockHeight, NetworkDescription, TimeDelta, Timestamp},
16 identifiers::{ApplicationId, BlobId, ChainId, EventId, IndexAndEvent, StreamId},
17 time::Duration,
18};
19use linera_cache::{Arc as CacheArc, ValueCache};
20use linera_chain::{
21 types::{CertificateValue, ConfirmedBlock, ConfirmedBlockCertificate, LiteCertificate},
22 ChainStateView,
23};
24use linera_execution::{
25 BlobState, ExecutionRuntimeConfig, SharedCommittees, UserContractCode, UserServiceCode,
26 WasmRuntime,
27};
28use linera_views::{
29 backends::dual::{DualStoreRootKeyAssignment, StoreInUse},
30 batch::Batch,
31 context::ViewContext,
32 store::{
33 KeyValueDatabase, KeyValueStore, ReadableKeyValueStore as _, WritableKeyValueStore as _,
34 },
35 views::View,
36 ViewError,
37};
38use serde::{Deserialize, Serialize};
39use tracing::{debug, instrument};
40#[cfg(with_testing)]
41use {
42 futures::channel::oneshot::{self, Receiver},
43 linera_views::{random::generate_test_namespace, store::TestKeyValueDatabase},
44 std::{cmp::Reverse, collections::BTreeMap},
45};
46
47use crate::{ChainRuntimeContext, Clock, Storage};
48
49#[cfg(with_metrics)]
51pub mod metrics {
52 use std::sync::LazyLock;
53
54 use linera_base::prometheus_util::{
55 exponential_bucket_latencies, register_histogram_vec, register_int_counter,
56 register_int_counter_vec,
57 };
58 use prometheus::{HistogramVec, IntCounter, IntCounterVec};
59
60 pub(super) const SOURCE_LABEL: &str = "source";
62 pub(super) const CACHE: &str = "cache";
64 pub(super) const DB: &str = "db";
66
67 pub(super) static CONTAINS_BLOB_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
69 register_int_counter_vec(
70 "contains_blob",
71 "The metric counting how often a blob is tested for existence from storage",
72 &[SOURCE_LABEL],
73 )
74 });
75
76 pub(super) static CONTAINS_BLOBS_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
78 register_int_counter_vec(
79 "contains_blobs",
80 "The metric counting how often multiple blobs are tested for existence from storage",
81 &[SOURCE_LABEL],
82 )
83 });
84
85 pub(super) static CONTAINS_BLOB_STATE_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
87 register_int_counter_vec(
88 "contains_blob_state",
89 "The metric counting how often a blob state is tested for existence from storage",
90 &[SOURCE_LABEL],
91 )
92 });
93
94 pub(super) static CONTAINS_CERTIFICATE_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
96 register_int_counter_vec(
97 "contains_certificate",
98 "The metric counting how often a certificate is tested for existence from storage",
99 &[SOURCE_LABEL],
100 )
101 });
102
103 #[doc(hidden)]
105 pub static READ_CONFIRMED_BLOCK_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
106 register_int_counter_vec(
107 "read_confirmed_block",
108 "The metric counting how often a hashed confirmed block is read from storage",
109 &[SOURCE_LABEL],
110 )
111 });
112
113 #[doc(hidden)]
115 pub(super) static READ_CONFIRMED_BLOCKS_COUNTER: LazyLock<IntCounterVec> =
116 LazyLock::new(|| {
117 register_int_counter_vec(
118 "read_confirmed_blocks",
119 "The metric counting how often confirmed blocks are read from storage",
120 &[SOURCE_LABEL],
121 )
122 });
123
124 #[doc(hidden)]
126 pub(super) static READ_BLOB_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
127 register_int_counter_vec(
128 "read_blob",
129 "The metric counting how often a blob is read from storage",
130 &[SOURCE_LABEL],
131 )
132 });
133
134 #[doc(hidden)]
136 pub(super) static READ_BLOB_STATE_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
137 register_int_counter_vec(
138 "read_blob_state",
139 "The metric counting how often a blob state is read from storage",
140 &[SOURCE_LABEL],
141 )
142 });
143
144 #[doc(hidden)]
146 pub(super) static WRITE_BLOB_COUNTER: LazyLock<IntCounter> = LazyLock::new(|| {
147 register_int_counter(
148 "write_blob",
149 "The metric counting how often a blob is written to storage",
150 )
151 });
152
153 #[doc(hidden)]
155 pub static READ_CERTIFICATE_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
156 register_int_counter_vec(
157 "read_certificate",
158 "The metric counting how often a certificate is read from storage",
159 &[SOURCE_LABEL],
160 )
161 });
162
163 #[doc(hidden)]
165 pub(super) static READ_CERTIFICATES_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
166 register_int_counter_vec(
167 "read_certificates",
168 "The metric counting how often certificate are read from storage",
169 &[SOURCE_LABEL],
170 )
171 });
172
173 #[doc(hidden)]
175 pub static WRITE_CERTIFICATE_COUNTER: LazyLock<IntCounter> = LazyLock::new(|| {
176 register_int_counter(
177 "write_certificate",
178 "The metric counting how often a certificate is written to storage",
179 )
180 });
181
182 #[doc(hidden)]
184 pub(crate) static LOAD_CHAIN_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
185 register_histogram_vec(
186 "load_chain_latency",
187 "The latency to load a chain state",
188 &[],
189 exponential_bucket_latencies(1000.0),
190 )
191 });
192
193 #[doc(hidden)]
195 pub(super) static READ_EVENT_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
196 register_int_counter_vec(
197 "read_event",
198 "The metric counting how often an event is read from storage",
199 &[SOURCE_LABEL],
200 )
201 });
202
203 pub(super) static CONTAINS_EVENT_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
205 register_int_counter_vec(
206 "contains_event",
207 "The metric counting how often an event is tested for existence from storage",
208 &[SOURCE_LABEL],
209 )
210 });
211
212 #[doc(hidden)]
214 pub(super) static WRITE_EVENT_COUNTER: LazyLock<IntCounter> = LazyLock::new(|| {
215 register_int_counter(
216 "write_event",
217 "The metric counting how often an event is written to storage",
218 )
219 });
220
221 #[doc(hidden)]
223 pub(super) static READ_BLOCK_HASH_BY_HEIGHT_COUNTER: LazyLock<IntCounterVec> =
224 LazyLock::new(|| {
225 register_int_counter_vec(
226 "read_block_hash_by_height",
227 "The metric counting how often a block hash is read by height from storage",
228 &[SOURCE_LABEL],
229 )
230 });
231
232 #[doc(hidden)]
234 pub(super) static READ_NETWORK_DESCRIPTION: LazyLock<IntCounterVec> = LazyLock::new(|| {
235 register_int_counter_vec(
236 "network_description",
237 "The metric counting how often the network description is read from storage",
238 &[SOURCE_LABEL],
239 )
240 });
241
242 #[doc(hidden)]
244 pub(super) static WRITE_NETWORK_DESCRIPTION: LazyLock<IntCounter> = LazyLock::new(|| {
245 register_int_counter(
246 "write_network_description",
247 "The metric counting how often the network description is written to storage",
248 )
249 });
250}
251
252pub(crate) const BLOB_KEY: &[u8] = &[42];
254
255pub(crate) const BLOB_STATE_KEY: &[u8] = &[49];
257
258pub(crate) const LITE_CERTIFICATE_KEY: &[u8] = &[91];
260
261pub(crate) const BLOCK_KEY: &[u8] = &[221];
263
264pub(crate) const NETWORK_DESCRIPTION_KEY: &[u8] = &[119];
266
267fn get_block_keys() -> Vec<Vec<u8>> {
268 vec![LITE_CERTIFICATE_KEY.to_vec(), BLOCK_KEY.to_vec()]
269}
270
271#[derive(Default)]
272#[expect(clippy::type_complexity)]
273pub(crate) struct MultiPartitionBatch {
274 keys_value_bytes: Vec<(Vec<u8>, Vec<(Vec<u8>, Vec<u8>)>)>,
275}
276
277impl MultiPartitionBatch {
278 pub(crate) fn new() -> Self {
279 Self::default()
280 }
281
282 pub(crate) fn put_key_values(
283 &mut self,
284 root_key: Vec<u8>,
285 key_values: Vec<(Vec<u8>, Vec<u8>)>,
286 ) {
287 self.keys_value_bytes.push((root_key, key_values));
288 }
289
290 pub(crate) fn put_key_value(&mut self, root_key: Vec<u8>, key: Vec<u8>, value: Vec<u8>) {
291 self.put_key_values(root_key, vec![(key, value)]);
292 }
293
294 fn add_blob(&mut self, blob: &Blob) {
295 #[cfg(with_metrics)]
296 metrics::WRITE_BLOB_COUNTER.inc();
297 let root_key = RootKey::Blob(blob.id()).bytes();
298 let key = BLOB_KEY.to_vec();
299 self.put_key_value(root_key, key, blob.bytes().to_vec());
300 }
301
302 fn add_blob_state(&mut self, blob_id: BlobId, blob_state: &BlobState) -> Result<(), ViewError> {
303 let root_key = RootKey::Blob(blob_id).bytes();
304 let key = BLOB_STATE_KEY.to_vec();
305 let value = bcs::to_bytes(blob_state)?;
306 self.put_key_value(root_key, key, value);
307 Ok(())
308 }
309
310 fn add_certificate(
319 &mut self,
320 certificate: &ConfirmedBlockCertificate,
321 ) -> Result<(), ViewError> {
322 #[cfg(with_metrics)]
323 metrics::WRITE_CERTIFICATE_COUNTER.inc();
324 let hash = certificate.hash();
325
326 let root_key = RootKey::ConfirmedBlock(hash).bytes();
328 let mut key_values = Vec::new();
329 let key = LITE_CERTIFICATE_KEY.to_vec();
330 let value = bcs::to_bytes(&certificate.lite_certificate())?;
331 key_values.push((key, value));
332 let key = BLOCK_KEY.to_vec();
333 let value = bcs::to_bytes(&certificate.value())?;
334 key_values.push((key, value));
335 self.put_key_values(root_key, key_values);
336
337 let chain_id = certificate.value().block().header.chain_id;
339 let height = certificate.value().block().header.height;
340 let index_root_key = RootKey::BlockByHeight(chain_id).bytes();
341 let height_key = to_height_key(height);
342 let index_value = bcs::to_bytes(&hash)?;
343 self.put_key_value(index_root_key, height_key, index_value);
344
345 Ok(())
346 }
347
348 fn add_event(&mut self, event_id: &EventId, value: Vec<u8>) {
349 #[cfg(with_metrics)]
350 metrics::WRITE_EVENT_COUNTER.inc();
351 let key = to_event_key(event_id);
352 let root_key = RootKey::Event(event_id.chain_id).bytes();
353 self.put_key_value(root_key, key, value);
354 }
355
356 fn add_network_description(
357 &mut self,
358 information: &NetworkDescription,
359 ) -> Result<(), ViewError> {
360 #[cfg(with_metrics)]
361 metrics::WRITE_NETWORK_DESCRIPTION.inc();
362 let root_key = RootKey::NetworkDescription.bytes();
363 let key = NETWORK_DESCRIPTION_KEY.to_vec();
364 let value = bcs::to_bytes(information)?;
365 self.put_key_value(root_key, key, value);
366 Ok(())
367 }
368}
369
370#[derive(Clone, Copy, Debug)]
372pub struct StorageCacheConfig {
373 pub blob_cache_size: usize,
375 pub confirmed_block_cache_size: usize,
377 pub certificate_cache_size: usize,
379 pub certificate_raw_cache_size: usize,
381 pub event_cache_size: usize,
383 pub block_hash_by_height_cache_size: usize,
385 pub cache_cleanup_interval_secs: u64,
387}
388
389#[cfg(with_testing)]
391pub const DEFAULT_STORAGE_CACHE_CONFIG: StorageCacheConfig = StorageCacheConfig {
392 blob_cache_size: 1000,
393 confirmed_block_cache_size: 1000,
394 certificate_cache_size: 1000,
395 certificate_raw_cache_size: 1000,
396 event_cache_size: 1000,
397 block_hash_by_height_cache_size: 1000,
398 cache_cleanup_interval_secs: linera_cache::DEFAULT_CLEANUP_INTERVAL_SECS,
399};
400
401type RawCertificate = (Vec<u8>, Vec<u8>);
403
404#[derive(Clone)]
410pub struct StorageCaches {
411 pub(crate) blob: Arc<ValueCache<BlobId, Blob>>,
412 pub(crate) confirmed_block: Arc<ValueCache<CryptoHash, ConfirmedBlock>>,
413 pub(crate) certificate: Arc<ValueCache<CryptoHash, ConfirmedBlockCertificate>>,
414 pub(crate) certificate_raw: Arc<ValueCache<CryptoHash, RawCertificate>>,
415 pub(crate) event: Arc<ValueCache<EventId, Vec<u8>>>,
416 pub(crate) block_hash_by_height: Arc<ValueCache<(ChainId, BlockHeight), CryptoHash>>,
417 pub(crate) network_description: Arc<OnceLock<NetworkDescription>>,
418}
419
420impl StorageCaches {
421 pub fn new(sizes: StorageCacheConfig) -> Self {
423 let interval = sizes.cache_cleanup_interval_secs;
424 Self {
425 blob: Arc::new(ValueCache::new(
426 "storage_blob",
427 sizes.blob_cache_size,
428 interval,
429 )),
430 confirmed_block: Arc::new(ValueCache::new(
431 "storage_confirmed_block",
432 sizes.confirmed_block_cache_size,
433 interval,
434 )),
435 certificate: Arc::new(ValueCache::new(
436 "storage_certificate",
437 sizes.certificate_cache_size,
438 interval,
439 )),
440 certificate_raw: Arc::new(ValueCache::new(
441 "storage_certificate_raw",
442 sizes.certificate_raw_cache_size,
443 interval,
444 )),
445 event: Arc::new(ValueCache::new(
446 "storage_event",
447 sizes.event_cache_size,
448 interval,
449 )),
450 block_hash_by_height: Arc::new(ValueCache::new(
451 "storage_block_hash_by_height",
452 sizes.block_hash_by_height_cache_size,
453 interval,
454 )),
455 network_description: Arc::new(OnceLock::new()),
456 }
457 }
458}
459
460#[derive(Clone)]
462pub struct DbStorage<Database, Clock = WallClock> {
463 pub(crate) database: Arc<Database>,
464 clock: Clock,
465 thread_pool: Arc<linera_execution::ThreadPool>,
466 wasm_runtime: Option<WasmRuntime>,
467 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
468 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
469 shared_committees: SharedCommittees,
470 caches: StorageCaches,
471 execution_runtime_config: ExecutionRuntimeConfig,
472}
473
474#[derive(Debug, Serialize, Deserialize)]
476pub enum RootKey {
477 ChainState(ChainId),
479 ConfirmedBlock(CryptoHash),
481 Blob(BlobId),
483 Event(ChainId),
485 Placeholder,
487 NetworkDescription,
489 BlockExporterState(u32),
491 BlockByHeight(ChainId),
493}
494
495const CHAIN_ID_TAG: u8 = 0;
496const BLOB_ID_TAG: u8 = 2;
497
498impl RootKey {
499 pub fn bytes(&self) -> Vec<u8> {
501 bcs::to_bytes(self).unwrap()
502 }
503}
504
505#[derive(Debug, Serialize, Deserialize)]
506pub(crate) struct RestrictedEventId {
507 pub stream_id: StreamId,
508 pub index: u32,
509}
510
511pub(crate) fn to_event_key(event_id: &EventId) -> Vec<u8> {
512 let restricted_event_id = RestrictedEventId {
513 stream_id: event_id.stream_id.clone(),
514 index: event_id.index,
515 };
516 bcs::to_bytes(&restricted_event_id).unwrap()
517}
518
519pub(crate) fn to_height_key(height: BlockHeight) -> Vec<u8> {
520 bcs::to_bytes(&height).unwrap()
521}
522
523fn is_chain_state(root_key: &[u8]) -> bool {
524 if root_key.is_empty() {
525 return false;
526 }
527 root_key[0] == CHAIN_ID_TAG
528}
529
530#[cfg(test)]
531mod tests {
532 use linera_base::{
533 crypto::{CryptoHash, TestString},
534 data_types::{BlockHeight, Epoch, Round, Timestamp},
535 identifiers::{
536 ApplicationId, BlobId, BlobType, ChainId, EventId, GenericApplicationId, StreamId,
537 StreamName,
538 },
539 };
540 use linera_chain::{
541 block::{Block, BlockBody, BlockHeader, ConfirmedBlock},
542 types::ConfirmedBlockCertificate,
543 };
544 use linera_views::{
545 memory::MemoryDatabase,
546 store::{KeyValueDatabase, ReadableKeyValueStore as _},
547 };
548
549 use crate::{
550 db_storage::{
551 to_event_key, to_height_key, MultiPartitionBatch, RootKey, BLOB_ID_TAG, CHAIN_ID_TAG,
552 },
553 DbStorage, Storage, TestClock,
554 };
555
556 #[test]
563 fn test_root_key_blob_serialization() {
564 let hash = CryptoHash::default();
565 let blob_type = BlobType::default();
566 let blob_id = BlobId::new(hash, blob_type);
567 let root_key = RootKey::Blob(blob_id).bytes();
568 assert_eq!(root_key[0], BLOB_ID_TAG);
569 assert_eq!(bcs::from_bytes::<BlobId>(&root_key[1..]).unwrap(), blob_id);
570 }
571
572 #[test]
575 fn test_root_key_chainstate_serialization() {
576 let hash = CryptoHash::default();
577 let chain_id = ChainId(hash);
578 let root_key = RootKey::ChainState(chain_id).bytes();
579 assert_eq!(root_key[0], CHAIN_ID_TAG);
580 assert_eq!(
581 bcs::from_bytes::<ChainId>(&root_key[1..]).unwrap(),
582 chain_id
583 );
584 }
585
586 #[test]
589 fn test_root_key_event_serialization() {
590 let hash = CryptoHash::test_hash("49");
591 let chain_id = ChainId(hash);
592 let application_description_hash = CryptoHash::test_hash("42");
593 let application_id = ApplicationId::new(application_description_hash);
594 let application_id = GenericApplicationId::User(application_id);
595 let stream_name = StreamName(bcs::to_bytes("linera_stream").unwrap());
596 let stream_id = StreamId {
597 application_id,
598 stream_name,
599 };
600 let prefix = bcs::to_bytes(&stream_id).unwrap();
601
602 let index = 1567;
603 let event_id = EventId {
604 chain_id,
605 stream_id,
606 index,
607 };
608 let key = to_event_key(&event_id);
609 assert!(key.starts_with(&prefix));
610 }
611
612 #[test]
615 fn test_root_key_block_by_height_serialization() {
616 use linera_base::data_types::BlockHeight;
617
618 let hash = CryptoHash::default();
619 let chain_id = ChainId(hash);
620 let height = BlockHeight(42);
621
622 let root_key = RootKey::BlockByHeight(chain_id).bytes();
624 let deserialized_chain_id: ChainId = bcs::from_bytes(&root_key[1..]).unwrap();
625 assert_eq!(deserialized_chain_id, chain_id);
626
627 let height_key = to_height_key(height);
629 let deserialized_height: BlockHeight = bcs::from_bytes(&height_key).unwrap();
630 assert_eq!(deserialized_height, height);
631 }
632
633 #[cfg(with_testing)]
634 #[tokio::test]
635 async fn test_add_certificate_creates_height_index() {
636 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
638
639 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
641 let height = BlockHeight(5);
642 let block = Block {
643 header: BlockHeader {
644 chain_id,
645 epoch: Epoch::ZERO,
646 height,
647 timestamp: Timestamp::from(0),
648 state_hash: CryptoHash::new(&TestString::new("state_hash")),
649 previous_block_hash: None,
650 authenticated_signer: None,
651 transactions_hash: CryptoHash::new(&TestString::new("transactions_hash")),
652 messages_hash: CryptoHash::new(&TestString::new("messages_hash")),
653 previous_message_blocks_hash: CryptoHash::new(&TestString::new(
654 "prev_msg_blocks_hash",
655 )),
656 previous_event_blocks_hash: CryptoHash::new(&TestString::new(
657 "prev_event_blocks_hash",
658 )),
659 oracle_responses_hash: CryptoHash::new(&TestString::new("oracle_responses_hash")),
660 events_hash: CryptoHash::new(&TestString::new("events_hash")),
661 blobs_hash: CryptoHash::new(&TestString::new("blobs_hash")),
662 operation_results_hash: CryptoHash::new(&TestString::new("operation_results_hash")),
663 },
664 body: BlockBody {
665 transactions: vec![],
666 messages: vec![],
667 previous_message_blocks: Default::default(),
668 previous_event_blocks: Default::default(),
669 oracle_responses: vec![],
670 events: vec![],
671 blobs: vec![],
672 operation_results: vec![],
673 },
674 };
675 let confirmed_block = ConfirmedBlock::new(block);
676 let certificate = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
677
678 let mut batch = MultiPartitionBatch::new();
680 batch.add_certificate(&certificate).unwrap();
681 storage.write_batch(batch).await.unwrap();
682
683 let hash = certificate.hash();
685 let index_root_key = RootKey::BlockByHeight(chain_id).bytes();
686 let store = storage.database.open_shared(&index_root_key).unwrap();
687 let height_key = to_height_key(height);
688 let value_bytes = store.read_value_bytes(&height_key).await.unwrap();
689
690 assert!(value_bytes.is_some(), "Height index was not created");
691 let stored_hash: CryptoHash = bcs::from_bytes(&value_bytes.unwrap()).unwrap();
692 assert_eq!(stored_hash, hash, "Height index contains wrong hash");
693 }
694
695 #[cfg(with_testing)]
696 #[tokio::test]
697 async fn test_read_certificates_by_heights() {
698 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
699 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
700
701 let mut batch = MultiPartitionBatch::new();
703 let mut expected_certs = vec![];
704
705 for height in [1, 3, 5] {
706 let block = Block {
707 header: BlockHeader {
708 chain_id,
709 epoch: Epoch::ZERO,
710 height: BlockHeight(height),
711 timestamp: Timestamp::from(0),
712 state_hash: CryptoHash::new(&TestString::new("state_hash_{height}")),
713 previous_block_hash: None,
714 authenticated_signer: None,
715 transactions_hash: CryptoHash::new(&TestString::new("tx_hash_{height}")),
716 messages_hash: CryptoHash::new(&TestString::new("msg_hash_{height}")),
717 previous_message_blocks_hash: CryptoHash::new(&TestString::new(
718 "pmb_hash_{height}",
719 )),
720 previous_event_blocks_hash: CryptoHash::new(&TestString::new(
721 "peb_hash_{height}",
722 )),
723 oracle_responses_hash: CryptoHash::new(&TestString::new(
724 "oracle_hash_{height}",
725 )),
726 events_hash: CryptoHash::new(&TestString::new("events_hash_{height}")),
727 blobs_hash: CryptoHash::new(&TestString::new("blobs_hash_{height}")),
728 operation_results_hash: CryptoHash::new(&TestString::new(
729 "op_results_hash_{height}",
730 )),
731 },
732 body: BlockBody {
733 transactions: vec![],
734 messages: vec![],
735 previous_message_blocks: Default::default(),
736 previous_event_blocks: Default::default(),
737 oracle_responses: vec![],
738 events: vec![],
739 blobs: vec![],
740 operation_results: vec![],
741 },
742 };
743 let confirmed_block = ConfirmedBlock::new(block);
744 let cert = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
745 expected_certs.push((height, cert.clone()));
746 batch.add_certificate(&cert).unwrap();
747 }
748 storage.write_batch(batch).await.unwrap();
749
750 let heights = vec![BlockHeight(1), BlockHeight(3), BlockHeight(5)];
752 let result = storage
753 .read_certificates_by_heights(chain_id, &heights)
754 .await
755 .unwrap();
756 assert_eq!(result.len(), 3);
757 assert_eq!(
758 result[0].as_ref().unwrap().hash(),
759 expected_certs[0].1.hash()
760 );
761 assert_eq!(
762 result[1].as_ref().unwrap().hash(),
763 expected_certs[1].1.hash()
764 );
765 assert_eq!(
766 result[2].as_ref().unwrap().hash(),
767 expected_certs[2].1.hash()
768 );
769
770 let heights = vec![BlockHeight(5), BlockHeight(1), BlockHeight(3)];
772 let result = storage
773 .read_certificates_by_heights(chain_id, &heights)
774 .await
775 .unwrap();
776 assert_eq!(result.len(), 3);
777 assert_eq!(
778 result[0].as_ref().unwrap().hash(),
779 expected_certs[2].1.hash()
780 );
781 assert_eq!(
782 result[1].as_ref().unwrap().hash(),
783 expected_certs[0].1.hash()
784 );
785 assert_eq!(
786 result[2].as_ref().unwrap().hash(),
787 expected_certs[1].1.hash()
788 );
789
790 let heights = vec![
792 BlockHeight(1),
793 BlockHeight(2),
794 BlockHeight(3),
795 BlockHeight(3),
796 ];
797 let result = storage
798 .read_certificates_by_heights(chain_id, &heights)
799 .await
800 .unwrap();
801 assert_eq!(result.len(), 4); assert!(result[0].is_some());
803 assert!(result[1].is_none()); assert!(result[2].is_some());
805 assert_eq!(
806 result[2].as_ref().unwrap().hash(),
807 result[3].as_ref().unwrap().hash()
808 ); let heights = vec![];
812 let result = storage
813 .read_certificates_by_heights(chain_id, &heights)
814 .await
815 .unwrap();
816 assert_eq!(result.len(), 0);
817 }
818
819 #[cfg(with_testing)]
820 #[tokio::test]
821 async fn test_read_certificates_by_heights_multiple_chains() {
822 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
823
824 let chain_a = ChainId(CryptoHash::test_hash("chain_a"));
826 let chain_b = ChainId(CryptoHash::test_hash("chain_b"));
827
828 let mut batch = MultiPartitionBatch::new();
829
830 let block_a = Block {
831 header: BlockHeader {
832 chain_id: chain_a,
833 epoch: Epoch::ZERO,
834 height: BlockHeight(10),
835 timestamp: Timestamp::from(0),
836 state_hash: CryptoHash::new(&TestString::new("state_hash_a")),
837 previous_block_hash: None,
838 authenticated_signer: None,
839 transactions_hash: CryptoHash::new(&TestString::new("tx_hash_a")),
840 messages_hash: CryptoHash::new(&TestString::new("msg_hash_a")),
841 previous_message_blocks_hash: CryptoHash::new(&TestString::new("pmb_hash_a")),
842 previous_event_blocks_hash: CryptoHash::new(&TestString::new("peb_hash_a")),
843 oracle_responses_hash: CryptoHash::new(&TestString::new("oracle_hash_a")),
844 events_hash: CryptoHash::new(&TestString::new("events_hash_a")),
845 blobs_hash: CryptoHash::new(&TestString::new("blobs_hash_a")),
846 operation_results_hash: CryptoHash::new(&TestString::new("op_results_hash_a")),
847 },
848 body: BlockBody {
849 transactions: vec![],
850 messages: vec![],
851 previous_message_blocks: Default::default(),
852 previous_event_blocks: Default::default(),
853 oracle_responses: vec![],
854 events: vec![],
855 blobs: vec![],
856 operation_results: vec![],
857 },
858 };
859 let confirmed_block_a = ConfirmedBlock::new(block_a);
860 let cert_a = ConfirmedBlockCertificate::new(confirmed_block_a, Round::Fast, vec![]);
861 batch.add_certificate(&cert_a).unwrap();
862
863 let block_b = Block {
864 header: BlockHeader {
865 chain_id: chain_b,
866 epoch: Epoch::ZERO,
867 height: BlockHeight(10),
868 timestamp: Timestamp::from(0),
869 state_hash: CryptoHash::new(&TestString::new("state_hash_b")),
870 previous_block_hash: None,
871 authenticated_signer: None,
872 transactions_hash: CryptoHash::new(&TestString::new("tx_hash_b")),
873 messages_hash: CryptoHash::new(&TestString::new("msg_hash_b")),
874 previous_message_blocks_hash: CryptoHash::new(&TestString::new("pmb_hash_b")),
875 previous_event_blocks_hash: CryptoHash::new(&TestString::new("peb_hash_b")),
876 oracle_responses_hash: CryptoHash::new(&TestString::new("oracle_hash_b")),
877 events_hash: CryptoHash::new(&TestString::new("events_hash_b")),
878 blobs_hash: CryptoHash::new(&TestString::new("blobs_hash_b")),
879 operation_results_hash: CryptoHash::new(&TestString::new("op_results_hash_b")),
880 },
881 body: BlockBody {
882 transactions: vec![],
883 messages: vec![],
884 previous_message_blocks: Default::default(),
885 previous_event_blocks: Default::default(),
886 oracle_responses: vec![],
887 events: vec![],
888 blobs: vec![],
889 operation_results: vec![],
890 },
891 };
892 let confirmed_block_b = ConfirmedBlock::new(block_b);
893 let cert_b = ConfirmedBlockCertificate::new(confirmed_block_b, Round::Fast, vec![]);
894 batch.add_certificate(&cert_b).unwrap();
895
896 storage.write_batch(batch).await.unwrap();
897
898 let result = storage
900 .read_certificates_by_heights(chain_a, &[BlockHeight(10)])
901 .await
902 .unwrap();
903 assert_eq!(result[0].as_ref().unwrap().hash(), cert_a.hash());
904
905 let result = storage
907 .read_certificates_by_heights(chain_b, &[BlockHeight(10)])
908 .await
909 .unwrap();
910 assert_eq!(result[0].as_ref().unwrap().hash(), cert_b.hash());
911
912 let result = storage
914 .read_certificates_by_heights(chain_a, &[BlockHeight(20)])
915 .await
916 .unwrap();
917 assert!(result[0].is_none());
918 }
919
920 #[cfg(with_testing)]
921 #[tokio::test]
922 async fn test_read_certificates_by_heights_consistency() {
923 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
924 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
925
926 let mut batch = MultiPartitionBatch::new();
928 let block = Block {
929 header: BlockHeader {
930 chain_id,
931 epoch: Epoch::ZERO,
932 height: BlockHeight(7),
933 timestamp: Timestamp::from(0),
934 state_hash: CryptoHash::new(&TestString::new("state_hash")),
935 previous_block_hash: None,
936 authenticated_signer: None,
937 transactions_hash: CryptoHash::new(&TestString::new("tx_hash")),
938 messages_hash: CryptoHash::new(&TestString::new("msg_hash")),
939 previous_message_blocks_hash: CryptoHash::new(&TestString::new("pmb_hash")),
940 previous_event_blocks_hash: CryptoHash::new(&TestString::new("peb_hash")),
941 oracle_responses_hash: CryptoHash::new(&TestString::new("oracle_hash")),
942 events_hash: CryptoHash::new(&TestString::new("events_hash")),
943 blobs_hash: CryptoHash::new(&TestString::new("blobs_hash")),
944 operation_results_hash: CryptoHash::new(&TestString::new("op_results_hash")),
945 },
946 body: BlockBody {
947 transactions: vec![],
948 messages: vec![],
949 previous_message_blocks: Default::default(),
950 previous_event_blocks: Default::default(),
951 oracle_responses: vec![],
952 events: vec![],
953 blobs: vec![],
954 operation_results: vec![],
955 },
956 };
957 let confirmed_block = ConfirmedBlock::new(block);
958 let cert = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
959 let hash = cert.hash();
960 batch.add_certificate(&cert).unwrap();
961 storage.write_batch(batch).await.unwrap();
962
963 let cert_by_hash = storage.read_certificate(hash).await.unwrap().unwrap();
965
966 let certs_by_height = storage
968 .read_certificates_by_heights(chain_id, &[BlockHeight(7)])
969 .await
970 .unwrap();
971 let cert_by_height = certs_by_height[0].as_ref().unwrap();
972
973 assert_eq!(cert_by_hash.hash(), cert_by_height.hash());
975 assert_eq!(
976 cert_by_hash.value().block().header,
977 cert_by_height.value().block().header
978 );
979 }
980
981 #[cfg(with_testing)]
985 #[tokio::test]
986 async fn test_write_certificate_height_indices_populates_reverse_index() {
987 use linera_views::{batch::Batch, store::WritableKeyValueStore as _};
988
989 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
990 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
991
992 let block = Block {
995 header: BlockHeader {
996 chain_id,
997 epoch: Epoch::ZERO,
998 height: BlockHeight(10),
999 timestamp: Timestamp::from(0),
1000 state_hash: CryptoHash::new(&TestString::new("state_hash")),
1001 previous_block_hash: None,
1002 authenticated_signer: None,
1003 transactions_hash: CryptoHash::new(&TestString::new("tx_hash")),
1004 messages_hash: CryptoHash::new(&TestString::new("msg_hash")),
1005 previous_message_blocks_hash: CryptoHash::new(&TestString::new("pmb_hash")),
1006 previous_event_blocks_hash: CryptoHash::new(&TestString::new("peb_hash")),
1007 oracle_responses_hash: CryptoHash::new(&TestString::new("oracle_hash")),
1008 events_hash: CryptoHash::new(&TestString::new("events_hash")),
1009 blobs_hash: CryptoHash::new(&TestString::new("blobs_hash")),
1010 operation_results_hash: CryptoHash::new(&TestString::new("op_results_hash")),
1011 },
1012 body: BlockBody {
1013 transactions: vec![],
1014 messages: vec![],
1015 previous_message_blocks: Default::default(),
1016 previous_event_blocks: Default::default(),
1017 oracle_responses: vec![],
1018 events: vec![],
1019 blobs: vec![],
1020 operation_results: vec![],
1021 },
1022 };
1023 let confirmed_block = ConfirmedBlock::new(block);
1024 let cert = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
1025 let hash = cert.hash();
1026 let height = BlockHeight(10);
1027
1028 let root_key = RootKey::ConfirmedBlock(hash).bytes();
1030 let store = storage.database.open_shared(&root_key).unwrap();
1031 let mut batch = Batch::new();
1032 batch.put_key_value_bytes(
1033 crate::db_storage::LITE_CERTIFICATE_KEY.to_vec(),
1034 bcs::to_bytes(&cert.lite_certificate()).unwrap(),
1035 );
1036 batch.put_key_value_bytes(
1037 crate::db_storage::BLOCK_KEY.to_vec(),
1038 bcs::to_bytes(&cert.value()).unwrap(),
1039 );
1040 store.write_batch(batch).await.unwrap();
1041
1042 let result = storage
1044 .read_certificates_by_heights(chain_id, &[height])
1045 .await
1046 .unwrap();
1047 assert!(
1048 result[0].is_none(),
1049 "Height index should not exist before write_certificate_height_indices"
1050 );
1051
1052 let cert_by_hash = storage.read_certificate(hash).await.unwrap();
1054 assert!(cert_by_hash.is_some(), "Certificate should exist by hash");
1055
1056 storage
1058 .write_certificate_height_indices(chain_id, &[(height, hash)])
1059 .await
1060 .unwrap();
1061
1062 let result = storage
1064 .read_certificates_by_heights(chain_id, &[height])
1065 .await
1066 .unwrap();
1067 assert!(
1068 result[0].is_some(),
1069 "Height index should exist after write_certificate_height_indices"
1070 );
1071 assert_eq!(
1072 result[0].as_ref().unwrap().hash(),
1073 hash,
1074 "Certificate retrieved by height should match original"
1075 );
1076 }
1077}
1078
1079#[derive(Clone, Copy)]
1082pub struct ChainStatesFirstAssignment;
1083
1084impl DualStoreRootKeyAssignment for ChainStatesFirstAssignment {
1085 fn assigned_store(root_key: &[u8]) -> Result<StoreInUse, bcs::Error> {
1086 if root_key.is_empty() {
1087 return Ok(StoreInUse::Second);
1088 }
1089 let store = match is_chain_state(root_key) {
1090 true => StoreInUse::First,
1091 false => StoreInUse::Second,
1092 };
1093 Ok(store)
1094 }
1095}
1096
1097#[derive(Clone)]
1099pub struct WallClock;
1100
1101#[cfg_attr(not(web), async_trait)]
1102#[cfg_attr(web, async_trait(?Send))]
1103impl Clock for WallClock {
1104 fn current_time(&self) -> Timestamp {
1105 Timestamp::now()
1106 }
1107
1108 async fn sleep_until(&self, timestamp: Timestamp) {
1109 let delta = timestamp.delta_since(Timestamp::now());
1110 if delta > TimeDelta::ZERO {
1111 linera_base::time::timer::sleep(delta.as_duration()).await
1112 }
1113 }
1114
1115 async fn sleep_for(&self, duration: Duration) {
1116 linera_base::time::timer::sleep(duration).await
1117 }
1118}
1119
1120#[cfg(with_testing)]
1121#[derive(Default)]
1122struct TestClockInner {
1123 time: Timestamp,
1124 sleeps: BTreeMap<Reverse<Timestamp>, Vec<oneshot::Sender<()>>>,
1125 sleep_callback: Option<Box<dyn Fn(Timestamp) -> bool + Send + Sync>>,
1128}
1129
1130#[cfg(with_testing)]
1131impl TestClockInner {
1132 fn set(&mut self, time: Timestamp) {
1133 self.time = time;
1134 let senders = self.sleeps.split_off(&Reverse(time));
1135 for sender in senders.into_values().flatten() {
1136 sender.send(()).ok();
1138 }
1139 }
1140
1141 fn add_sleep_until(&mut self, time: Timestamp) -> Receiver<()> {
1142 let (sender, receiver) = oneshot::channel();
1143 let should_auto_advance = self
1144 .sleep_callback
1145 .as_ref()
1146 .is_some_and(|callback| callback(time));
1147 if should_auto_advance && time > self.time {
1148 self.set(time);
1150 sender.send(()).ok();
1152 } else if self.time >= time {
1153 sender.send(()).ok();
1155 } else {
1156 self.sleeps.entry(Reverse(time)).or_default().push(sender);
1157 }
1158 receiver
1159 }
1160}
1161
1162#[cfg(with_testing)]
1165#[derive(Clone, Default)]
1166pub struct TestClock(Arc<std::sync::Mutex<TestClockInner>>);
1167
1168#[cfg(with_testing)]
1169#[cfg_attr(not(web), async_trait)]
1170#[cfg_attr(web, async_trait(?Send))]
1171impl Clock for TestClock {
1172 fn current_time(&self) -> Timestamp {
1173 self.lock().time
1174 }
1175
1176 async fn sleep_until(&self, timestamp: Timestamp) {
1177 let receiver = self.lock().add_sleep_until(timestamp);
1178 receiver.await.ok();
1180 }
1181}
1182
1183#[cfg(with_testing)]
1184impl TestClock {
1185 pub fn new() -> Self {
1187 TestClock(Arc::default())
1188 }
1189
1190 pub fn set(&self, time: Timestamp) {
1192 self.lock().set(time);
1193 }
1194
1195 pub fn add(&self, delta: TimeDelta) {
1197 let mut guard = self.lock();
1198 let time = guard.time.saturating_add(delta);
1199 guard.set(time);
1200 }
1201
1202 pub fn current_time(&self) -> Timestamp {
1204 self.lock().time
1205 }
1206
1207 pub fn set_sleep_callback<F>(&self, callback: F)
1212 where
1213 F: Fn(Timestamp) -> bool + Send + Sync + 'static,
1214 {
1215 self.lock().sleep_callback = Some(Box::new(callback));
1216 }
1217
1218 pub fn clear_sleep_callback(&self) {
1220 self.lock().sleep_callback = None;
1221 }
1222
1223 fn lock(&self) -> std::sync::MutexGuard<'_, TestClockInner> {
1224 self.0.lock().expect("poisoned TestClock mutex")
1225 }
1226}
1227
1228#[cfg_attr(not(web), async_trait)]
1229#[cfg_attr(web, async_trait(?Send))]
1230impl<Database, C> Storage for DbStorage<Database, C>
1231where
1232 Database: KeyValueDatabase<
1233 Store: KeyValueStore + Clone + linera_base::util::traits::AutoTraits + 'static,
1234 Error: Send + Sync,
1235 > + Clone
1236 + linera_base::util::traits::AutoTraits
1237 + 'static,
1238 C: Clock + Clone + Send + Sync + 'static,
1239{
1240 type Context = ViewContext<ChainRuntimeContext<Self>, Database::Store>;
1241 type Clock = C;
1242 type BlockExporterContext = ViewContext<u32, Database::Store>;
1243
1244 fn clock(&self) -> &C {
1245 &self.clock
1246 }
1247
1248 fn thread_pool(&self) -> &Arc<linera_execution::ThreadPool> {
1249 &self.thread_pool
1250 }
1251
1252 #[instrument(level = "trace", skip_all, fields(chain_id = %chain_id))]
1253 async fn load_chain(
1254 &self,
1255 chain_id: ChainId,
1256 ) -> Result<ChainStateView<Self::Context>, ViewError> {
1257 #[cfg(with_metrics)]
1258 let _metric = metrics::LOAD_CHAIN_LATENCY.measure_latency();
1259 let runtime_context = ChainRuntimeContext {
1260 storage: self.clone(),
1261 thread_pool: self.thread_pool.clone(),
1262 chain_id,
1263 execution_runtime_config: self.execution_runtime_config,
1264 user_contracts: self.user_contracts.clone(),
1265 user_services: self.user_services.clone(),
1266 };
1267 let root_key = RootKey::ChainState(chain_id).bytes();
1268 let store = self.database.open_exclusive(&root_key)?;
1269 let context = ViewContext::create_root_context(store, runtime_context).await?;
1270 ChainStateView::load(context).await
1271 }
1272
1273 #[instrument(level = "trace", skip_all, fields(%blob_id))]
1274 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1275 if self.caches.blob.contains(&blob_id) {
1276 #[cfg(with_metrics)]
1277 metrics::CONTAINS_BLOB_COUNTER
1278 .with_label_values(&[metrics::CACHE])
1279 .inc();
1280 return Ok(true);
1281 }
1282 let root_key = RootKey::Blob(blob_id).bytes();
1283 let store = self.database.open_shared(&root_key)?;
1284 let test = store.contains_key(BLOB_KEY).await?;
1285 #[cfg(with_metrics)]
1286 metrics::CONTAINS_BLOB_COUNTER
1287 .with_label_values(&[metrics::DB])
1288 .inc();
1289 Ok(test)
1290 }
1291
1292 #[instrument(skip_all, fields(blob_count = blob_ids.len()))]
1293 async fn missing_blobs(&self, blob_ids: &[BlobId]) -> Result<Vec<BlobId>, ViewError> {
1294 let mut missing_blobs = Vec::new();
1295 #[cfg(with_metrics)]
1296 let mut cache_hits: u64 = 0;
1297 #[cfg(with_metrics)]
1298 let mut db_checks: u64 = 0;
1299 for blob_id in blob_ids {
1300 if self.caches.blob.contains(blob_id) {
1301 #[cfg(with_metrics)]
1302 {
1303 cache_hits += 1;
1304 }
1305 continue;
1306 }
1307 #[cfg(with_metrics)]
1308 {
1309 db_checks += 1;
1310 }
1311 let root_key = RootKey::Blob(*blob_id).bytes();
1312 let store = self.database.open_shared(&root_key)?;
1313 if !store.contains_key(BLOB_KEY).await? {
1314 missing_blobs.push(*blob_id);
1315 }
1316 }
1317 #[cfg(with_metrics)]
1318 {
1319 if cache_hits > 0 {
1320 metrics::CONTAINS_BLOBS_COUNTER
1321 .with_label_values(&[metrics::CACHE])
1322 .inc_by(cache_hits);
1323 }
1324 if db_checks > 0 {
1325 metrics::CONTAINS_BLOBS_COUNTER
1326 .with_label_values(&[metrics::DB])
1327 .inc_by(db_checks);
1328 }
1329 }
1330 Ok(missing_blobs)
1331 }
1332
1333 #[instrument(skip_all, fields(%blob_id))]
1334 async fn contains_blob_state(&self, blob_id: BlobId) -> Result<bool, ViewError> {
1335 let root_key = RootKey::Blob(blob_id).bytes();
1336 let store = self.database.open_shared(&root_key)?;
1337 let test = store.contains_key(BLOB_STATE_KEY).await?;
1338 #[cfg(with_metrics)]
1339 metrics::CONTAINS_BLOB_STATE_COUNTER
1340 .with_label_values(&[metrics::DB])
1341 .inc();
1342 Ok(test)
1343 }
1344
1345 #[instrument(skip_all, fields(%hash))]
1346 async fn read_confirmed_block(
1347 &self,
1348 hash: CryptoHash,
1349 ) -> Result<Option<CacheArc<ConfirmedBlock>>, ViewError> {
1350 if let Some(block) = self.caches.confirmed_block.get(&hash) {
1351 #[cfg(with_metrics)]
1352 metrics::READ_CONFIRMED_BLOCK_COUNTER
1353 .with_label_values(&[metrics::CACHE])
1354 .inc();
1355 return Ok(Some(block));
1356 }
1357 let root_key = RootKey::ConfirmedBlock(hash).bytes();
1358 let store = self.database.open_shared(&root_key)?;
1359 let value = store.read_value::<ConfirmedBlock>(BLOCK_KEY).await?;
1360 #[cfg(with_metrics)]
1361 metrics::READ_CONFIRMED_BLOCK_COUNTER
1362 .with_label_values(&[metrics::DB])
1363 .inc();
1364 match value {
1365 Some(block) => Ok(Some(self.caches.confirmed_block.insert(&hash, block))),
1366 None => Ok(None),
1367 }
1368 }
1369
1370 #[instrument(skip_all)]
1371 async fn read_confirmed_blocks<I: IntoIterator<Item = CryptoHash> + Send>(
1372 &self,
1373 hashes: I,
1374 ) -> Result<Vec<Option<CacheArc<ConfirmedBlock>>>, ViewError> {
1375 let hashes = hashes.into_iter().collect::<Vec<_>>();
1376 if hashes.is_empty() {
1377 return Ok(Vec::new());
1378 }
1379 let mut results = vec![None; hashes.len()];
1380 let mut misses = Vec::new();
1381 for (i, hash) in hashes.iter().enumerate() {
1382 if let Some(block) = self.caches.confirmed_block.get(hash) {
1383 results[i] = Some(block);
1384 } else {
1385 misses.push(i);
1386 }
1387 }
1388 if !misses.is_empty() {
1389 let miss_hashes: Vec<_> = misses.iter().map(|&i| hashes[i]).collect();
1390 let root_keys = Self::get_root_keys_for_certificates(&miss_hashes);
1391 for (miss_idx, root_key) in misses.iter().zip(root_keys) {
1392 let store = self.database.open_shared(&root_key)?;
1393 if let Some(block) = store.read_value::<ConfirmedBlock>(BLOCK_KEY).await? {
1394 results[*miss_idx] = Some(
1395 self.caches
1396 .confirmed_block
1397 .insert(&hashes[*miss_idx], block),
1398 );
1399 }
1400 }
1401 }
1402 #[cfg(with_metrics)]
1403 {
1404 let cache_hits = (hashes.len() - misses.len()) as u64;
1405 if cache_hits > 0 {
1406 metrics::READ_CONFIRMED_BLOCKS_COUNTER
1407 .with_label_values(&[metrics::CACHE])
1408 .inc_by(cache_hits);
1409 }
1410 let db_reads = misses.len() as u64;
1411 if db_reads > 0 {
1412 metrics::READ_CONFIRMED_BLOCKS_COUNTER
1413 .with_label_values(&[metrics::DB])
1414 .inc_by(db_reads);
1415 }
1416 }
1417 Ok(results)
1418 }
1419
1420 #[instrument(skip_all, fields(%blob_id))]
1421 async fn read_blob(&self, blob_id: BlobId) -> Result<Option<CacheArc<Blob>>, ViewError> {
1422 if let Some(blob) = self.caches.blob.get(&blob_id) {
1423 #[cfg(with_metrics)]
1424 metrics::READ_BLOB_COUNTER
1425 .with_label_values(&[metrics::CACHE])
1426 .inc();
1427 return Ok(Some(blob));
1428 }
1429 let root_key = RootKey::Blob(blob_id).bytes();
1430 let store = self.database.open_shared(&root_key)?;
1431 let maybe_blob_bytes = store.read_value_bytes(BLOB_KEY).await?;
1432 #[cfg(with_metrics)]
1433 metrics::READ_BLOB_COUNTER
1434 .with_label_values(&[metrics::DB])
1435 .inc();
1436 match maybe_blob_bytes {
1437 Some(blob_bytes) => {
1438 let blob = Blob::new_with_id_unchecked(blob_id, blob_bytes);
1439 Ok(Some(self.caches.blob.insert(&blob_id, blob)))
1440 }
1441 None => Ok(None),
1442 }
1443 }
1444
1445 #[instrument(skip_all, fields(blob_ids_len = %blob_ids.len()))]
1446 async fn read_blobs(
1447 &self,
1448 blob_ids: &[BlobId],
1449 ) -> Result<Vec<Option<CacheArc<Blob>>>, ViewError> {
1450 if blob_ids.is_empty() {
1451 return Ok(Vec::new());
1452 }
1453 futures::future::try_join_all(blob_ids.iter().map(|blob_id| self.read_blob(*blob_id))).await
1459 }
1460
1461 #[instrument(skip_all, fields(%blob_id))]
1462 async fn read_blob_state(&self, blob_id: BlobId) -> Result<Option<BlobState>, ViewError> {
1463 let root_key = RootKey::Blob(blob_id).bytes();
1464 let store = self.database.open_shared(&root_key)?;
1465 let blob_state = store.read_value::<BlobState>(BLOB_STATE_KEY).await?;
1466 #[cfg(with_metrics)]
1467 metrics::READ_BLOB_STATE_COUNTER
1468 .with_label_values(&[metrics::DB])
1469 .inc();
1470 Ok(blob_state)
1471 }
1472
1473 #[instrument(skip_all, fields(blob_ids_len = %blob_ids.len()))]
1474 async fn read_blob_states(
1475 &self,
1476 blob_ids: &[BlobId],
1477 ) -> Result<Vec<Option<BlobState>>, ViewError> {
1478 if blob_ids.is_empty() {
1479 return Ok(Vec::new());
1480 }
1481 futures::future::try_join_all(
1482 blob_ids
1483 .iter()
1484 .map(|blob_id| self.read_blob_state(*blob_id)),
1485 )
1486 .await
1487 }
1488
1489 #[instrument(skip_all, fields(blob_id = %blob.id()))]
1490 async fn write_blob(&self, blob: &Blob) -> Result<(), ViewError> {
1491 let mut batch = MultiPartitionBatch::new();
1492 batch.add_blob(blob);
1493 self.write_batch(batch).await?;
1494 Ok(())
1495 }
1496
1497 #[instrument(skip_all, fields(blob_ids_len = %blob_ids.len()))]
1498 async fn maybe_write_blob_states(
1499 &self,
1500 blob_ids: &[BlobId],
1501 blob_state: BlobState,
1502 ) -> Result<(), ViewError> {
1503 if blob_ids.is_empty() {
1504 return Ok(());
1505 }
1506 let mut maybe_blob_states = Vec::new();
1507 for blob_id in blob_ids {
1508 let root_key = RootKey::Blob(*blob_id).bytes();
1509 let store = self.database.open_shared(&root_key)?;
1510 let maybe_blob_state = store.read_value::<BlobState>(BLOB_STATE_KEY).await?;
1511 maybe_blob_states.push(maybe_blob_state);
1512 }
1513 let mut batch = MultiPartitionBatch::new();
1514 for (maybe_blob_state, blob_id) in maybe_blob_states.iter().zip(blob_ids) {
1515 match maybe_blob_state {
1516 None => {
1517 batch.add_blob_state(*blob_id, &blob_state)?;
1518 }
1519 Some(state) => {
1520 if state.epoch < blob_state.epoch {
1521 batch.add_blob_state(*blob_id, &blob_state)?;
1522 }
1523 }
1524 }
1525 }
1526 self.write_batch(batch).await?;
1530 Ok(())
1531 }
1532
1533 #[instrument(skip_all, fields(blobs_len = %blobs.len()))]
1534 async fn maybe_write_blobs(&self, blobs: &[Blob]) -> Result<Vec<bool>, ViewError> {
1535 if blobs.is_empty() {
1536 return Ok(Vec::new());
1537 }
1538 let mut batch = MultiPartitionBatch::new();
1539 let mut blob_states = Vec::new();
1540 for blob in blobs {
1541 let root_key = RootKey::Blob(blob.id()).bytes();
1542 let store = self.database.open_shared(&root_key)?;
1543 let has_state = store.contains_key(BLOB_STATE_KEY).await?;
1544 blob_states.push(has_state);
1545 if has_state {
1546 batch.add_blob(blob);
1547 }
1548 }
1549 self.write_batch(batch).await?;
1550 Ok(blob_states)
1551 }
1552
1553 #[instrument(skip_all, fields(blobs_len = %blobs.len()))]
1554 async fn write_blobs(&self, blobs: &[Blob]) -> Result<(), ViewError> {
1555 if blobs.is_empty() {
1556 return Ok(());
1557 }
1558 let mut batch = MultiPartitionBatch::new();
1559 for blob in blobs {
1560 batch.add_blob(blob);
1561 }
1562 self.write_batch(batch).await
1563 }
1564
1565 #[instrument(skip_all, fields(blobs_len = %blobs.len()))]
1566 async fn write_blobs_and_certificate(
1567 &self,
1568 blobs: &[Blob],
1569 certificate: &ConfirmedBlockCertificate,
1570 ) -> Result<(), ViewError> {
1571 let mut batch = MultiPartitionBatch::new();
1572 for blob in blobs {
1573 batch.add_blob(blob);
1574 }
1575 batch.add_certificate(certificate)?;
1576 self.write_batch(batch).await?;
1577 let block = certificate.value().block();
1579 let chain_id = block.header.chain_id;
1580 let height = block.header.height;
1581 let hash = certificate.hash();
1582 self.caches
1583 .block_hash_by_height
1584 .insert(&(chain_id, height), hash);
1585 Ok(())
1586 }
1587
1588 fn cache_certificate(
1589 &self,
1590 certificate: ConfirmedBlockCertificate,
1591 ) -> CacheArc<ConfirmedBlockCertificate> {
1592 self.caches
1593 .certificate
1594 .insert(&certificate.hash(), certificate)
1595 }
1596
1597 fn cache_blob(&self, blob: Blob) -> CacheArc<Blob> {
1598 self.caches.blob.insert(&blob.id(), blob)
1599 }
1600
1601 fn cache_confirmed_block(&self, block: ConfirmedBlock) -> CacheArc<ConfirmedBlock> {
1602 self.caches.confirmed_block.insert(&block.hash(), block)
1603 }
1604
1605 #[instrument(skip_all, fields(%hash))]
1606 async fn contains_certificate(&self, hash: CryptoHash) -> Result<bool, ViewError> {
1607 if self.caches.certificate.contains(&hash) || self.caches.certificate_raw.contains(&hash) {
1608 #[cfg(with_metrics)]
1609 metrics::CONTAINS_CERTIFICATE_COUNTER
1610 .with_label_values(&[metrics::CACHE])
1611 .inc();
1612 return Ok(true);
1613 }
1614 let root_key = RootKey::ConfirmedBlock(hash).bytes();
1615 let store = self.database.open_shared(&root_key)?;
1616 let results = store.contains_keys(&get_block_keys()).await?;
1617 #[cfg(with_metrics)]
1618 metrics::CONTAINS_CERTIFICATE_COUNTER
1619 .with_label_values(&[metrics::DB])
1620 .inc();
1621 Ok(results[0] && results[1])
1622 }
1623
1624 #[instrument(skip_all, fields(%hash))]
1625 async fn read_certificate(
1626 &self,
1627 hash: CryptoHash,
1628 ) -> Result<Option<CacheArc<ConfirmedBlockCertificate>>, ViewError> {
1629 if let Some(cert) = self.caches.certificate.get(&hash) {
1631 #[cfg(with_metrics)]
1632 metrics::READ_CERTIFICATE_COUNTER
1633 .with_label_values(&[metrics::CACHE])
1634 .inc();
1635 return Ok(Some(cert));
1636 }
1637 if let Some(raw) = self.caches.certificate_raw.get(&hash) {
1639 #[cfg(with_metrics)]
1640 metrics::READ_CERTIFICATE_COUNTER
1641 .with_label_values(&[metrics::CACHE])
1642 .inc();
1643 return self.deserialize_and_cache_certificate(&raw.0, &raw.1);
1644 }
1645 let root_key = RootKey::ConfirmedBlock(hash).bytes();
1647 let store = self.database.open_shared(&root_key)?;
1648 let values = store.read_multi_values_bytes(&get_block_keys()).await?;
1649 #[cfg(with_metrics)]
1650 metrics::READ_CERTIFICATE_COUNTER
1651 .with_label_values(&[metrics::DB])
1652 .inc();
1653 let Some(lite_cert_bytes) = values[0].as_ref() else {
1654 return Ok(None);
1655 };
1656 let Some(confirmed_block_bytes) = values[1].as_ref() else {
1657 return Ok(None);
1658 };
1659 self.caches.certificate_raw.insert(
1660 &hash,
1661 (lite_cert_bytes.clone(), confirmed_block_bytes.clone()),
1662 );
1663 self.deserialize_and_cache_certificate(lite_cert_bytes, confirmed_block_bytes)
1664 }
1665
1666 #[instrument(skip_all)]
1667 async fn read_certificates(
1668 &self,
1669 hashes: &[CryptoHash],
1670 ) -> Result<Vec<Option<CacheArc<ConfirmedBlockCertificate>>>, ViewError> {
1671 let raw_certs = self.read_certificates_raw(hashes).await?;
1672
1673 raw_certs
1674 .into_iter()
1675 .map(|maybe_raw| {
1676 let Some(raw) = maybe_raw else {
1677 return Ok(None);
1678 };
1679 self.deserialize_and_cache_certificate(&raw.0, &raw.1)
1680 })
1681 .collect()
1682 }
1683
1684 #[instrument(skip_all)]
1685 async fn read_certificates_raw(
1686 &self,
1687 hashes: &[CryptoHash],
1688 ) -> Result<Vec<Option<CacheArc<(Vec<u8>, Vec<u8>)>>>, ViewError> {
1689 if hashes.is_empty() {
1690 return Ok(Vec::new());
1691 }
1692 let mut results = vec![None; hashes.len()];
1693 let mut misses = Vec::new();
1694 for (i, hash) in hashes.iter().enumerate() {
1695 if let Some(raw) = self.caches.certificate_raw.get(hash) {
1696 results[i] = Some(raw);
1697 } else {
1698 misses.push(i);
1699 }
1700 }
1701 if !misses.is_empty() {
1702 let miss_hashes: Vec<_> = misses.iter().map(|&i| hashes[i]).collect();
1703 let root_keys = Self::get_root_keys_for_certificates(&miss_hashes);
1704 for (miss_idx, root_key) in misses.iter().zip(root_keys) {
1705 let store = self.database.open_shared(&root_key)?;
1706 let values = store.read_multi_values_bytes(&get_block_keys()).await?;
1707 if let (Some(lite), Some(block)) = (values[0].as_ref(), values[1].as_ref()) {
1708 results[*miss_idx] = Some(
1709 self.caches
1710 .certificate_raw
1711 .insert(&hashes[*miss_idx], (lite.clone(), block.clone())),
1712 );
1713 }
1714 }
1715 }
1716 #[cfg(with_metrics)]
1717 {
1718 let cache_hits = (hashes.len() - misses.len()) as u64;
1719 if cache_hits > 0 {
1720 metrics::READ_CERTIFICATES_COUNTER
1721 .with_label_values(&[metrics::CACHE])
1722 .inc_by(cache_hits);
1723 }
1724 let db_reads = misses.len() as u64;
1725 if db_reads > 0 {
1726 metrics::READ_CERTIFICATES_COUNTER
1727 .with_label_values(&[metrics::DB])
1728 .inc_by(db_reads);
1729 }
1730 }
1731 Ok(results)
1732 }
1733
1734 async fn read_certificate_hashes_by_heights(
1735 &self,
1736 chain_id: ChainId,
1737 heights: &[BlockHeight],
1738 ) -> Result<Vec<Option<CryptoHash>>, ViewError> {
1739 if heights.is_empty() {
1740 return Ok(Vec::new());
1741 }
1742
1743 let mut results = vec![None; heights.len()];
1744 let mut misses = Vec::new();
1745 for (i, &height) in heights.iter().enumerate() {
1746 if let Some(hash) = self.caches.block_hash_by_height.get(&(chain_id, height)) {
1747 results[i] = Some(*hash);
1748 } else {
1749 misses.push(i);
1750 }
1751 }
1752 #[cfg(with_metrics)]
1753 {
1754 let cache_hits = (heights.len() - misses.len()) as u64;
1755 if cache_hits > 0 {
1756 metrics::READ_BLOCK_HASH_BY_HEIGHT_COUNTER
1757 .with_label_values(&[metrics::CACHE])
1758 .inc_by(cache_hits);
1759 }
1760 }
1761 if !misses.is_empty() {
1762 let miss_keys: Vec<Vec<u8>> =
1763 misses.iter().map(|&i| to_height_key(heights[i])).collect();
1764 let index_root_key = RootKey::BlockByHeight(chain_id).bytes();
1765 let store = self.database.open_shared(&index_root_key)?;
1766 let hash_bytes = store.read_multi_values_bytes(&miss_keys).await?;
1767 #[cfg(with_metrics)]
1768 {
1769 let db_reads = misses.len() as u64;
1770 metrics::READ_BLOCK_HASH_BY_HEIGHT_COUNTER
1771 .with_label_values(&[metrics::DB])
1772 .inc_by(db_reads);
1773 }
1774 for (miss_idx, opt_bytes) in misses.iter().zip(hash_bytes) {
1775 if let Some(bytes) = opt_bytes {
1776 let hash = bcs::from_bytes::<CryptoHash>(&bytes)?;
1777 self.caches
1778 .block_hash_by_height
1779 .insert(&(chain_id, heights[*miss_idx]), hash);
1780 results[*miss_idx] = Some(hash);
1781 }
1782 }
1783 }
1784
1785 Ok(results)
1786 }
1787
1788 #[instrument(skip_all)]
1789 async fn read_certificates_by_heights_raw(
1790 &self,
1791 chain_id: ChainId,
1792 heights: &[BlockHeight],
1793 ) -> Result<Vec<Option<CacheArc<(Vec<u8>, Vec<u8>)>>>, ViewError> {
1794 let hashes: Vec<Option<CryptoHash>> = self
1795 .read_certificate_hashes_by_heights(chain_id, heights)
1796 .await?;
1797
1798 let mut indices: HashMap<CryptoHash, Vec<usize>> = HashMap::new();
1800 for (index, maybe_hash) in hashes.iter().enumerate() {
1801 if let Some(hash) = maybe_hash {
1802 indices.entry(*hash).or_default().push(index);
1803 }
1804 }
1805
1806 let unique_hashes = indices.keys().copied().collect::<Vec<_>>();
1808
1809 let mut result = vec![None; heights.len()];
1810
1811 for (raw_cert, hash) in self
1812 .read_certificates_raw(&unique_hashes)
1813 .await?
1814 .into_iter()
1815 .zip(unique_hashes)
1816 {
1817 if let Some(idx_list) = indices.get(&hash) {
1818 for &index in idx_list {
1819 result[index] = raw_cert.clone();
1820 }
1821 } else {
1822 tracing::warn!(
1824 hash=?hash,
1825 "certificate hash not found in indices map",
1826 );
1827 }
1828 }
1829
1830 Ok(result)
1831 }
1832
1833 #[instrument(skip_all, fields(%chain_id, heights_len = heights.len()))]
1834 async fn read_certificates_by_heights(
1835 &self,
1836 chain_id: ChainId,
1837 heights: &[BlockHeight],
1838 ) -> Result<Vec<Option<CacheArc<ConfirmedBlockCertificate>>>, ViewError> {
1839 self.read_certificates_by_heights_raw(chain_id, heights)
1840 .await?
1841 .into_iter()
1842 .map(|maybe_raw| match maybe_raw {
1843 None => Ok(None),
1844 Some(raw) => self.deserialize_and_cache_certificate(&raw.0, &raw.1),
1845 })
1846 .collect()
1847 }
1848
1849 #[instrument(skip_all, fields(%chain_id, indices_len = indices.len()))]
1850 async fn write_certificate_height_indices(
1851 &self,
1852 chain_id: ChainId,
1853 indices: &[(BlockHeight, CryptoHash)],
1854 ) -> Result<(), ViewError> {
1855 if indices.is_empty() {
1856 return Ok(());
1857 }
1858
1859 let mut batch = MultiPartitionBatch::new();
1860 let index_root_key = RootKey::BlockByHeight(chain_id).bytes();
1861 let key_values: Vec<(Vec<u8>, Vec<u8>)> = indices
1862 .iter()
1863 .map(|(height, hash)| {
1864 let height_key = to_height_key(*height);
1865 let hash_value = bcs::to_bytes(hash).unwrap();
1866 (height_key, hash_value)
1867 })
1868 .collect();
1869 batch.put_key_values(index_root_key, key_values);
1870 self.write_batch(batch).await
1871 }
1872
1873 #[instrument(skip_all, fields(event_id = ?event_id))]
1874 async fn read_event(&self, event_id: EventId) -> Result<Option<CacheArc<Vec<u8>>>, ViewError> {
1875 if let Some(event) = self.caches.event.get(&event_id) {
1876 #[cfg(with_metrics)]
1877 metrics::READ_EVENT_COUNTER
1878 .with_label_values(&[metrics::CACHE])
1879 .inc();
1880 return Ok(Some(event));
1881 }
1882 let event_key = to_event_key(&event_id);
1883 let root_key = RootKey::Event(event_id.chain_id).bytes();
1884 let store = self.database.open_shared(&root_key)?;
1885 let event = store.read_value_bytes(&event_key).await?;
1886 #[cfg(with_metrics)]
1887 metrics::READ_EVENT_COUNTER
1888 .with_label_values(&[metrics::DB])
1889 .inc();
1890 match event {
1891 Some(event_bytes) => Ok(Some(self.caches.event.insert(&event_id, event_bytes))),
1892 None => Ok(None),
1893 }
1894 }
1895
1896 #[instrument(skip_all, fields(event_id = ?event_id))]
1897 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1898 if self.caches.event.contains(&event_id) {
1899 #[cfg(with_metrics)]
1900 metrics::CONTAINS_EVENT_COUNTER
1901 .with_label_values(&[metrics::CACHE])
1902 .inc();
1903 return Ok(true);
1904 }
1905 let event_key = to_event_key(&event_id);
1906 let root_key = RootKey::Event(event_id.chain_id).bytes();
1907 let store = self.database.open_shared(&root_key)?;
1908 let exists = store.contains_key(&event_key).await?;
1909 #[cfg(with_metrics)]
1910 metrics::CONTAINS_EVENT_COUNTER
1911 .with_label_values(&[metrics::DB])
1912 .inc();
1913 Ok(exists)
1914 }
1915
1916 #[instrument(skip_all, fields(%chain_id, %stream_id, %start_index))]
1917 async fn read_events_from_index(
1918 &self,
1919 chain_id: &ChainId,
1920 stream_id: &StreamId,
1921 start_index: u32,
1922 ) -> Result<Vec<IndexAndEvent>, ViewError> {
1923 let root_key = RootKey::Event(*chain_id).bytes();
1924 let store = self.database.open_shared(&root_key)?;
1925 let mut entries = Vec::new();
1928 let mut db_keys = Vec::new();
1929 let prefix = bcs::to_bytes(stream_id).unwrap();
1930 for short_key in store.find_keys_by_prefix(&prefix).await? {
1931 let index = bcs::from_bytes::<u32>(&short_key)?;
1932 if index >= start_index {
1933 let event_id = EventId {
1934 chain_id: *chain_id,
1935 stream_id: stream_id.clone(),
1936 index,
1937 };
1938 let cached = self.caches.event.get(&event_id).map(|arc| (*arc).clone());
1939 if cached.is_none() {
1940 let mut key = prefix.clone();
1941 key.extend(short_key);
1942 db_keys.push(key);
1943 }
1944 entries.push((index, cached));
1945 }
1946 }
1947 let mut db_values = if db_keys.is_empty() {
1948 Vec::new()
1949 } else {
1950 store.read_multi_values_bytes(&db_keys).await?
1951 }
1952 .into_iter();
1953 let mut returned_values = Vec::with_capacity(entries.len());
1954 for (index, cached) in entries {
1955 let event = match cached {
1956 Some(event) => event,
1957 None => {
1958 let event_bytes = db_values
1959 .next()
1960 .expect("one database value per cache miss")
1961 .unwrap();
1962 let event_id = EventId {
1963 chain_id: *chain_id,
1964 stream_id: stream_id.clone(),
1965 index,
1966 };
1967 self.caches.event.insert(&event_id, event_bytes.clone());
1968 event_bytes
1969 }
1970 };
1971 returned_values.push(IndexAndEvent { index, event });
1972 }
1973 Ok(returned_values)
1974 }
1975
1976 #[instrument(skip_all)]
1977 async fn write_events(
1978 &self,
1979 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1980 ) -> Result<(), ViewError> {
1981 let mut batch = MultiPartitionBatch::new();
1982 for (event_id, value) in events {
1983 batch.add_event(&event_id, value);
1984 }
1985 self.write_batch(batch).await
1986 }
1987
1988 #[instrument(skip_all)]
1989 async fn read_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1990 if let Some(desc) = self.caches.network_description.get() {
1991 #[cfg(with_metrics)]
1992 metrics::READ_NETWORK_DESCRIPTION
1993 .with_label_values(&[metrics::CACHE])
1994 .inc();
1995 return Ok(Some(desc.clone()));
1996 }
1997 let root_key = RootKey::NetworkDescription.bytes();
1998 let store = self.database.open_shared(&root_key)?;
1999 let maybe_value: Option<NetworkDescription> =
2000 store.read_value(NETWORK_DESCRIPTION_KEY).await?;
2001 #[cfg(with_metrics)]
2002 metrics::READ_NETWORK_DESCRIPTION
2003 .with_label_values(&[metrics::DB])
2004 .inc();
2005 if let Some(ref desc) = maybe_value {
2006 if self.caches.network_description.set(desc.clone()).is_err() {
2007 debug!("network description cache was already populated concurrently");
2008 }
2009 }
2010 Ok(maybe_value)
2011 }
2012
2013 #[instrument(skip_all)]
2014 async fn write_network_description(
2015 &self,
2016 information: &NetworkDescription,
2017 ) -> Result<(), ViewError> {
2018 let mut batch = MultiPartitionBatch::new();
2019 batch.add_network_description(information)?;
2020 self.write_batch(batch).await?;
2021 Ok(())
2022 }
2023
2024 fn shared_committees(&self) -> &SharedCommittees {
2025 &self.shared_committees
2026 }
2027
2028 fn wasm_runtime(&self) -> Option<WasmRuntime> {
2029 self.wasm_runtime
2030 }
2031
2032 #[instrument(skip_all)]
2033 async fn block_exporter_context(
2034 &self,
2035 block_exporter_id: u32,
2036 ) -> Result<Self::BlockExporterContext, ViewError> {
2037 let root_key = RootKey::BlockExporterState(block_exporter_id).bytes();
2038 let store = self.database.open_exclusive(&root_key)?;
2039 Ok(ViewContext::create_root_context(store, block_exporter_id).await?)
2040 }
2041}
2042
2043impl<Database, C> DbStorage<Database, C>
2044where
2045 Database: KeyValueDatabase + Clone,
2046 Database::Store: KeyValueStore + Clone,
2047 C: Clock,
2048 Database::Error: Send + Sync,
2049{
2050 #[instrument(skip_all)]
2051 fn get_root_keys_for_certificates(hashes: &[CryptoHash]) -> Vec<Vec<u8>> {
2052 hashes
2053 .iter()
2054 .map(|hash| RootKey::ConfirmedBlock(*hash).bytes())
2055 .collect()
2056 }
2057
2058 fn deserialize_and_cache_certificate(
2059 &self,
2060 lite_cert_bytes: &[u8],
2061 confirmed_block_bytes: &[u8],
2062 ) -> Result<Option<CacheArc<ConfirmedBlockCertificate>>, ViewError> {
2063 let lite = bcs::from_bytes::<LiteCertificate>(lite_cert_bytes)?;
2064 let block = bcs::from_bytes::<ConfirmedBlock>(confirmed_block_bytes)?;
2065 let hash = block.hash();
2066 self.caches.confirmed_block.insert(&hash, block.clone());
2067 let certificate = lite
2068 .with_value(block)
2069 .ok_or(ViewError::InconsistentEntries)?;
2070 let arc = self.caches.certificate.insert(&hash, certificate);
2071 Ok(Some(arc))
2072 }
2073
2074 #[instrument(skip_all)]
2075 async fn write_entry(
2076 store: &Database::Store,
2077 key_values: Vec<(Vec<u8>, Vec<u8>)>,
2078 ) -> Result<(), ViewError> {
2079 let mut batch = Batch::new();
2080 for (key, value) in key_values {
2081 batch.put_key_value_bytes(key, value);
2082 }
2083 store.write_batch(batch).await?;
2084 Ok(())
2085 }
2086
2087 #[instrument(skip_all, fields(batch_size = batch.keys_value_bytes.len()))]
2088 pub(crate) async fn write_batch(&self, batch: MultiPartitionBatch) -> Result<(), ViewError> {
2089 if batch.keys_value_bytes.is_empty() {
2090 return Ok(());
2091 }
2092 let mut futures = Vec::new();
2093 for (root_key, key_values) in batch.keys_value_bytes {
2094 let store = self.database.open_shared(&root_key)?;
2095 futures.push(async move { Self::write_entry(&store, key_values).await });
2096 }
2097 futures::future::try_join_all(futures).await?;
2098 Ok(())
2099 }
2100}
2101
2102impl<Database, C> DbStorage<Database, C>
2103where
2104 Database: KeyValueDatabase + Clone + 'static,
2105 Database::Error: Send + Sync,
2106 Database::Store: KeyValueStore + Clone + 'static,
2107 C: Clock + Clone + Send + Sync + 'static,
2108{
2109 pub(crate) fn new(
2110 database: Database,
2111 wasm_runtime: Option<WasmRuntime>,
2112 cache_sizes: StorageCacheConfig,
2113 clock: C,
2114 ) -> Self {
2115 Self {
2116 database: Arc::new(database),
2117 clock,
2118 #[cfg_attr(web, expect(clippy::arc_with_non_send_sync))]
2120 thread_pool: Arc::new(linera_execution::ThreadPool::new(20)),
2121 wasm_runtime,
2122 user_contracts: Arc::new(papaya::HashMap::new()),
2123 user_services: Arc::new(papaya::HashMap::new()),
2124 shared_committees: SharedCommittees::new(),
2125 caches: StorageCaches::new(cache_sizes),
2126 execution_runtime_config: ExecutionRuntimeConfig::default(),
2127 }
2128 }
2129
2130 pub fn with_allow_application_logs(mut self, allow: bool) -> Self {
2132 self.execution_runtime_config.allow_application_logs = allow;
2133 self
2134 }
2135}
2136
2137impl<Database> DbStorage<Database, WallClock>
2138where
2139 Database: KeyValueDatabase + Clone + 'static,
2140 Database::Error: Send + Sync,
2141 Database::Store: KeyValueStore + Clone + 'static,
2142{
2143 pub async fn maybe_create_and_connect(
2145 config: &Database::Config,
2146 namespace: &str,
2147 wasm_runtime: Option<WasmRuntime>,
2148 cache_sizes: StorageCacheConfig,
2149 ) -> Result<Self, ViewError> {
2150 let database = Database::maybe_create_and_connect(config, namespace).await?;
2151 let storage = Self::new(database, wasm_runtime, cache_sizes, WallClock);
2152 Ok(storage)
2153 }
2154
2155 pub async fn connect(
2157 config: &Database::Config,
2158 namespace: &str,
2159 wasm_runtime: Option<WasmRuntime>,
2160 cache_sizes: StorageCacheConfig,
2161 ) -> Result<Self, ViewError> {
2162 let database = Database::connect(config, namespace).await?;
2163 let storage = Self::new(database, wasm_runtime, cache_sizes, WallClock);
2164 Ok(storage)
2165 }
2166
2167 pub async fn list_blob_ids(
2169 config: &Database::Config,
2170 namespace: &str,
2171 ) -> Result<Vec<BlobId>, ViewError> {
2172 let database = Database::connect(config, namespace).await?;
2173 let root_keys = database.list_root_keys().await?;
2174 let mut blob_ids = Vec::new();
2175 for root_key in root_keys {
2176 if !root_key.is_empty() && root_key[0] == BLOB_ID_TAG {
2177 let root_key_red = &root_key[1..];
2178 let blob_id = bcs::from_bytes(root_key_red)?;
2179 blob_ids.push(blob_id);
2180 }
2181 }
2182 Ok(blob_ids)
2183 }
2184}
2185
2186impl<Database> DbStorage<Database, WallClock>
2187where
2188 Database: KeyValueDatabase + Clone + Send + Sync + 'static,
2189 Database::Error: Send + Sync,
2190{
2191 pub async fn list_chain_ids(
2193 config: &Database::Config,
2194 namespace: &str,
2195 ) -> Result<Vec<ChainId>, ViewError> {
2196 let database = Database::connect(config, namespace).await?;
2197 let root_keys = database.list_root_keys().await?;
2198 let mut chain_ids = Vec::new();
2199 for root_key in root_keys {
2200 if !root_key.is_empty() && root_key[0] == CHAIN_ID_TAG {
2201 let root_key_red = &root_key[1..];
2202 let chain_id = bcs::from_bytes(root_key_red)?;
2203 chain_ids.push(chain_id);
2204 }
2205 }
2206 Ok(chain_ids)
2207 }
2208}
2209
2210#[cfg(with_testing)]
2211impl<Database> DbStorage<Database, TestClock>
2212where
2213 Database: TestKeyValueDatabase + Clone + Send + Sync + 'static,
2214 Database::Store: KeyValueStore + Clone + Send + Sync + 'static,
2215 Database::Error: Send + Sync,
2216{
2217 pub async fn make_test_storage(wasm_runtime: Option<WasmRuntime>) -> Self {
2219 let config = Database::new_test_config().await.unwrap();
2220 let namespace = generate_test_namespace();
2221 DbStorage::<Database, TestClock>::new_for_testing(
2222 config,
2223 &namespace,
2224 wasm_runtime,
2225 TestClock::new(),
2226 )
2227 .await
2228 .unwrap()
2229 }
2230
2231 pub async fn new_for_testing(
2233 config: Database::Config,
2234 namespace: &str,
2235 wasm_runtime: Option<WasmRuntime>,
2236 clock: TestClock,
2237 ) -> Result<Self, ViewError> {
2238 let database = Database::recreate_and_connect(&config, namespace).await?;
2239 let storage = Self::new(database, wasm_runtime, DEFAULT_STORAGE_CACHE_CONFIG, clock);
2240 storage.assert_is_migrated_storage().await?;
2241 Ok(storage)
2242 }
2243}