1use std::{
5 collections::{BTreeMap, 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,
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_interval, exponential_bucket_latencies, linear_bucket_interval,
56 register_histogram, register_histogram_vec, register_int_counter, register_int_counter_vec,
57 };
58 use prometheus::{Histogram, 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 pub(super) static CERTIFICATE_LITE_BYTES: LazyLock<Histogram> = LazyLock::new(|| {
188 register_histogram(
189 "certificate_lite_bytes",
190 "Serialized size of the lite-certificate (signatures + metadata) in bytes",
191 exponential_bucket_interval(128.0, 2_097_152.0),
192 )
193 });
194
195 pub(super) static CERTIFICATE_VALUE_BYTES: LazyLock<Histogram> = LazyLock::new(|| {
199 register_histogram(
200 "certificate_value_bytes",
201 "Serialized size of the certificate value (block payload) in bytes",
202 exponential_bucket_interval(256.0, 16_777_216.0),
203 )
204 });
205
206 pub(super) static CERTIFICATE_SIGNER_COUNT: LazyLock<Histogram> = LazyLock::new(|| {
210 register_histogram(
211 "certificate_signer_count",
212 "Number of validator signatures attached to each confirmed certificate",
213 linear_bucket_interval(1.0, 1.0, 20.0),
214 )
215 });
216
217 #[doc(hidden)]
219 pub(crate) static LOAD_CHAIN_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
220 register_histogram_vec(
221 "load_chain_latency",
222 "The latency to load a chain state",
223 &[],
224 exponential_bucket_latencies(1000.0),
225 )
226 });
227
228 #[doc(hidden)]
230 pub(super) static READ_EVENT_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
231 register_int_counter_vec(
232 "read_event",
233 "The metric counting how often an event is read from storage",
234 &[SOURCE_LABEL],
235 )
236 });
237
238 pub(super) static CONTAINS_EVENT_COUNTER: LazyLock<IntCounterVec> = LazyLock::new(|| {
240 register_int_counter_vec(
241 "contains_event",
242 "The metric counting how often an event is tested for existence from storage",
243 &[SOURCE_LABEL],
244 )
245 });
246
247 #[doc(hidden)]
249 pub(super) static WRITE_EVENT_COUNTER: LazyLock<IntCounter> = LazyLock::new(|| {
250 register_int_counter(
251 "write_event",
252 "The metric counting how often an event is written to storage",
253 )
254 });
255
256 #[doc(hidden)]
258 pub(super) static READ_BLOCK_HASH_BY_HEIGHT_COUNTER: LazyLock<IntCounterVec> =
259 LazyLock::new(|| {
260 register_int_counter_vec(
261 "read_block_hash_by_height",
262 "The metric counting how often a block hash is read by height from storage",
263 &[SOURCE_LABEL],
264 )
265 });
266
267 #[doc(hidden)]
269 pub(super) static READ_EVENT_BLOCK_HEIGHT_COUNTER: LazyLock<IntCounterVec> =
270 LazyLock::new(|| {
271 register_int_counter_vec(
272 "read_event_block_height",
273 "The metric counting how often an event block height is read from storage",
274 &[SOURCE_LABEL],
275 )
276 });
277
278 #[doc(hidden)]
280 pub(super) static READ_NETWORK_DESCRIPTION: LazyLock<IntCounterVec> = LazyLock::new(|| {
281 register_int_counter_vec(
282 "network_description",
283 "The metric counting how often the network description is read from storage",
284 &[SOURCE_LABEL],
285 )
286 });
287
288 #[doc(hidden)]
290 pub(super) static WRITE_NETWORK_DESCRIPTION: LazyLock<IntCounter> = LazyLock::new(|| {
291 register_int_counter(
292 "write_network_description",
293 "The metric counting how often the network description is written to storage",
294 )
295 });
296}
297
298const BLOB_KEY: &[u8] = &[0];
300
301const BLOB_STATE_KEY: &[u8] = &[1];
303
304const LITE_CERTIFICATE_KEY: &[u8] = &[2];
306
307const BLOCK_KEY: &[u8] = &[3];
309
310const NETWORK_DESCRIPTION_KEY: &[u8] = &[4];
312
313fn get_block_keys() -> Vec<Vec<u8>> {
314 vec![LITE_CERTIFICATE_KEY.to_vec(), BLOCK_KEY.to_vec()]
315}
316
317#[derive(Default)]
318#[expect(clippy::type_complexity)]
319struct MultiPartitionBatch {
320 keys_value_bytes: BTreeMap<Vec<u8>, Vec<(Vec<u8>, Vec<u8>)>>,
321}
322
323impl MultiPartitionBatch {
324 fn new() -> Self {
325 Self::default()
326 }
327
328 fn put_key_values(&mut self, root_key: Vec<u8>, key_values: Vec<(Vec<u8>, Vec<u8>)>) {
329 let entry = self.keys_value_bytes.entry(root_key).or_default();
330 entry.extend(key_values);
331 }
332
333 fn put_key_value(&mut self, root_key: Vec<u8>, key: Vec<u8>, value: Vec<u8>) {
334 self.put_key_values(root_key, vec![(key, value)]);
335 }
336
337 fn add_blob(&mut self, blob: &Blob) {
338 #[cfg(with_metrics)]
339 metrics::WRITE_BLOB_COUNTER.inc();
340 let root_key = RootKey::BlobId(blob.id()).bytes();
341 let key = BLOB_KEY.to_vec();
342 self.put_key_value(root_key, key, blob.bytes().to_vec());
343 }
344
345 fn add_blob_state(&mut self, blob_id: BlobId, blob_state: &BlobState) -> Result<(), ViewError> {
346 let root_key = RootKey::BlobId(blob_id).bytes();
347 let key = BLOB_STATE_KEY.to_vec();
348 let value = bcs::to_bytes(blob_state)?;
349 self.put_key_value(root_key, key, value);
350 Ok(())
351 }
352
353 fn add_certificate(
362 &mut self,
363 certificate: &ConfirmedBlockCertificate,
364 ) -> Result<(), ViewError> {
365 #[cfg(with_metrics)]
366 {
367 metrics::WRITE_CERTIFICATE_COUNTER.inc();
368 metrics::CERTIFICATE_SIGNER_COUNT.observe(certificate.signatures().len() as f64);
369 }
370 let hash = certificate.hash();
371
372 let root_key = RootKey::BlockHash(hash).bytes();
374 let mut key_values = Vec::new();
375 let key = LITE_CERTIFICATE_KEY.to_vec();
376 let value = bcs::to_bytes(&certificate.lite_certificate())?;
377 #[cfg(with_metrics)]
378 metrics::CERTIFICATE_LITE_BYTES.observe(value.len() as f64);
379 key_values.push((key, value));
380 let key = BLOCK_KEY.to_vec();
381 let value = bcs::to_bytes(&certificate.value())?;
382 #[cfg(with_metrics)]
383 metrics::CERTIFICATE_VALUE_BYTES.observe(value.len() as f64);
384 key_values.push((key, value));
385 self.put_key_values(root_key, key_values);
386
387 let chain_id = certificate.value().block().header.chain_id;
389 let height = certificate.value().block().header.height;
390 let index_root_key = RootKey::BlockByHeight(chain_id).bytes();
391 let height_key = to_height_key(height);
392 let index_value = bcs::to_bytes(&hash)?;
393 self.put_key_value(index_root_key, height_key, index_value);
394
395 let event_index_root_key = RootKey::EventBlockHeight(chain_id).bytes();
397 let height_value = bcs::to_bytes(&height)?;
398 for event in certificate.value().block().body.events.iter().flatten() {
399 let event_key = to_event_key(&EventId {
400 chain_id,
401 stream_id: event.stream_id.clone(),
402 index: event.index,
403 });
404 self.put_key_value(
405 event_index_root_key.clone(),
406 event_key,
407 height_value.clone(),
408 );
409 }
410
411 Ok(())
412 }
413
414 fn add_event(&mut self, event_id: &EventId, value: Vec<u8>) {
415 #[cfg(with_metrics)]
416 metrics::WRITE_EVENT_COUNTER.inc();
417 let key = to_event_key(event_id);
418 let root_key = RootKey::Event(event_id.chain_id).bytes();
419 self.put_key_value(root_key, key, value);
420 }
421
422 fn add_network_description(
423 &mut self,
424 information: &NetworkDescription,
425 ) -> Result<(), ViewError> {
426 #[cfg(with_metrics)]
427 metrics::WRITE_NETWORK_DESCRIPTION.inc();
428 let root_key = RootKey::NetworkDescription.bytes();
429 let key = NETWORK_DESCRIPTION_KEY.to_vec();
430 let value = bcs::to_bytes(information)?;
431 self.put_key_value(root_key, key, value);
432 Ok(())
433 }
434}
435
436#[derive(Clone, Copy, Debug)]
438pub struct StorageCacheConfig {
439 pub blob_cache_size: usize,
441 pub confirmed_block_cache_size: usize,
443 pub certificate_cache_size: usize,
445 pub certificate_raw_cache_size: usize,
447 pub event_cache_size: usize,
449 pub block_hash_by_height_cache_size: usize,
451 pub event_block_height_cache_size: usize,
453 pub cache_cleanup_interval_secs: u64,
455}
456
457#[cfg(with_testing)]
459pub const DEFAULT_STORAGE_CACHE_CONFIG: StorageCacheConfig = StorageCacheConfig {
460 blob_cache_size: 1000,
461 confirmed_block_cache_size: 1000,
462 certificate_cache_size: 1000,
463 certificate_raw_cache_size: 1000,
464 event_cache_size: 1000,
465 block_hash_by_height_cache_size: 1000,
466 event_block_height_cache_size: 1000,
467 cache_cleanup_interval_secs: linera_cache::DEFAULT_CLEANUP_INTERVAL_SECS,
468};
469
470type RawCertificate = (Vec<u8>, Vec<u8>);
472
473#[derive(Clone)]
479pub struct StorageCaches {
480 pub(crate) blob: Arc<ValueCache<BlobId, Blob>>,
481 pub(crate) confirmed_block: Arc<ValueCache<CryptoHash, ConfirmedBlock>>,
482 pub(crate) certificate: Arc<ValueCache<CryptoHash, ConfirmedBlockCertificate>>,
483 pub(crate) certificate_raw: Arc<ValueCache<CryptoHash, RawCertificate>>,
484 pub(crate) event: Arc<ValueCache<EventId, Vec<u8>>>,
485 pub(crate) block_hash_by_height: Arc<ValueCache<(ChainId, BlockHeight), CryptoHash>>,
486 pub(crate) event_block_height: Arc<ValueCache<EventId, BlockHeight>>,
487 pub(crate) network_description: Arc<OnceLock<NetworkDescription>>,
488}
489
490impl StorageCaches {
491 pub fn new(sizes: StorageCacheConfig) -> Self {
493 let interval = sizes.cache_cleanup_interval_secs;
494 Self {
495 blob: Arc::new(ValueCache::new(
496 "storage_blob",
497 sizes.blob_cache_size,
498 interval,
499 )),
500 confirmed_block: Arc::new(ValueCache::new(
501 "storage_confirmed_block",
502 sizes.confirmed_block_cache_size,
503 interval,
504 )),
505 certificate: Arc::new(ValueCache::new(
506 "storage_certificate",
507 sizes.certificate_cache_size,
508 interval,
509 )),
510 certificate_raw: Arc::new(ValueCache::new(
511 "storage_certificate_raw",
512 sizes.certificate_raw_cache_size,
513 interval,
514 )),
515 event: Arc::new(ValueCache::new(
516 "storage_event",
517 sizes.event_cache_size,
518 interval,
519 )),
520 block_hash_by_height: Arc::new(ValueCache::new(
521 "storage_block_hash_by_height",
522 sizes.block_hash_by_height_cache_size,
523 interval,
524 )),
525 event_block_height: Arc::new(ValueCache::new(
526 "storage_event_block_height",
527 sizes.event_block_height_cache_size,
528 interval,
529 )),
530 network_description: Arc::new(OnceLock::new()),
531 }
532 }
533}
534
535#[derive(Clone)]
537pub struct DbStorage<Database, Clock = WallClock> {
538 database: Arc<Database>,
539 clock: Clock,
540 thread_pool: Arc<linera_execution::ThreadPool>,
541 wasm_runtime: Option<WasmRuntime>,
542 user_contracts: Arc<papaya::HashMap<ApplicationId, UserContractCode>>,
543 user_services: Arc<papaya::HashMap<ApplicationId, UserServiceCode>>,
544 shared_committees: SharedCommittees,
545 caches: StorageCaches,
546 execution_runtime_config: ExecutionRuntimeConfig,
547}
548
549#[derive(Debug, Serialize, Deserialize)]
551pub enum RootKey {
552 NetworkDescription,
554 BlockExporterState(u32),
556 ChainState(ChainId),
558 BlockHash(CryptoHash),
560 BlobId(BlobId),
562 Event(ChainId),
564 BlockByHeight(ChainId),
566 EventBlockHeight(ChainId),
568}
569
570const CHAIN_ID_TAG: u8 = 2;
571const BLOB_ID_TAG: u8 = 4;
572const EVENT_ID_TAG: u8 = 5;
573
574impl RootKey {
575 pub fn bytes(&self) -> Vec<u8> {
577 bcs::to_bytes(self).unwrap()
578 }
579}
580
581#[derive(Debug, Serialize, Deserialize)]
582struct RestrictedEventId {
583 pub stream_id: StreamId,
584 pub index: u32,
585}
586
587fn to_event_key(event_id: &EventId) -> Vec<u8> {
588 let restricted_event_id = RestrictedEventId {
589 stream_id: event_id.stream_id.clone(),
590 index: event_id.index,
591 };
592 bcs::to_bytes(&restricted_event_id).unwrap()
593}
594
595pub(crate) fn to_height_key(height: BlockHeight) -> Vec<u8> {
596 bcs::to_bytes(&height).unwrap()
597}
598
599fn is_chain_state(root_key: &[u8]) -> bool {
600 if root_key.is_empty() {
601 return false;
602 }
603 root_key[0] == CHAIN_ID_TAG
604}
605
606#[derive(Clone, Copy)]
609pub struct ChainStatesFirstAssignment;
610
611impl DualStoreRootKeyAssignment for ChainStatesFirstAssignment {
612 fn assigned_store(root_key: &[u8]) -> Result<StoreInUse, bcs::Error> {
613 if root_key.is_empty() {
614 return Ok(StoreInUse::Second);
615 }
616 let store = match is_chain_state(root_key) {
617 true => StoreInUse::First,
618 false => StoreInUse::Second,
619 };
620 Ok(store)
621 }
622}
623
624#[derive(Clone)]
626pub struct WallClock;
627
628#[cfg_attr(not(web), async_trait)]
629#[cfg_attr(web, async_trait(?Send))]
630impl Clock for WallClock {
631 fn current_time(&self) -> Timestamp {
632 Timestamp::now()
633 }
634
635 async fn sleep_until(&self, timestamp: Timestamp) {
636 let delta = timestamp.delta_since(Timestamp::now());
637 if delta > TimeDelta::ZERO {
638 linera_base::time::timer::sleep(delta.as_duration()).await
639 }
640 }
641
642 async fn sleep_for(&self, duration: Duration) {
643 linera_base::time::timer::sleep(duration).await
644 }
645}
646
647#[cfg(with_testing)]
648#[derive(Default)]
649struct TestClockInner {
650 time: Timestamp,
651 sleeps: BTreeMap<Reverse<Timestamp>, Vec<oneshot::Sender<()>>>,
652 sleep_callback: Option<Box<dyn Fn(Timestamp) -> bool + Send + Sync>>,
655}
656
657#[cfg(with_testing)]
658impl TestClockInner {
659 fn set(&mut self, time: Timestamp) {
660 self.time = time;
661 let senders = self.sleeps.split_off(&Reverse(time));
662 for sender in senders.into_values().flatten() {
663 sender.send(()).ok();
665 }
666 }
667
668 fn add_sleep_until(&mut self, time: Timestamp) -> Receiver<()> {
669 let (sender, receiver) = oneshot::channel();
670 let should_auto_advance = self
671 .sleep_callback
672 .as_ref()
673 .is_some_and(|callback| callback(time));
674 if should_auto_advance && time > self.time {
675 self.set(time);
677 sender.send(()).ok();
679 } else if self.time >= time {
680 sender.send(()).ok();
682 } else {
683 self.sleeps.entry(Reverse(time)).or_default().push(sender);
684 }
685 receiver
686 }
687}
688
689#[cfg(with_testing)]
692#[derive(Clone, Default)]
693pub struct TestClock(Arc<std::sync::Mutex<TestClockInner>>);
694
695#[cfg(with_testing)]
696#[cfg_attr(not(web), async_trait)]
697#[cfg_attr(web, async_trait(?Send))]
698impl Clock for TestClock {
699 fn current_time(&self) -> Timestamp {
700 self.lock().time
701 }
702
703 async fn sleep_until(&self, timestamp: Timestamp) {
704 let receiver = self.lock().add_sleep_until(timestamp);
705 receiver.await.ok();
707 }
708}
709
710#[cfg(with_testing)]
711impl TestClock {
712 pub fn new() -> Self {
714 TestClock(Arc::default())
715 }
716
717 pub fn set(&self, time: Timestamp) {
719 self.lock().set(time);
720 }
721
722 pub fn add(&self, delta: TimeDelta) {
724 let mut guard = self.lock();
725 let time = guard.time.saturating_add(delta);
726 guard.set(time);
727 }
728
729 pub fn current_time(&self) -> Timestamp {
731 self.lock().time
732 }
733
734 #[cfg(with_testing)]
739 pub fn set_sleep_callback<F>(&self, callback: F)
740 where
741 F: Fn(Timestamp) -> bool + Send + Sync + 'static,
742 {
743 self.lock().sleep_callback = Some(Box::new(callback));
744 }
745
746 fn lock(&self) -> std::sync::MutexGuard<'_, TestClockInner> {
747 self.0.lock().expect("poisoned TestClock mutex")
748 }
749}
750
751#[cfg_attr(not(web), async_trait)]
752#[cfg_attr(web, async_trait(?Send))]
753impl<Database, C> Storage for DbStorage<Database, C>
754where
755 Database: KeyValueDatabase<
756 Store: KeyValueStore + Clone + linera_base::util::traits::AutoTraits + 'static,
757 Error: Send + Sync,
758 > + Clone
759 + linera_base::util::traits::AutoTraits
760 + 'static,
761 C: Clock + Clone + Send + Sync + 'static,
762{
763 type Context = ViewContext<ChainRuntimeContext<Self>, Database::Store>;
764 type Clock = C;
765 type BlockExporterContext = ViewContext<u32, Database::Store>;
766
767 fn clock(&self) -> &C {
768 &self.clock
769 }
770
771 fn thread_pool(&self) -> &Arc<linera_execution::ThreadPool> {
772 &self.thread_pool
773 }
774
775 fn shared_committees(&self) -> &SharedCommittees {
776 &self.shared_committees
777 }
778
779 #[instrument(level = "trace", skip_all, fields(chain_id = %chain_id))]
780 async fn load_chain(
781 &self,
782 chain_id: ChainId,
783 ) -> Result<ChainStateView<Self::Context>, ViewError> {
784 #[cfg(with_metrics)]
785 let _metric = metrics::LOAD_CHAIN_LATENCY.measure_latency();
786 let runtime_context = ChainRuntimeContext {
787 storage: self.clone(),
788 thread_pool: self.thread_pool.clone(),
789 chain_id,
790 execution_runtime_config: self.execution_runtime_config,
791 user_contracts: self.user_contracts.clone(),
792 user_services: self.user_services.clone(),
793 };
794 let root_key = RootKey::ChainState(chain_id).bytes();
795 let store = self.database.open_exclusive(&root_key)?;
796 let context = ViewContext::create_root_context(store, runtime_context).await?;
797 ChainStateView::load(context).await
798 }
799
800 #[instrument(level = "trace", skip_all, fields(%blob_id))]
801 async fn contains_blob(&self, blob_id: BlobId) -> Result<bool, ViewError> {
802 if self.caches.blob.contains(&blob_id) {
803 #[cfg(with_metrics)]
804 metrics::CONTAINS_BLOB_COUNTER
805 .with_label_values(&[metrics::CACHE])
806 .inc();
807 return Ok(true);
808 }
809 let root_key = RootKey::BlobId(blob_id).bytes();
810 let store = self.database.open_shared(&root_key)?;
811 let test = store.contains_key(BLOB_KEY).await?;
812 #[cfg(with_metrics)]
813 metrics::CONTAINS_BLOB_COUNTER
814 .with_label_values(&[metrics::DB])
815 .inc();
816 Ok(test)
817 }
818
819 #[instrument(skip_all, fields(blob_count = blob_ids.len()))]
820 async fn missing_blobs(&self, blob_ids: &[BlobId]) -> Result<Vec<BlobId>, ViewError> {
821 let mut missing_blobs = Vec::new();
822 #[cfg(with_metrics)]
823 let mut cache_hits: u64 = 0;
824 #[cfg(with_metrics)]
825 let mut db_checks: u64 = 0;
826 for blob_id in blob_ids {
827 if self.caches.blob.contains(blob_id) {
828 #[cfg(with_metrics)]
829 {
830 cache_hits += 1;
831 }
832 continue;
833 }
834 #[cfg(with_metrics)]
835 {
836 db_checks += 1;
837 }
838 let root_key = RootKey::BlobId(*blob_id).bytes();
839 let store = self.database.open_shared(&root_key)?;
840 if !store.contains_key(BLOB_KEY).await? {
841 missing_blobs.push(*blob_id);
842 }
843 }
844 #[cfg(with_metrics)]
845 {
846 if cache_hits > 0 {
847 metrics::CONTAINS_BLOBS_COUNTER
848 .with_label_values(&[metrics::CACHE])
849 .inc_by(cache_hits);
850 }
851 if db_checks > 0 {
852 metrics::CONTAINS_BLOBS_COUNTER
853 .with_label_values(&[metrics::DB])
854 .inc_by(db_checks);
855 }
856 }
857 Ok(missing_blobs)
858 }
859
860 #[instrument(skip_all, fields(%blob_id))]
861 async fn contains_blob_state(&self, blob_id: BlobId) -> Result<bool, ViewError> {
862 let root_key = RootKey::BlobId(blob_id).bytes();
863 let store = self.database.open_shared(&root_key)?;
864 let test = store.contains_key(BLOB_STATE_KEY).await?;
865 #[cfg(with_metrics)]
866 metrics::CONTAINS_BLOB_STATE_COUNTER
867 .with_label_values(&[metrics::DB])
868 .inc();
869 Ok(test)
870 }
871
872 #[instrument(skip_all, fields(%hash))]
873 async fn read_confirmed_block(
874 &self,
875 hash: CryptoHash,
876 ) -> Result<Option<CacheArc<ConfirmedBlock>>, ViewError> {
877 if let Some(block) = self.caches.confirmed_block.get(&hash) {
878 #[cfg(with_metrics)]
879 metrics::READ_CONFIRMED_BLOCK_COUNTER
880 .with_label_values(&[metrics::CACHE])
881 .inc();
882 return Ok(Some(block));
883 }
884 let root_key = RootKey::BlockHash(hash).bytes();
885 let store = self.database.open_shared(&root_key)?;
886 let value = store.read_value::<ConfirmedBlock>(BLOCK_KEY).await?;
887 #[cfg(with_metrics)]
888 metrics::READ_CONFIRMED_BLOCK_COUNTER
889 .with_label_values(&[metrics::DB])
890 .inc();
891 match value {
892 Some(block) => Ok(Some(self.caches.confirmed_block.insert(&hash, block))),
893 None => Ok(None),
894 }
895 }
896
897 #[instrument(skip_all)]
898 async fn read_confirmed_blocks<I: IntoIterator<Item = CryptoHash> + Send>(
899 &self,
900 hashes: I,
901 ) -> Result<Vec<Option<CacheArc<ConfirmedBlock>>>, ViewError> {
902 let hashes = hashes.into_iter().collect::<Vec<_>>();
903 if hashes.is_empty() {
904 return Ok(Vec::new());
905 }
906 let mut results = vec![None; hashes.len()];
907 let mut misses = Vec::new();
908 for (i, hash) in hashes.iter().enumerate() {
909 if let Some(block) = self.caches.confirmed_block.get(hash) {
910 results[i] = Some(block);
911 } else {
912 misses.push(i);
913 }
914 }
915 if !misses.is_empty() {
916 let miss_hashes: Vec<_> = misses.iter().map(|&i| hashes[i]).collect();
917 let root_keys = Self::get_root_keys_for_certificates(&miss_hashes);
918 for (miss_idx, root_key) in misses.iter().zip(root_keys) {
919 let store = self.database.open_shared(&root_key)?;
920 if let Some(block) = store.read_value::<ConfirmedBlock>(BLOCK_KEY).await? {
921 results[*miss_idx] = Some(
922 self.caches
923 .confirmed_block
924 .insert(&hashes[*miss_idx], block),
925 );
926 }
927 }
928 }
929 #[cfg(with_metrics)]
930 {
931 let cache_hits = (hashes.len() - misses.len()) as u64;
932 if cache_hits > 0 {
933 metrics::READ_CONFIRMED_BLOCKS_COUNTER
934 .with_label_values(&[metrics::CACHE])
935 .inc_by(cache_hits);
936 }
937 let db_reads = misses.len() as u64;
938 if db_reads > 0 {
939 metrics::READ_CONFIRMED_BLOCKS_COUNTER
940 .with_label_values(&[metrics::DB])
941 .inc_by(db_reads);
942 }
943 }
944 Ok(results)
945 }
946
947 #[instrument(skip_all, fields(%blob_id))]
948 async fn read_blob(&self, blob_id: BlobId) -> Result<Option<CacheArc<Blob>>, ViewError> {
949 if let Some(blob) = self.caches.blob.get(&blob_id) {
950 #[cfg(with_metrics)]
951 metrics::READ_BLOB_COUNTER
952 .with_label_values(&[metrics::CACHE])
953 .inc();
954 return Ok(Some(blob));
955 }
956 let root_key = RootKey::BlobId(blob_id).bytes();
957 let store = self.database.open_shared(&root_key)?;
958 let maybe_blob_bytes = store.read_value_bytes(BLOB_KEY).await?;
959 #[cfg(with_metrics)]
960 metrics::READ_BLOB_COUNTER
961 .with_label_values(&[metrics::DB])
962 .inc();
963 match maybe_blob_bytes {
964 Some(blob_bytes) => {
965 let blob = Blob::new_with_id_unchecked(blob_id, blob_bytes);
966 Ok(Some(self.caches.blob.insert(&blob_id, blob)))
967 }
968 None => Ok(None),
969 }
970 }
971
972 #[instrument(skip_all, fields(blob_ids_len = %blob_ids.len()))]
973 async fn read_blobs(
974 &self,
975 blob_ids: &[BlobId],
976 ) -> Result<Vec<Option<CacheArc<Blob>>>, ViewError> {
977 if blob_ids.is_empty() {
978 return Ok(Vec::new());
979 }
980 futures::future::try_join_all(blob_ids.iter().map(|blob_id| self.read_blob(*blob_id))).await
986 }
987
988 #[instrument(skip_all, fields(%blob_id))]
989 async fn read_blob_state(&self, blob_id: BlobId) -> Result<Option<BlobState>, ViewError> {
990 let root_key = RootKey::BlobId(blob_id).bytes();
991 let store = self.database.open_shared(&root_key)?;
992 let blob_state = store.read_value::<BlobState>(BLOB_STATE_KEY).await?;
993 #[cfg(with_metrics)]
994 metrics::READ_BLOB_STATE_COUNTER
995 .with_label_values(&[metrics::DB])
996 .inc();
997 Ok(blob_state)
998 }
999
1000 #[instrument(skip_all, fields(blob_ids_len = %blob_ids.len()))]
1001 async fn read_blob_states(
1002 &self,
1003 blob_ids: &[BlobId],
1004 ) -> Result<Vec<Option<BlobState>>, ViewError> {
1005 if blob_ids.is_empty() {
1006 return Ok(Vec::new());
1007 }
1008 futures::future::try_join_all(
1009 blob_ids
1010 .iter()
1011 .map(|blob_id| self.read_blob_state(*blob_id)),
1012 )
1013 .await
1014 }
1015
1016 #[instrument(skip_all, fields(blob_id = %blob.id()))]
1017 async fn write_blob(&self, blob: &Blob) -> Result<(), ViewError> {
1018 let mut batch = MultiPartitionBatch::new();
1019 batch.add_blob(blob);
1020 self.write_batch(batch).await?;
1021 Ok(())
1022 }
1023
1024 #[instrument(skip_all, fields(blob_ids_len = %blob_ids.len()))]
1025 async fn maybe_write_blob_states(
1026 &self,
1027 blob_ids: &[BlobId],
1028 blob_state: BlobState,
1029 ) -> Result<(), ViewError> {
1030 if blob_ids.is_empty() {
1031 return Ok(());
1032 }
1033 let mut maybe_blob_states = Vec::new();
1034 for blob_id in blob_ids {
1035 let root_key = RootKey::BlobId(*blob_id).bytes();
1036 let store = self.database.open_shared(&root_key)?;
1037 let maybe_blob_state = store.read_value::<BlobState>(BLOB_STATE_KEY).await?;
1038 maybe_blob_states.push(maybe_blob_state);
1039 }
1040 let mut batch = MultiPartitionBatch::new();
1041 for (maybe_blob_state, blob_id) in maybe_blob_states.iter().zip(blob_ids) {
1042 match maybe_blob_state {
1043 None => {
1044 batch.add_blob_state(*blob_id, &blob_state)?;
1045 }
1046 Some(state) => {
1047 if state.epoch < blob_state.epoch {
1048 batch.add_blob_state(*blob_id, &blob_state)?;
1049 }
1050 }
1051 }
1052 }
1053 self.write_batch(batch).await?;
1057 Ok(())
1058 }
1059
1060 #[instrument(skip_all, fields(blobs_len = %blobs.len()))]
1061 async fn maybe_write_blobs(&self, blobs: &[Blob]) -> Result<Vec<bool>, ViewError> {
1062 if blobs.is_empty() {
1063 return Ok(Vec::new());
1064 }
1065 let mut batch = MultiPartitionBatch::new();
1066 let mut blob_states = Vec::new();
1067 for blob in blobs {
1068 let root_key = RootKey::BlobId(blob.id()).bytes();
1069 let store = self.database.open_shared(&root_key)?;
1070 let has_state = store.contains_key(BLOB_STATE_KEY).await?;
1071 blob_states.push(has_state);
1072 if has_state {
1073 batch.add_blob(blob);
1074 }
1075 }
1076 self.write_batch(batch).await?;
1077 Ok(blob_states)
1078 }
1079
1080 #[instrument(skip_all, fields(blobs_len = %blobs.len()))]
1081 async fn write_blobs(&self, blobs: &[Blob]) -> Result<(), ViewError> {
1082 if blobs.is_empty() {
1083 return Ok(());
1084 }
1085 let mut batch = MultiPartitionBatch::new();
1086 for blob in blobs {
1087 batch.add_blob(blob);
1088 }
1089 self.write_batch(batch).await
1090 }
1091
1092 #[instrument(skip_all, fields(blobs_len = %blobs.len()))]
1093 async fn write_blobs_and_certificate(
1094 &self,
1095 blobs: &[Blob],
1096 certificate: &ConfirmedBlockCertificate,
1097 ) -> Result<(), ViewError> {
1098 let mut batch = MultiPartitionBatch::new();
1099 for blob in blobs {
1100 batch.add_blob(blob);
1101 }
1102 batch.add_certificate(certificate)?;
1103 self.write_batch(batch).await?;
1104 let block = certificate.value().block();
1106 let chain_id = block.header.chain_id;
1107 let height = block.header.height;
1108 let hash = certificate.hash();
1109 self.caches
1110 .block_hash_by_height
1111 .insert(&(chain_id, height), hash);
1112 for event in block.body.events.iter().flatten() {
1113 let event_id = EventId {
1114 chain_id,
1115 stream_id: event.stream_id.clone(),
1116 index: event.index,
1117 };
1118 self.caches.event_block_height.insert(&event_id, height);
1119 }
1120 Ok(())
1121 }
1122
1123 fn cache_certificate(
1124 &self,
1125 certificate: ConfirmedBlockCertificate,
1126 ) -> CacheArc<ConfirmedBlockCertificate> {
1127 self.caches
1128 .certificate
1129 .insert(&certificate.hash(), certificate)
1130 }
1131
1132 fn cache_blob(&self, blob: Blob) -> CacheArc<Blob> {
1133 self.caches.blob.insert(&blob.id(), blob)
1134 }
1135
1136 fn cache_confirmed_block(&self, block: ConfirmedBlock) -> CacheArc<ConfirmedBlock> {
1137 self.caches.confirmed_block.insert(&block.hash(), block)
1138 }
1139
1140 #[instrument(skip_all, fields(%hash))]
1141 async fn contains_certificate(&self, hash: CryptoHash) -> Result<bool, ViewError> {
1142 if self.caches.certificate.contains(&hash) || self.caches.certificate_raw.contains(&hash) {
1143 #[cfg(with_metrics)]
1144 metrics::CONTAINS_CERTIFICATE_COUNTER
1145 .with_label_values(&[metrics::CACHE])
1146 .inc();
1147 return Ok(true);
1148 }
1149 let root_key = RootKey::BlockHash(hash).bytes();
1150 let store = self.database.open_shared(&root_key)?;
1151 let results = store.contains_keys(&get_block_keys()).await?;
1152 #[cfg(with_metrics)]
1153 metrics::CONTAINS_CERTIFICATE_COUNTER
1154 .with_label_values(&[metrics::DB])
1155 .inc();
1156 Ok(results[0] && results[1])
1157 }
1158
1159 #[instrument(skip_all, fields(%hash))]
1160 async fn read_certificate(
1161 &self,
1162 hash: CryptoHash,
1163 ) -> Result<Option<CacheArc<ConfirmedBlockCertificate>>, ViewError> {
1164 if let Some(cert) = self.caches.certificate.get(&hash) {
1166 #[cfg(with_metrics)]
1167 metrics::READ_CERTIFICATE_COUNTER
1168 .with_label_values(&[metrics::CACHE])
1169 .inc();
1170 return Ok(Some(cert));
1171 }
1172 if let Some(raw) = self.caches.certificate_raw.get(&hash) {
1174 #[cfg(with_metrics)]
1175 metrics::READ_CERTIFICATE_COUNTER
1176 .with_label_values(&[metrics::CACHE])
1177 .inc();
1178 return self.deserialize_and_cache_certificate(&raw.0, &raw.1);
1179 }
1180 let root_key = RootKey::BlockHash(hash).bytes();
1182 let store = self.database.open_shared(&root_key)?;
1183 let values = store.read_multi_values_bytes(&get_block_keys()).await?;
1184 #[cfg(with_metrics)]
1185 metrics::READ_CERTIFICATE_COUNTER
1186 .with_label_values(&[metrics::DB])
1187 .inc();
1188 let Some(lite_cert_bytes) = values[0].as_ref() else {
1189 return Ok(None);
1190 };
1191 let Some(confirmed_block_bytes) = values[1].as_ref() else {
1192 return Ok(None);
1193 };
1194 self.caches.certificate_raw.insert(
1195 &hash,
1196 (lite_cert_bytes.clone(), confirmed_block_bytes.clone()),
1197 );
1198 self.deserialize_and_cache_certificate(lite_cert_bytes, confirmed_block_bytes)
1199 }
1200
1201 #[instrument(skip_all)]
1202 async fn read_certificates(
1203 &self,
1204 hashes: &[CryptoHash],
1205 ) -> Result<Vec<Option<CacheArc<ConfirmedBlockCertificate>>>, ViewError> {
1206 let raw_certs = self.read_certificates_raw(hashes).await?;
1207
1208 raw_certs
1209 .into_iter()
1210 .map(|maybe_raw| {
1211 let Some(raw) = maybe_raw else {
1212 return Ok(None);
1213 };
1214 self.deserialize_and_cache_certificate(&raw.0, &raw.1)
1215 })
1216 .collect()
1217 }
1218
1219 #[instrument(skip_all)]
1220 async fn read_certificates_raw(
1221 &self,
1222 hashes: &[CryptoHash],
1223 ) -> Result<Vec<Option<CacheArc<(Vec<u8>, Vec<u8>)>>>, ViewError> {
1224 if hashes.is_empty() {
1225 return Ok(Vec::new());
1226 }
1227 let mut results = vec![None; hashes.len()];
1228 let mut misses = Vec::new();
1229 for (i, hash) in hashes.iter().enumerate() {
1230 if let Some(raw) = self.caches.certificate_raw.get(hash) {
1231 results[i] = Some(raw);
1232 } else {
1233 misses.push(i);
1234 }
1235 }
1236 if !misses.is_empty() {
1237 let miss_hashes: Vec<_> = misses.iter().map(|&i| hashes[i]).collect();
1238 let root_keys = Self::get_root_keys_for_certificates(&miss_hashes);
1239 for (miss_idx, root_key) in misses.iter().zip(root_keys) {
1240 let store = self.database.open_shared(&root_key)?;
1241 let values = store.read_multi_values_bytes(&get_block_keys()).await?;
1242 if let (Some(lite), Some(block)) = (values[0].as_ref(), values[1].as_ref()) {
1243 results[*miss_idx] = Some(
1244 self.caches
1245 .certificate_raw
1246 .insert(&hashes[*miss_idx], (lite.clone(), block.clone())),
1247 );
1248 }
1249 }
1250 }
1251 #[cfg(with_metrics)]
1252 {
1253 let cache_hits = (hashes.len() - misses.len()) as u64;
1254 if cache_hits > 0 {
1255 metrics::READ_CERTIFICATES_COUNTER
1256 .with_label_values(&[metrics::CACHE])
1257 .inc_by(cache_hits);
1258 }
1259 let db_reads = misses.len() as u64;
1260 if db_reads > 0 {
1261 metrics::READ_CERTIFICATES_COUNTER
1262 .with_label_values(&[metrics::DB])
1263 .inc_by(db_reads);
1264 }
1265 }
1266 Ok(results)
1267 }
1268
1269 async fn read_certificate_hashes_by_heights(
1270 &self,
1271 chain_id: ChainId,
1272 heights: &[BlockHeight],
1273 ) -> Result<Vec<Option<CryptoHash>>, ViewError> {
1274 if heights.is_empty() {
1275 return Ok(Vec::new());
1276 }
1277
1278 let mut results = vec![None; heights.len()];
1279 let mut misses = Vec::new();
1280 for (i, &height) in heights.iter().enumerate() {
1281 if let Some(hash) = self.caches.block_hash_by_height.get(&(chain_id, height)) {
1282 results[i] = Some(*hash);
1283 } else {
1284 misses.push(i);
1285 }
1286 }
1287 #[cfg(with_metrics)]
1288 {
1289 let cache_hits = (heights.len() - misses.len()) as u64;
1290 if cache_hits > 0 {
1291 metrics::READ_BLOCK_HASH_BY_HEIGHT_COUNTER
1292 .with_label_values(&[metrics::CACHE])
1293 .inc_by(cache_hits);
1294 }
1295 }
1296 if !misses.is_empty() {
1297 let miss_keys: Vec<Vec<u8>> =
1298 misses.iter().map(|&i| to_height_key(heights[i])).collect();
1299 let index_root_key = RootKey::BlockByHeight(chain_id).bytes();
1300 let store = self.database.open_shared(&index_root_key)?;
1301 let hash_bytes = store.read_multi_values_bytes(&miss_keys).await?;
1302 #[cfg(with_metrics)]
1303 {
1304 let db_reads = misses.len() as u64;
1305 metrics::READ_BLOCK_HASH_BY_HEIGHT_COUNTER
1306 .with_label_values(&[metrics::DB])
1307 .inc_by(db_reads);
1308 }
1309 for (miss_idx, opt_bytes) in misses.iter().zip(hash_bytes) {
1310 if let Some(bytes) = opt_bytes {
1311 let hash = bcs::from_bytes::<CryptoHash>(&bytes)?;
1312 self.caches
1313 .block_hash_by_height
1314 .insert(&(chain_id, heights[*miss_idx]), hash);
1315 results[*miss_idx] = Some(hash);
1316 }
1317 }
1318 }
1319
1320 Ok(results)
1321 }
1322
1323 async fn read_event_block_heights(
1324 &self,
1325 event_ids: &[EventId],
1326 ) -> Result<Vec<Option<BlockHeight>>, ViewError> {
1327 if event_ids.is_empty() {
1328 return Ok(Vec::new());
1329 }
1330
1331 let mut results = vec![None; event_ids.len()];
1332 let mut misses: Vec<usize> = Vec::new();
1334 for (i, event_id) in event_ids.iter().enumerate() {
1335 if let Some(height) = self.caches.event_block_height.get(event_id) {
1336 results[i] = Some(*height);
1337 } else {
1338 misses.push(i);
1339 }
1340 }
1341 #[cfg(with_metrics)]
1342 {
1343 let cache_hits = (event_ids.len() - misses.len()) as u64;
1344 if cache_hits > 0 {
1345 metrics::READ_EVENT_BLOCK_HEIGHT_COUNTER
1346 .with_label_values(&[metrics::CACHE])
1347 .inc_by(cache_hits);
1348 }
1349 }
1350 if misses.is_empty() {
1351 return Ok(results);
1352 }
1353 let mut chain_groups = BTreeMap::<_, Vec<_>>::new();
1355 for &i in &misses {
1356 let event_id = &event_ids[i];
1357 chain_groups
1358 .entry(event_id.chain_id)
1359 .or_default()
1360 .push((i, to_event_key(event_id)));
1361 }
1362 for (chain_id, entries) in chain_groups {
1363 let root_key = RootKey::EventBlockHeight(chain_id).bytes();
1364 let store = self.database.open_shared(&root_key)?;
1365 let keys = entries
1366 .iter()
1367 .map(|(_, key)| key.clone())
1368 .collect::<Vec<_>>();
1369 let values = store.read_multi_values_bytes(&keys).await?;
1370 #[cfg(with_metrics)]
1371 {
1372 let db_reads = entries.len() as u64;
1373 metrics::READ_EVENT_BLOCK_HEIGHT_COUNTER
1374 .with_label_values(&[metrics::DB])
1375 .inc_by(db_reads);
1376 }
1377 for ((original_index, _), value) in entries.into_iter().zip(values) {
1378 if let Some(bytes) = value {
1379 let height = bcs::from_bytes::<BlockHeight>(&bytes)?;
1380 self.caches
1381 .event_block_height
1382 .insert(&event_ids[original_index], height);
1383 results[original_index] = Some(height);
1384 }
1385 }
1386 }
1387 Ok(results)
1388 }
1389
1390 #[instrument(skip_all)]
1391 async fn read_certificates_by_heights_raw(
1392 &self,
1393 chain_id: ChainId,
1394 heights: &[BlockHeight],
1395 ) -> Result<Vec<Option<CacheArc<(Vec<u8>, Vec<u8>)>>>, ViewError> {
1396 let hashes: Vec<Option<CryptoHash>> = self
1397 .read_certificate_hashes_by_heights(chain_id, heights)
1398 .await?;
1399
1400 let mut indices: HashMap<CryptoHash, Vec<usize>> = HashMap::new();
1402 for (index, maybe_hash) in hashes.iter().enumerate() {
1403 if let Some(hash) = maybe_hash {
1404 indices.entry(*hash).or_default().push(index);
1405 }
1406 }
1407
1408 let unique_hashes = indices.keys().copied().collect::<Vec<_>>();
1410
1411 let mut result = vec![None; heights.len()];
1412
1413 for (raw_cert, hash) in self
1414 .read_certificates_raw(&unique_hashes)
1415 .await?
1416 .into_iter()
1417 .zip(unique_hashes)
1418 {
1419 if let Some(idx_list) = indices.get(&hash) {
1420 for &index in idx_list {
1421 result[index] = raw_cert.clone();
1422 }
1423 } else {
1424 tracing::error!(?hash, "certificate hash not found in indices map",);
1426 }
1427 }
1428
1429 Ok(result)
1430 }
1431
1432 #[instrument(skip_all, fields(%chain_id, heights_len = heights.len()))]
1433 async fn read_certificates_by_heights(
1434 &self,
1435 chain_id: ChainId,
1436 heights: &[BlockHeight],
1437 ) -> Result<Vec<Option<CacheArc<ConfirmedBlockCertificate>>>, ViewError> {
1438 self.read_certificates_by_heights_raw(chain_id, heights)
1439 .await?
1440 .into_iter()
1441 .map(|maybe_raw| match maybe_raw {
1442 None => Ok(None),
1443 Some(raw) => self.deserialize_and_cache_certificate(&raw.0, &raw.1),
1444 })
1445 .collect()
1446 }
1447
1448 #[instrument(skip_all, fields(event_id = ?event_id))]
1449 async fn read_event(&self, event_id: EventId) -> Result<Option<CacheArc<Vec<u8>>>, ViewError> {
1450 if let Some(event) = self.caches.event.get(&event_id) {
1451 #[cfg(with_metrics)]
1452 metrics::READ_EVENT_COUNTER
1453 .with_label_values(&[metrics::CACHE])
1454 .inc();
1455 return Ok(Some(event));
1456 }
1457 let event_key = to_event_key(&event_id);
1458 let root_key = RootKey::Event(event_id.chain_id).bytes();
1459 let store = self.database.open_shared(&root_key)?;
1460 let event = store.read_value_bytes(&event_key).await?;
1461 #[cfg(with_metrics)]
1462 metrics::READ_EVENT_COUNTER
1463 .with_label_values(&[metrics::DB])
1464 .inc();
1465 match event {
1466 Some(event_bytes) => Ok(Some(self.caches.event.insert(&event_id, event_bytes))),
1467 None => Ok(None),
1468 }
1469 }
1470
1471 #[instrument(skip_all, fields(event_id = ?event_id))]
1472 async fn contains_event(&self, event_id: EventId) -> Result<bool, ViewError> {
1473 if self.caches.event.contains(&event_id) {
1474 #[cfg(with_metrics)]
1475 metrics::CONTAINS_EVENT_COUNTER
1476 .with_label_values(&[metrics::CACHE])
1477 .inc();
1478 return Ok(true);
1479 }
1480 let event_key = to_event_key(&event_id);
1481 let root_key = RootKey::Event(event_id.chain_id).bytes();
1482 let store = self.database.open_shared(&root_key)?;
1483 let exists = store.contains_key(&event_key).await?;
1484 #[cfg(with_metrics)]
1485 metrics::CONTAINS_EVENT_COUNTER
1486 .with_label_values(&[metrics::DB])
1487 .inc();
1488 Ok(exists)
1489 }
1490
1491 #[instrument(skip_all, fields(chain_id = %chain_id, stream_id = %stream_id, start_index = %start_index))]
1492 async fn read_events_from_index(
1493 &self,
1494 chain_id: &ChainId,
1495 stream_id: &StreamId,
1496 start_index: u32,
1497 ) -> Result<Vec<IndexAndEvent>, ViewError> {
1498 let root_key = RootKey::Event(*chain_id).bytes();
1499 let store = self.database.open_shared(&root_key)?;
1500 let mut entries = Vec::new();
1503 let mut db_keys = Vec::new();
1504 let prefix = bcs::to_bytes(stream_id).unwrap();
1505 for short_key in store.find_keys_by_prefix(&prefix).await? {
1506 let index = bcs::from_bytes::<u32>(&short_key)?;
1507 if index >= start_index {
1508 let event_id = EventId {
1509 chain_id: *chain_id,
1510 stream_id: stream_id.clone(),
1511 index,
1512 };
1513 let cached = self.caches.event.get(&event_id).map(|arc| (*arc).clone());
1514 if cached.is_none() {
1515 let mut key = prefix.clone();
1516 key.extend(short_key);
1517 db_keys.push(key);
1518 }
1519 entries.push((index, cached));
1520 }
1521 }
1522 let mut db_values = if db_keys.is_empty() {
1523 Vec::new()
1524 } else {
1525 store.read_multi_values_bytes(&db_keys).await?
1526 }
1527 .into_iter();
1528 let mut returned_values = Vec::with_capacity(entries.len());
1529 for (index, cached) in entries {
1530 let event = match cached {
1531 Some(event) => event,
1532 None => {
1533 let event_bytes = db_values
1534 .next()
1535 .expect("one database value per cache miss")
1536 .unwrap();
1537 let event_id = EventId {
1538 chain_id: *chain_id,
1539 stream_id: stream_id.clone(),
1540 index,
1541 };
1542 self.caches.event.insert(&event_id, event_bytes.clone());
1543 event_bytes
1544 }
1545 };
1546 returned_values.push(IndexAndEvent { index, event });
1547 }
1548 Ok(returned_values)
1549 }
1550
1551 #[instrument(skip_all)]
1552 async fn write_events(
1553 &self,
1554 events: impl IntoIterator<Item = (EventId, Vec<u8>)> + Send,
1555 ) -> Result<(), ViewError> {
1556 let mut batch = MultiPartitionBatch::new();
1557 for (event_id, value) in events {
1558 batch.add_event(&event_id, value);
1559 }
1560 self.write_batch(batch).await
1561 }
1562
1563 #[instrument(skip_all)]
1564 async fn read_network_description(&self) -> Result<Option<NetworkDescription>, ViewError> {
1565 if let Some(desc) = self.caches.network_description.get() {
1566 #[cfg(with_metrics)]
1567 metrics::READ_NETWORK_DESCRIPTION
1568 .with_label_values(&[metrics::CACHE])
1569 .inc();
1570 return Ok(Some(desc.clone()));
1571 }
1572 let root_key = RootKey::NetworkDescription.bytes();
1573 let store = self.database.open_shared(&root_key)?;
1574 let maybe_value: Option<NetworkDescription> =
1575 store.read_value(NETWORK_DESCRIPTION_KEY).await?;
1576 #[cfg(with_metrics)]
1577 metrics::READ_NETWORK_DESCRIPTION
1578 .with_label_values(&[metrics::DB])
1579 .inc();
1580 if let Some(ref desc) = maybe_value {
1581 if self.caches.network_description.set(desc.clone()).is_err() {
1582 debug!("network description cache was already populated concurrently");
1583 }
1584 }
1585 Ok(maybe_value)
1586 }
1587
1588 #[instrument(skip_all)]
1589 async fn write_network_description(
1590 &self,
1591 information: &NetworkDescription,
1592 ) -> Result<(), ViewError> {
1593 let mut batch = MultiPartitionBatch::new();
1594 batch.add_network_description(information)?;
1595 self.write_batch(batch).await?;
1596 Ok(())
1597 }
1598
1599 fn wasm_runtime(&self) -> Option<WasmRuntime> {
1600 self.wasm_runtime
1601 }
1602
1603 #[instrument(skip_all)]
1604 async fn block_exporter_context(
1605 &self,
1606 block_exporter_id: u32,
1607 ) -> Result<Self::BlockExporterContext, ViewError> {
1608 let root_key = RootKey::BlockExporterState(block_exporter_id).bytes();
1609 let store = self.database.open_exclusive(&root_key)?;
1610 Ok(ViewContext::create_root_context(store, block_exporter_id).await?)
1611 }
1612
1613 async fn list_blob_ids(&self) -> Result<Vec<BlobId>, ViewError> {
1614 let root_keys = self.database.list_root_keys().await?;
1615 let mut blob_ids = Vec::new();
1616 for root_key in root_keys {
1617 if !root_key.is_empty() && root_key[0] == BLOB_ID_TAG {
1618 let root_key_red = &root_key[1..];
1619 let blob_id = bcs::from_bytes(root_key_red)?;
1620 blob_ids.push(blob_id);
1621 }
1622 }
1623 Ok(blob_ids)
1624 }
1625
1626 async fn list_chain_ids(&self) -> Result<Vec<ChainId>, ViewError> {
1627 let root_keys = self.database.list_root_keys().await?;
1628 let mut chain_ids = Vec::new();
1629 for root_key in root_keys {
1630 if !root_key.is_empty() && root_key[0] == CHAIN_ID_TAG {
1631 let root_key_red = &root_key[1..];
1632 let chain_id = bcs::from_bytes(root_key_red)?;
1633 chain_ids.push(chain_id);
1634 }
1635 }
1636 Ok(chain_ids)
1637 }
1638
1639 async fn list_event_ids(&self) -> Result<Vec<EventId>, ViewError> {
1640 let root_keys = self.database.list_root_keys().await?;
1641 let mut event_ids = Vec::new();
1642 for root_key in root_keys {
1643 if !root_key.is_empty() && root_key[0] == EVENT_ID_TAG {
1644 let root_key_red = &root_key[1..];
1645 let chain_id = bcs::from_bytes(root_key_red)?;
1646 let store = self.database.open_shared(&root_key)?;
1647 let keys = store.find_keys_by_prefix(&[]).await?;
1648 for key in keys {
1649 let restricted_event_id = bcs::from_bytes::<RestrictedEventId>(&key)?;
1650 let event_id = EventId {
1651 chain_id,
1652 stream_id: restricted_event_id.stream_id,
1653 index: restricted_event_id.index,
1654 };
1655 event_ids.push(event_id);
1656 }
1657 }
1658 }
1659 Ok(event_ids)
1660 }
1661}
1662
1663impl<Database, C> DbStorage<Database, C>
1664where
1665 Database: KeyValueDatabase + Clone,
1666 Database::Store: KeyValueStore + Clone,
1667 C: Clock,
1668 Database::Error: Send + Sync,
1669{
1670 #[instrument(skip_all)]
1671 fn get_root_keys_for_certificates(hashes: &[CryptoHash]) -> Vec<Vec<u8>> {
1672 hashes
1673 .iter()
1674 .map(|hash| RootKey::BlockHash(*hash).bytes())
1675 .collect()
1676 }
1677
1678 fn deserialize_and_cache_certificate(
1679 &self,
1680 lite_cert_bytes: &[u8],
1681 confirmed_block_bytes: &[u8],
1682 ) -> Result<Option<CacheArc<ConfirmedBlockCertificate>>, ViewError> {
1683 let lite = bcs::from_bytes::<LiteCertificate>(lite_cert_bytes)?;
1684 let block = bcs::from_bytes::<ConfirmedBlock>(confirmed_block_bytes)?;
1685 let hash = block.hash();
1686 self.caches.confirmed_block.insert(&hash, block.clone());
1687 let certificate = lite
1688 .into_confirmed_certificate(block)
1689 .ok_or(ViewError::InconsistentEntries)?;
1690 let arc = self.caches.certificate.insert(&hash, certificate);
1691 Ok(Some(arc))
1692 }
1693
1694 #[instrument(skip_all)]
1695 async fn write_entry(
1696 store: &Database::Store,
1697 key_values: Vec<(Vec<u8>, Vec<u8>)>,
1698 ) -> Result<(), ViewError> {
1699 let mut batch = Batch::new();
1700 for (key, value) in key_values {
1701 batch.put_key_value_bytes(key, value);
1702 }
1703 store.write_batch(batch).await?;
1704 Ok(())
1705 }
1706
1707 #[instrument(skip_all, fields(batch_size = batch.keys_value_bytes.len()))]
1708 async fn write_batch(&self, batch: MultiPartitionBatch) -> Result<(), ViewError> {
1709 if batch.keys_value_bytes.is_empty() {
1710 return Ok(());
1711 }
1712 let mut futures = Vec::new();
1713 for (root_key, key_values) in batch.keys_value_bytes {
1714 let store = self.database.open_shared(&root_key)?;
1715 futures.push(async move { Self::write_entry(&store, key_values).await });
1716 }
1717 futures::future::try_join_all(futures).await?;
1718 Ok(())
1719 }
1720}
1721
1722impl<Database, C> DbStorage<Database, C> {
1723 fn new(
1724 database: Database,
1725 wasm_runtime: Option<WasmRuntime>,
1726 cache_sizes: StorageCacheConfig,
1727 clock: C,
1728 ) -> Self {
1729 Self {
1730 database: Arc::new(database),
1731 clock,
1732 #[cfg_attr(web, expect(clippy::arc_with_non_send_sync))]
1734 thread_pool: Arc::new(linera_execution::ThreadPool::new(20)),
1735 wasm_runtime,
1736 user_contracts: Arc::new(papaya::HashMap::new()),
1737 user_services: Arc::new(papaya::HashMap::new()),
1738 shared_committees: SharedCommittees::new(),
1739 caches: StorageCaches::new(cache_sizes),
1740 execution_runtime_config: ExecutionRuntimeConfig::default(),
1741 }
1742 }
1743
1744 pub fn with_allow_application_logs(mut self, allow: bool) -> Self {
1746 self.execution_runtime_config.allow_application_logs = allow;
1747 self
1748 }
1749}
1750
1751impl<Database> DbStorage<Database, WallClock>
1752where
1753 Database: KeyValueDatabase + Clone + 'static,
1754 Database::Error: Send + Sync,
1755 Database::Store: KeyValueStore + Clone + 'static,
1756{
1757 pub async fn maybe_create_and_connect(
1759 config: &Database::Config,
1760 namespace: &str,
1761 wasm_runtime: Option<WasmRuntime>,
1762 cache_sizes: StorageCacheConfig,
1763 ) -> Result<Self, Database::Error> {
1764 let database = Database::maybe_create_and_connect(config, namespace).await?;
1765 Ok(Self::new(database, wasm_runtime, cache_sizes, WallClock))
1766 }
1767
1768 pub async fn connect(
1770 config: &Database::Config,
1771 namespace: &str,
1772 wasm_runtime: Option<WasmRuntime>,
1773 cache_sizes: StorageCacheConfig,
1774 ) -> Result<Self, Database::Error> {
1775 let database = Database::connect(config, namespace).await?;
1776 Ok(Self::new(database, wasm_runtime, cache_sizes, WallClock))
1777 }
1778}
1779
1780#[cfg(with_testing)]
1781impl<Database, C> DbStorage<Database, C>
1782where
1783 Database: linera_views::backends::DatabaseBackup,
1784{
1785 pub fn backup_to(&self, dir: &std::path::Path) -> anyhow::Result<()> {
1787 self.database.backup_to(dir)
1788 }
1789}
1790
1791#[cfg(with_testing)]
1792impl<Database> DbStorage<Database, TestClock>
1793where
1794 Database: TestKeyValueDatabase + Clone + Send + Sync + 'static,
1795 Database::Store: KeyValueStore + Clone + Send + Sync + 'static,
1796 Database::Error: Send + Sync,
1797{
1798 pub async fn make_test_storage(wasm_runtime: Option<WasmRuntime>) -> Self {
1800 let config = Database::new_test_config().await.unwrap();
1801 let namespace = generate_test_namespace();
1802 DbStorage::<Database, TestClock>::new_for_testing(
1803 config,
1804 &namespace,
1805 wasm_runtime,
1806 TestClock::new(),
1807 )
1808 .await
1809 .unwrap()
1810 }
1811
1812 pub async fn new_for_testing(
1814 config: Database::Config,
1815 namespace: &str,
1816 wasm_runtime: Option<WasmRuntime>,
1817 clock: TestClock,
1818 ) -> Result<Self, Database::Error> {
1819 let database = Database::recreate_and_connect(&config, namespace).await?;
1820 Ok(Self::new(
1821 database,
1822 wasm_runtime,
1823 DEFAULT_STORAGE_CACHE_CONFIG,
1824 clock,
1825 ))
1826 }
1827
1828 pub async fn connect_for_testing(
1830 config: Database::Config,
1831 namespace: &str,
1832 wasm_runtime: Option<WasmRuntime>,
1833 clock: TestClock,
1834 ) -> Result<Self, Database::Error> {
1835 let database = Database::connect(&config, namespace).await?;
1836 Ok(Self::new(
1837 database,
1838 wasm_runtime,
1839 DEFAULT_STORAGE_CACHE_CONFIG,
1840 clock,
1841 ))
1842 }
1843}
1844
1845#[cfg(test)]
1846mod tests {
1847 use linera_base::{
1848 crypto::{CryptoHash, TestString},
1849 data_types::{Amount, Blob, BlobContent, BlockHeight, Event, OracleResponse, Round},
1850 identifiers::{
1851 Account, AccountOwner, ApplicationId, BlobId, BlobType, ChainId, EventId,
1852 GenericApplicationId, StreamId, StreamName,
1853 },
1854 };
1855 use linera_chain::{
1856 block::{Block, ConfirmedBlock},
1857 data_types::{OperationResult, Transaction},
1858 test::BlockBuilder,
1859 types::ConfirmedBlockCertificate,
1860 };
1861 use linera_execution::{
1862 system::{SystemMessage, SystemOperation},
1863 Message, MessageKind, Operation, OutgoingMessage,
1864 };
1865 use linera_views::{
1866 memory::MemoryDatabase,
1867 store::{KeyValueDatabase, ReadableKeyValueStore as _},
1868 };
1869
1870 use crate::{
1871 db_storage::{
1872 to_event_key, to_height_key, MultiPartitionBatch, RootKey, BLOB_ID_TAG, CHAIN_ID_TAG,
1873 EVENT_ID_TAG,
1874 },
1875 DbStorage, Storage, TestClock,
1876 };
1877
1878 fn populated_block(chain_id: ChainId, height: u64) -> Block {
1882 let owner = AccountOwner::CHAIN;
1883 let stream_id = StreamId {
1884 application_id: GenericApplicationId::System,
1885 stream_name: StreamName(b"test_stream".to_vec()),
1886 };
1887 BlockBuilder::new(chain_id, BlockHeight(height))
1888 .with_state_hash(CryptoHash::new(&TestString::new(format!(
1889 "state_hash_{height}"
1890 ))))
1891 .with_transaction(Transaction::ExecuteOperation(Operation::System(Box::new(
1892 SystemOperation::Transfer {
1893 owner,
1894 recipient: Account::chain(chain_id),
1895 amount: Amount::ONE,
1896 },
1897 ))))
1898 .with_messages(vec![OutgoingMessage {
1899 destination: chain_id,
1900 authenticated_owner: None,
1901 grant: Amount::ZERO,
1902 refund_grant_to: None,
1903 kind: MessageKind::Simple,
1904 message: Message::System(SystemMessage::Credit {
1905 target: owner,
1906 amount: Amount::ONE,
1907 source: owner,
1908 }),
1909 }])
1910 .with_events(vec![Event {
1911 stream_id,
1912 index: 0,
1913 value: b"event".to_vec(),
1914 }])
1915 .with_oracle_responses(vec![OracleResponse::Round(Some(0))])
1916 .with_blobs(vec![Blob::new(BlobContent::new_data(b"blob".to_vec()))])
1917 .with_operation_result(OperationResult(b"result".to_vec()))
1918 .build()
1919 }
1920
1921 #[test]
1928 fn test_root_key_blob_serialization() {
1929 let hash = CryptoHash::default();
1930 let blob_type = BlobType::default();
1931 let blob_id = BlobId::new(hash, blob_type);
1932 let root_key = RootKey::BlobId(blob_id).bytes();
1933 assert_eq!(root_key[0], BLOB_ID_TAG);
1934 assert_eq!(bcs::from_bytes::<BlobId>(&root_key[1..]).unwrap(), blob_id);
1935 }
1936
1937 #[test]
1940 fn test_root_key_chainstate_serialization() {
1941 let hash = CryptoHash::default();
1942 let chain_id = ChainId(hash);
1943 let root_key = RootKey::ChainState(chain_id).bytes();
1944 assert_eq!(root_key[0], CHAIN_ID_TAG);
1945 assert_eq!(
1946 bcs::from_bytes::<ChainId>(&root_key[1..]).unwrap(),
1947 chain_id
1948 );
1949 }
1950
1951 #[test]
1954 fn test_root_key_event_serialization() {
1955 let hash = CryptoHash::test_hash("49");
1956 let chain_id = ChainId(hash);
1957 let application_description_hash = CryptoHash::test_hash("42");
1958 let application_id = ApplicationId::new(application_description_hash);
1959 let application_id = GenericApplicationId::User(application_id);
1960 let stream_name = StreamName(bcs::to_bytes("linera_stream").unwrap());
1961 let stream_id = StreamId {
1962 application_id,
1963 stream_name,
1964 };
1965 let prefix = bcs::to_bytes(&stream_id).unwrap();
1966
1967 let index = 1567;
1968 let event_id = EventId {
1969 chain_id,
1970 stream_id,
1971 index,
1972 };
1973 let root_key = RootKey::Event(chain_id).bytes();
1974 assert_eq!(root_key[0], EVENT_ID_TAG);
1975 let key = to_event_key(&event_id);
1976 assert!(key.starts_with(&prefix));
1977 }
1978
1979 #[test]
1982 fn test_root_key_block_by_height_serialization() {
1983 use linera_base::data_types::BlockHeight;
1984
1985 let hash = CryptoHash::default();
1986 let chain_id = ChainId(hash);
1987 let height = BlockHeight(42);
1988
1989 let root_key = RootKey::BlockByHeight(chain_id).bytes();
1991 let deserialized_chain_id: ChainId = bcs::from_bytes(&root_key[1..]).unwrap();
1992 assert_eq!(deserialized_chain_id, chain_id);
1993
1994 let height_key = to_height_key(height);
1996 let deserialized_height: BlockHeight = bcs::from_bytes(&height_key).unwrap();
1997 assert_eq!(deserialized_height, height);
1998 }
1999
2000 #[cfg(with_testing)]
2001 #[tokio::test]
2002 async fn test_add_certificate_creates_height_index() {
2003 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
2005
2006 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
2008 let height = BlockHeight(5);
2009 let block = populated_block(chain_id, height.0);
2010 let confirmed_block = ConfirmedBlock::new(block);
2011 let certificate = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
2012
2013 let mut batch = MultiPartitionBatch::new();
2015 batch.add_certificate(&certificate).unwrap();
2016 storage.write_batch(batch).await.unwrap();
2017
2018 let hash = certificate.hash();
2020 let index_root_key = RootKey::BlockByHeight(chain_id).bytes();
2021 let store = storage.database.open_shared(&index_root_key).unwrap();
2022 let height_key = to_height_key(height);
2023 let value_bytes = store.read_value_bytes(&height_key).await.unwrap();
2024
2025 assert!(value_bytes.is_some(), "Height index was not created");
2026 let stored_hash: CryptoHash = bcs::from_bytes(&value_bytes.unwrap()).unwrap();
2027 assert_eq!(stored_hash, hash, "Height index contains wrong hash");
2028 }
2029
2030 #[cfg(with_testing)]
2031 #[tokio::test]
2032 async fn test_read_certificates_by_heights() {
2033 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
2034 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
2035
2036 let mut batch = MultiPartitionBatch::new();
2038 let mut expected_certs = vec![];
2039
2040 for height in [1, 3, 5] {
2041 let block = populated_block(chain_id, height);
2042 let confirmed_block = ConfirmedBlock::new(block);
2043 let cert = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
2044 expected_certs.push((height, cert.clone()));
2045 batch.add_certificate(&cert).unwrap();
2046 }
2047 storage.write_batch(batch).await.unwrap();
2048
2049 let heights = vec![BlockHeight(1), BlockHeight(3), BlockHeight(5)];
2051 let result = storage
2052 .read_certificates_by_heights(chain_id, &heights)
2053 .await
2054 .unwrap();
2055 assert_eq!(result.len(), 3);
2056 assert_eq!(
2057 result[0].as_ref().unwrap().hash(),
2058 expected_certs[0].1.hash()
2059 );
2060 assert_eq!(
2061 result[1].as_ref().unwrap().hash(),
2062 expected_certs[1].1.hash()
2063 );
2064 assert_eq!(
2065 result[2].as_ref().unwrap().hash(),
2066 expected_certs[2].1.hash()
2067 );
2068
2069 let heights = vec![BlockHeight(5), BlockHeight(1), BlockHeight(3)];
2071 let result = storage
2072 .read_certificates_by_heights(chain_id, &heights)
2073 .await
2074 .unwrap();
2075 assert_eq!(result.len(), 3);
2076 assert_eq!(
2077 result[0].as_ref().unwrap().hash(),
2078 expected_certs[2].1.hash()
2079 );
2080 assert_eq!(
2081 result[1].as_ref().unwrap().hash(),
2082 expected_certs[0].1.hash()
2083 );
2084 assert_eq!(
2085 result[2].as_ref().unwrap().hash(),
2086 expected_certs[1].1.hash()
2087 );
2088
2089 let heights = vec![
2091 BlockHeight(1),
2092 BlockHeight(2),
2093 BlockHeight(3),
2094 BlockHeight(3),
2095 ];
2096 let result = storage
2097 .read_certificates_by_heights(chain_id, &heights)
2098 .await
2099 .unwrap();
2100 assert_eq!(result.len(), 4); assert!(result[0].is_some());
2102 assert!(result[1].is_none()); assert!(result[2].is_some());
2104 assert!(result[3].is_some());
2105 assert_eq!(
2106 result[2].as_ref().unwrap().hash(),
2107 result[3].as_ref().unwrap().hash()
2108 ); let heights = vec![];
2112 let result = storage
2113 .read_certificates_by_heights(chain_id, &heights)
2114 .await
2115 .unwrap();
2116 assert_eq!(result.len(), 0);
2117 }
2118
2119 #[cfg(with_testing)]
2120 #[tokio::test]
2121 async fn test_read_certificates_by_heights_multiple_chains() {
2122 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
2123
2124 let chain_a = ChainId(CryptoHash::test_hash("chain_a"));
2126 let chain_b = ChainId(CryptoHash::test_hash("chain_b"));
2127
2128 let mut batch = MultiPartitionBatch::new();
2129
2130 let block_a = populated_block(chain_a, 10);
2131 let confirmed_block_a = ConfirmedBlock::new(block_a);
2132 let cert_a = ConfirmedBlockCertificate::new(confirmed_block_a, Round::Fast, vec![]);
2133 batch.add_certificate(&cert_a).unwrap();
2134
2135 let block_b = populated_block(chain_b, 10);
2136 let confirmed_block_b = ConfirmedBlock::new(block_b);
2137 let cert_b = ConfirmedBlockCertificate::new(confirmed_block_b, Round::Fast, vec![]);
2138 batch.add_certificate(&cert_b).unwrap();
2139
2140 storage.write_batch(batch).await.unwrap();
2141
2142 let result = storage
2144 .read_certificates_by_heights(chain_a, &[BlockHeight(10)])
2145 .await
2146 .unwrap();
2147 assert_eq!(result[0].as_ref().unwrap().hash(), cert_a.hash());
2148
2149 let result = storage
2151 .read_certificates_by_heights(chain_b, &[BlockHeight(10)])
2152 .await
2153 .unwrap();
2154 assert_eq!(result[0].as_ref().unwrap().hash(), cert_b.hash());
2155
2156 let result = storage
2158 .read_certificates_by_heights(chain_a, &[BlockHeight(20)])
2159 .await
2160 .unwrap();
2161 assert!(result[0].is_none());
2162 }
2163
2164 #[cfg(with_testing)]
2165 #[tokio::test]
2166 async fn test_read_certificates_by_heights_consistency() {
2167 let storage = DbStorage::<MemoryDatabase, TestClock>::make_test_storage(None).await;
2168 let chain_id = ChainId(CryptoHash::test_hash("test_chain"));
2169
2170 let mut batch = MultiPartitionBatch::new();
2172 let block = populated_block(chain_id, 7);
2173 let confirmed_block = ConfirmedBlock::new(block);
2174 let cert = ConfirmedBlockCertificate::new(confirmed_block, Round::Fast, vec![]);
2175 let hash = cert.hash();
2176 batch.add_certificate(&cert).unwrap();
2177 storage.write_batch(batch).await.unwrap();
2178
2179 let cert_by_hash = storage.read_certificate(hash).await.unwrap().unwrap();
2181
2182 let certs_by_height = storage
2184 .read_certificates_by_heights(chain_id, &[BlockHeight(7)])
2185 .await
2186 .unwrap();
2187 let cert_by_height = certs_by_height[0].as_ref().unwrap();
2188
2189 assert_eq!(cert_by_hash.hash(), cert_by_height.hash());
2191 assert_eq!(
2192 cert_by_hash.value().block().header,
2193 cert_by_height.value().block().header
2194 );
2195 }
2196}