Skip to main content

iota_core/
test_authority_clients.rs

1// Copyright (c) 2021, Facebook, Inc. and its affiliates
2// Copyright (c) Mysten Labs, Inc.
3// Modifications Copyright (c) 2024 IOTA Stiftung
4// SPDX-License-Identifier: Apache-2.0
5
6use std::{
7    net::SocketAddr,
8    sync::{Arc, Mutex},
9    time::Duration,
10};
11
12use async_trait::async_trait;
13use iota_config::genesis::Genesis;
14use iota_metrics::spawn_monitored_task;
15use iota_types::{
16    crypto::AuthorityKeyPair,
17    digests::TransactionDigest,
18    effects::TransactionEffectsAPI,
19    error::{IotaError, IotaResult},
20    iota_system_state::IotaSystemState,
21    messages_checkpoint::{CheckpointRequest, CheckpointResponse},
22    messages_grpc::{
23        GetTxStatusRequest, HandleCapabilityNotificationRequestV1,
24        HandleCapabilityNotificationResponseV1, HandleCertificateRequestV1,
25        HandleCertificateResponseV1, HandleSoftBundleCertificatesRequestV1,
26        HandleSoftBundleCertificatesResponseV1, HandleTransactionResponse, ObjectInfoRequest,
27        ObjectInfoResponse, SystemStateRequest, TransactionInfoRequest, TransactionInfoResponse,
28        TxStatusUpdate, ValidatorHealthRequest, ValidatorHealthResponse,
29    },
30    transaction::{Transaction, VerifiedTransaction},
31};
32use tracing::info;
33
34use crate::{
35    authority::{AuthorityState, test_authority_builder::TestAuthorityBuilder},
36    authority_client::{
37        validator::ValidatorAPI, validator_peer::ValidatorPeerAPI, validator_v2::ValidatorV2API,
38    },
39};
40
41#[derive(Clone, Copy, Default)]
42pub struct LocalAuthorityClientFaultConfig {
43    pub fail_before_handle_transaction: bool,
44    pub fail_after_handle_transaction: bool,
45    pub fail_before_handle_confirmation: bool,
46    pub fail_after_handle_confirmation: bool,
47    pub overload_retry_after_handle_transaction: Option<Duration>,
48}
49
50impl LocalAuthorityClientFaultConfig {
51    pub fn reset(&mut self) {
52        *self = Self::default();
53    }
54}
55
56#[derive(Clone)]
57pub struct LocalAuthorityClient {
58    pub state: Arc<AuthorityState>,
59    pub fault_config: LocalAuthorityClientFaultConfig,
60}
61
62#[async_trait]
63impl ValidatorPeerAPI for LocalAuthorityClient {
64    async fn get_checkpoint_v2(
65        &self,
66        _request: CheckpointRequest,
67    ) -> Result<CheckpointResponse, IotaError> {
68        unimplemented!()
69    }
70}
71#[async_trait]
72impl ValidatorV2API for LocalAuthorityClient {
73    async fn submit_tx(
74        &self,
75        _transactions: Vec<Transaction>,
76        _client_addr: Option<SocketAddr>,
77    ) -> Result<Vec<(TransactionDigest, TxStatusUpdate)>, IotaError> {
78        unimplemented!()
79    }
80    async fn get_tx_status(
81        &self,
82        _request: GetTxStatusRequest,
83        _client_addr: Option<SocketAddr>,
84    ) -> Result<Vec<(TransactionDigest, TxStatusUpdate)>, IotaError> {
85        unimplemented!()
86    }
87    async fn notify_capabilities_v2(
88        &self,
89        request: HandleCapabilityNotificationRequestV1,
90    ) -> Result<HandleCapabilityNotificationResponseV1, IotaError> {
91        let state = self.state.clone();
92        let epoch_store = state.load_epoch_store_one_call_per_task();
93
94        let verified_authority_capabilities =
95            epoch_store.verify_authority_capabilities(request.message)?;
96
97        info!(
98            "Received capability notification (v2): {:?}",
99            verified_authority_capabilities.data()
100        );
101
102        epoch_store.record_capabilities_v1(verified_authority_capabilities.data())?;
103
104        Ok(HandleCapabilityNotificationResponseV1 { _unused: false })
105    }
106    async fn health_check(
107        &self,
108        _request: ValidatorHealthRequest,
109    ) -> Result<ValidatorHealthResponse, IotaError> {
110        unimplemented!()
111    }
112}
113#[async_trait]
114impl ValidatorAPI for LocalAuthorityClient {
115    async fn handle_transaction(
116        &self,
117        transaction: Transaction,
118        _client_addr: Option<SocketAddr>,
119    ) -> Result<HandleTransactionResponse, IotaError> {
120        if self.fault_config.fail_before_handle_transaction {
121            return Err(IotaError::from("Mock error before handle_transaction"));
122        }
123        let state = self.state.clone();
124        let epoch_store = self.state.load_epoch_store_one_call_per_task();
125        let transaction = epoch_store
126            .signature_verifier
127            .verify_tx(transaction.data())
128            .map(|_| VerifiedTransaction::new_from_verified(transaction))?;
129        let result = state.handle_transaction(&epoch_store, transaction).await;
130        if self.fault_config.fail_after_handle_transaction {
131            return Err(IotaError::GenericAuthority {
132                error: "Mock error after handle_transaction".to_owned(),
133            });
134        }
135        if let Some(duration) = self.fault_config.overload_retry_after_handle_transaction {
136            return Err(IotaError::ValidatorOverloadedRetryAfter {
137                retry_after_secs: duration.as_secs(),
138            });
139        }
140        result
141    }
142
143    async fn handle_certificate_v1(
144        &self,
145        request: HandleCertificateRequestV1,
146        _client_addr: Option<SocketAddr>,
147    ) -> Result<HandleCertificateResponseV1, IotaError> {
148        let state = self.state.clone();
149        let fault_config = self.fault_config;
150        spawn_monitored_task!(Self::handle_certificate(state, request, fault_config))
151            .await
152            .unwrap()
153    }
154
155    async fn handle_soft_bundle_certificates_v1(
156        &self,
157        _request: HandleSoftBundleCertificatesRequestV1,
158        _client_addr: Option<SocketAddr>,
159    ) -> Result<HandleSoftBundleCertificatesResponseV1, IotaError> {
160        unimplemented!()
161    }
162
163    async fn handle_object_info_request(
164        &self,
165        request: ObjectInfoRequest,
166    ) -> Result<ObjectInfoResponse, IotaError> {
167        let state = self.state.clone();
168        state.handle_object_info_request(request).await
169    }
170
171    /// Handle Object information requests .
172    async fn handle_transaction_info_request(
173        &self,
174        request: TransactionInfoRequest,
175    ) -> Result<TransactionInfoResponse, IotaError> {
176        let state = self.state.clone();
177        state.handle_transaction_info_request(request).await
178    }
179
180    async fn handle_checkpoint(
181        &self,
182        request: CheckpointRequest,
183    ) -> Result<CheckpointResponse, IotaError> {
184        let state = self.state.clone();
185
186        state.handle_checkpoint_request(&request)
187    }
188
189    async fn handle_system_state_object(
190        &self,
191        _request: SystemStateRequest,
192    ) -> Result<IotaSystemState, IotaError> {
193        self.state.get_iota_system_state_object_for_testing()
194    }
195
196    async fn handle_capability_notification_v1(
197        &self,
198        request: HandleCapabilityNotificationRequestV1,
199    ) -> Result<HandleCapabilityNotificationResponseV1, IotaError> {
200        let state = self.state.clone();
201        let epoch_store = state.load_epoch_store_one_call_per_task();
202
203        // Verify the message signature
204        let verified_authority_capabilities =
205            epoch_store.verify_authority_capabilities(request.message)?;
206
207        // Process the verified capabilities
208        info!(
209            "Received capability notification: {:?}",
210            verified_authority_capabilities.data()
211        );
212
213        // For test clients, directly record capabilities since we don't have consensus
214        epoch_store.record_capabilities_v1(verified_authority_capabilities.data())?;
215
216        Ok(HandleCapabilityNotificationResponseV1 { _unused: false })
217    }
218}
219
220impl LocalAuthorityClient {
221    pub async fn new(secret: AuthorityKeyPair, genesis: &Genesis) -> Self {
222        let state = TestAuthorityBuilder::new()
223            .with_genesis_and_keypair(genesis, &secret)
224            .build()
225            .await;
226        Self {
227            state,
228            fault_config: LocalAuthorityClientFaultConfig::default(),
229        }
230    }
231
232    pub fn new_from_authority(state: Arc<AuthorityState>) -> Self {
233        Self {
234            state,
235            fault_config: LocalAuthorityClientFaultConfig::default(),
236        }
237    }
238
239    // One difference between this implementation and actual certificate execution,
240    // is that this assumes shared object locks have already been acquired and
241    // tries to execute shared object transactions as well as owned object
242    // transactions.
243    async fn handle_certificate(
244        state: Arc<AuthorityState>,
245        request: HandleCertificateRequestV1,
246        fault_config: LocalAuthorityClientFaultConfig,
247    ) -> Result<HandleCertificateResponseV1, IotaError> {
248        if fault_config.fail_before_handle_confirmation {
249            return Err(IotaError::GenericAuthority {
250                error: "Mock error before handle_confirmation_transaction".to_owned(),
251            });
252        }
253        // Check existing effects before verifying the cert to allow querying certs
254        // finalized from previous epochs.
255        let tx_digest = *request.certificate.digest();
256        let epoch_store = state.epoch_store_for_testing();
257        let signed_effects = match state
258            .get_signed_effects_and_maybe_resign(&tx_digest, &epoch_store)
259        {
260            Ok(Some(effects)) => effects,
261            _ => {
262                let certificate = epoch_store
263                    .signature_verifier
264                    .verify_cert(request.certificate)
265                    .await?;
266                // let certificate = certificate.verify(epoch_store.committee())?;
267                state.enqueue_certificates_for_execution(vec![certificate.clone()], &epoch_store);
268                let effects = state.notify_read_effects(&certificate).await?;
269                state.sign_effects(effects, &epoch_store)?
270            }
271        }
272        .into_inner();
273
274        let events = if request.include_events {
275            if signed_effects.events_digest().is_some() {
276                Some(state.get_transaction_events(signed_effects.transaction_digest())?)
277            } else {
278                None
279            }
280        } else {
281            None
282        };
283
284        if fault_config.fail_after_handle_confirmation {
285            return Err(IotaError::GenericAuthority {
286                error: "Mock error after handle_confirmation_transaction".to_owned(),
287            });
288        }
289
290        let input_objects = request
291            .include_input_objects
292            .then(|| state.get_transaction_input_objects(&signed_effects))
293            .and_then(Result::ok);
294
295        let output_objects = request
296            .include_output_objects
297            .then(|| state.get_transaction_output_objects(&signed_effects))
298            .and_then(Result::ok);
299
300        Ok(HandleCertificateResponseV1 {
301            signed_effects,
302            events,
303            input_objects,
304            output_objects,
305            auxiliary_data: None, // We don't have any aux data generated presently
306        })
307    }
308}
309
310type GetTxStatusResult = IotaResult<Vec<(TransactionDigest, TxStatusUpdate)>>;
311
312#[derive(Clone)]
313pub struct MockAuthorityApi {
314    delay: Duration,
315    count: Arc<Mutex<u32>>,
316    handle_object_info_request_result: Option<IotaResult<ObjectInfoResponse>>,
317    handle_capability_notification_result:
318        Option<IotaResult<HandleCapabilityNotificationResponseV1>>,
319    tx_status_stub: Arc<Mutex<Option<GetTxStatusResult>>>,
320}
321
322impl MockAuthorityApi {
323    pub fn new(delay: Duration, count: Arc<Mutex<u32>>) -> Self {
324        MockAuthorityApi {
325            delay,
326            count,
327            handle_object_info_request_result: None,
328            handle_capability_notification_result: None,
329            tx_status_stub: Arc::new(Mutex::new(None)),
330        }
331    }
332
333    pub fn set_handle_object_info_request(&mut self, result: IotaResult<ObjectInfoResponse>) {
334        self.handle_object_info_request_result = Some(result);
335    }
336
337    pub fn set_handle_capability_notification(
338        &mut self,
339        result: IotaResult<HandleCapabilityNotificationResponseV1>,
340    ) {
341        self.handle_capability_notification_result = Some(result);
342    }
343
344    pub fn stub_tx_status(&self, response: GetTxStatusResult) {
345        *self.tx_status_stub.lock().unwrap() = Some(response);
346    }
347}
348
349#[async_trait]
350impl ValidatorPeerAPI for MockAuthorityApi {
351    async fn get_checkpoint_v2(
352        &self,
353        _request: CheckpointRequest,
354    ) -> Result<CheckpointResponse, IotaError> {
355        unimplemented!()
356    }
357}
358#[async_trait]
359impl ValidatorV2API for MockAuthorityApi {
360    async fn submit_tx(
361        &self,
362        _transactions: Vec<Transaction>,
363        _client_addr: Option<SocketAddr>,
364    ) -> Result<Vec<(TransactionDigest, TxStatusUpdate)>, IotaError> {
365        unimplemented!()
366    }
367    async fn get_tx_status(
368        &self,
369        _request: GetTxStatusRequest,
370        _client_addr: Option<SocketAddr>,
371    ) -> Result<Vec<(TransactionDigest, TxStatusUpdate)>, IotaError> {
372        let Some(result) = self.tx_status_stub.lock().unwrap().clone() else {
373            return Err(IotaError::Unknown(
374                "MockAuthorityApi::get_tx_status was called without a stub".to_string(),
375            ));
376        };
377        tokio::time::sleep(self.delay).await;
378        result
379    }
380    async fn notify_capabilities_v2(
381        &self,
382        _request: HandleCapabilityNotificationRequestV1,
383    ) -> Result<HandleCapabilityNotificationResponseV1, IotaError> {
384        tokio::time::sleep(self.delay).await;
385
386        match &self.handle_capability_notification_result {
387            Some(result) => result.clone(),
388            None => Ok(HandleCapabilityNotificationResponseV1 { _unused: false }),
389        }
390    }
391    async fn health_check(
392        &self,
393        _request: ValidatorHealthRequest,
394    ) -> Result<ValidatorHealthResponse, IotaError> {
395        unimplemented!()
396    }
397}
398#[async_trait]
399impl ValidatorAPI for MockAuthorityApi {
400    /// Initiate a new transaction to an IOTA or Primary account.
401    async fn handle_transaction(
402        &self,
403        _transaction: Transaction,
404        _client_addr: Option<SocketAddr>,
405    ) -> Result<HandleTransactionResponse, IotaError> {
406        unimplemented!();
407    }
408
409    async fn handle_certificate_v1(
410        &self,
411        _request: HandleCertificateRequestV1,
412        _client_addr: Option<SocketAddr>,
413    ) -> Result<HandleCertificateResponseV1, IotaError> {
414        unimplemented!()
415    }
416
417    async fn handle_soft_bundle_certificates_v1(
418        &self,
419        _request: HandleSoftBundleCertificatesRequestV1,
420        _client_addr: Option<SocketAddr>,
421    ) -> Result<HandleSoftBundleCertificatesResponseV1, IotaError> {
422        unimplemented!()
423    }
424
425    /// Handle Object information requests .
426    async fn handle_object_info_request(
427        &self,
428        _request: ObjectInfoRequest,
429    ) -> Result<ObjectInfoResponse, IotaError> {
430        self.handle_object_info_request_result.clone().unwrap()
431    }
432
433    /// Handle Object information requests .
434    async fn handle_transaction_info_request(
435        &self,
436        request: TransactionInfoRequest,
437    ) -> Result<TransactionInfoResponse, IotaError> {
438        let count = {
439            let mut count = self.count.lock().unwrap();
440            *count += 1;
441            *count
442        };
443
444        // timeout until the 15th request
445        if count < 15 {
446            tokio::time::sleep(self.delay).await;
447        }
448
449        Err(IotaError::TransactionNotFound {
450            digest: request.transaction_digest,
451        })
452    }
453
454    async fn handle_checkpoint(
455        &self,
456        _request: CheckpointRequest,
457    ) -> Result<CheckpointResponse, IotaError> {
458        unimplemented!();
459    }
460
461    async fn handle_system_state_object(
462        &self,
463        _request: SystemStateRequest,
464    ) -> Result<IotaSystemState, IotaError> {
465        unimplemented!();
466    }
467
468    async fn handle_capability_notification_v1(
469        &self,
470        _request: HandleCapabilityNotificationRequestV1,
471    ) -> Result<HandleCapabilityNotificationResponseV1, IotaError> {
472        tokio::time::sleep(self.delay).await;
473
474        match &self.handle_capability_notification_result {
475            Some(result) => result.clone(),
476            None => Ok(HandleCapabilityNotificationResponseV1 { _unused: false }),
477        }
478    }
479}
480
481#[derive(Clone)]
482pub struct HandleTransactionTestAuthorityClient {
483    pub tx_info_resp_to_return: IotaResult<HandleTransactionResponse>,
484    pub cert_resp_to_return: IotaResult<HandleCertificateResponseV1>,
485    // If set, sleep for this duration before responding to a request.
486    // This is useful in testing a timeout scenario.
487    pub sleep_duration_before_responding: Option<Duration>,
488}
489
490#[async_trait]
491impl ValidatorPeerAPI for HandleTransactionTestAuthorityClient {
492    async fn get_checkpoint_v2(
493        &self,
494        _request: CheckpointRequest,
495    ) -> Result<CheckpointResponse, IotaError> {
496        unimplemented!()
497    }
498}
499#[async_trait]
500impl ValidatorV2API for HandleTransactionTestAuthorityClient {
501    async fn submit_tx(
502        &self,
503        _transactions: Vec<Transaction>,
504        _client_addr: Option<SocketAddr>,
505    ) -> Result<Vec<(TransactionDigest, TxStatusUpdate)>, IotaError> {
506        unimplemented!()
507    }
508    async fn get_tx_status(
509        &self,
510        _request: GetTxStatusRequest,
511        _client_addr: Option<SocketAddr>,
512    ) -> Result<Vec<(TransactionDigest, TxStatusUpdate)>, IotaError> {
513        unimplemented!()
514    }
515    async fn notify_capabilities_v2(
516        &self,
517        _request: HandleCapabilityNotificationRequestV1,
518    ) -> Result<HandleCapabilityNotificationResponseV1, IotaError> {
519        unimplemented!()
520    }
521    async fn health_check(
522        &self,
523        _request: ValidatorHealthRequest,
524    ) -> Result<ValidatorHealthResponse, IotaError> {
525        unimplemented!()
526    }
527}
528
529#[async_trait]
530impl ValidatorAPI for HandleTransactionTestAuthorityClient {
531    async fn handle_transaction(
532        &self,
533        _transaction: Transaction,
534        _client_addr: Option<SocketAddr>,
535    ) -> Result<HandleTransactionResponse, IotaError> {
536        if let Some(duration) = self.sleep_duration_before_responding {
537            tokio::time::sleep(duration).await;
538        }
539        self.tx_info_resp_to_return.clone()
540    }
541
542    async fn handle_certificate_v1(
543        &self,
544        _request: HandleCertificateRequestV1,
545        _client_addr: Option<SocketAddr>,
546    ) -> Result<HandleCertificateResponseV1, IotaError> {
547        if let Some(duration) = self.sleep_duration_before_responding {
548            tokio::time::sleep(duration).await;
549        }
550        self.cert_resp_to_return.clone()
551    }
552
553    async fn handle_soft_bundle_certificates_v1(
554        &self,
555        _request: HandleSoftBundleCertificatesRequestV1,
556        _client_addr: Option<SocketAddr>,
557    ) -> Result<HandleSoftBundleCertificatesResponseV1, IotaError> {
558        unimplemented!()
559    }
560
561    async fn handle_object_info_request(
562        &self,
563        _request: ObjectInfoRequest,
564    ) -> Result<ObjectInfoResponse, IotaError> {
565        unimplemented!()
566    }
567
568    async fn handle_transaction_info_request(
569        &self,
570        _request: TransactionInfoRequest,
571    ) -> Result<TransactionInfoResponse, IotaError> {
572        unimplemented!()
573    }
574
575    async fn handle_checkpoint(
576        &self,
577        _request: CheckpointRequest,
578    ) -> Result<CheckpointResponse, IotaError> {
579        unimplemented!()
580    }
581
582    async fn handle_system_state_object(
583        &self,
584        _request: SystemStateRequest,
585    ) -> Result<IotaSystemState, IotaError> {
586        unimplemented!()
587    }
588
589    async fn handle_capability_notification_v1(
590        &self,
591        _request: HandleCapabilityNotificationRequestV1,
592    ) -> Result<HandleCapabilityNotificationResponseV1, IotaError> {
593        unimplemented!()
594    }
595}
596
597impl HandleTransactionTestAuthorityClient {
598    pub fn new() -> Self {
599        Self {
600            tx_info_resp_to_return: Err(IotaError::Unknown("".to_string())),
601            cert_resp_to_return: Err(IotaError::Unknown("".to_string())),
602            sleep_duration_before_responding: None,
603        }
604    }
605
606    pub fn set_tx_info_response(&mut self, resp: HandleTransactionResponse) {
607        self.tx_info_resp_to_return = Ok(resp);
608    }
609
610    pub fn set_tx_info_response_error(&mut self, error: IotaError) {
611        self.tx_info_resp_to_return = Err(error);
612    }
613
614    pub fn reset_tx_info_response(&mut self) {
615        self.tx_info_resp_to_return = Err(IotaError::Unknown("".to_string()));
616    }
617
618    pub fn set_cert_resp_to_return(&mut self, resp: HandleCertificateResponseV1) {
619        self.cert_resp_to_return = Ok(resp);
620    }
621
622    pub fn set_cert_resp_to_return_error(&mut self, error: IotaError) {
623        self.cert_resp_to_return = Err(error);
624    }
625
626    pub fn reset_cert_response(&mut self) {
627        self.cert_resp_to_return = Err(IotaError::Unknown("".to_string()));
628    }
629
630    pub fn set_sleep_duration_before_responding(&mut self, duration: Duration) {
631        self.sleep_duration_before_responding = Some(duration);
632    }
633}
634
635impl Default for HandleTransactionTestAuthorityClient {
636    fn default() -> Self {
637        Self::new()
638    }
639}