1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
use std::sync::OnceLock;

/// Decomposed amount table.
///
static DECOMPOSED_AMOUNTS: OnceLock<[u64; 172]> = OnceLock::new();

#[rustfmt::skip]
pub fn decomposed_amounts() -> &'static [u64; 172] {
    DECOMPOSED_AMOUNTS.get_or_init(|| {
        [
        1,                   2,                   3,                   4,                   5,                   6,                   7,                   8,                   9,
        10,                  20,                  30,                  40,                  50,                  60,                  70,                  80,                  90,
        100,                 200,                 300,                 400,                 500,                 600,                 700,                 800,                 900,
        1000,                2000,                3000,                4000,                5000,                6000,                7000,                8000,                9000,
        10000,               20000,               30000,               40000,               50000,               60000,               70000,               80000,               90000,
        100000,              200000,              300000,              400000,              500000,              600000,              700000,              800000,              900000,
        1000000,             2000000,             3000000,             4000000,             5000000,             6000000,             7000000,             8000000,             9000000,
        10000000,            20000000,            30000000,            40000000,            50000000,            60000000,            70000000,            80000000,            90000000,
        100000000,           200000000,           300000000,           400000000,           500000000,           600000000,           700000000,           800000000,           900000000,
        1000000000,          2000000000,          3000000000,          4000000000,          5000000000,          6000000000,          7000000000,          8000000000,          9000000000,
        10000000000,         20000000000,         30000000000,         40000000000,         50000000000,         60000000000,         70000000000,         80000000000,         90000000000,
        100000000000,        200000000000,        300000000000,        400000000000,        500000000000,        600000000000,        700000000000,        800000000000,        900000000000,
        1000000000000,       2000000000000,       3000000000000,       4000000000000,       5000000000000,       6000000000000,       7000000000000,       8000000000000,       9000000000000,
        10000000000000,      20000000000000,      30000000000000,      40000000000000,      50000000000000,      60000000000000,      70000000000000,      80000000000000,      90000000000000,
        100000000000000,     200000000000000,     300000000000000,     400000000000000,     500000000000000,     600000000000000,     700000000000000,     800000000000000,     900000000000000,
        1000000000000000,    2000000000000000,    3000000000000000,    4000000000000000,    5000000000000000,    6000000000000000,    7000000000000000,    8000000000000000,    9000000000000000,
        10000000000000000,   20000000000000000,   30000000000000000,   40000000000000000,   50000000000000000,   60000000000000000,   70000000000000000,   80000000000000000,   90000000000000000,
        100000000000000000,  200000000000000000,  300000000000000000,  400000000000000000,  500000000000000000,  600000000000000000,  700000000000000000,  800000000000000000,  900000000000000000,
        1000000000000000000, 2000000000000000000, 3000000000000000000, 4000000000000000000, 5000000000000000000, 6000000000000000000, 7000000000000000000, 8000000000000000000, 9000000000000000000,
        10000000000000000000
        ]
    })
}

/// Checks that an output amount is decomposed.
///
/// This is also used during miner tx verification.
///
/// ref: <https://monero-book.cuprate.org/consensus_rules/transactions/ring_signatures.html#output-amount>
/// ref: <https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#output-amounts>
#[inline]
pub fn is_decomposed_amount(amount: &u64) -> bool {
    decomposed_amounts().binary_search(amount).is_ok()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn decomposed_amounts_return_decomposed() {
        for amount in decomposed_amounts() {
            assert!(is_decomposed_amount(amount))
        }
    }

    #[test]
    fn non_decomposed_amounts_return_not_decomposed() {
        assert!(!is_decomposed_amount(&21));
        assert!(!is_decomposed_amount(&345431));
        assert!(!is_decomposed_amount(&20000001));
    }
}