meta_module_tests/
meta-module-tests.rs

1use std::future::Future;
2
3use anyhow::{Result, bail};
4use clap::Parser;
5use devimint::cmd;
6use devimint::federation::Client;
7use devimint::util::poll_simple;
8use fedimint_core::PeerId;
9use serde_json::json;
10use tracing::{info, warn};
11
12#[derive(Parser, Debug)]
13pub enum TestCli {
14    Sanity,
15}
16
17#[tokio::main]
18async fn main() -> anyhow::Result<()> {
19    let opts = TestCli::parse();
20
21    match opts {
22        TestCli::Sanity => sanity_tests().await,
23    }
24}
25
26async fn sanity_tests() -> anyhow::Result<()> {
27    devimint::run_devfed_test()
28        .call(|dev_fed, _process_mgr| async move {
29            let client = dev_fed
30                .fed()
31                .await?
32                .new_joined_client("meta-module-client")
33                .await?;
34
35            async fn get_consensus(client: &Client) -> anyhow::Result<serde_json::Value> {
36                cmd!(client, "module", "meta", "get").out_json().await
37            }
38
39            async fn get_submissions(
40                client: &Client,
41                peer_id: PeerId,
42            ) -> anyhow::Result<serde_json::Value> {
43                cmd!(
44                    client,
45                    "--our-id",
46                    &peer_id.to_string(),
47                    "--password",
48                    "notset",
49                    "module",
50                    "meta",
51                    "get-submissions"
52                )
53                .out_json()
54                .await
55            }
56
57            async fn submit(
58                client: &Client,
59                peer_id: PeerId,
60                value: &serde_json::Value,
61            ) -> anyhow::Result<serde_json::Value> {
62                info!(%peer_id, ?value, "Peer submitting value");
63
64                cmd!(
65                    client,
66                    "--our-id",
67                    &peer_id.to_string(),
68                    "--password",
69                    "notset",
70                    "module",
71                    "meta",
72                    "submit",
73                    &value.to_string(),
74                )
75                .out_json()
76                .await
77            }
78
79            pub async fn poll_value<Fut>(
80                name: &str,
81                f: impl Fn() -> Fut,
82                expected_value: serde_json::Value,
83            ) -> Result<serde_json::Value>
84            where
85                Fut: Future<Output = Result<serde_json::Value, anyhow::Error>>,
86            {
87                poll_simple(name, || async {
88                    let value = f().await?;
89                    if value == expected_value {
90                        Ok(value)
91                    } else {
92                        bail!("Incorrect value: {}, expected: {}", value, expected_value);
93                    }
94                })
95                .await
96            }
97
98            async fn get_meta_fields(client: &Client) -> anyhow::Result<serde_json::Value> {
99                cmd!(client, "dev", "meta-fields",).out_json().await
100            }
101
102            // check starting conditions
103            assert_eq!(get_consensus(&client).await?, serde_json::Value::Null);
104            assert_eq!(
105                get_submissions(&client, PeerId::from(1)).await?,
106                json! {
107                    {}
108                }
109            );
110
111            let submission_value = json! {
112                { "foo": "bar" }
113            };
114            let minority_submission_value = json! {
115                { "bar": "baz" }
116            };
117
118            // check submissions visible
119            submit(&client, PeerId::from(1), &submission_value).await?;
120
121            info!("Checking submission");
122            poll_value(
123                "submission visible on same peer",
124                || async { get_submissions(&client, PeerId::from(1)).await },
125                json! {
126                    {  "1": submission_value }
127                },
128            )
129            .await?;
130            poll_value(
131                "submission visible on a different peer",
132                || async { get_submissions(&client, PeerId::from(3)).await },
133                json! {
134                    {  "1": submission_value }
135                },
136            )
137            .await?;
138
139            // form a consensus with a minority vote
140            submit(&client, PeerId::from(0), &minority_submission_value).await?;
141            submit(&client, PeerId::from(2), &submission_value).await?;
142            assert_eq!(
143                submit(&client, PeerId::from(3), &submission_value).await?,
144                serde_json::Value::Bool(true)
145            );
146
147            info!(expected = %submission_value, "Checking consensus");
148            if let Err(e) = poll_value(
149                "consensus set",
150                || async { get_consensus(&client).await },
151                json! {
152                    {
153                        "revision": 0,
154                        "value":submission_value
155                    }
156                },
157            )
158            .await
159            {
160                let submissions = get_submissions(&client, PeerId::from(3)).await?;
161                warn!(%submissions, "Getting expected consensus value failed");
162                return Err(e);
163            }
164
165            // minority vote should be still visible
166            poll_value(
167                "minor submission visible",
168                || async { get_submissions(&client, PeerId::from(0)).await },
169                json! {
170                    {  "0": minority_submission_value}
171                },
172            )
173            .await?;
174
175            // If the peer with outstanding vote votes for the consensu value,
176            // their submission will clear.
177            submit(&client, PeerId::from(0), &submission_value).await?;
178            poll_value(
179                "submission cleared",
180                || async { get_submissions(&client, PeerId::from(1)).await },
181                json! { {} },
182            )
183            .await?;
184
185            let meta_fields = get_meta_fields(&client).await?;
186            assert_eq!(
187                meta_fields,
188                json! {
189                    {
190                        "revision": 0,
191                        "values": submission_value,
192                    }
193                }
194            );
195
196            Ok(())
197        })
198        .await
199}