diff --git a/CHANGELOG.md b/CHANGELOG.md index cc5d589cbd8f..6179a502cbe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ - [#3720](https://github.com/ChainSafe/forest/pull/3720) Implement the `Filecoin.GetParentMessages` lotus-compatible RPC API. +- [#3726](https://github.com/ChainSafe/forest/pull/3726) Implement the + `Filecoin.StateMinerFaults` lotus-compatible RPC API. - [#3735](https://github.com/ChainSafe/forest/pull/3735) Implement the `Filecoin.StateAccountKey` lotus-compatible RPC API. - [#3727](https://github.com/ChainSafe/forest/pull/3727) Added glif.io calibnet diff --git a/Cargo.toml b/Cargo.toml index b5cbc0b0371d..eac0bf2c4796 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ fil_actor_power_state = "7.0.0" fil_actor_reward_state = "7.0.0" fil_actor_system_state = "7.0.0" fil_actor_verifreg_state = "7.0.0" -fil_actors_shared = "7.0.0" +fil_actors_shared = { version = "7.0.0", features = ["json"] } filecoin-proofs-api = { version = "16.0", default-features = false } flume = "0.11" fs_extra = "1.2" diff --git a/src/lotus_json/mod.rs b/src/lotus_json/mod.rs index 3ecb31c89e6d..85c723d8c693 100644 --- a/src/lotus_json/mod.rs +++ b/src/lotus_json/mod.rs @@ -124,6 +124,8 @@ use crate::ipld::{json::IpldJson, Ipld}; use derive_more::From; use fil_actor_interface::power::Claim; +use fil_actors_shared::fvm_ipld_bitfield::json::BitFieldJson; +use fil_actors_shared::fvm_ipld_bitfield::BitField; use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::json; use std::{fmt::Display, str::FromStr}; @@ -503,3 +505,16 @@ impl HasLotusJson for Ipld { lotus_json.0 } } + +impl HasLotusJson for BitField { + type LotusJson = BitFieldJson; + fn snapshots() -> Vec<(serde_json::Value, Self)> { + vec![] + } + fn into_lotus_json(self) -> Self::LotusJson { + BitFieldJson(self) + } + fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { + lotus_json.0 + } +} diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 760b49efc58a..9462e8314f83 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -114,6 +114,7 @@ where .with_method(STATE_GET_ACTOR, state_get_actor::) .with_method(STATE_MARKET_BALANCE, state_market_balance::) .with_method(STATE_MARKET_DEALS, state_market_deals::) + .with_method(STATE_MINER_FAULTS, state_miner_faults::) .with_method(STATE_MINER_POWER, state_miner_power::) .with_method(STATE_GET_RECEIPT, state_get_receipt::) .with_method(STATE_WAIT_MSG, state_wait_msg::) diff --git a/src/rpc/state_api.rs b/src/rpc/state_api.rs index 943114b3a285..383a78fe100f 100644 --- a/src/rpc/state_api.rs +++ b/src/rpc/state_api.rs @@ -20,6 +20,7 @@ use anyhow::Context as _; use cid::Cid; use fil_actor_interface::market; use fil_actor_interface::miner::MinerPower; +use fil_actors_shared::fvm_ipld_bitfield::BitField; use futures::StreamExt; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::{CborStore, DAG_CBOR}; @@ -176,6 +177,22 @@ pub(in crate::rpc) async fn state_miner_power( + data: Data>, + Params(LotusJson((address, key))): Params>, +) -> Result, JsonRpcError> { + let ts = data + .state_manager + .chain_store() + .load_required_tipset(&key)?; + + data.state_manager + .miner_faults(&address, &ts) + .map_err(|e| e.into()) + .map(|r| r.into()) +} + /// returns the message receipt for the given message pub(in crate::rpc) async fn state_get_receipt( data: Data>, diff --git a/src/rpc_api/mod.rs b/src/rpc_api/mod.rs index d7c05bbd455a..742f2c1e1567 100644 --- a/src/rpc_api/mod.rs +++ b/src/rpc_api/mod.rs @@ -80,6 +80,7 @@ pub static ACCESS_MAP: Lazy> = Lazy::new(|| { access.insert(state_api::STATE_GET_ACTOR, Access::Read); access.insert(state_api::STATE_MARKET_BALANCE, Access::Read); access.insert(state_api::STATE_MARKET_DEALS, Access::Read); + access.insert(state_api::STATE_MINER_FAULTS, Access::Read); access.insert(state_api::STATE_MINER_POWER, Access::Read); access.insert(state_api::STATE_GET_RECEIPT, Access::Read); access.insert(state_api::STATE_WAIT_MSG, Access::Read); @@ -232,6 +233,7 @@ pub mod state_api { pub const STATE_GET_ACTOR: &str = "Filecoin.StateGetActor"; pub const STATE_MARKET_BALANCE: &str = "Filecoin.StateMarketBalance"; pub const STATE_MARKET_DEALS: &str = "Filecoin.StateMarketDeals"; + pub const STATE_MINER_FAULTS: &str = "Filecoin.StateMinerFaults"; pub const STATE_MINER_POWER: &str = "Filecoin.StateMinerPower"; pub const STATE_GET_RECEIPT: &str = "Filecoin.StateGetReceipt"; pub const STATE_WAIT_MSG: &str = "Filecoin.StateWaitMsg"; diff --git a/src/rpc_client/state_ops.rs b/src/rpc_client/state_ops.rs index fdc3e44879a3..137cce7ca9b3 100644 --- a/src/rpc_client/state_ops.rs +++ b/src/rpc_client/state_ops.rs @@ -16,6 +16,7 @@ use crate::{ }; use cid::Cid; use fil_actor_interface::miner::MinerPower; +use fil_actors_shared::fvm_ipld_bitfield::BitField; use fil_actors_shared::v10::runtime::DomainSeparationTag; use libipld_core::ipld::Ipld; @@ -57,6 +58,10 @@ impl ApiInfo { RpcRequest::new(STATE_NETWORK_NAME, ()) } + pub fn state_miner_faults_req(miner: Address, tsk: TipsetKeys) -> RpcRequest { + RpcRequest::new(STATE_MINER_FAULTS, (miner, tsk)) + } + pub fn state_miner_power_req(miner: Address, tsk: TipsetKeys) -> RpcRequest { RpcRequest::new(STATE_MINER_POWER, (miner, tsk)) } diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs index fde274567da8..115cf4f1b360 100644 --- a/src/state_manager/mod.rs +++ b/src/state_manager/mod.rs @@ -39,6 +39,7 @@ use cid::Cid; use fil_actor_interface::miner::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 futures::{channel::oneshot, select, FutureExt}; use fvm_ipld_blockstore::Blockstore; @@ -979,6 +980,34 @@ where Ok(out) } + /// Retrieves miner faults. + pub fn miner_faults( + self: &Arc, + addr: &Address, + ts: &Arc, + ) -> Result { + let actor = self + .get_actor(addr, *ts.parent_state())? + .ok_or_else(|| Error::State("Miner actor not found".to_string()))?; + + let state = miner::State::load(self.blockstore(), actor.code, actor.state)?; + + let mut faults = Vec::new(); + + state.for_each_deadline( + &self.chain_config.policy, + self.blockstore(), + |_, deadline| { + deadline.for_each(self.blockstore(), |_, partition| { + faults.push(partition.faulty_sectors().clone()); + Ok(()) + }) + }, + )?; + + Ok(BitField::union(faults.iter())) + } + /// Retrieves miner power. pub fn miner_power( self: &Arc, diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index f8e6bba45a5b..c1851e07aa2c 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -374,6 +374,10 @@ fn snapshot_tests(store: &ManyCar, n_tipsets: usize) -> anyhow::Result