Skip to content

Commit ffe94ed

Browse files
authored
Merge pull request #133 from input-output-hk/add_active_voter_stats_from_vote_count
Add active voter stats from vote count
2 parents 5748d63 + c0cf9f4 commit ffe94ed

File tree

11 files changed

+292
-51
lines changed

11 files changed

+292
-51
lines changed

catalyst-toolbox/src/bin/cli/rewards/voters.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ impl VotersRewards {
9090
entry.sort_by_key(|p| p.voteplan.chain_proposal_index);
9191
acc
9292
});
93-
9493
let vote_count = serde_json::from_reader::<_, HashMap<Identifier, Vec<AccountVotes>>>(
9594
jcli_lib::utils::io::open_file_read(&Some(votes_count_path))?,
9695
)?

catalyst-toolbox/src/bin/cli/stats/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ use color_eyre::Report;
88
use live::LiveStatsCommand;
99
use snapshot::SnapshotCommand;
1010
use structopt::StructOpt;
11-
use voters::InitialVotersCommand;
11+
use voters::VotersCommand;
1212

1313
#[derive(StructOpt, Debug)]
1414
pub enum Stats {
15-
Voters(InitialVotersCommand),
15+
Voters(VotersCommand),
1616
Live(LiveStatsCommand),
1717
Archive(ArchiveCommand),
1818
Snapshot(SnapshotCommand),

catalyst-toolbox/src/bin/cli/stats/snapshot.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use catalyst_toolbox::stats::distribution::Stats;
12
use catalyst_toolbox::stats::snapshot::read_initials;
23
use catalyst_toolbox::stats::voters::calculate_wallet_distribution_from_initials;
34
use color_eyre::Report;
@@ -28,17 +29,19 @@ impl SnapshotCommand {
2829

2930
match self.command {
3031
Command::Count => calculate_wallet_distribution_from_initials(
32+
Stats::new(self.threshold)?,
3133
initials,
3234
vec![],
33-
self.threshold,
3435
self.support_lovelace,
36+
|stats, _, _| stats.add(1),
3537
)?
3638
.print_count_per_level(),
3739
Command::Ada => calculate_wallet_distribution_from_initials(
40+
Stats::new(self.threshold)?,
3841
initials,
3942
vec![],
40-
self.threshold,
4143
self.support_lovelace,
44+
|stats, value, _| stats.add(value),
4245
)?
4346
.print_ada_per_level(),
4447
};
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use catalyst_toolbox::stats::distribution::Stats;
2+
use catalyst_toolbox::stats::voters::calculate_active_wallet_distribution;
3+
use color_eyre::Report;
4+
use std::ops::Range;
5+
use std::path::PathBuf;
6+
use structopt::StructOpt;
7+
use thiserror::Error;
8+
9+
#[derive(StructOpt, Debug)]
10+
pub struct ActiveVotersCommand {
11+
#[structopt(long = "support-lovelace")]
12+
pub support_lovelace: bool,
13+
#[structopt(long = "block0")]
14+
pub block0: String,
15+
#[structopt(long = "threshold")]
16+
pub threshold: u64,
17+
#[structopt(long = "votes-count-file")]
18+
pub votes_count_path: PathBuf,
19+
#[structopt(long = "votes-count-levels")]
20+
pub votes_count_levels: Option<PathBuf>,
21+
#[structopt(subcommand)]
22+
pub command: Command,
23+
}
24+
25+
#[derive(StructOpt, Debug)]
26+
pub enum Command {
27+
Count,
28+
Ada,
29+
Votes,
30+
}
31+
32+
impl ActiveVotersCommand {
33+
pub fn exec(&self) -> Result<(), Report> {
34+
match self.command {
35+
Command::Count => calculate_active_wallet_distribution(
36+
Stats::new(self.threshold)?,
37+
&self.block0,
38+
&self.votes_count_path,
39+
self.support_lovelace,
40+
|stats, value, _| stats.add(value),
41+
)?
42+
.print_count_per_level(),
43+
Command::Ada => calculate_active_wallet_distribution(
44+
Stats::new(self.threshold)?,
45+
&self.block0,
46+
&self.votes_count_path,
47+
self.support_lovelace,
48+
|stats, value, _| stats.add(value),
49+
)?
50+
.print_ada_per_level(),
51+
Command::Votes => calculate_active_wallet_distribution(
52+
Stats::new_with_levels(get_casted_votes_levels(&self.votes_count_levels)?),
53+
&self.block0,
54+
&self.votes_count_path,
55+
self.support_lovelace,
56+
|stats, _, weight| stats.add_with_weight(1, weight as u32),
57+
)?
58+
.print_count_per_level(),
59+
};
60+
61+
Ok(())
62+
}
63+
}
64+
65+
fn get_casted_votes_levels(path: &Option<PathBuf>) -> Result<Vec<Range<u64>>, Error> {
66+
if let Some(path) = path {
67+
serde_json::from_reader(jcli_lib::utils::io::open_file_read(&Some(path))?)
68+
.map_err(Into::into)
69+
} else {
70+
Ok(default_casted_votes_levels())
71+
}
72+
}
73+
74+
fn default_casted_votes_levels() -> Vec<Range<u64>> {
75+
vec![
76+
(1..5),
77+
(5..10),
78+
(10..20),
79+
(20..50),
80+
(50..100),
81+
(100..200),
82+
(200..400),
83+
(400..800),
84+
(800..5_000),
85+
]
86+
}
87+
88+
#[allow(clippy::large_enum_variant)]
89+
#[derive(Error, Debug)]
90+
pub enum Error {
91+
#[error(transparent)]
92+
Io(#[from] std::io::Error),
93+
#[error(transparent)]
94+
Serde(#[from] serde_json::Error),
95+
}

catalyst-toolbox/src/bin/cli/stats/voters.rs renamed to catalyst-toolbox/src/bin/cli/stats/voters/initials.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use catalyst_toolbox::stats::distribution::Stats;
12
use catalyst_toolbox::stats::voters::calculate_wallet_distribution;
23
use color_eyre::Report;
34
use structopt::StructOpt;
@@ -23,14 +24,20 @@ pub enum Command {
2324
impl InitialVotersCommand {
2425
pub fn exec(&self) -> Result<(), Report> {
2526
match self.command {
26-
Command::Count => {
27-
calculate_wallet_distribution(&self.block0, self.threshold, self.support_lovelace)?
28-
.print_count_per_level()
29-
}
30-
Command::Ada => {
31-
calculate_wallet_distribution(&self.block0, self.threshold, self.support_lovelace)?
32-
.print_ada_per_level()
33-
}
27+
Command::Count => calculate_wallet_distribution(
28+
&self.block0,
29+
Stats::new(self.threshold)?,
30+
self.support_lovelace,
31+
|stats, value, _| stats.add(value),
32+
)?
33+
.print_count_per_level(),
34+
Command::Ada => calculate_wallet_distribution(
35+
&self.block0,
36+
Stats::new(self.threshold)?,
37+
self.support_lovelace,
38+
|stats, value, _| stats.add(value),
39+
)?
40+
.print_ada_per_level(),
3441
};
3542

3643
Ok(())
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
mod active;
2+
mod initials;
3+
4+
use active::ActiveVotersCommand;
5+
use color_eyre::Report;
6+
use initials::InitialVotersCommand;
7+
use structopt::StructOpt;
8+
9+
#[derive(StructOpt, Debug)]
10+
pub enum VotersCommand {
11+
Initials(InitialVotersCommand),
12+
Active(ActiveVotersCommand),
13+
}
14+
15+
impl VotersCommand {
16+
pub fn exec(self) -> Result<(), Report> {
17+
match self {
18+
Self::Initials(initials) => initials.exec(),
19+
Self::Active(active) => active.exec(),
20+
}
21+
}
22+
}

catalyst-toolbox/src/recovery/tally.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use chain_addr::{Discrimination, Kind};
22
use chain_core::property::Fragment as _;
33
use chain_crypto::{Ed25519Extended, SecretKey};
4-
use chain_impl_mockchain::accounting::account::SpendingCounterIncreasing;
54
use chain_impl_mockchain::{
65
account::{self, LedgerError, SpendingCounter},
6+
accounting::account::SpendingCounterIncreasing,
77
block::{Block, BlockDate, HeaderId},
88
certificate::{self, VoteCast, VotePlan, VotePlanId},
99
chaineval::ConsensusEvalContext,
@@ -468,7 +468,7 @@ impl FragmentReplayer {
468468
utxo.value.into(),
469469
SpendingCounterIncreasing::default().get_valid_counters(),
470470
)
471-
.unwrap();
471+
.expect("cannot update wallet state");
472472
wallets.insert(utxo.address.clone(), wallet);
473473
if committee_members.contains(&utxo.address) {
474474
trace!("Committee account found {}", &utxo.address);

catalyst-toolbox/src/rewards/voters.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
use jormungandr_lib::crypto::{account::Identifier, hash::Hash};
1+
use chain_addr::{Discrimination, Kind};
2+
use chain_impl_mockchain::transaction::UnspecifiedAccountIdentifier;
3+
use jormungandr_lib::{
4+
crypto::{account::Identifier, hash::Hash},
5+
interfaces::Address,
6+
};
27
use rust_decimal::Decimal;
38
use snapshot_lib::{registration::MainnetRewardAddress, SnapshotInfo};
49
use std::collections::{BTreeMap, HashMap, HashSet};
@@ -108,6 +113,24 @@ fn filter_active_addresses(
108113
.collect()
109114
}
110115

116+
pub fn account_hex_to_address(
117+
account_hex: String,
118+
discrimination: Discrimination,
119+
) -> Result<Address, hex::FromHexError> {
120+
let mut buffer = [0u8; 32];
121+
hex::decode_to_slice(account_hex, &mut buffer)?;
122+
let identifier: UnspecifiedAccountIdentifier = UnspecifiedAccountIdentifier::from(buffer);
123+
Ok(Address::from(chain_addr::Address(
124+
discrimination,
125+
Kind::Account(
126+
identifier
127+
.to_single_account()
128+
.expect("Only single accounts are supported")
129+
.into(),
130+
),
131+
)))
132+
}
133+
111134
fn rewards_to_mainnet_addresses(
112135
rewards: HashMap<Identifier, Rewards>,
113136
voters: Vec<SnapshotInfo>,

catalyst-toolbox/src/stats/distribution.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
use core::ops::Range;
22
use std::collections::HashMap;
3+
use thiserror::Error;
34

4-
fn levels(threshold: u64) -> Vec<Range<u64>> {
5-
vec![
6-
(0..threshold),
7-
(threshold..10_000),
5+
fn levels(threshold: u64) -> Result<Vec<Range<u64>>, Error> {
6+
if !(450..=1_000).contains(&threshold) {
7+
return Err(Error::InvalidThreshold(threshold));
8+
}
9+
10+
Ok(vec![
11+
(0..450),
12+
(450..threshold),
13+
(threshold..1_000),
14+
(1_000..2_000),
15+
(2_000..5_000),
16+
(5_000..10_000),
817
(10_000..20_000),
918
(20_000..50_000),
1019
(50_000..100_000),
@@ -16,7 +25,7 @@ fn levels(threshold: u64) -> Vec<Range<u64>> {
1625
(10_000_000..25_000_000),
1726
(25_000_000..50_000_000),
1827
(50_000_000..32_000_000_000),
19-
]
28+
])
2029
}
2130

2231
#[derive(Default)]
@@ -30,25 +39,33 @@ pub struct Stats {
3039
}
3140

3241
impl Stats {
33-
pub fn new(threshold: u64) -> Self {
42+
pub fn new(threshold: u64) -> Result<Self, Error> {
43+
Ok(Self::new_with_levels(levels(threshold)?))
44+
}
45+
46+
pub fn new_with_levels(levels: Vec<Range<u64>>) -> Self {
3447
Self {
35-
content: levels(threshold)
48+
content: levels
3649
.into_iter()
3750
.map(|range| (range, Default::default()))
3851
.collect(),
3952
}
4053
}
4154

42-
pub fn add(&mut self, value: u64) {
55+
pub fn add_with_weight(&mut self, value: u64, weight: u32) {
4356
for (range, record) in self.content.iter_mut() {
4457
if range.contains(&value) {
45-
record.count += 1;
58+
record.count += weight;
4659
record.total += value;
4760
return;
4861
}
4962
}
5063
}
5164

65+
pub fn add(&mut self, value: u64) {
66+
self.add_with_weight(value, 1);
67+
}
68+
5269
pub fn print_count_per_level(&self) {
5370
let mut keys = self.content.keys().cloned().collect::<Vec<Range<u64>>>();
5471
keys.sort_by_key(|x| x.start);
@@ -86,7 +103,9 @@ impl Stats {
86103
}
87104

88105
fn format_big_number(n: u64) -> String {
89-
if n % 1_000_000_000 == 0 {
106+
if n == 0 {
107+
n.to_string()
108+
} else if n % 1_000_000_000 == 0 {
90109
format!("{} MLD", n / 1_000_000)
91110
} else if n % 1_00000 == 0 {
92111
format!("{} M", n / 1_000_000)
@@ -96,3 +115,10 @@ fn format_big_number(n: u64) -> String {
96115
n.to_string()
97116
}
98117
}
118+
119+
#[allow(clippy::large_enum_variant)]
120+
#[derive(Error, Debug)]
121+
pub enum Error {
122+
#[error("invalid threshold for distribution levels ({0}). It should be more than 450 and less that 1000")]
123+
InvalidThreshold(u64),
124+
}

0 commit comments

Comments
 (0)