Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions src/state_manager/actor_queries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// Copyright 2019-2026 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use super::*;
use crate::shim::actors::miner::{MinerInfo, MinerPower, Partition};
use crate::shim::actors::verifreg::ext::VerifiedRegistryStateExt as _;
use crate::shim::actors::verifreg::{Allocation, AllocationID, Claim};
use ahash::HashMap;
use fil_actor_verifreg_state::v12::DataCap;
use fil_actor_verifreg_state::v13::ClaimID;
use fil_actors_shared::fvm_ipld_bitfield::BitField;

impl<DB> StateManager<DB>
where
DB: Blockstore + Send + Sync + 'static,
{
/// Retrieves market state
pub fn market_state(&self, ts: &Tipset) -> Result<market::State, Error> {
let actor = self.get_required_actor(&Address::MARKET_ACTOR, *ts.parent_state())?;
let market_state = market::State::load(self.blockstore(), actor.code, actor.state)?;
Ok(market_state)
}

/// Retrieves market balance in escrow and locked tables.
pub fn market_balance(&self, addr: &Address, ts: &Tipset) -> Result<MarketBalance, Error> {
let market_state = self.market_state(ts)?;
let new_addr = self.lookup_required_id(addr, ts)?;
let out = MarketBalance {
escrow: {
market_state
.escrow_table(self.blockstore())?
.get(&new_addr)?
},
locked: {
market_state
.locked_table(self.blockstore())?
.get(&new_addr)?
},
};

Ok(out)
}

/// Retrieves miner info.
pub fn miner_info(&self, addr: &Address, ts: &Tipset) -> Result<MinerInfo, Error> {
let actor = self
.get_actor(addr, *ts.parent_state())?
.ok_or_else(|| Error::state("Miner actor not found"))?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add addr to the info to make it more useful

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.ok_or_else(|| Error::state("Miner actor not found"))?;
.ok_or_else(|| Error::state(format!("Miner actor {addr} not found")))?;

let state = miner::State::load(self.blockstore(), actor.code, actor.state)?;

Ok(state.info(self.blockstore())?)
}

/// Retrieves miner faults.
pub fn miner_faults(&self, addr: &Address, ts: &Tipset) -> Result<BitField, Error> {
self.all_partition_sectors(addr, ts, |partition| partition.faulty_sectors().clone())
}

/// Retrieves miner recoveries.
pub fn miner_recoveries(&self, addr: &Address, ts: &Tipset) -> Result<BitField, Error> {
self.all_partition_sectors(addr, ts, |partition| partition.recovering_sectors().clone())
}

fn all_partition_sectors(
&self,
addr: &Address,
ts: &Tipset,
get_sector: impl Fn(Partition<'_>) -> BitField,
) -> Result<BitField, Error> {
let actor = self
.get_actor(addr, *ts.parent_state())?
.ok_or_else(|| Error::state("Miner actor not found"))?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.ok_or_else(|| Error::state("Miner actor not found"))?;
.ok_or_else(|| Error::state(format!("Actor {addr} not found at epoch {}", ts.epoch())))?;


let state = miner::State::load(self.blockstore(), actor.code, actor.state)?;

let mut partitions = Vec::new();

state.for_each_deadline(
&self.chain_config().policy,
self.blockstore(),
|_, deadline| {
deadline.for_each(self.blockstore(), |_, partition| {
partitions.push(get_sector(partition));
Ok(())
})
},
)?;

Ok(BitField::union(partitions.iter()))
}

/// Retrieves miner power.
pub fn miner_power(&self, addr: &Address, ts: &Tipset) -> Result<MinerPower, Error> {
if let Some((miner_power, total_power)) = self.get_power(ts.parent_state(), Some(addr))? {
return Ok(MinerPower {
miner_power,
total_power,
has_min_power: true,
});
}

Ok(MinerPower {
has_min_power: false,
miner_power: Default::default(),
total_power: Default::default(),
})
}

pub fn get_verified_registry_actor_state(
&self,
ts: &Tipset,
) -> anyhow::Result<verifreg::State> {
let act = self
.get_actor(&Address::VERIFIED_REGISTRY_ACTOR, *ts.parent_state())
.map_err(Error::state)?
.ok_or_else(|| Error::state("actor not found"))?;
verifreg::State::load(self.blockstore(), act.code, act.state)
}

pub fn get_claim(
&self,
addr: &Address,
ts: &Tipset,
claim_id: ClaimID,
) -> anyhow::Result<Option<Claim>> {
let id_address = self.lookup_required_id(addr, ts)?;
let state = self.get_verified_registry_actor_state(ts)?;
state.get_claim(self.blockstore(), id_address, claim_id)
}

pub fn get_all_claims(&self, ts: &Tipset) -> anyhow::Result<HashMap<ClaimID, Claim>> {
let state = self.get_verified_registry_actor_state(ts)?;
state.get_all_claims(self.blockstore())
}

pub fn get_allocation(
&self,
addr: &Address,
ts: &Tipset,
allocation_id: AllocationID,
) -> anyhow::Result<Option<Allocation>> {
let id_address = self.lookup_required_id(addr, ts)?;
let state = self.get_verified_registry_actor_state(ts)?;
state.get_allocation(self.blockstore(), id_address.id()?, allocation_id)
}

pub fn get_all_allocations(
&self,
ts: &Tipset,
) -> anyhow::Result<HashMap<AllocationID, Allocation>> {
let state = self.get_verified_registry_actor_state(ts)?;
state.get_all_allocations(self.blockstore())
}

pub fn verified_client_status(
&self,
addr: &Address,
ts: &Tipset,
) -> anyhow::Result<Option<DataCap>> {
let id = self.lookup_required_id(addr, ts)?;
let network_version = self.get_network_version(ts.epoch());

// Pre-network v17 (actor v9), the verified client data cap lives in the
// verified registry actor; at/after v17 it lives in the datacap token actor.
// Original: https://github.com/filecoin-project/lotus/blob/5e76b05b17771da6939c7b0bf65127c3dc70ee23/node/impl/full/state.go#L1627-L1664.
if (u32::from(network_version.0)) < 17 {
let state = self.get_verified_registry_actor_state(ts)?;
return state.verified_client_data_cap(self.blockstore(), id);
}

let act = self
.get_actor(&Address::DATACAP_TOKEN_ACTOR, *ts.parent_state())
.map_err(Error::state)?
.ok_or_else(|| Error::state("Miner actor not found"))?;
Comment on lines +171 to +174
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the actor name in this error path.

Line 174 reports "Miner actor not found", but this branch is loading Address::DATACAP_TOKEN_ACTOR. That makes missing-datacap failures harder to diagnose in RPC logs and responses.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/state_manager/actor_queries.rs` around lines 171 - 174, The error message
for the branch that loads Address::DATACAP_TOKEN_ACTOR is wrong; update the
Error::state call in the get_actor result handling (where
get_actor(&Address::DATACAP_TOKEN_ACTOR, *ts.parent_state()) is used) to return
a message indicating the datacap actor is missing (e.g., "Datacap actor not
found") instead of "Miner actor not found" so logs and RPC responses correctly
reflect the failing actor.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akaladarshi is this valid?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Contributor

@hanabi1224 hanabi1224 Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this is from the original code, but maybe we should correct it here


let state = datacap::State::load(self.blockstore(), act.code, act.state)?;

state.verified_client_data_cap(self.blockstore(), id)
}
}
108 changes: 108 additions & 0 deletions src/state_manager/address_resolution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2019-2026 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use super::*;
use crate::shim::address::{Payload, Protocol};
use anyhow::Context as _;
use bls_signatures::{PublicKey as BlsPublicKey, Serialize as _};

impl<DB> StateManager<DB>
where
DB: Blockstore + Send + Sync + 'static,
{
/// Returns a BLS public key from provided address
pub fn get_bls_public_key(
db: &Arc<DB>,
addr: &Address,
state_cid: cid::Cid,
) -> Result<BlsPublicKey, Error> {
let state = StateTree::new_from_root(Arc::clone(db), &state_cid)
.map_err(|e| Error::Other(e.to_string()))?;
let kaddr =
resolve_to_key_addr(&state, db, addr).context("Failed to resolve key address")?;

match kaddr.into_payload() {
Payload::BLS(key) => BlsPublicKey::from_bytes(&key)
.context("Failed to construct bls public key")
.map_err(Error::from),
_ => Err(Error::state(
"Address must be BLS address to load bls public key",
)),
}
}

/// Looks up ID [Address] from the state at the given [Tipset].
pub fn lookup_id(&self, addr: &Address, ts: &Tipset) -> Result<Option<Address>, Error> {
let state_tree = StateTree::new_from_root(self.blockstore_owned(), ts.parent_state())
.map_err(|e| format!("{e:?}"))?;
Ok(state_tree
.lookup_id(addr)
.map_err(|e| Error::Other(e.to_string()))?
.map(Address::new_id))
}

/// Looks up required ID [Address] from the state at the given [Tipset].
pub fn lookup_required_id(&self, addr: &Address, ts: &Tipset) -> Result<Address, Error> {
self.lookup_id(addr, ts)?
.ok_or_else(|| Error::Other(format!("Failed to lookup the id address {addr}")))
}

/// Similar to `resolve_to_key_addr` in the `forest_vm` [`crate::state_manager`] but does not
/// allow `Actor` type of addresses. Uses `ts` to generate the VM state.
pub async fn resolve_to_key_addr(
self: &Arc<Self>,
addr: &Address,
ts: &Tipset,
) -> anyhow::Result<Address> {
match addr.protocol() {
Protocol::BLS | Protocol::Secp256k1 | Protocol::Delegated => return Ok(*addr),
Protocol::Actor => {
return Err(Error::Other(
"cannot resolve actor address to key address".to_string(),
)
.into());
}
_ => {}
};

// First try to resolve the actor in the parent state, so we don't have to
// compute anything.
let state = StateTree::new_from_root(self.blockstore_owned(), ts.parent_state())?;
if let Ok(addr) = resolve_to_key_addr(&state, self.blockstore(), addr) {
return Ok(addr);
}

// If that fails, compute the tip-set and try again.
let TipsetState { state_root, .. } = self.load_tipset_state(ts).await?;
let state = StateTree::new_from_root(self.blockstore_owned(), &state_root)?;

resolve_to_key_addr(&state, self.blockstore(), addr)
}

pub async fn resolve_to_deterministic_address(
self: &Arc<Self>,
address: Address,
ts: &Tipset,
) -> anyhow::Result<Address> {
use crate::shim::address::Protocol::*;
match address.protocol() {
BLS | Secp256k1 | Delegated => Ok(address),
Actor => anyhow::bail!("cannot resolve actor address to key address"),
_ => {
// First try to resolve the actor in the parent state, so we don't have to compute anything.
if let Ok(state) =
StateTree::new_from_root(self.blockstore_owned(), ts.parent_state())
&& let Ok(address) = state
.resolve_to_deterministic_addr(self.chain_store().blockstore(), address)
{
return Ok(address);
}

// If that fails, compute the tip-set and try again.
let TipsetState { state_root, .. } = self.load_tipset_state(ts).await?;
let state = StateTree::new_from_root(self.blockstore_owned(), &state_root)?;
state.resolve_to_deterministic_addr(self.chain_store().blockstore(), address)
}
}
}
}
Loading
Loading