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