1#![allow(clippy::cast_possible_truncation)]
5
6use std::{
7 collections::{BTreeMap, HashMap, HashSet},
8 sync::Arc,
9 time::Duration,
10 vec,
11};
12
13use async_trait::async_trait;
14use futures::{
15 future::Either,
16 lock::{Mutex, MutexGuard},
17 stream, Future, StreamExt as _,
18};
19use linera_base::{
20 crypto::{
21 AccountPublicKey, CryptoHash, ValidatorKeypair, ValidatorPublicKey, ValidatorSecretKey,
22 },
23 data_types::*,
24 identifiers::{AccountOwner, BlobId, ChainId, EventId},
25 ownership::ChainOwnership,
26};
27use linera_chain::{
28 data_types::BlockProposal,
29 types::{
30 CertificateKind, Certified, ConfirmedBlock, ConfirmedBlockCertificate, GenericCertificate,
31 LiteCertificate, Timeout, ValidatedBlock, ValidatedBlockCertificate,
32 },
33};
34use linera_execution::{committee::Committee, ResourceControlPolicy, WasmRuntime};
35use linera_storage::{Arc as CacheArc, DbStorage, ResultReadCertificates, Storage, TestClock};
36#[cfg(all(not(target_arch = "wasm32"), feature = "storage-service"))]
37use linera_storage_service::client::StorageServiceDatabase;
38use linera_version::VersionInfo;
39#[cfg(feature = "scylladb")]
40use linera_views::scylla_db::ScyllaDbDatabase;
41use linera_views::{
42 memory::MemoryDatabase,
43 random::generate_test_namespace,
44 store::{KeyValueStore, TestKeyValueDatabase},
45};
46use tokio::sync::oneshot;
47use tokio_stream::wrappers::UnboundedReceiverStream;
48#[cfg(feature = "rocksdb")]
49use {
50 linera_views::rocks_db::RocksDbDatabase,
51 tokio::sync::{Semaphore, SemaphorePermit},
52};
53
54use crate::{
55 chain_worker::ChainWorkerConfig,
56 client::{chain_client, Client},
57 data_types::*,
58 environment::{TestSigner, TestWallet},
59 node::{
60 CrossChainMessageDelivery, NodeError, NotificationStream, ValidatorNode,
61 ValidatorNodeProvider,
62 },
63 notifier::ChannelNotifier,
64 worker::{
65 Notification, ProcessableCertificate, WorkerState, DEFAULT_BLOCK_CACHE_SIZE,
66 DEFAULT_EXECUTION_STATE_CACHE_SIZE,
67 },
68};
69
70#[derive(Debug, PartialEq, Clone, Copy)]
72#[allow(missing_docs)]
73pub enum FaultType {
74 Honest,
75 Offline,
76 OfflineWithInfo,
77 NoChains,
78 DontSendConfirmVote,
79 DontProcessValidated,
80 DontSendValidateVote,
81}
82
83struct LocalValidator<S>
90where
91 S: Storage,
92{
93 state: WorkerState<S>,
94 notifier: Arc<ChannelNotifier<Notification>>,
95 notification_stream_aborts: Vec<stream::AbortHandle>,
98 subscribe_calls: usize,
102}
103
104#[derive(Clone)]
106pub struct LocalValidatorClient<S>
107where
108 S: Storage,
109{
110 public_key: ValidatorPublicKey,
111 client: Arc<Mutex<LocalValidator<S>>>,
112 fault_type: FaultType,
113 chain_info_gate: Arc<tokio::sync::RwLock<()>>,
121 chain_info_gate_armed: Arc<std::sync::atomic::AtomicBool>,
122}
123
124impl<S> ValidatorNode for LocalValidatorClient<S>
125where
126 S: Storage + Clone + Send + Sync + 'static,
127{
128 type NotificationStream = NotificationStream;
129
130 fn address(&self) -> String {
131 format!("local:{}", self.public_key)
132 }
133
134 async fn handle_block_proposal(
135 &self,
136 proposal: BlockProposal,
137 ) -> Result<ChainInfoResponse, NodeError> {
138 self.spawn_and_receive(move |validator, sender| {
139 validator.do_handle_block_proposal(proposal, sender)
140 })
141 .await
142 }
143
144 async fn handle_lite_certificate(
145 &self,
146 certificate: LiteCertificate<'_>,
147 _delivery: CrossChainMessageDelivery,
148 ) -> Result<ChainInfoResponse, NodeError> {
149 let certificate = certificate.cloned();
150 self.spawn_and_receive(move |validator, sender| {
151 validator.do_handle_lite_certificate(certificate, sender)
152 })
153 .await
154 }
155
156 async fn handle_timeout_certificate(
157 &self,
158 certificate: GenericCertificate<Timeout>,
159 ) -> Result<ChainInfoResponse, NodeError> {
160 self.spawn_and_receive(move |validator, sender| {
161 validator.do_handle_certificate::<Timeout>(certificate, sender)
162 })
163 .await
164 }
165
166 async fn handle_validated_certificate(
167 &self,
168 certificate: ValidatedBlockCertificate,
169 ) -> Result<ChainInfoResponse, NodeError> {
170 self.spawn_and_receive(move |validator, sender| {
171 validator.do_handle_certificate::<ValidatedBlock>(certificate, sender)
172 })
173 .await
174 }
175
176 async fn handle_confirmed_certificate(
177 &self,
178 certificate: CacheArc<ConfirmedBlockCertificate>,
179 _delivery: CrossChainMessageDelivery,
180 ) -> Result<ChainInfoResponse, NodeError> {
181 self.spawn_and_receive(move |validator, sender| {
182 validator.do_handle_certificate::<ConfirmedBlock>(
183 CacheArc::unwrap_or_clone(certificate),
184 sender,
185 )
186 })
187 .await
188 }
189
190 async fn handle_chain_info_query(
191 &self,
192 query: ChainInfoQuery,
193 ) -> Result<ChainInfoResponse, NodeError> {
194 self.spawn_and_receive(move |validator, sender| {
195 validator.do_handle_chain_info_query(query, sender)
196 })
197 .await
198 }
199
200 async fn subscribe(&self, chains: Vec<ChainId>) -> Result<NotificationStream, NodeError> {
201 self.spawn_and_receive(move |validator, sender| validator.do_subscribe(chains, sender))
202 .await
203 }
204
205 async fn get_version_info(&self) -> Result<VersionInfo, NodeError> {
206 Ok(Default::default())
207 }
208
209 async fn get_network_description(&self) -> Result<NetworkDescription, NodeError> {
210 Ok(self
211 .client
212 .lock()
213 .await
214 .state
215 .storage_client()
216 .read_network_description()
217 .await
218 .transpose()
219 .ok_or_else(|| NodeError::ViewError {
220 error: "missing NetworkDescription".to_owned(),
221 })??)
222 }
223
224 async fn upload_blob(&self, content: BlobContent) -> Result<BlobId, NodeError> {
225 self.spawn_and_receive(move |validator, sender| validator.do_upload_blob(content, sender))
226 .await
227 }
228
229 async fn download_blob(&self, blob_id: BlobId) -> Result<BlobContent, NodeError> {
230 self.spawn_and_receive(move |validator, sender| validator.do_download_blob(blob_id, sender))
231 .await
232 }
233
234 async fn download_blobs(
235 &self,
236 blob_ids: Vec<BlobId>,
237 ) -> Result<crate::node::BlobStream, NodeError> {
238 let this = self.clone();
239 let stream = futures::stream::unfold(blob_ids.into_iter(), move |mut iter| {
240 let this = this.clone();
241 async move {
242 let blob_id = iter.next()?;
243 let result = this.download_blob(blob_id).await;
244 Some((result, iter))
245 }
246 });
247 Ok(Box::pin(stream))
248 }
249
250 async fn download_pending_blob(
251 &self,
252 chain_id: ChainId,
253 blob_id: BlobId,
254 ) -> Result<BlobContent, NodeError> {
255 self.spawn_and_receive(move |validator, sender| {
256 validator.do_download_pending_blob(chain_id, blob_id, sender)
257 })
258 .await
259 }
260
261 async fn handle_pending_blob(
262 &self,
263 chain_id: ChainId,
264 blob: BlobContent,
265 ) -> Result<ChainInfoResponse, NodeError> {
266 self.spawn_and_receive(move |validator, sender| {
267 validator.do_handle_pending_blob(chain_id, blob, sender)
268 })
269 .await
270 }
271
272 async fn download_certificate(
273 &self,
274 hash: CryptoHash,
275 ) -> Result<ConfirmedBlockCertificate, NodeError> {
276 self.spawn_and_receive(move |validator, sender| {
277 validator.do_download_certificate(hash, sender)
278 })
279 .await
280 }
281
282 async fn download_certificates(
283 &self,
284 hashes: Vec<CryptoHash>,
285 ) -> Result<Vec<ConfirmedBlockCertificate>, NodeError> {
286 self.spawn_and_receive(move |validator, sender| {
287 validator.do_download_certificates(hashes, sender)
288 })
289 .await
290 }
291
292 async fn download_certificates_by_heights(
293 &self,
294 chain_id: ChainId,
295 heights: Vec<BlockHeight>,
296 ) -> Result<Vec<ConfirmedBlockCertificate>, NodeError> {
297 self.spawn_and_receive(move |validator, sender| {
298 validator.do_download_certificates_by_heights(chain_id, heights, sender)
299 })
300 .await
301 }
302
303 async fn blob_last_used_by(&self, blob_id: BlobId) -> Result<CryptoHash, NodeError> {
304 self.spawn_and_receive(move |validator, sender| {
305 validator.do_blob_last_used_by(blob_id, sender)
306 })
307 .await
308 }
309
310 async fn blob_last_used_by_certificate(
311 &self,
312 blob_id: BlobId,
313 ) -> Result<ConfirmedBlockCertificate, NodeError> {
314 self.spawn_and_receive(move |validator, sender| {
315 validator.do_blob_last_used_by_certificate(blob_id, sender)
316 })
317 .await
318 }
319
320 async fn missing_blob_ids(&self, blob_ids: Vec<BlobId>) -> Result<Vec<BlobId>, NodeError> {
321 self.spawn_and_receive(move |validator, sender| {
322 validator.do_missing_blob_ids(blob_ids, sender)
323 })
324 .await
325 }
326
327 async fn event_block_heights(
328 &self,
329 event_ids: Vec<EventId>,
330 ) -> Result<Vec<Option<BlockHeight>>, NodeError> {
331 self.spawn_and_receive(move |validator, sender| {
332 validator.do_event_block_heights(event_ids, sender)
333 })
334 .await
335 }
336
337 async fn get_shard_info(
338 &self,
339 _chain_id: ChainId,
340 ) -> Result<crate::data_types::ShardInfo, NodeError> {
341 Ok(crate::data_types::ShardInfo {
343 shard_id: 0,
344 total_shards: 1,
345 })
346 }
347}
348
349impl<S> LocalValidatorClient<S>
350where
351 S: Storage + Clone + Send + Sync + 'static,
352{
353 fn new(public_key: ValidatorPublicKey, state: WorkerState<S>) -> Self {
354 let client = LocalValidator {
355 state,
356 notifier: Arc::new(ChannelNotifier::default()),
357 notification_stream_aborts: Vec::new(),
358 subscribe_calls: 0,
359 };
360 Self {
361 public_key,
362 client: Arc::new(Mutex::new(client)),
363 fault_type: FaultType::Honest,
364 chain_info_gate: Arc::default(),
365 chain_info_gate_armed: Arc::default(),
366 }
367 }
368
369 pub fn name(&self) -> ValidatorPublicKey {
371 self.public_key
372 }
373
374 pub fn fault_type(&self) -> FaultType {
376 self.fault_type
377 }
378
379 pub async fn subscribe_calls(&self) -> usize {
381 self.client.lock().await.subscribe_calls
382 }
383
384 pub async fn disconnect_notification_subscribers(&self) {
387 let mut validator = self.client.lock().await;
388 for abort in validator.notification_stream_aborts.drain(..) {
389 abort.abort();
390 }
391 }
392
393 fn set_fault_type(&mut self, fault_type: FaultType) {
394 self.fault_type = fault_type;
395 }
396
397 pub async fn chain_info_with_manager_values(
399 &mut self,
400 chain_id: ChainId,
401 ) -> Result<Box<ChainInfo>, NodeError> {
402 let query = ChainInfoQuery::new(chain_id).with_manager_values();
403 let response = self.handle_chain_info_query(query).await?;
404 Ok(response.info)
405 }
406
407 async fn spawn_and_receive<F, R, T>(&self, f: F) -> T
410 where
411 T: Send + 'static,
412 R: Future<Output = Result<(), T>> + Send,
413 F: FnOnce(Self, oneshot::Sender<T>) -> R + Send + 'static,
414 {
415 let validator = self.clone();
416 let (sender, receiver) = oneshot::channel();
417 tokio::spawn(async move {
418 if f(validator, sender).await.is_err() {
419 tracing::debug!("result could not be sent");
420 }
421 });
422 receiver.await.unwrap()
423 }
424
425 async fn do_handle_block_proposal(
426 self,
427 proposal: BlockProposal,
428 sender: oneshot::Sender<Result<ChainInfoResponse, NodeError>>,
429 ) -> Result<(), Result<ChainInfoResponse, NodeError>> {
430 let result = match self.fault_type {
431 FaultType::Offline | FaultType::OfflineWithInfo => Err(NodeError::ClientIoError {
432 error: "offline".to_string(),
433 }),
434 FaultType::NoChains => Err(NodeError::InactiveChain(proposal.content.block.chain_id)),
435 FaultType::DontSendValidateVote
436 | FaultType::Honest
437 | FaultType::DontSendConfirmVote
438 | FaultType::DontProcessValidated => {
439 let (response_result, _actions) = self
440 .client
441 .lock()
442 .await
443 .state
444 .handle_block_proposal(proposal)
445 .await;
446 let result = response_result.map_err(NodeError::from);
447 if self.fault_type == FaultType::DontSendValidateVote {
448 Err(NodeError::ClientIoError {
449 error: "refusing to validate".to_string(),
450 })
451 } else {
452 result
453 }
454 }
455 };
456 sender.send(result)
458 }
459
460 async fn do_handle_lite_certificate(
461 self,
462 certificate: LiteCertificate<'_>,
463 sender: oneshot::Sender<Result<ChainInfoResponse, NodeError>>,
464 ) -> Result<(), Result<ChainInfoResponse, NodeError>> {
465 let client = self.client.clone();
466 let validator = client.lock().await;
467 let result = async move {
468 match validator.state.full_certificate(certificate).await? {
469 Either::Left(confirmed) => {
470 self.do_handle_certificate_internal::<ConfirmedBlock>(confirmed, &validator)
471 .await
472 }
473 Either::Right(validated) => {
474 self.do_handle_certificate_internal::<ValidatedBlock>(validated, &validator)
475 .await
476 }
477 }
478 }
479 .await;
480 sender.send(result)
481 }
482
483 async fn do_handle_certificate_internal<T: ProcessableCertificate>(
484 &self,
485 certificate: T::Certificate,
486 validator: &MutexGuard<'_, LocalValidator<S>>,
487 ) -> Result<ChainInfoResponse, NodeError> {
488 match self.fault_type {
489 FaultType::DontProcessValidated if T::KIND == CertificateKind::Validated => {
490 Err(NodeError::ClientIoError {
491 error: "refusing to process validated block".to_string(),
492 })
493 }
494 FaultType::NoChains => Err(NodeError::InactiveChain(certificate.value().chain_id())),
495 FaultType::Honest
496 | FaultType::DontSendConfirmVote
497 | FaultType::DontProcessValidated
498 | FaultType::DontSendValidateVote => {
499 let result = validator
500 .state
501 .fully_handle_certificate_with_notifications(certificate, &validator.notifier)
502 .await
503 .map_err(Into::into);
504 if T::KIND == CertificateKind::Validated
505 && self.fault_type == FaultType::DontSendConfirmVote
506 {
507 Err(NodeError::ClientIoError {
508 error: "refusing to confirm".to_string(),
509 })
510 } else {
511 result
512 }
513 }
514 FaultType::Offline | FaultType::OfflineWithInfo => Err(NodeError::ClientIoError {
515 error: "offline".to_string(),
516 }),
517 }
518 }
519
520 async fn do_handle_certificate<T: ProcessableCertificate>(
521 self,
522 certificate: T::Certificate,
523 sender: oneshot::Sender<Result<ChainInfoResponse, NodeError>>,
524 ) -> Result<(), Result<ChainInfoResponse, NodeError>> {
525 let validator = self.client.lock().await;
526 let result = self
527 .do_handle_certificate_internal::<T>(certificate, &validator)
528 .await;
529 sender.send(result)
530 }
531
532 async fn do_handle_chain_info_query(
533 self,
534 query: ChainInfoQuery,
535 sender: oneshot::Sender<Result<ChainInfoResponse, NodeError>>,
536 ) -> Result<(), Result<ChainInfoResponse, NodeError>> {
537 let gate = self.chain_info_gate.clone();
538 let _open = if self
539 .chain_info_gate_armed
540 .load(std::sync::atomic::Ordering::Relaxed)
541 {
542 Some(gate.read().await)
543 } else {
544 None
545 };
546 let validator = self.client.lock().await;
547 let result = match self.fault_type {
548 FaultType::Offline => Err(NodeError::ClientIoError {
549 error: "offline".to_string(),
550 }),
551 FaultType::NoChains => Err(NodeError::InactiveChain(query.chain_id)),
552 FaultType::Honest
553 | FaultType::DontSendConfirmVote
554 | FaultType::DontProcessValidated
555 | FaultType::DontSendValidateVote
556 | FaultType::OfflineWithInfo => validator
557 .state
558 .handle_chain_info_query(query)
559 .await
560 .map_err(Into::into),
561 };
562 sender.send(result)
563 }
564
565 async fn do_subscribe(
566 self,
567 chains: Vec<ChainId>,
568 sender: oneshot::Sender<Result<NotificationStream, NodeError>>,
569 ) -> Result<(), Result<NotificationStream, NodeError>> {
570 let mut validator = self.client.lock().await;
571 validator.subscribe_calls += 1;
572 if matches!(
575 self.fault_type,
576 FaultType::Offline | FaultType::OfflineWithInfo
577 ) {
578 return sender.send(Err(NodeError::ClientIoError {
579 error: "offline".to_string(),
580 }));
581 }
582 let rx = validator.notifier.subscribe(chains);
583 let (stream, abort) = stream::abortable(UnboundedReceiverStream::new(rx));
584 let abort_on_drop = AbortOnDrop(abort.clone());
589 let stream = stream.map(move |notification| {
590 let _abort_on_drop = &abort_on_drop;
591 notification
592 });
593 validator
594 .notification_stream_aborts
595 .retain(|abort| !abort.is_aborted());
596 validator.notification_stream_aborts.push(abort);
597 let stream: NotificationStream = Box::pin(stream);
598 sender.send(Ok(stream))
599 }
600
601 async fn do_upload_blob(
602 self,
603 content: BlobContent,
604 sender: oneshot::Sender<Result<BlobId, NodeError>>,
605 ) -> Result<(), Result<BlobId, NodeError>> {
606 let validator = self.client.lock().await;
607 let blob = Blob::new(content);
608 let id = blob.id();
609 let storage = validator.state.storage_client();
610 let result = match storage.maybe_write_blobs(&[blob]).await {
611 Ok(has_state) if has_state.first() == Some(&true) => Ok(id),
612 Ok(_) => Err(NodeError::BlobsNotFound(vec![id])),
613 Err(error) => Err(error.into()),
614 };
615 sender.send(result)
616 }
617
618 async fn do_download_blob(
619 self,
620 blob_id: BlobId,
621 sender: oneshot::Sender<Result<BlobContent, NodeError>>,
622 ) -> Result<(), Result<BlobContent, NodeError>> {
623 let validator = self.client.lock().await;
624 let blob = validator
625 .state
626 .storage_client()
627 .read_blob(blob_id)
628 .await
629 .map_err(Into::into);
630 let blob = match blob {
631 Ok(blob) => blob.ok_or_else(|| NodeError::BlobsNotFound(vec![blob_id])),
632 Err(error) => Err(error),
633 };
634 sender.send(blob.map(|blob| CacheArc::unwrap_or_clone(blob).into_content()))
635 }
636
637 async fn do_download_pending_blob(
638 self,
639 chain_id: ChainId,
640 blob_id: BlobId,
641 sender: oneshot::Sender<Result<BlobContent, NodeError>>,
642 ) -> Result<(), Result<BlobContent, NodeError>> {
643 let validator = self.client.lock().await;
644 let result = validator
645 .state
646 .download_pending_blob(chain_id, blob_id)
647 .await
648 .map_err(Into::into);
649 sender.send(result.map(|blob| blob.content().clone()))
650 }
651
652 async fn do_handle_pending_blob(
653 self,
654 chain_id: ChainId,
655 blob: BlobContent,
656 sender: oneshot::Sender<Result<ChainInfoResponse, NodeError>>,
657 ) -> Result<(), Result<ChainInfoResponse, NodeError>> {
658 let validator = self.client.lock().await;
659 let result = validator
660 .state
661 .handle_pending_blob(chain_id, Blob::new(blob))
662 .await
663 .map_err(Into::into);
664 sender.send(result)
665 }
666
667 async fn do_download_certificate(
668 self,
669 hash: CryptoHash,
670 sender: oneshot::Sender<Result<ConfirmedBlockCertificate, NodeError>>,
671 ) -> Result<(), Result<ConfirmedBlockCertificate, NodeError>> {
672 let validator = self.client.lock().await;
673 let certificate = validator
674 .state
675 .storage_client()
676 .read_certificate(hash)
677 .await
678 .map_err(Into::into);
679
680 let certificate = match certificate {
681 Err(error) => Err(error),
682 Ok(entry) => match entry {
683 Some(certificate) => Ok(CacheArc::unwrap_or_clone(certificate)),
684 None => {
685 panic!("Missing certificate: {hash}");
686 }
687 },
688 };
689
690 sender.send(certificate)
691 }
692
693 async fn do_download_certificates(
694 self,
695 hashes: Vec<CryptoHash>,
696 sender: oneshot::Sender<Result<Vec<ConfirmedBlockCertificate>, NodeError>>,
697 ) -> Result<(), Result<Vec<ConfirmedBlockCertificate>, NodeError>> {
698 let validator = self.client.lock().await;
699 let certificates = validator
700 .state
701 .storage_client()
702 .read_certificates(&hashes)
703 .await
704 .map_err(Into::into);
705
706 let certificates = match certificates {
707 Err(error) => Err(error),
708 Ok(certificates) => match ResultReadCertificates::new(certificates, hashes) {
709 ResultReadCertificates::Certificates(certificates) => Ok(certificates),
710 ResultReadCertificates::InvalidHashes(hashes) => {
711 panic!("Missing certificates: {hashes:?}")
712 }
713 },
714 };
715
716 sender.send(certificates)
717 }
718
719 async fn do_download_certificates_by_heights(
720 self,
721 chain_id: ChainId,
722 heights: Vec<BlockHeight>,
723 sender: oneshot::Sender<Result<Vec<ConfirmedBlockCertificate>, NodeError>>,
724 ) -> Result<(), Result<Vec<ConfirmedBlockCertificate>, NodeError>> {
725 let (query_sender, query_receiver) = oneshot::channel();
727 let query = ChainInfoQuery::new(chain_id).with_sent_certificate_hashes_by_heights(heights);
728
729 let self_clone = self.clone();
730 self.do_handle_chain_info_query(query, query_sender)
731 .await
732 .expect("Failed to handle chain info query");
733
734 let chain_info_response = query_receiver.await.map_err(|_| {
736 Err(NodeError::ClientIoError {
737 error: "Failed to receive chain info response".to_string(),
738 })
739 })?;
740
741 let hashes = match chain_info_response {
742 Ok(response) => response.info.requested_sent_certificate_hashes,
743 Err(e) => {
744 return sender.send(Err(e));
745 }
746 };
747
748 let (cert_sender, cert_receiver) = oneshot::channel();
750 self_clone
751 .do_download_certificates(hashes, cert_sender)
752 .await?;
753
754 let result = cert_receiver.await.map_err(|_| {
756 Err(NodeError::ClientIoError {
757 error: "Failed to receive certificates".to_string(),
758 })
759 })?;
760
761 sender.send(result)
762 }
763
764 async fn do_blob_last_used_by(
765 self,
766 blob_id: BlobId,
767 sender: oneshot::Sender<Result<CryptoHash, NodeError>>,
768 ) -> Result<(), Result<CryptoHash, NodeError>> {
769 let validator = self.client.lock().await;
770 let blob_state = validator
771 .state
772 .storage_client()
773 .read_blob_state(blob_id)
774 .await
775 .map_err(Into::into);
776 let certificate_hash = match blob_state {
777 Err(err) => Err(err),
778 Ok(blob_state) => match blob_state {
779 None => Err(NodeError::BlobsNotFound(vec![blob_id])),
780 Some(blob_state) => blob_state
781 .last_used_by
782 .ok_or_else(|| NodeError::BlobsNotFound(vec![blob_id])),
783 },
784 };
785
786 sender.send(certificate_hash)
787 }
788
789 async fn do_blob_last_used_by_certificate(
790 self,
791 blob_id: BlobId,
792 sender: oneshot::Sender<Result<ConfirmedBlockCertificate, NodeError>>,
793 ) -> Result<(), Result<ConfirmedBlockCertificate, NodeError>> {
794 match self.blob_last_used_by(blob_id).await {
795 Ok(cert_hash) => {
796 let cert = self.download_certificate(cert_hash).await;
797 sender.send(cert)
798 }
799 Err(err) => sender.send(Err(err)),
800 }
801 }
802
803 async fn do_missing_blob_ids(
804 self,
805 blob_ids: Vec<BlobId>,
806 sender: oneshot::Sender<Result<Vec<BlobId>, NodeError>>,
807 ) -> Result<(), Result<Vec<BlobId>, NodeError>> {
808 let validator = self.client.lock().await;
809 let missing_blob_ids = validator
810 .state
811 .storage_client()
812 .missing_blobs(&blob_ids)
813 .await
814 .map_err(Into::into);
815 sender.send(missing_blob_ids)
816 }
817
818 async fn do_event_block_heights(
819 self,
820 event_ids: Vec<EventId>,
821 sender: oneshot::Sender<Result<Vec<Option<BlockHeight>>, NodeError>>,
822 ) -> Result<(), Result<Vec<Option<BlockHeight>>, NodeError>> {
823 let validator = self.client.lock().await;
824 let heights = validator
825 .state
826 .storage_client()
827 .read_event_block_heights(&event_ids)
828 .await
829 .map_err(Into::into);
830 sender.send(heights)
831 }
832}
833
834pub async fn wait_until<F, Fut>(timeout: Duration, mut condition: F) -> bool
841where
842 F: FnMut() -> Fut,
843 Fut: Future<Output = bool>,
844{
845 let deadline = std::time::Instant::now() + timeout;
846 loop {
847 if condition().await {
848 return true;
849 }
850 if std::time::Instant::now() >= deadline {
851 return false;
852 }
853 tokio::time::sleep(Duration::from_millis(20)).await;
854 }
855}
856
857struct AbortOnDrop(stream::AbortHandle);
860
861impl Drop for AbortOnDrop {
862 fn drop(&mut self) {
863 self.0.abort();
864 }
865}
866
867#[derive(Clone)]
869pub struct NodeProvider<S>
870where
871 S: Storage,
872{
873 clients: Arc<std::sync::Mutex<Vec<LocalValidatorClient<S>>>>,
874 failures_left: Arc<std::sync::atomic::AtomicUsize>,
877 builds: Arc<std::sync::atomic::AtomicUsize>,
880 slow_build_micros: Arc<std::sync::atomic::AtomicU64>,
884 clock: Option<TestClock>,
885}
886
887impl<S> NodeProvider<S>
888where
889 S: Storage + Clone,
890{
891 fn all_nodes(&self) -> Vec<LocalValidatorClient<S>> {
892 self.clients.lock().unwrap().clone()
893 }
894
895 fn without_failure_injection(&self) -> Self {
900 Self {
901 clients: self.clients.clone(),
902 failures_left: Arc::default(),
903 builds: Arc::default(),
905 slow_build_micros: Arc::default(),
906 clock: self.clock.clone(),
907 }
908 }
909}
910
911impl<S> ValidatorNodeProvider for NodeProvider<S>
912where
913 S: Storage + Clone + Send + Sync + 'static,
914{
915 type Node = LocalValidatorClient<S>;
916
917 fn make_node(&self, _name: &str) -> Result<Self::Node, NodeError> {
918 unimplemented!()
919 }
920
921 fn make_nodes_from_list<A>(
922 &self,
923 validators: impl IntoIterator<Item = (ValidatorPublicKey, A)>,
924 ) -> Result<impl Iterator<Item = (ValidatorPublicKey, Self::Node)>, NodeError>
925 where
926 A: AsRef<str>,
927 {
928 self.builds
929 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
930 let slow = self
931 .slow_build_micros
932 .load(std::sync::atomic::Ordering::Relaxed);
933 if slow > 0 {
934 if let Some(clock) = &self.clock {
935 clock.add(TimeDelta::from_micros(slow));
936 }
937 }
938 if self
939 .failures_left
940 .fetch_update(
941 std::sync::atomic::Ordering::Relaxed,
942 std::sync::atomic::Ordering::Relaxed,
943 |n| n.checked_sub(1),
944 )
945 .is_ok()
946 {
947 return Err(NodeError::CannotResolveValidatorAddress {
948 address: "injected failure".to_string(),
949 });
950 }
951 let list = self.clients.lock().unwrap();
952 Ok(validators
953 .into_iter()
954 .map(|(public_key, address)| {
955 list.iter()
956 .find(|client| client.public_key == public_key)
957 .ok_or_else(|| NodeError::CannotResolveValidatorAddress {
958 address: address.as_ref().to_string(),
959 })
960 .map(|client| (public_key, client.clone()))
961 })
962 .collect::<Result<Vec<_>, _>>()?
963 .into_iter())
964 }
965}
966
967impl<S> FromIterator<LocalValidatorClient<S>> for NodeProvider<S>
968where
969 S: Storage,
970{
971 fn from_iter<T>(iter: T) -> Self
972 where
973 T: IntoIterator<Item = LocalValidatorClient<S>>,
974 {
975 Self {
976 clients: Arc::new(std::sync::Mutex::new(iter.into_iter().collect())),
977 failures_left: Arc::default(),
978 builds: Arc::default(),
979 slow_build_micros: Arc::default(),
980 clock: None,
981 }
982 }
983}
984
985#[allow(missing_docs)]
992pub struct TestBuilder<B: StorageBuilder> {
993 storage_builder: B,
994 pub initial_committee: Committee,
995 admin_description: Option<ChainDescription>,
996 network_description: Option<NetworkDescription>,
997 genesis_storage_builder: GenesisStorageBuilder,
998 node_provider: NodeProvider<B::Storage>,
999 pub validator_storages: HashMap<ValidatorPublicKey, B::Storage>,
1000 pub validator_key_pairs: HashMap<ValidatorPublicKey, ValidatorSecretKey>,
1001 chain_client_storages: Vec<B::Storage>,
1002 pub chain_owners: BTreeMap<ChainId, AccountOwner>,
1003 pub signer: TestSigner,
1004}
1005
1006#[async_trait]
1008pub trait StorageBuilder {
1009 type Storage: Storage + Clone + Send + Sync + 'static;
1011
1012 async fn build(&mut self) -> Result<Self::Storage, anyhow::Error>;
1014
1015 fn clock(&self) -> &TestClock;
1017}
1018
1019#[derive(Default)]
1020struct GenesisStorageBuilder {
1021 accounts: Vec<GenesisAccount>,
1022}
1023
1024struct GenesisAccount {
1025 description: ChainDescription,
1026 public_key: AccountPublicKey,
1027}
1028
1029impl GenesisStorageBuilder {
1030 fn add(&mut self, description: ChainDescription, public_key: AccountPublicKey) {
1031 self.accounts.push(GenesisAccount {
1032 description,
1033 public_key,
1034 })
1035 }
1036
1037 async fn build<S>(&self, storage: S) -> S
1038 where
1039 S: Storage + Clone + Send + Sync + 'static,
1040 {
1041 for account in &self.accounts {
1042 storage
1043 .create_chain(account.description.clone())
1044 .await
1045 .unwrap();
1046 }
1047 storage
1048 }
1049}
1050
1051pub type ChainClient<S> = crate::client::ChainClient<crate::environment::Impl<S, NodeProvider<S>>>;
1053
1054impl<S: Storage + Clone + Send + Sync + 'static> ChainClient<S> {
1055 pub async fn read_confirmed_blocks_downward(
1057 &self,
1058 from: CryptoHash,
1059 limit: u32,
1060 ) -> anyhow::Result<Vec<Arc<ConfirmedBlock>>> {
1061 let mut hash = Some(from);
1062 let mut values = Vec::new();
1063 for _ in 0..limit {
1064 let Some(next_hash) = hash else {
1065 break;
1066 };
1067 let value = self.read_confirmed_block(next_hash).await?;
1068 hash = value.block().header.previous_block_hash;
1069 values.push(value);
1070 }
1071 Ok(values)
1072 }
1073}
1074
1075impl<B> TestBuilder<B>
1076where
1077 B: StorageBuilder,
1078{
1079 pub fn clock(&self) -> &TestClock {
1082 self.storage_builder.clock()
1083 }
1084
1085 pub async fn new(
1087 storage_builder: B,
1088 count: usize,
1089 with_faulty_validators: usize,
1090 signer: TestSigner,
1091 ) -> Result<Self, anyhow::Error> {
1092 Self::build(
1093 storage_builder,
1094 count,
1095 with_faulty_validators,
1096 signer,
1097 None,
1098 None,
1099 )
1100 .await
1101 }
1102
1103 pub async fn new_with_block_export(
1106 storage_builder: B,
1107 count: usize,
1108 with_faulty_validators: usize,
1109 signer: TestSigner,
1110 ) -> Result<Self, anyhow::Error> {
1111 Self::build(
1112 storage_builder,
1113 count,
1114 with_faulty_validators,
1115 signer,
1116 Some(Self::test_block_export_config()),
1117 None,
1118 )
1119 .await
1120 }
1121
1122 pub async fn new_with_block_export_and_ttl(
1125 storage_builder: B,
1126 count: usize,
1127 with_faulty_validators: usize,
1128 signer: TestSigner,
1129 chain_worker_ttl: Duration,
1130 ) -> Result<Self, anyhow::Error> {
1131 Self::build(
1132 storage_builder,
1133 count,
1134 with_faulty_validators,
1135 signer,
1136 Some(Self::test_block_export_config()),
1137 Some(chain_worker_ttl),
1138 )
1139 .await
1140 }
1141
1142 pub async fn new_with_block_export_config(
1146 storage_builder: B,
1147 count: usize,
1148 with_faulty_validators: usize,
1149 signer: TestSigner,
1150 config: crate::BlockExportConfig,
1151 ) -> Result<Self, anyhow::Error> {
1152 Self::build(
1153 storage_builder,
1154 count,
1155 with_faulty_validators,
1156 signer,
1157 Some(config),
1158 None,
1159 )
1160 .await
1161 }
1162
1163 pub fn test_block_export_config() -> crate::BlockExportConfig {
1166 crate::BlockExportConfig {
1167 retry_delay: Duration::from_millis(20),
1168 max_retry_delay: Duration::from_millis(200),
1169 ..crate::BlockExportConfig::default()
1170 }
1171 }
1172
1173 async fn build(
1174 mut storage_builder: B,
1175 count: usize,
1176 with_faulty_validators: usize,
1177 mut signer: TestSigner,
1178 block_export: Option<crate::BlockExportConfig>,
1179 chain_worker_ttl: Option<Duration>,
1180 ) -> Result<Self, anyhow::Error> {
1181 let mut validators = Vec::new();
1182 for _ in 0..count {
1183 let validator_keypair = ValidatorKeypair::generate();
1184 let account_public_key = signer.generate_new();
1185 validators.push((validator_keypair, account_public_key));
1186 }
1187 let for_committee = validators
1188 .iter()
1189 .map(|(validating, account)| (validating.public_key, *account))
1190 .collect::<Vec<_>>();
1191 let initial_committee = Committee::make_simple(for_committee);
1192 let node_provider = NodeProvider {
1195 clients: Arc::new(std::sync::Mutex::new(Vec::new())),
1196 failures_left: Arc::default(),
1197 builds: Arc::default(),
1198 slow_build_micros: Arc::default(),
1199 clock: Some(storage_builder.clock().clone()),
1200 };
1201 let mut validator_storages = HashMap::new();
1202 let mut validator_key_pairs = HashMap::new();
1203 let mut faulty_validators = HashSet::new();
1204 for (i, (validator_keypair, _account_public_key)) in validators.into_iter().enumerate() {
1205 let validator_public_key = validator_keypair.public_key;
1206 let storage = storage_builder.build().await?;
1207 let secret_key_copy = validator_keypair.secret_key.copy();
1208 let config = ChainWorkerConfig {
1209 nickname: format!("Node {i}"),
1210 ttl: chain_worker_ttl
1214 .or_else(|| block_export.is_some().then(|| Duration::from_secs(60))),
1215 exported_heights_fold_interval: Duration::ZERO,
1216 ..ChainWorkerConfig::default()
1217 }
1218 .with_key_pair(Some(validator_keypair.secret_key));
1219 let mut state = WorkerState::new(storage.clone(), config, None);
1220 if let Some(export_config) = block_export.clone() {
1221 let handle = crate::spawn_block_export_queue(
1222 storage.clone(),
1223 Arc::new(node_provider.without_failure_injection()),
1224 export_config,
1225 Some(validator_public_key),
1226 );
1227 state = state.with_block_export(handle);
1228 }
1229 let mut validator = LocalValidatorClient::new(validator_public_key, state);
1230 if i < with_faulty_validators {
1231 faulty_validators.insert(validator_public_key);
1232 validator.set_fault_type(FaultType::NoChains);
1233 }
1234 node_provider.clients.lock().unwrap().push(validator);
1235 validator_storages.insert(validator_public_key, storage);
1236 validator_key_pairs.insert(validator_public_key, secret_key_copy);
1237 }
1238 tracing::info!(
1239 "Test will use the following faulty validators: {:?}",
1240 faulty_validators
1241 );
1242 Ok(Self {
1243 storage_builder,
1244 initial_committee,
1245 admin_description: None,
1246 network_description: None,
1247 genesis_storage_builder: GenesisStorageBuilder::default(),
1248 node_provider,
1249 validator_storages,
1250 validator_key_pairs,
1251 chain_client_storages: Vec::new(),
1252 chain_owners: BTreeMap::new(),
1253 signer,
1254 })
1255 }
1256
1257 pub fn with_policy(mut self, policy: ResourceControlPolicy) -> Self {
1259 let validators = self.initial_committee.validators().clone();
1260 self.initial_committee =
1261 Committee::new(validators, policy).expect("committee votes should not overflow");
1262 self
1263 }
1264
1265 pub fn with_cross_chain_message_chunk_limit(self, limit: usize) -> Self {
1267 let validator_clients = self.node_provider.clients.lock().unwrap();
1268 for validator in validator_clients.iter() {
1269 let mut inner = validator.client.try_lock().expect("no contention at setup");
1270 inner.state.set_cross_chain_message_chunk_limit(limit);
1271 }
1272 drop(validator_clients);
1273 self
1274 }
1275
1276 pub fn fail_next_validator_set_builds(&self, count: usize) {
1279 self.node_provider
1280 .failures_left
1281 .store(count, std::sync::atomic::Ordering::Relaxed);
1282 }
1283
1284 pub fn validator_set_build_failures_left(&self) -> usize {
1287 self.node_provider
1288 .failures_left
1289 .load(std::sync::atomic::Ordering::Relaxed)
1290 }
1291
1292 pub async fn stall_chain_info_queries(
1295 &self,
1296 index: usize,
1297 ) -> tokio::sync::OwnedRwLockWriteGuard<()> {
1298 let node = &self.node_provider.all_nodes()[index];
1299 node.chain_info_gate_armed
1300 .store(true, std::sync::atomic::Ordering::Relaxed);
1301 node.chain_info_gate.clone().write_owned().await
1302 }
1303
1304 pub fn slow_validator_set_builds(&self, by: TimeDelta) {
1307 self.node_provider
1308 .slow_build_micros
1309 .store(by.as_micros(), std::sync::atomic::Ordering::Relaxed);
1310 }
1311
1312 pub fn validator_set_builds(&self) -> usize {
1314 self.node_provider
1315 .builds
1316 .load(std::sync::atomic::Ordering::Relaxed)
1317 }
1318
1319 pub async fn subscribe_calls(&self, index: usize) -> usize {
1321 self.node_provider.all_nodes()[index]
1322 .subscribe_calls()
1323 .await
1324 }
1325
1326 pub async fn disconnect_notification_subscribers(&self) {
1330 for validator in self.node_provider.all_nodes() {
1331 validator.disconnect_notification_subscribers().await;
1332 }
1333 }
1334
1335 pub fn fault_type(&self, public_key: &ValidatorPublicKey) -> Option<FaultType> {
1338 self.node_provider
1339 .clients
1340 .lock()
1341 .unwrap()
1342 .iter()
1343 .find(|client| client.public_key == *public_key)
1344 .map(|client| client.fault_type())
1345 }
1346
1347 pub fn set_fault_type(&mut self, indexes: impl AsRef<[usize]>, fault_type: FaultType) {
1349 let mut faulty_validators = vec![];
1350 let mut validator_clients = self.node_provider.clients.lock().unwrap();
1351 for index in indexes.as_ref() {
1352 let validator = &mut validator_clients[*index];
1353 validator.set_fault_type(fault_type);
1354 faulty_validators.push(validator.public_key);
1355 }
1356 tracing::info!(
1357 "Making the following validators {:?}: {:?}",
1358 fault_type,
1359 faulty_validators
1360 );
1361 }
1362
1363 pub async fn add_root_chain(
1368 &mut self,
1369 index: u32,
1370 balance: Amount,
1371 ) -> anyhow::Result<ChainClient<B::Storage>> {
1372 self.add_root_chain_with_ownership(index, balance, ChainOwnership::single)
1373 .await
1374 }
1375
1376 pub async fn add_root_chain_with_ownership(
1382 &mut self,
1383 index: u32,
1384 balance: Amount,
1385 make_ownership: impl FnOnce(AccountOwner) -> ChainOwnership,
1386 ) -> anyhow::Result<ChainClient<B::Storage>> {
1387 if self.admin_description.is_none() && index != 0 {
1389 Box::pin(self.add_root_chain(0, Amount::ZERO)).await?;
1390 }
1391 let origin = ChainOrigin::Root(index);
1392 let public_key = self.signer.generate_new();
1393 let open_chain_config = InitialChainConfig {
1394 ownership: make_ownership(public_key.into()),
1395 epoch: Epoch(0),
1396 account: AccountOwner::CHAIN,
1397 balance,
1398 application_permissions: ApplicationPermissions::default(),
1399 };
1400 let description = ChainDescription::new(origin, open_chain_config, Timestamp::from(0));
1401 let committee_blob = Blob::new_committee(bcs::to_bytes(&self.initial_committee).unwrap());
1402 if index == 0 {
1403 self.admin_description = Some(description.clone());
1404 self.network_description = Some(NetworkDescription {
1405 admin_chain_id: description.id(),
1406 genesis_config_hash: CryptoHash::test_hash("genesis config"),
1408 genesis_timestamp: Timestamp::from(0),
1409 genesis_committee_blob_hash: committee_blob.id().hash,
1410 name: "test network".to_string(),
1411 });
1412 }
1413 self.genesis_storage_builder
1415 .add(description.clone(), public_key);
1416
1417 let network_description = self.network_description.as_ref().unwrap();
1418
1419 for validator in self.node_provider.all_nodes() {
1420 let storage = self
1421 .validator_storages
1422 .get_mut(&validator.public_key)
1423 .unwrap();
1424 storage
1425 .write_network_description(network_description)
1426 .await
1427 .expect("writing the NetworkDescription should succeed");
1428 storage
1429 .write_blob(&committee_blob)
1430 .await
1431 .expect("writing a blob should succeed");
1432 storage.create_chain(description.clone()).await.unwrap();
1433 }
1434 for storage in &mut self.chain_client_storages {
1435 storage.create_chain(description.clone()).await.unwrap();
1436 }
1437 let chain_id = description.id();
1438 self.chain_owners.insert(chain_id, public_key.into());
1439 self.make_client(chain_id, None, BlockHeight::ZERO).await
1440 }
1441
1442 pub fn genesis_chains(&self) -> Vec<(AccountPublicKey, Amount)> {
1444 let mut result = Vec::new();
1445 for (i, genesis_account) in self.genesis_storage_builder.accounts.iter().enumerate() {
1446 assert_eq!(
1447 genesis_account.description.origin(),
1448 ChainOrigin::Root(i as u32)
1449 );
1450 result.push((
1451 genesis_account.public_key,
1452 genesis_account.description.config().balance,
1453 ));
1454 }
1455 result
1456 }
1457
1458 pub fn admin_chain_id(&self) -> ChainId {
1460 self.admin_description
1461 .as_ref()
1462 .expect("admin chain not initialized")
1463 .id()
1464 }
1465
1466 pub fn admin_description(&self) -> Option<&ChainDescription> {
1468 self.admin_description.as_ref()
1469 }
1470
1471 pub fn make_node_provider(&self) -> NodeProvider<B::Storage> {
1473 self.node_provider.clone()
1474 }
1475
1476 pub fn validator_storage(&mut self, index: usize) -> B::Storage {
1479 let public_key = self.node(index).public_key;
1480 self.validator_storages.get(&public_key).unwrap().clone()
1481 }
1482
1483 pub fn node(&mut self, index: usize) -> LocalValidatorClient<B::Storage> {
1485 self.node_provider.clients.lock().unwrap()[index].clone()
1486 }
1487
1488 pub async fn make_storage(&mut self) -> anyhow::Result<B::Storage> {
1490 let storage = self.storage_builder.build().await?;
1491 let network_description = self.network_description.as_ref().unwrap();
1492 let committee_blob = Blob::new_committee(bcs::to_bytes(&self.initial_committee).unwrap());
1493 storage
1494 .write_network_description(network_description)
1495 .await
1496 .expect("writing the NetworkDescription should succeed");
1497 storage
1498 .write_blob(&committee_blob)
1499 .await
1500 .expect("writing a blob should succeed");
1501 Ok(self.genesis_storage_builder.build(storage).await)
1502 }
1503
1504 pub async fn make_client_with_options(
1506 &mut self,
1507 chain_id: ChainId,
1508 block_hash: Option<CryptoHash>,
1509 block_height: BlockHeight,
1510 options: chain_client::Options,
1511 follow_only: bool,
1512 ) -> anyhow::Result<ChainClient<B::Storage>> {
1513 let storage = self.make_storage().await?;
1516 self.chain_client_storages.push(storage.clone());
1517 let mode = if follow_only {
1518 crate::client::ListeningMode::FollowChain
1519 } else {
1520 crate::client::ListeningMode::FullChain
1521 };
1522 let client = Arc::new(Client::new(
1523 crate::environment::Impl {
1524 network: self.make_node_provider(),
1525 storage,
1526 signer: self.signer.clone(),
1527 wallet: TestWallet::default(),
1528 },
1529 self.admin_chain_id(),
1530 false,
1531 [(chain_id, mode)],
1532 format!("Client node for {chain_id:.8}"),
1533 Some(Duration::from_secs(30)),
1534 Some(Duration::from_secs(1)),
1535 1000,
1536 options,
1537 DEFAULT_BLOCK_CACHE_SIZE,
1538 DEFAULT_EXECUTION_STATE_CACHE_SIZE,
1539 &crate::client::RequestsSchedulerConfig::default(),
1540 ));
1541 Ok(client.create_chain_client(
1542 chain_id,
1543 block_hash,
1544 block_height,
1545 &None,
1546 self.chain_owners.get(&chain_id).copied(),
1547 None,
1548 follow_only,
1549 ))
1550 }
1551
1552 pub async fn make_client(
1554 &mut self,
1555 chain_id: ChainId,
1556 block_hash: Option<CryptoHash>,
1557 block_height: BlockHeight,
1558 ) -> anyhow::Result<ChainClient<B::Storage>> {
1559 self.make_client_with_options(
1560 chain_id,
1561 block_hash,
1562 block_height,
1563 chain_client::Options::test_default(),
1564 false,
1565 )
1566 .await
1567 }
1568
1569 pub async fn check_that_validators_have_certificate(
1571 &self,
1572 chain_id: ChainId,
1573 block_height: BlockHeight,
1574 target_count: usize,
1575 ) -> Option<ConfirmedBlockCertificate> {
1576 let query = ChainInfoQuery::new(chain_id)
1577 .with_sent_certificate_hashes_by_heights(vec![block_height]);
1578 let mut count = 0;
1579 let mut certificate = None;
1580 for validator in self.node_provider.all_nodes() {
1581 if let Ok(response) = validator.handle_chain_info_query(query.clone()).await {
1582 if response.check(validator.public_key).is_ok() {
1583 let ChainInfo {
1584 mut requested_sent_certificate_hashes,
1585 ..
1586 } = *response.info;
1587 debug_assert!(requested_sent_certificate_hashes.len() <= 1);
1588 if let Some(cert_hash) = requested_sent_certificate_hashes.pop() {
1589 if let Ok(cert) = validator.download_certificate(cert_hash).await {
1590 if cert.inner().block().header.chain_id == chain_id
1591 && cert.inner().block().header.height == block_height
1592 {
1593 cert.check(&self.initial_committee).unwrap();
1594 count += 1;
1595 certificate = Some(cert);
1596 }
1597 }
1598 }
1599 }
1600 }
1601 }
1602 assert!(count >= target_count);
1603 certificate
1604 }
1605
1606 pub async fn check_that_validators_are_in_round(
1609 &self,
1610 chain_id: ChainId,
1611 block_height: BlockHeight,
1612 round: Round,
1613 target_count: usize,
1614 ) {
1615 let query = ChainInfoQuery::new(chain_id);
1616 let mut count = 0;
1617 for validator in self.node_provider.all_nodes() {
1618 if let Ok(response) = validator.handle_chain_info_query(query.clone()).await {
1619 if response.info.manager.current_round == round
1620 && response.info.next_block_height == block_height
1621 && response.check(validator.public_key).is_ok()
1622 {
1623 count += 1;
1624 }
1625 }
1626 }
1627 assert!(count >= target_count);
1628 }
1629
1630 pub async fn exported_heights(
1633 &self,
1634 index: usize,
1635 chain_id: ChainId,
1636 ) -> BTreeMap<ValidatorPublicKey, BlockHeight> {
1637 let validator = self.node_provider.all_nodes()[index].clone();
1638 let guard = validator.client.lock().await;
1639 let chain = guard.state.chain_state_view(chain_id).await.unwrap();
1640 chain.exported_heights.get().clone().into()
1641 }
1642
1643 pub async fn resident_chain_workers(&self, index: usize) -> usize {
1645 let validator = self.node_provider.all_nodes()[index].clone();
1646 let guard = validator.client.lock().await;
1647 guard.state.resident_chain_worker_count()
1648 }
1649
1650 pub async fn next_block_height(&self, index: usize, chain_id: ChainId) -> BlockHeight {
1652 let validator = self.node_provider.all_nodes()[index].clone();
1653 let response = validator
1654 .handle_chain_info_query(ChainInfoQuery::new(chain_id))
1655 .await
1656 .unwrap();
1657 response.info.next_block_height
1658 }
1659
1660 pub async fn check_that_validators_have_empty_outboxes(&self, chain_id: ChainId) {
1662 for validator in self.node_provider.all_nodes() {
1663 let guard = validator.client.lock().await;
1664 let chain = guard.state.chain_state_view(chain_id).await.unwrap();
1665 assert_eq!(chain.outboxes.indices().await.unwrap(), []);
1666 }
1667 }
1668}
1669
1670#[cfg(feature = "rocksdb")]
1671static ROCKS_DB_SEMAPHORE: Semaphore = Semaphore::const_new(5);
1673
1674#[derive(Default)]
1677struct CommonStorageBuilder {
1678 namespace: String,
1679 instance_counter: usize,
1680 wasm_runtime: Option<WasmRuntime>,
1681 clock: TestClock,
1682}
1683
1684impl CommonStorageBuilder {
1685 fn with_wasm_runtime(wasm_runtime: impl Into<Option<WasmRuntime>>) -> Self {
1686 Self {
1687 wasm_runtime: wasm_runtime.into(),
1688 ..Self::default()
1689 }
1690 }
1691
1692 async fn build_storage<DB>(
1693 &mut self,
1694 config: DB::Config,
1695 ) -> anyhow::Result<DbStorage<DB, TestClock>>
1696 where
1697 DB: TestKeyValueDatabase + Clone + Send + Sync + 'static,
1698 DB::Store: KeyValueStore + Clone + Send + Sync + 'static,
1699 DB::Error: std::error::Error + Send + Sync + 'static,
1700 {
1701 self.instance_counter += 1;
1702 if self.namespace.is_empty() {
1703 self.namespace = generate_test_namespace();
1704 }
1705 let namespace = format!("{}_{}", self.namespace, self.instance_counter);
1706 Ok(
1707 DbStorage::new_for_testing(config, &namespace, self.wasm_runtime, self.clock.clone())
1708 .await?,
1709 )
1710 }
1711}
1712
1713#[derive(Default)]
1715pub struct MemoryStorageBuilder {
1716 inner: CommonStorageBuilder,
1717}
1718
1719impl MemoryStorageBuilder {
1720 pub fn with_wasm_runtime(wasm_runtime: impl Into<Option<WasmRuntime>>) -> Self {
1723 Self {
1724 inner: CommonStorageBuilder::with_wasm_runtime(wasm_runtime),
1725 }
1726 }
1727}
1728
1729#[async_trait]
1730impl StorageBuilder for MemoryStorageBuilder {
1731 type Storage = DbStorage<MemoryDatabase, TestClock>;
1732
1733 async fn build(&mut self) -> Result<Self::Storage, anyhow::Error> {
1734 let config = MemoryDatabase::new_test_config().await?;
1735 self.inner.build_storage::<MemoryDatabase>(config).await
1736 }
1737
1738 fn clock(&self) -> &TestClock {
1739 &self.inner.clock
1740 }
1741}
1742
1743#[cfg(feature = "rocksdb")]
1744pub struct RocksDbStorageBuilder {
1746 inner: CommonStorageBuilder,
1747 _permit: SemaphorePermit<'static>,
1748}
1749
1750#[cfg(feature = "rocksdb")]
1751impl RocksDbStorageBuilder {
1752 pub async fn new() -> Self {
1754 Self {
1755 inner: CommonStorageBuilder::default(),
1756 _permit: ROCKS_DB_SEMAPHORE.acquire().await.unwrap(),
1757 }
1758 }
1759
1760 #[cfg(any(feature = "wasmer", feature = "wasmtime"))]
1763 pub async fn with_wasm_runtime(wasm_runtime: impl Into<Option<WasmRuntime>>) -> Self {
1764 Self {
1765 inner: CommonStorageBuilder::with_wasm_runtime(wasm_runtime),
1766 _permit: ROCKS_DB_SEMAPHORE.acquire().await.unwrap(),
1767 }
1768 }
1769}
1770
1771#[cfg(feature = "rocksdb")]
1772#[async_trait]
1773impl StorageBuilder for RocksDbStorageBuilder {
1774 type Storage = DbStorage<RocksDbDatabase, TestClock>;
1775
1776 async fn build(&mut self) -> Result<Self::Storage, anyhow::Error> {
1777 let config = RocksDbDatabase::new_test_config().await?;
1778 self.inner.build_storage::<RocksDbDatabase>(config).await
1779 }
1780
1781 fn clock(&self) -> &TestClock {
1782 &self.inner.clock
1783 }
1784}
1785
1786#[cfg(all(not(target_arch = "wasm32"), feature = "storage-service"))]
1787#[derive(Default)]
1789pub struct ServiceStorageBuilder {
1790 inner: CommonStorageBuilder,
1791}
1792
1793#[cfg(all(not(target_arch = "wasm32"), feature = "storage-service"))]
1794impl ServiceStorageBuilder {
1795 pub fn new() -> Self {
1797 Self::with_wasm_runtime(None)
1798 }
1799
1800 pub fn with_wasm_runtime(wasm_runtime: impl Into<Option<WasmRuntime>>) -> Self {
1802 Self {
1803 inner: CommonStorageBuilder::with_wasm_runtime(wasm_runtime),
1804 }
1805 }
1806}
1807
1808#[cfg(all(not(target_arch = "wasm32"), feature = "storage-service"))]
1809#[async_trait]
1810impl StorageBuilder for ServiceStorageBuilder {
1811 type Storage = DbStorage<StorageServiceDatabase, TestClock>;
1812
1813 async fn build(&mut self) -> anyhow::Result<Self::Storage> {
1814 let config = StorageServiceDatabase::new_test_config().await?;
1815 self.inner
1816 .build_storage::<StorageServiceDatabase>(config)
1817 .await
1818 }
1819
1820 fn clock(&self) -> &TestClock {
1821 &self.inner.clock
1822 }
1823}
1824
1825#[cfg(feature = "scylladb")]
1826#[derive(Default)]
1828pub struct ScyllaDbStorageBuilder {
1829 inner: CommonStorageBuilder,
1830}
1831
1832#[cfg(feature = "scylladb")]
1833impl ScyllaDbStorageBuilder {
1834 pub fn with_wasm_runtime(wasm_runtime: impl Into<Option<WasmRuntime>>) -> Self {
1837 Self {
1838 inner: CommonStorageBuilder::with_wasm_runtime(wasm_runtime),
1839 }
1840 }
1841}
1842
1843#[cfg(feature = "scylladb")]
1844#[async_trait]
1845impl StorageBuilder for ScyllaDbStorageBuilder {
1846 type Storage = DbStorage<ScyllaDbDatabase, TestClock>;
1847
1848 async fn build(&mut self) -> Result<Self::Storage, anyhow::Error> {
1849 let config = ScyllaDbDatabase::new_test_config().await?;
1850 self.inner.build_storage::<ScyllaDbDatabase>(config).await
1851 }
1852
1853 fn clock(&self) -> &TestClock {
1854 &self.inner.clock
1855 }
1856}
1857
1858pub trait ClientOutcomeResultExt<T, E> {
1860 fn unwrap_ok_committed(self) -> T;
1863
1864 fn unwrap_ok_or_conflict(self) -> Result<T, Box<ConfirmedBlockCertificate>>;
1867}
1868
1869impl<T, E: std::fmt::Debug> ClientOutcomeResultExt<T, E> for Result<ClientOutcome<T>, E> {
1870 fn unwrap_ok_committed(self) -> T {
1871 match self.unwrap() {
1872 ClientOutcome::Committed(t) => t,
1873 ClientOutcome::WaitForTimeout(timeout) => {
1874 panic!("unexpected timeout: {timeout}")
1875 }
1876 ClientOutcome::Conflict(certificate) => {
1877 panic!("unexpected conflict: {}", certificate.hash())
1878 }
1879 }
1880 }
1881
1882 fn unwrap_ok_or_conflict(self) -> Result<T, Box<ConfirmedBlockCertificate>> {
1883 match self.unwrap() {
1884 ClientOutcome::Committed(t) => Ok(t),
1885 ClientOutcome::Conflict(certificate) => Err(certificate),
1886 ClientOutcome::WaitForTimeout(timeout) => {
1887 panic!("unexpected timeout: {timeout}")
1888 }
1889 }
1890 }
1891}