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