mint_module_tests/
mint-module-tests.rs

1use anyhow::Result;
2use clap::Parser;
3use devimint::cmd;
4use devimint::federation::Federation;
5use fedimint_logging::LOG_DEVIMINT;
6use rand::Rng;
7use tracing::info;
8
9#[derive(Debug, Parser)]
10enum Cmd {
11    Restore,
12    Sanity,
13}
14
15#[tokio::main]
16async fn main() -> anyhow::Result<()> {
17    match Cmd::parse() {
18        Cmd::Restore => restore().await,
19        Cmd::Sanity => sanity().await,
20    }
21}
22
23async fn restore() -> anyhow::Result<()> {
24    devimint::run_devfed_test()
25        .call(|fed, _process_mgr| async move {
26            let fed = fed.fed().await?;
27
28            test_restore_gap_test(fed).await?;
29            Ok(())
30        })
31        .await
32}
33
34pub async fn test_restore_gap_test(fed: &Federation) -> Result<()> {
35    let client = fed.new_joined_client("restore-gap-test").await?;
36    const PEGIN_SATS: u64 = 300000;
37    fed.pegin_client(PEGIN_SATS, &client).await?;
38
39    for i in 0..20 {
40        let gap = rand::thread_rng().gen_range(0..20);
41        info!(target: LOG_DEVIMINT, gap, "Gap");
42        cmd!(
43            client,
44            "dev",
45            "advance-note-idx",
46            "--amount",
47            "1024msat",
48            "--count",
49            // we are not guaranteed to use a 1024 note on every payment,
50            // so create some random small gaps, so it's very unlikely we
51            // would cross the default gap limit accidentally
52            &gap.to_string()
53        )
54        .run()
55        .await?;
56
57        // We need to get the balance of the client to know how much to reissue, due to
58        // the mint base fees it decreases slightly every time we reissue.
59        let notes = cmd!(client, "info").out_json().await?;
60        let balance = notes["total_amount_msat"].as_u64().unwrap();
61
62        let reissure_amount = if i % 2 == 0 {
63            // half of the time, reissue everything
64            balance
65        } else {
66            // other half, random amount
67            rand::thread_rng().gen_range(10..(balance))
68        };
69        info!(target: LOG_DEVIMINT, i, reissure_amount, "Reissue");
70
71        let notes = cmd!(client, "spend", reissure_amount)
72            .out_json()
73            .await?
74            .get("notes")
75            .expect("Output didn't contain e-cash notes")
76            .as_str()
77            .unwrap()
78            .to_owned();
79
80        // Test we can reissue our own notes
81        cmd!(client, "reissue", notes).out_json().await?;
82    }
83
84    let secret = cmd!(client, "print-secret").out_json().await?["secret"]
85        .as_str()
86        .map(ToOwned::to_owned)
87        .unwrap();
88
89    let pre_notes = cmd!(client, "info").out_json().await?;
90
91    let pre_balance = pre_notes["total_amount_msat"].as_u64().unwrap();
92
93    info!(target: LOG_DEVIMINT, %pre_notes, pre_balance, "State before backup");
94
95    // we need to have some funds
96    assert!(0 < pre_balance);
97
98    // without existing backup
99    {
100        let client =
101            devimint::federation::Client::create("restore-gap-test-without-backup").await?;
102        let _ = cmd!(
103            client,
104            "restore",
105            "--mnemonic",
106            &secret,
107            "--invite-code",
108            fed.invite_code()?
109        )
110        .out_json()
111        .await?;
112
113        let _ = cmd!(client, "dev", "wait-complete").out_json().await?;
114        let post_notes = cmd!(client, "info").out_json().await?;
115        let post_balance = post_notes["total_amount_msat"].as_u64().unwrap();
116        info!(target: LOG_DEVIMINT, %post_notes, post_balance, "State after backup");
117        assert_eq!(pre_balance, post_balance);
118        assert_eq!(pre_notes, post_notes);
119    }
120
121    Ok(())
122}
123
124async fn sanity() -> anyhow::Result<()> {
125    devimint::run_devfed_test()
126        .call(|_fed, _process_mgr| async move { Ok(()) })
127        .await
128}