diff --git a/CHANGELOG.md b/CHANGELOG.md index 0993c3420be..ddbf5e77798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,9 @@ - [#5020](https://github.com/ChainSafe/forest/issues/5020) Add support for the `Filecoin.EthGetTransactionByBlockNumberAndIndex` RPC method. +- [#4907](https://github.com/ChainSafe/forest/issues/4907) Add support for the + `Filecoin.StateMinerInitialPledgeForSector` RPC method. + ### Changed ### Removed diff --git a/src/rpc/methods/state.rs b/src/rpc/methods/state.rs index 4631a5c5ba6..0cdc2358d42 100644 --- a/src/rpc/methods/state.rs +++ b/src/rpc/methods/state.rs @@ -2800,3 +2800,95 @@ impl TryFrom<&ChainConfig> for ForkUpgradeParams { }) } } + +pub enum StateMinerInitialPledgeForSector {} +impl RpcMethod<4> for StateMinerInitialPledgeForSector { + const NAME: &'static str = "Filecoin.StateMinerInitialPledgeForSector"; + const PARAM_NAMES: [&'static str; 4] = [ + "sector_duration", + "sector_size", + "verified_size", + "tipset_key", + ]; + const API_PATHS: ApiPaths = ApiPaths::V1; + const PERMISSION: Permission = Permission::Read; + + type Params = (ChainEpoch, SectorSize, u64, ApiTipsetKey); + type Ok = TokenAmount; + + async fn handle( + ctx: Ctx, + (sector_duration, sector_size, verified_size, ApiTipsetKey(tsk)): Self::Params, + ) -> Result { + if sector_duration <= 0 { + return Err(anyhow::anyhow!("sector duration must be greater than 0").into()); + } + + if verified_size > sector_size as u64 { + return Err( + anyhow::anyhow!("verified deal size cannot be larger than sector size").into(), + ); + } + + let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?; + + let power_state: power::State = ctx.state_manager.get_actor_state(&ts)?; + let power_smoothed = power_state.total_power_smoothed(); + let pledge_collateral = power_state.total_locked(); + + let reward_state: reward::State = ctx.state_manager.get_actor_state(&ts)?; + + let genesis_info = GenesisInfo::from_chain_config(ctx.chain_config().clone()); + let circ_supply = genesis_info.get_vm_circulating_supply_detailed( + ts.epoch(), + &ctx.store_owned(), + ts.parent_state(), + )?; + + let deal_weight = BigInt::from(0); + let verified_deal_weight = BigInt::from(verified_size) * sector_duration; + let sector_weight = qa_power_for_weight( + sector_size.into(), + sector_duration, + &deal_weight, + &verified_deal_weight, + ); + + let (epochs_since_start, duration) = get_pledge_ramp_params(&ctx, ts.epoch(), &ts)?; + + let initial_pledge: TokenAmount = reward_state + .initial_pledge_for_power( + §or_weight, + pledge_collateral, + power_smoothed, + &circ_supply.fil_circulating.into(), + epochs_since_start, + duration, + )? + .into(); + + let (value, _) = (initial_pledge * INITIAL_PLEDGE_NUM).div_rem(INITIAL_PLEDGE_DEN); + Ok(value) + } +} + +fn get_pledge_ramp_params( + ctx: &Ctx, + height: ChainEpoch, + ts: &Tipset, +) -> Result<(ChainEpoch, u64), anyhow::Error> { + let state_tree = ctx.state_manager.get_state_tree(ts.parent_state())?; + + let power_state: power::State = state_tree + .get_actor_state() + .map_err(|e| anyhow::anyhow!("loading power actor state: {e}"))?; + + if power_state.ramp_start_epoch() > 0 { + Ok(( + height - power_state.ramp_start_epoch(), + power_state.ramp_duration_epochs(), + )) + } else { + Ok((0, 0)) + } +} diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index cb1c9856f0e..c0d19e968c8 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -217,6 +217,7 @@ macro_rules! for_each_method { $callback!(crate::rpc::state::StateVMCirculatingSupplyInternal); $callback!(crate::rpc::state::StateWaitMsg); $callback!(crate::rpc::state::StateWaitMsgV0); + $callback!(crate::rpc::state::StateMinerInitialPledgeForSector); // sync vertical $callback!(crate::rpc::sync::SyncCheckBad); diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index e709a86dad6..287d24816d9 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -20,6 +20,7 @@ use crate::rpc::types::{ApiTipsetKey, MessageFilter, MessageLookup}; use crate::rpc::{prelude::*, Permission}; use crate::shim::actors::market; use crate::shim::actors::MarketActorStateLoad as _; +use crate::shim::sector::SectorSize; use crate::shim::{ address::{Address, Protocol}, crypto::Signature, @@ -810,6 +811,12 @@ fn state_tests_with_tipset( let mut tests = vec![ RpcTest::identity(StateNetworkName::request(())?), RpcTest::identity(StateGetNetworkParams::request(())?), + RpcTest::identity(StateMinerInitialPledgeForSector::request(( + 1, + SectorSize::_2KiB, + 1024, + tipset.key().into(), + ))?), RpcTest::identity(StateGetActor::request(( Address::SYSTEM_ACTOR, tipset.key().into(),