Skip to main content

linera_service/cli_wrappers/
remote_net.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{env, sync::Arc};
5
6use anyhow::Result;
7use async_trait::async_trait;
8use linera_base::data_types::Amount;
9use linera_persistent::{self as persistent, Persist};
10use tempfile::{tempdir, TempDir};
11
12use super::{
13    local_net::PathProvider, ClientWrapper, Faucet, LineraNet, LineraNetConfig, Network,
14    OnClientDrop,
15};
16
17/// Configuration for connecting to an external (remote) Linera network in tests.
18pub struct RemoteNetTestingConfig {
19    faucet: Faucet,
20    close_chains: OnClientDrop,
21}
22
23impl RemoteNetTestingConfig {
24    /// Creates a new [`RemoteNetTestingConfig`] for running tests with an external Linera
25    /// network.
26    ///
27    /// The faucet URL is obtained from the `LINERA_FAUCET_URL` environment variable.
28    /// If `close_chains` is true, chains will be closed on drop, otherwise they will be left active.
29    pub fn new(close_chains: OnClientDrop) -> Self {
30        Self {
31            faucet: Faucet::new(
32                env::var("LINERA_FAUCET_URL")
33                    .expect("Missing `LINERA_FAUCET_URL` environment variable"),
34            ),
35            close_chains,
36        }
37    }
38}
39
40#[async_trait]
41impl LineraNetConfig for RemoteNetTestingConfig {
42    type Net = RemoteNet;
43
44    async fn instantiate(self) -> Result<(Self::Net, ClientWrapper)> {
45        let mut net = RemoteNet::new(None, &self.faucet, self.close_chains)
46            .await
47            .expect("Creating RemoteNet should not fail");
48
49        let client = net.make_client().await;
50        // The tests assume we've created a genesis config with 2
51        // chains with 10 tokens each. We create the first chain here
52        client.wallet_init(Some(&self.faucet)).await?;
53        client.request_chain(&self.faucet, true).await?;
54
55        // And the remaining 2 here
56        for _ in 0..2 {
57            client
58                .open_and_assign(&client, Amount::from_tokens(100))
59                .await
60                .unwrap();
61        }
62
63        Ok((net, client))
64    }
65}
66
67/// Remote net
68#[derive(Clone)]
69pub struct RemoteNet {
70    network: Network,
71    testing_prng_seed: Option<u64>,
72    next_client_id: usize,
73    tmp_dir: Arc<TempDir>,
74    close_chains: OnClientDrop,
75}
76
77#[async_trait]
78impl LineraNet for RemoteNet {
79    async fn ensure_is_running(&mut self) -> Result<()> {
80        // Leaving this just returning for now.
81        // We would have to connect to each validator in the remote net then run
82        // ensure_connected_cluster_is_running
83        Ok(())
84    }
85
86    async fn make_client(&mut self) -> ClientWrapper {
87        let path_provider = PathProvider::TemporaryDirectory {
88            tmp_dir: self.tmp_dir.clone(),
89        };
90        let client = ClientWrapper::new(
91            path_provider,
92            self.network,
93            self.testing_prng_seed,
94            self.next_client_id,
95            self.close_chains,
96        );
97        if let Some(seed) = self.testing_prng_seed {
98            self.testing_prng_seed = Some(seed + 1);
99        }
100        self.next_client_id += 1;
101        client
102    }
103
104    async fn terminate(&mut self) -> Result<()> {
105        // We're not killing the remote net :)
106        Ok(())
107    }
108}
109
110impl RemoteNet {
111    async fn new(
112        testing_prng_seed: Option<u64>,
113        faucet: &Faucet,
114        close_chains: OnClientDrop,
115    ) -> Result<Self> {
116        let tmp_dir = Arc::new(tempdir()?);
117        // Write json config to disk
118        persistent::File::new(
119            tmp_dir.path().join("genesis.json").as_path(),
120            faucet.genesis_config().await?,
121        )?
122        .persist()
123        .await?;
124        Ok(Self {
125            network: Network::Grpc,
126            testing_prng_seed,
127            next_client_id: 0,
128            tmp_dir,
129            close_chains,
130        })
131    }
132}