Skip to content
Merged
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
190 changes: 190 additions & 0 deletions src/state_manager/actor_queries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// 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(format!(
"Miner actor {addr} not found at epoch {}",
ts.epoch()
))
})?;
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(format!(
"Miner 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());

// This is a copy of Lotus code, we need to treat all the actors below version 9
// differently. Which maps to network below version 17.
// 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(format!(
"Data cap actor {} not found",
Address::DATACAP_TOKEN_ACTOR
))
})?;

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

state.verified_client_data_cap(self.blockstore(), id)
}
}
116 changes: 116 additions & 0 deletions src/state_manager/address_resolution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// 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,
) -> 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)
}

/// Similar to [`StateTree::resolve_to_deterministic_addr`] but does not allow [`crate::shim::address::Protocol::Actor`] type of addresses.
/// Uses the [`Tipset`] `ts` to generate the VM state.
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"),
ID => {
let id = address.id()?;
if let Some(cached) = self.id_to_deterministic_address_cache.get_cloned(&id) {
return Ok(cached);
}
// First try to resolve the actor in the parent state, so we don't have to compute anything.
let resolved = 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)
{
address
} else {
// 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)?
};
self.id_to_deterministic_address_cache.push(id, resolved);
Ok(resolved)
}
}
}
}
Loading
Loading