iota_bridge/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5pub mod abi;
6pub mod action_executor;
7pub mod client;
8pub mod config;
9pub mod crypto;
10pub mod encoding;
11pub mod error;
12pub mod eth_client;
13pub mod eth_syncer;
14pub mod eth_transaction_builder;
15pub mod events;
16pub mod iota_client;
17pub mod iota_syncer;
18pub mod iota_transaction_builder;
19pub mod metered_eth_provider;
20pub mod metrics;
21pub mod monitor;
22pub mod node;
23pub mod orchestrator;
24pub mod server;
25pub mod storage;
26pub mod types;
27pub mod utils;
28
29#[cfg(test)]
30pub(crate) mod eth_mock_provider;
31
32#[cfg(test)]
33pub(crate) mod iota_mock_client;
34
35#[cfg(test)]
36pub mod test_utils;
37
38pub const BRIDGE_ENABLE_PROTOCOL_VERSION: u64 = 1;
39
40#[cfg(test)]
41pub mod e2e_tests;
42
43#[macro_export]
44macro_rules! retry_with_max_elapsed_time {
45    ($func:expr, $max_elapsed_time:expr) => {{
46        // The following delay sequence (in secs) will be used, applied with jitter
47        // 0.4, 0.8, 1.6, 3.2, 6.4, 12.8, 25.6, 30, 60, 120, 120 ...
48        let backoff = backoff::ExponentialBackoff {
49            initial_interval: Duration::from_millis(400),
50            randomization_factor: 0.1,
51            multiplier: 2.0,
52            max_interval: Duration::from_secs(120),
53            max_elapsed_time: Some($max_elapsed_time),
54            ..Default::default()
55        };
56        backoff::future::retry(backoff, || {
57            let fut = async {
58                let result = $func.await;
59                match result {
60                    Ok(_) => {
61                        return Ok(result);
62                    }
63                    Err(e) => {
64                        // For simplicity we treat every error as transient so we can retry until
65                        // max_elapsed_time
66                        tracing::debug!("Retrying due to error: {:?}", e);
67                        return Err(backoff::Error::transient(e));
68                    }
69                }
70            };
71            std::boxed::Box::pin(fut)
72        })
73        .await
74    }};
75}
76
77#[cfg(test)]
78mod tests {
79    use std::time::Duration;
80
81    use super::*;
82
83    async fn example_func_ok() -> anyhow::Result<()> {
84        Ok(())
85    }
86
87    async fn example_func_err() -> anyhow::Result<()> {
88        tracing::info!("example_func_err");
89        Err(anyhow::anyhow!(""))
90    }
91
92    #[tokio::test]
93    #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
94    async fn test_retry_with_max_elapsed_time() {
95        telemetry_subscribers::init_for_testing();
96        // no retry is needed, should return immediately. We give it a very small
97        // max_elapsed_time and it should still finish in time.
98        let max_elapsed_time = Duration::from_millis(20);
99        retry_with_max_elapsed_time!(example_func_ok(), max_elapsed_time)
100            .unwrap()
101            .unwrap();
102
103        // now call a function that always errors and expect it to return before
104        // max_elapsed_time runs out
105        let max_elapsed_time = Duration::from_secs(10);
106        let instant = std::time::Instant::now();
107        retry_with_max_elapsed_time!(example_func_err(), max_elapsed_time).unwrap_err();
108        assert!(instant.elapsed() < max_elapsed_time);
109    }
110}