Skip to content

Commit

Permalink
feat(cli): forest-cli f3 powertable get-proportion
Browse files Browse the repository at this point in the history
  • Loading branch information
hanabi1224 committed Dec 3, 2024
1 parent 11ec5bd commit 801fc73
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 14 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
- [#4995](https://github.com/ChainSafe/forest/pull/4995) Added
`forest-cli f3 powertable get` CLI command.

- [#5028](https://github.com/ChainSafe/forest/pull/5028) Added
`forest-cli f3 powertable get-proportion` CLI command.

- [#4704](https://github.com/ChainSafe/forest/issues/4704) Add support for the
`Filecoin.EthGetTransactionReceiptLimited` RPC method.

Expand Down
115 changes: 101 additions & 14 deletions src/cli/subcommands/f3_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ use crate::{
prelude::*,
},
};
use ahash::HashSet;
use anyhow::Context as _;
use cid::Cid;
use clap::{Subcommand, ValueEnum};
use itertools::Itertools as _;
use sailfish::TemplateSimple;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
Expand Down Expand Up @@ -196,9 +198,9 @@ impl F3CertsCommands {
}
}

/// Gets F3 power table at a specific instance ID or latest instance if none is specified.
#[derive(Debug, Subcommand)]
pub enum F3PowerTableCommands {
/// Gets F3 power table at a specific instance ID or latest instance if none is specified.
#[command(visible_alias = "g")]
Get {
/// instance ID. (default: latest)
Expand All @@ -207,25 +209,25 @@ pub enum F3PowerTableCommands {
#[arg(long, default_value_t = false)]
ec: bool,
},
/// Gets the total proportion of power for a list of actors at a given instance.
#[command(visible_alias = "gp")]
GetProportion {
actor_ids: Vec<u64>,
/// instance ID. (default: latest)
#[arg(long, required = false)]
instance: Option<u64>,
/// Whether to get the power table from EC. (default: false)
#[arg(long, required = false, default_value_t = false)]
ec: bool,
},
}

impl F3PowerTableCommands {
pub async fn run(self, client: rpc::Client) -> anyhow::Result<()> {
match self {
Self::Get { instance, ec } => {
let instance = if let Some(instance) = instance {
instance
} else {
let progress = F3GetProgress::call(&client, ()).await?;
progress.id
};
let (tsk, power_table_cid) =
Self::get_power_table_tsk_by_instance(&client, instance).await?;
let power_table = if ec {
F3GetECPowerTable::call(&client, (tsk.into(),)).await?
} else {
F3GetF3PowerTable::call(&client, (tsk.into(),)).await?
};
let (instance, power_table_cid, power_table) =
Self::get_power_table(&client, instance, ec).await?;
let total = power_table
.iter()
.fold(num::BigInt::ZERO, |acc, entry| acc + &entry.power);
Expand All @@ -245,11 +247,75 @@ impl F3PowerTableCommands {
};
println!("{}", serde_json::to_string_pretty(&result)?);
}
Self::GetProportion {
actor_ids,
instance,
ec,
} => {
anyhow::ensure!(
!actor_ids.is_empty(),
"at least one actor ID must be specified"
);
let (instance, power_table_cid, power_table) =
Self::get_power_table(&client, instance, ec).await?;
let total = power_table
.iter()
.fold(num::BigInt::ZERO, |acc, entry| acc + &entry.power);
let mut scaled_total = 0;
let mut scaled_sum = 0;
let mut actor_id_set = HashSet::from_iter(actor_ids);
for entry in power_table.iter() {
let scaled_power = scale_power(&entry.power, &total)?;
scaled_total += scaled_power;
if actor_id_set.remove(&entry.id) {
scaled_sum += scaled_power;
}
}

anyhow::ensure!(
actor_id_set.is_empty(),
"actor ID {} not found in power table",
actor_id_set.into_iter().map(|i| i.to_string()).join(",")
);

let result = F3PowerTableGetProportionCommandResult {
instance,
from_ec: ec,
power_table: F3PowerTableCliMinimalJson {
cid: power_table_cid,
scaled_total,
},
scaled_sum,
proportion: (scaled_sum as f64) / (scaled_total as f64),
};
println!("{}", serde_json::to_string_pretty(&result)?);
}
};

Ok(())
}

async fn get_power_table(
client: &rpc::Client,
instance: Option<u64>,
ec: bool,
) -> anyhow::Result<(u64, Cid, Vec<F3PowerEntry>)> {
let instance = if let Some(instance) = instance {
instance
} else {
let progress = F3GetProgress::call(client, ()).await?;
progress.id
};
let (tsk, power_table_cid) =
Self::get_power_table_tsk_by_instance(client, instance).await?;
let power_table = if ec {
F3GetECPowerTable::call(client, (tsk.into(),)).await?
} else {
F3GetF3PowerTable::call(client, (tsk.into(),)).await?
};
Ok((instance, power_table_cid, power_table))
}

async fn get_power_table_tsk_by_instance(
client: &rpc::Client,
instance: u64,
Expand Down Expand Up @@ -300,6 +366,27 @@ pub struct F3PowerTableCliJson {
scaled_total: i64,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct F3PowerTableGetProportionCommandResult {
instance: u64,
#[serde(rename = "FromEC")]
from_ec: bool,
power_table: F3PowerTableCliMinimalJson,
scaled_sum: i64,
proportion: f64,
}

#[serde_as]
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct F3PowerTableCliMinimalJson {
#[serde(rename = "CID")]
#[serde_as(as = "DisplayFromStr")]
cid: Cid,
scaled_total: i64,
}

fn scale_power(power: &num::BigInt, total: &num::BigInt) -> anyhow::Result<i64> {
const MAX_POWER: i64 = 0xffff;
if total < power {
Expand Down

0 comments on commit 801fc73

Please sign in to comment.