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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use crate::rewards::VoteCount;
use crate::stats::distribution::Stats;
use chain_addr::{Discrimination, Kind};
use chain_crypto::Ed25519;
use chain_impl_mockchain::vote::CommitteeId;
use jormungandr_automation::testing::block0::get_block;
use jormungandr_lib::{
    crypto::account::Identifier,
    interfaces::{Address, Block0Configuration, Initial, InitialUTxO},
};
use std::path::Path;

fn blacklist_addresses(genesis: &Block0Configuration) -> Vec<Address> {
    let discrimination = genesis.blockchain_configuration.discrimination;

    genesis
        .blockchain_configuration
        .committees
        .iter()
        .cloned()
        .map(|x| {
            let committee_id: CommitteeId = x.into();
            let public: chain_crypto::PublicKey<Ed25519> =
                chain_crypto::PublicKey::from_binary(committee_id.as_ref()).unwrap();

            Address(
                if discrimination == Discrimination::Production {
                    "ca".to_string()
                } else {
                    "ta".to_string()
                },
                chain_addr::Address(discrimination, Kind::Account(public)),
            )
        })
        .collect()
}

fn vote_counts_as_utxo(
    votes_count: VoteCount,
    genesis: &Block0Configuration,
) -> Vec<(InitialUTxO, u32)> {
    genesis
        .initial
        .iter()
        .filter_map(|initials| {
            if let Initial::Fund(funds) = initials {
                for utxo in funds {
                    if let Some(hash_set) = votes_count.get(&addr_to_id(&utxo.address)) {
                        return Some((utxo.clone(), hash_set.len() as u32));
                    }
                }
            }
            None
        })
        .collect()
}

pub fn addr_to_id(address: &Address) -> Identifier {
    match &address.1 .1 {
        Kind::Account(pk) => pk.clone().into(),
        _ => unimplemented!(),
    }
}

pub fn calculate_active_wallet_distribution<S: Into<String>, P: AsRef<Path>>(
    stats: Stats,
    block0: S,
    votes_count_path: P,
    support_lovelace: bool,
    update_fn: impl Fn(&mut Stats, u64, u64),
) -> Result<Stats, crate::stats::Error> {
    let block0 = block0.into();
    let genesis = get_block(block0)?;

    let vote_count: VoteCount = serde_json::from_reader(jcli_lib::utils::io::open_file_read(
        &Some(votes_count_path.as_ref()),
    )?)?;

    let blacklist = blacklist_addresses(&genesis);
    let initials = vote_counts_as_utxo(vote_count, &genesis);

    calculate_wallet_distribution_from_initials_utxo(
        stats,
        initials,
        blacklist,
        support_lovelace,
        update_fn,
    )
}

pub fn calculate_wallet_distribution<S: Into<String>>(
    block0: S,
    stats: Stats,
    support_lovelace: bool,
    update_fn: impl Fn(&mut Stats, u64, u64),
) -> Result<Stats, crate::stats::Error> {
    let block0 = block0.into();
    let genesis = get_block(block0)?;
    let blacklist = blacklist_addresses(&genesis);

    calculate_wallet_distribution_from_initials(
        stats,
        genesis.initial,
        blacklist,
        support_lovelace,
        update_fn,
    )
}

pub fn calculate_wallet_distribution_from_initials(
    stats: Stats,
    initials: Vec<Initial>,
    blacklist: Vec<Address>,
    support_lovelace: bool,
    update_fn: impl Fn(&mut Stats, u64, u64),
) -> Result<Stats, crate::stats::Error> {
    let mut utxos = vec![];
    for initial in initials.into_iter() {
        if let Initial::Fund(initial_utxos) = initial {
            for x in initial_utxos.into_iter() {
                utxos.push(x)
            }
        }
    }

    calculate_wallet_distribution_from_initials_utxo(
        stats,
        utxos.iter().cloned().map(|x| (x, 1u32)).collect(),
        blacklist,
        support_lovelace,
        update_fn,
    )
}

pub fn calculate_wallet_distribution_from_initials_utxo(
    mut stats: Stats,
    initials: Vec<(InitialUTxO, u32)>,
    blacklist: Vec<Address>,
    support_lovelace: bool,
    update_fn: impl Fn(&mut Stats, u64, u64),
) -> Result<Stats, crate::stats::Error> {
    for (x, weight) in initials {
        if !blacklist.contains(&x.address) {
            let mut value: u64 = x.value.into();
            if support_lovelace {
                value /= 1_000_000;
            }
            update_fn(&mut stats, value, weight.into());
        }
    }
    Ok(stats)
}