diff --git a/CHANGELOG.md b/CHANGELOG.md index 12ccc2e510b0..16188f3bdabf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ ### Added +- [#3773](https://github.com/ChainSafe/forest/pull/3773) Implement the + `Filecoin.StateVMCirculatingSupplyInternal` lotus-compatible RPC API. - [#3748](https://github.com/ChainSafe/forest/pull/3748) Add timing for each message and gas charge in the JSON output of `forest-tool snapshot compute-state` and `Filecoin.StateCall` RPC API. diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 1ac18935d926..89cd21385e3f 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -134,6 +134,10 @@ where ) .with_method(STATE_READ_STATE, state_read_state::) .with_method(STATE_SECTOR_GET_INFO, state_sector_get_info::) + .with_method( + STATE_VM_CIRCULATING_SUPPLY_INTERNAL, + state_vm_circulating_supply_internal::, + ) // Gas API .with_method(GAS_ESTIMATE_FEE_CAP, gas_estimate_fee_cap::) .with_method(GAS_ESTIMATE_GAS_LIMIT, gas_estimate_gas_limit::) diff --git a/src/rpc/state_api.rs b/src/rpc/state_api.rs index 98c267869a4f..e344e241134d 100644 --- a/src/rpc/state_api.rs +++ b/src/rpc/state_api.rs @@ -7,14 +7,15 @@ use crate::cid_collections::CidHashSet; use crate::libp2p::NetworkMessage; use crate::lotus_json::LotusJson; use crate::rpc_api::data_types::{ - ApiActorState, ApiDeadline, ApiInvocResult, MarketDeal, MessageLookup, RPCState, - SectorOnChainInfo, + ApiActorState, ApiDeadline, ApiInvocResult, CirculatingSupply, MarketDeal, MessageLookup, + RPCState, SectorOnChainInfo, }; use crate::shim::{ address::Address, clock::ChainEpoch, executor::Receipt, message::Message, state_tree::ActorState, version::NetworkVersion, }; use crate::state_manager::chain_rand::ChainRand; +use crate::state_manager::vm_circ_supply::GenesisInfo; use crate::state_manager::{InvocResult, MarketBalance}; use crate::utils::db::car_stream::{CarBlock, CarWriter}; use ahash::{HashMap, HashMapExt}; @@ -586,3 +587,20 @@ pub(in crate::rpc) async fn state_sector_get_info( + data: Data>, + Params(LotusJson((tsk,))): Params>, +) -> Result, JsonRpcError> { + let ts = data.chain_store.load_required_tipset(&tsk)?; + + let genesis_info = GenesisInfo::from_chain_config(data.state_manager.chain_config()); + + Ok(LotusJson(genesis_info.get_vm_circulating_supply_detailed( + ts.epoch(), + &data.state_manager.blockstore_owned(), + ts.parent_state(), + )?)) +} diff --git a/src/rpc_api/data_types.rs b/src/rpc_api/data_types.rs index bcb5b570a147..aa94c9b78861 100644 --- a/src/rpc_api/data_types.rs +++ b/src/rpc_api/data_types.rs @@ -763,3 +763,22 @@ impl PartialEq for GasTrace { && self.storage_gas == other.storage_gas } } + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct CirculatingSupply { + #[serde(with = "crate::lotus_json")] + pub fil_vested: TokenAmount, + #[serde(with = "crate::lotus_json")] + pub fil_mined: TokenAmount, + #[serde(with = "crate::lotus_json")] + pub fil_burnt: TokenAmount, + #[serde(with = "crate::lotus_json")] + pub fil_locked: TokenAmount, + #[serde(with = "crate::lotus_json")] + pub fil_circulating: TokenAmount, + #[serde(with = "crate::lotus_json")] + pub fil_reserve_disbursed: TokenAmount, +} + +lotus_json_with_self!(CirculatingSupply); diff --git a/src/rpc_api/mod.rs b/src/rpc_api/mod.rs index 02df8b62282a..6dede748b0ed 100644 --- a/src/rpc_api/mod.rs +++ b/src/rpc_api/mod.rs @@ -96,6 +96,10 @@ pub static ACCESS_MAP: Lazy> = Lazy::new(|| { access.insert(state_api::STATE_GET_RANDOMNESS_FROM_BEACON, Access::Read); access.insert(state_api::STATE_READ_STATE, Access::Read); access.insert(state_api::STATE_SECTOR_GET_INFO, Access::Read); + access.insert( + state_api::STATE_VM_CIRCULATING_SUPPLY_INTERNAL, + Access::Read, + ); // Gas API access.insert(gas_api::GAS_ESTIMATE_GAS_LIMIT, Access::Read); @@ -257,6 +261,8 @@ pub mod state_api { pub const STATE_SECTOR_GET_INFO: &str = "Filecoin.StateSectorGetInfo"; pub const STATE_SEARCH_MSG: &str = "Filecoin.StateSearchMsg"; pub const STATE_SEARCH_MSG_LIMITED: &str = "Filecoin.StateSearchMsgLimited"; + pub const STATE_VM_CIRCULATING_SUPPLY_INTERNAL: &str = + "Filecoin.StateVMCirculatingSupplyInternal"; } /// Gas API diff --git a/src/rpc_client/state_ops.rs b/src/rpc_client/state_ops.rs index 50cfde6c522b..c8806b1fc663 100644 --- a/src/rpc_client/state_ops.rs +++ b/src/rpc_client/state_ops.rs @@ -7,7 +7,8 @@ use crate::{ blocks::TipsetKeys, rpc_api::{ data_types::{ - ApiActorState, ApiDeadline, ApiInvocResult, MessageLookup, SectorOnChainInfo, + ApiActorState, ApiDeadline, ApiInvocResult, CirculatingSupply, MessageLookup, + SectorOnChainInfo, }, state_api::*, }, @@ -134,6 +135,12 @@ impl ApiInfo { RpcRequest::new(STATE_CIRCULATING_SUPPLY, (tsk,)) } + pub fn state_vm_circulating_supply_internal_req( + tsk: TipsetKeys, + ) -> RpcRequest { + RpcRequest::new(STATE_VM_CIRCULATING_SUPPLY_INTERNAL, (tsk,)) + } + pub fn state_decode_params_req( recipient: Address, method_number: MethodNum, diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs index 189c01c9261f..0ff2fba3b02b 100644 --- a/src/state_manager/mod.rs +++ b/src/state_manager/mod.rs @@ -12,7 +12,7 @@ use anyhow::{bail, Context as _}; use fil_actor_interface::init::{self, State}; use rayon::prelude::ParallelBridge; pub use utils::is_valid_for_sending; -mod vm_circ_supply; +pub mod vm_circ_supply; pub use self::errors::*; use crate::beacon::BeaconSchedule; use crate::blocks::{Tipset, TipsetKeys}; diff --git a/src/state_manager/vm_circ_supply.rs b/src/state_manager/vm_circ_supply.rs index f9ae8e5bffd6..626e26997ac1 100644 --- a/src/state_manager/vm_circ_supply.rs +++ b/src/state_manager/vm_circ_supply.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use crate::chain::*; use crate::networks::{ChainConfig, Height}; +use crate::rpc_api::data_types::CirculatingSupply; use crate::shim::{ address::Address, clock::{ChainEpoch, EPOCHS_IN_DAY}, @@ -36,7 +37,7 @@ const CALICO_VESTING: [(ChainEpoch, usize); 6] = [ /// Genesis information used when calculating circulating supply. #[derive(Default, Clone)] -pub(in crate::state_manager) struct GenesisInfo { +pub struct GenesisInfo { vesting: GenesisInfoVesting, /// info about the Accounts in the genesis state @@ -71,22 +72,40 @@ impl GenesisInfo { db: &Arc, root: &Cid, ) -> Result { + let detailed = self.get_vm_circulating_supply_detailed(height, db, root)?; + + Ok(detailed.fil_circulating) + } + + pub fn get_vm_circulating_supply_detailed( + &self, + height: ChainEpoch, + db: &Arc, + root: &Cid, + ) -> anyhow::Result { let state_tree = StateTree::new_from_root(Arc::clone(db), root)?; + let fil_vested = get_fil_vested(self, height); let fil_mined = get_fil_mined(&state_tree)?; let fil_burnt = get_fil_burnt(&state_tree)?; let fil_locked = get_fil_locked(&state_tree)?; - let fil_reserve_distributed = if height > self.actors_v2_height { + let fil_reserve_disbursed = if height > self.actors_v2_height { get_fil_reserve_disbursed(&state_tree)? } else { TokenAmount::default() }; let fil_circulating = TokenAmount::max( - &fil_vested + &fil_mined + &fil_reserve_distributed - &fil_burnt - &fil_locked, + &fil_vested + &fil_mined + &fil_reserve_disbursed - &fil_burnt - &fil_locked, TokenAmount::default(), ); - - Ok(fil_circulating) + Ok(CirculatingSupply { + fil_vested, + fil_mined, + fil_burnt, + fil_locked, + fil_circulating, + fil_reserve_disbursed, + }) } } diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index 98400ff0b739..f0d9afd662c6 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -440,6 +440,9 @@ fn snapshot_tests(store: &ManyCar, n_tipsets: usize) -> anyhow::Result