From f87e01e2548bcfefe3d2fe956af60ddaa4e79b93 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Mon, 4 Dec 2023 11:56:08 +0100 Subject: [PATCH 1/6] [Feature] MinerGetBaseInfo Lotus-compatible RPC. --- src/rpc/mod.rs | 1 + src/rpc/state_api.rs | 18 ++++++- src/rpc_api/data_types.rs | 25 +++++++++- src/rpc_api/mod.rs | 2 + src/rpc_client/state_ops.rs | 9 ++++ src/shim/sector.rs | 5 +- src/state_manager/mod.rs | 87 ++++++++++++++++++++++++++++++++- src/tool/subcommands/api_cmd.rs | 5 ++ 8 files changed, 146 insertions(+), 6 deletions(-) diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 1ac18935d926..bc4395d3cce8 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -117,6 +117,7 @@ where .with_method(STATE_MARKET_BALANCE, state_market_balance::) .with_method(STATE_MARKET_DEALS, state_market_deals::) .with_method(STATE_MINER_INFO, state_miner_info::) + .with_method(MINER_GET_BASE_INFO, miner_get_base_info::) .with_method(STATE_MINER_ACTIVE_SECTORS, state_miner_active_sectors::) .with_method(STATE_MINER_FAULTS, state_miner_faults::) .with_method(STATE_MINER_POWER, state_miner_power::) diff --git a/src/rpc/state_api.rs b/src/rpc/state_api.rs index 98c267869a4f..4c37070c9afc 100644 --- a/src/rpc/state_api.rs +++ b/src/rpc/state_api.rs @@ -7,8 +7,8 @@ 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, MarketDeal, MessageLookup, MiningBaseInfo, + RPCState, SectorOnChainInfo, }; use crate::shim::{ address::Address, clock::ChainEpoch, executor::Receipt, message::Message, @@ -37,6 +37,20 @@ use tokio::task::JoinSet; type RandomnessParams = (i64, ChainEpoch, Vec, TipsetKeys); +pub(in crate::rpc) async fn miner_get_base_info( + data: Data>, + Params(LotusJson((address, epoch, tsk))): Params>, +) -> anyhow::Result>> { + let ts = data + .state_manager + .chain_store() + .load_required_tipset(&tsk)?; + + data.state_manager + .miner_get_base_info(data.state_manager.beacon_schedule(), ts, address, epoch) + .await + .map(|info| info.into()) +} /// runs the given message and returns its result without any persisted changes. pub(in crate::rpc) async fn state_call( data: Data>, diff --git a/src/rpc_api/data_types.rs b/src/rpc_api/data_types.rs index bcb5b570a147..81cb867de6bd 100644 --- a/src/rpc_api/data_types.rs +++ b/src/rpc_api/data_types.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use std::sync::Arc; -use crate::beacon::BeaconSchedule; +use crate::beacon::{BeaconEntry, BeaconSchedule}; use crate::blocks::TipsetKeys; use crate::chain::ChainStore; use crate::chain_sync::{BadBlockCache, SyncState}; @@ -14,6 +14,7 @@ use crate::libp2p::{Multihash, NetworkMessage}; use crate::lotus_json::{lotus_json_with_self, HasLotusJson, LotusJson}; use crate::message::signed_message::SignedMessage; use crate::message_pool::{MessagePool, MpoolRpcProvider}; +use crate::shim::sector::SectorInfo; use crate::shim::{ address::Address, clock::ChainEpoch, @@ -407,6 +408,28 @@ pub struct MinerPowerLotusJson { has_min_power: bool, } +// Note: kept the name in line with Lotus implementation for cross-referencing simplicity. +#[derive(Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct MiningBaseInfo { + #[serde(with = "crate::lotus_json")] + pub miner_power: fvm_shared2::sector::StoragePower, + #[serde(with = "crate::lotus_json")] + pub network_power: fvm_shared2::sector::StoragePower, + #[serde(with = "crate::lotus_json")] + pub sectors: Vec, + #[serde(with = "crate::lotus_json")] + pub worker_key: Address, + pub sector_size: fvm_shared2::sector::SectorSize, + #[serde(with = "crate::lotus_json")] + pub prev_beacon_entry: BeaconEntry, + #[serde(with = "crate::lotus_json")] + pub beacon_entries: Vec, + pub eligible_for_mining: bool, +} + +lotus_json_with_self!(MiningBaseInfo); + impl HasLotusJson for MinerPower { type LotusJson = MinerPowerLotusJson; fn snapshots() -> Vec<(serde_json::Value, Self)> { diff --git a/src/rpc_api/mod.rs b/src/rpc_api/mod.rs index 02df8b62282a..0d6c991605c9 100644 --- a/src/rpc_api/mod.rs +++ b/src/rpc_api/mod.rs @@ -81,6 +81,7 @@ pub static ACCESS_MAP: Lazy> = Lazy::new(|| { access.insert(state_api::STATE_MARKET_BALANCE, Access::Read); access.insert(state_api::STATE_MARKET_DEALS, Access::Read); access.insert(state_api::STATE_MINER_INFO, Access::Read); + access.insert(state_api::MINER_GET_BASE_INFO, Access::Read); access.insert(state_api::STATE_MINER_ACTIVE_SECTORS, Access::Read); access.insert(state_api::STATE_MINER_FAULTS, Access::Read); access.insert(state_api::STATE_MINER_POWER, Access::Read); @@ -240,6 +241,7 @@ pub mod state_api { pub const STATE_MARKET_BALANCE: &str = "Filecoin.StateMarketBalance"; pub const STATE_MARKET_DEALS: &str = "Filecoin.StateMarketDeals"; pub const STATE_MINER_INFO: &str = "Filecoin.StateMinerInfo"; + pub const MINER_GET_BASE_INFO: &str = "Filecoin.MinerGetBaseInfo"; pub const STATE_MINER_FAULTS: &str = "Filecoin.StateMinerFaults"; pub const STATE_MINER_POWER: &str = "Filecoin.StateMinerPower"; pub const STATE_MINER_DEADLINES: &str = "Filecoin.StateMinerDeadlines"; diff --git a/src/rpc_client/state_ops.rs b/src/rpc_client/state_ops.rs index 50cfde6c522b..d142af4d6715 100644 --- a/src/rpc_client/state_ops.rs +++ b/src/rpc_client/state_ops.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; +use crate::rpc_api::data_types::MiningBaseInfo; use crate::{ blocks::TipsetKeys, rpc_api::{ @@ -64,6 +65,14 @@ impl ApiInfo { RpcRequest::new(STATE_MINER_INFO, (miner, tsk)) } + pub fn miner_get_base_info_req( + miner: Address, + epoch: ChainEpoch, + tsk: TipsetKeys, + ) -> RpcRequest> { + RpcRequest::new(MINER_GET_BASE_INFO, (miner, epoch, tsk)) + } + pub fn state_call_req(message: Message, tsk: TipsetKeys) -> RpcRequest { RpcRequest::new(STATE_CALL, (message, tsk)) } diff --git a/src/shim/sector.rs b/src/shim/sector.rs index f97d3de5bbba..1dedd01a7a2b 100644 --- a/src/shim/sector.rs +++ b/src/shim/sector.rs @@ -18,6 +18,7 @@ use num_derive::FromPrimitive; use std::ops::Deref; pub use fvm_shared3::sector::StoragePower; +use serde::{Deserialize, Serialize}; pub type SectorNumber = fvm_shared3::sector::SectorNumber; @@ -104,7 +105,9 @@ impl quickcheck::Arbitrary for RegisteredSealProof { /// Represents a shim over `SectorInfo` from `fvm_shared` with convenience /// methods to convert to an older version of the type -#[derive(PartialEq, Debug, Clone, derive_more::From, derive_more::Into)] +#[derive( + Eq, PartialEq, Debug, Clone, derive_more::From, derive_more::Into, Serialize, Deserialize, +)] pub struct SectorInfo(SectorInfoV3); #[cfg(test)] diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs index 991f3ed99880..497c361ebe9d 100644 --- a/src/state_manager/mod.rs +++ b/src/state_manager/mod.rs @@ -14,7 +14,7 @@ use rayon::prelude::ParallelBridge; pub use utils::is_valid_for_sending; mod vm_circ_supply; pub use self::errors::*; -use crate::beacon::BeaconSchedule; +use crate::beacon::{BeaconEntry, BeaconSchedule}; use crate::blocks::{Tipset, TipsetKeys}; use crate::chain::{ index::{ChainIndex, ResolveNullTipset}, @@ -25,13 +25,14 @@ use crate::interpreter::{ }; use crate::message::{ChainMessage, Message as MessageTrait}; use crate::networks::ChainConfig; -use crate::rpc_api::data_types::{ApiInvocResult, MessageGasCost}; +use crate::rpc_api::data_types::{ApiInvocResult, MessageGasCost, MiningBaseInfo}; use crate::shim::{ address::{Address, Payload, Protocol, BLS_PUB_LEN}, clock::ChainEpoch, econ::TokenAmount, executor::{ApplyRet, Receipt}, message::Message, + randomness::Randomness, state_tree::{ActorState, StateTree}, version::NetworkVersion, }; @@ -39,14 +40,17 @@ use ahash::{HashMap, HashMapExt}; use chain_rand::ChainRand; use cid::Cid; +use crate::state_manager::chain_rand::draw_randomness; use fil_actor_interface::miner::SectorOnChainInfo; use fil_actor_interface::miner::{MinerInfo, MinerPower}; use fil_actor_interface::*; use fil_actors_shared::fvm_ipld_amt::Amtv0 as Amt; use fil_actors_shared::fvm_ipld_bitfield::BitField; use fil_actors_shared::v10::runtime::Policy; +use fil_actors_shared::v12::runtime::DomainSeparationTag; use futures::{channel::oneshot, select, FutureExt}; use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::to_vec; use itertools::Itertools as _; use lru::LruCache; use nonzero_ext::nonzero; @@ -1121,6 +1125,85 @@ where resolve_to_key_addr(&state, self.blockstore(), addr) } + pub async fn miner_get_base_info( + self: &Arc, + beacon_schedule: Arc, + tipset: Arc, + addr: Address, + epoch: ChainEpoch, + ) -> anyhow::Result> { + let prev_beacon = self + .chain_store() + .chain_index + .latest_beacon_entry(&tipset)?; + + let entries: Vec = beacon_schedule + .beacon_entries_for_block( + self.chain_config.network_version(epoch), + epoch, + tipset.epoch(), + &prev_beacon, + ) + .await?; + + let base = entries.last().unwrap_or(&prev_beacon); + + let (lb_tipset, lb_state_root) = ChainStore::get_lookback_tipset_for_round( + self.cs.chain_index.clone(), + self.chain_config.clone(), + tipset.clone(), + epoch, + )?; + + let actor = self + .get_actor(&addr, *tipset.parent_state())? + .ok_or_else(|| Error::State("miner actor does not exist".to_string()))?; + + let miner_state = miner::State::load(self.blockstore(), actor.code, actor.state)?; + + let addr_buf = to_vec(&addr)?; + let rand = draw_randomness( + base.data(), + DomainSeparationTag::WinningPoStChallengeSeed as i64, + epoch, + &addr_buf, + )?; + + let network_version = self.chain_config.network_version(tipset.epoch()); + let sectors = self.get_sectors_for_winning_post( + &lb_state_root, + network_version, + &addr, + Randomness::new(rand.to_vec()), + )?; + + if sectors.is_empty() { + return Ok(None); + } + + let (miner_power, total_power) = self + .get_power(&lb_state_root, Some(&addr))? + .ok_or_else(|| Error::State("failed to get power".to_string()))?; + + let info = miner_state.info(self.blockstore())?; + + let worker_key = self + .resolve_to_deterministic_address(info.worker.into(), Some(tipset.clone())) + .await?; + let eligible = self.eligible_to_mine(&addr, &tipset, &lb_tipset)?; + + Ok(Some(MiningBaseInfo { + miner_power: miner_power.quality_adj_power, + network_power: total_power.quality_adj_power, + sectors, + worker_key, + sector_size: info.sector_size, + prev_beacon_entry: prev_beacon, + beacon_entries: entries, + eligible_for_mining: eligible, + })) + } + /// Checks power actor state for if miner meets consensus minimum /// requirements. pub fn miner_has_min_power( diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index 98400ff0b739..c510556d7623 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -435,6 +435,11 @@ fn snapshot_tests(store: &ManyCar, n_tipsets: usize) -> anyhow::Result Date: Mon, 4 Dec 2023 11:57:57 +0100 Subject: [PATCH 2/6] fmt --- src/rpc/state_api.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpc/state_api.rs b/src/rpc/state_api.rs index fd020d40b054..c5f942fbddb8 100644 --- a/src/rpc/state_api.rs +++ b/src/rpc/state_api.rs @@ -7,8 +7,8 @@ use crate::cid_collections::CidHashSet; use crate::libp2p::NetworkMessage; use crate::lotus_json::LotusJson; use crate::rpc_api::data_types::{ - ApiActorState, ApiDeadline, ApiInvocResult, CirculatingSupply, MarketDeal, MessageLookup, MiningBaseInfo, - RPCState, SectorOnChainInfo, + ApiActorState, ApiDeadline, ApiInvocResult, CirculatingSupply, MarketDeal, MessageLookup, + MiningBaseInfo, RPCState, SectorOnChainInfo, }; use crate::shim::{ address::Address, clock::ChainEpoch, executor::Receipt, message::Message, From 9de6705c547d90023d29f640d3122446bd70159d Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Mon, 4 Dec 2023 11:58:50 +0100 Subject: [PATCH 3/6] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16188f3bdabf..acbd4af352a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ ### Added +- [#3773](https://github.com/ChainSafe/forest/pull/3786) Implement the + `Filecoin.MinerGetBaseInfo` lotus-compatible RPC API. - [#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 From 3ef3c5ae9be999ee3903a1b9c887e2ca7ea77545 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 8 Dec 2023 08:55:46 +0100 Subject: [PATCH 4/6] Update src/state_manager/mod.rs Co-authored-by: David Himmelstrup --- src/state_manager/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs index fb2a191bffb4..199f20685dbd 100644 --- a/src/state_manager/mod.rs +++ b/src/state_manager/mod.rs @@ -1184,7 +1184,7 @@ where let (miner_power, total_power) = self .get_power(&lb_state_root, Some(&addr))? - .ok_or_else(|| Error::State("failed to get power".to_string()))?; + .context("failed to get power")?; let info = miner_state.info(self.blockstore())?; From b0f0b9cdd422cfc84e7b6b26860a141ed2d3d2c2 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 8 Dec 2023 08:55:57 +0100 Subject: [PATCH 5/6] Update src/state_manager/mod.rs Co-authored-by: David Himmelstrup --- src/state_manager/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs index 199f20685dbd..bf32d23a5aaa 100644 --- a/src/state_manager/mod.rs +++ b/src/state_manager/mod.rs @@ -1158,7 +1158,7 @@ where let actor = self .get_actor(&addr, *tipset.parent_state())? - .ok_or_else(|| Error::State("miner actor does not exist".to_string()))?; + .context("miner actor does not exist")?; let miner_state = miner::State::load(self.blockstore(), actor.code, actor.state)?; From 6d956186fb3223b629b40410f7ecee86ad091e14 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 8 Dec 2023 08:56:06 +0100 Subject: [PATCH 6/6] Update src/rpc_api/data_types.rs Co-authored-by: David Himmelstrup --- src/rpc_api/data_types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc_api/data_types.rs b/src/rpc_api/data_types.rs index 84b700beaf02..34aa587565d5 100644 --- a/src/rpc_api/data_types.rs +++ b/src/rpc_api/data_types.rs @@ -413,7 +413,7 @@ pub struct MinerPowerLotusJson { #[serde(rename_all = "PascalCase")] pub struct MiningBaseInfo { #[serde(with = "crate::lotus_json")] - pub miner_power: fvm_shared2::sector::StoragePower, + pub miner_power: crate::shim::sector::StoragePower, #[serde(with = "crate::lotus_json")] pub network_power: fvm_shared2::sector::StoragePower, #[serde(with = "crate::lotus_json")]