1#[cfg(test)]
5mod tests;
6
7mod actor_queries;
8mod address_resolution;
9pub mod cache;
10pub mod chain_rand;
11pub mod circulating_supply;
12mod errors;
13mod execution;
14mod message_search;
15mod message_simulation;
16mod mining;
17mod state_computation;
18pub mod utils;
19
20pub use self::errors::*;
21pub use self::state_computation::{apply_block_messages_blocking, validate_tipsets_blocking};
22use crate::beacon::BeaconSchedule;
23use crate::blocks::{Tipset, TipsetKey};
24use crate::chain::{
25 ChainStore,
26 index::{ChainIndex, ResolveNullTipset},
27};
28use crate::db::DbImpl;
29use crate::interpreter::MessageCallbackCtx;
30use crate::lotus_json::{LotusJson, lotus_json_with_self};
31use crate::message::ChainMessage;
32use crate::networks::ChainConfig;
33use crate::prelude::*;
34use crate::rpc::state::ApiInvocResult;
35use crate::rpc::types::SectorOnChainInfo;
36use crate::shim::actors::init::{self, State};
37use crate::shim::actors::*;
38use crate::shim::address::AddressId;
39use crate::shim::{
40 actors::{LoadActorStateFromBlockstore, miner::ext::MinerStateExt as _},
41 executor::{Receipt, StampedEvent},
42};
43use crate::shim::{
44 address::Address,
45 clock::ChainEpoch,
46 econ::TokenAmount,
47 machine::{GLOBAL_MULTI_ENGINE, MultiEngine},
48 state_tree::{ActorState, StateTree},
49 version::NetworkVersion,
50};
51use crate::state_manager::cache::ForestCache;
52use crate::utils::cache::SizeTrackingCache;
53use crate::utils::get_size::GetSize;
54use anyhow::Context as _;
55use chain_rand::ChainRand;
56use nonzero_ext::nonzero;
57use schemars::JsonSchema;
58use serde::{Deserialize, Serialize};
59use std::num::NonZeroUsize;
60use tracing::warn;
61
62const DEFAULT_TIPSET_CACHE_SIZE: NonZeroUsize = nonzero!(8192usize); const DEFAULT_ID_TO_DETERMINISTIC_ADDRESS_CACHE_SIZE: NonZeroUsize = nonzero!(8192usize); const DEFAULT_TRACE_CACHE_SIZE: NonZeroUsize = nonzero!(16usize); pub const EVENTS_AMT_BITWIDTH: u32 = 5;
66pub type IdToAddressCache = SizeTrackingCache<AddressId, Address>;
67
68#[derive(Debug, Clone)]
73pub struct ExecutedMessage {
74 pub message: ChainMessage,
75 pub receipt: Receipt,
76 pub events: Option<Vec<StampedEvent>>,
77}
78
79impl GetSize for ExecutedMessage {
80 fn get_heap_size_with_tracker<T: get_size2::GetSizeTracker>(
81 &self,
82 mut tracker: T,
83 ) -> (usize, T) {
84 (
85 self.message.get_heap_size_with_tracker(&mut tracker).0
86 + self.receipt.get_heap_size_with_tracker(&mut tracker).0
87 + self.events.get_heap_size_with_tracker(&mut tracker).0,
88 tracker,
89 )
90 }
91}
92
93#[derive(Debug, Clone, GetSize)]
95pub struct ExecutedTipset {
96 #[get_size(ignore)]
98 pub state_root: Cid,
99 #[get_size(ignore)]
101 pub receipt_root: Cid,
102 pub executed_messages: Arc<Vec<ExecutedMessage>>,
105}
106
107#[derive(Debug, Clone, GetSize)]
109pub struct TipsetState {
110 #[get_size(ignore)]
112 pub state_root: Cid,
113 #[allow(dead_code)]
115 #[get_size(ignore)]
116 pub receipt_root: Cid,
117}
118
119impl From<ExecutedTipset> for TipsetState {
120 fn from(
121 ExecutedTipset {
122 state_root,
123 receipt_root,
124 ..
125 }: ExecutedTipset,
126 ) -> Self {
127 Self {
128 state_root,
129 receipt_root,
130 }
131 }
132}
133
134impl From<&ExecutedTipset> for TipsetState {
135 fn from(
136 ExecutedTipset {
137 state_root,
138 receipt_root,
139 ..
140 }: &ExecutedTipset,
141 ) -> Self {
142 Self {
143 state_root: *state_root,
144 receipt_root: *receipt_root,
145 }
146 }
147}
148
149#[derive(
151 Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, JsonSchema,
152)]
153#[serde(rename_all = "PascalCase")]
154pub struct MarketBalance {
155 #[schemars(with = "LotusJson<TokenAmount>")]
156 #[serde(with = "crate::lotus_json")]
157 pub escrow: TokenAmount,
158 #[schemars(with = "LotusJson<TokenAmount>")]
159 #[serde(with = "crate::lotus_json")]
160 pub locked: TokenAmount,
161}
162lotus_json_with_self!(MarketBalance);
163
164pub struct StateManager {
170 cs: ChainStore,
172 cache: ForestCache<TipsetKey, ExecutedTipset>,
174 trace_cache: ForestCache<TipsetKey, (CidWrapper, Vec<Arc<ApiInvocResult>>)>,
176 id_to_deterministic_address_cache: Option<IdToAddressCache>,
181 beacon: Arc<crate::beacon::BeaconSchedule>,
182 engine: Arc<MultiEngine>,
183 replay_semaphore: Arc<tokio::sync::Semaphore>,
185}
186
187impl ShallowClone for StateManager {
188 fn shallow_clone(&self) -> Self {
189 Self {
190 cs: self.cs.shallow_clone(),
191 cache: self.cache.shallow_clone(),
192 trace_cache: self.trace_cache.shallow_clone(),
193 id_to_deterministic_address_cache: self
194 .id_to_deterministic_address_cache
195 .as_ref()
196 .map(ShallowClone::shallow_clone),
197 beacon: self.beacon.shallow_clone(),
198 engine: self.engine.shallow_clone(),
199 replay_semaphore: self.replay_semaphore.shallow_clone(),
200 }
201 }
202}
203
204#[allow(clippy::type_complexity)]
205pub const NO_CALLBACK: Option<fn(MessageCallbackCtx<'_>) -> anyhow::Result<()>> = None;
206
207#[derive(Debug, Copy, Clone, Default)]
209pub enum VMFlush {
210 Flush,
211 #[default]
212 Skip,
213}
214
215impl StateManager {
216 pub fn new(cs: ChainStore) -> anyhow::Result<Self> {
217 Self::new_with_engine(cs, GLOBAL_MULTI_ENGINE.clone())
218 }
219
220 pub fn new_with_engine(cs: ChainStore, engine: Arc<MultiEngine>) -> anyhow::Result<Self> {
221 let genesis = cs.genesis_block_header();
222 let beacon = Arc::new(cs.chain_config().get_beacon_schedule(genesis.timestamp));
223
224 Ok(Self {
225 cs,
226 cache: ForestCache::new("tipset_state_executed_tipset"), trace_cache: ForestCache::with_size("tipset_trace", DEFAULT_TRACE_CACHE_SIZE),
228 beacon,
229 engine,
230 id_to_deterministic_address_cache: Some(SizeTrackingCache::new_with_metrics(
231 "id_to_deterministic_address",
232 DEFAULT_ID_TO_DETERMINISTIC_ADDRESS_CACHE_SIZE,
233 )),
234 replay_semaphore: Arc::new(tokio::sync::Semaphore::new(Self::replay_concurrency())),
235 })
236 }
237
238 fn replay_concurrency() -> usize {
244 static VALUE: std::sync::LazyLock<NonZeroUsize> = std::sync::LazyLock::new(|| {
245 let default = std::thread::available_parallelism()
246 .ok()
247 .and_then(|n| NonZeroUsize::new(n.get() / 2))
248 .unwrap_or(nonzero!(1usize));
249 crate::utils::misc::env::env_or_default("FOREST_RPC_REPLAY_CONCURRENCY", default)
250 });
251 VALUE.get()
252 }
253
254 async fn replay_permit(&self) -> tokio::sync::OwnedSemaphorePermit {
259 self.replay_semaphore
260 .clone()
261 .acquire_owned()
262 .await
263 .expect("replay semaphore is never closed")
264 }
265
266 pub fn with_id_address_cache_disabled(mut self) -> Self {
269 self.id_to_deterministic_address_cache = None;
270 self
271 }
272
273 #[cfg(test)]
275 pub(crate) fn id_to_deterministic_address_cache(&self) -> Option<&IdToAddressCache> {
276 self.id_to_deterministic_address_cache.as_ref()
277 }
278
279 pub fn heaviest_tipset(&self) -> Tipset {
281 self.chain_store().heaviest_tipset()
282 }
283
284 pub async fn maybe_rewind_heaviest_tipset(&self) -> anyhow::Result<()> {
289 while self.maybe_rewind_heaviest_tipset_once().await? {}
290 Ok(())
291 }
292
293 async fn maybe_rewind_heaviest_tipset_once(&self) -> anyhow::Result<bool> {
294 let head = self.heaviest_tipset();
295 if let Some(info) = self
296 .chain_config()
297 .network_height_with_actor_bundle(head.epoch())
298 {
299 let expected_height_info = info.info;
300 let expected_bundle = info.manifest(self.db())?;
301 let expected_bundle_metadata = expected_bundle.metadata()?;
302 let state = self.get_state_tree(head.parent_state())?;
303 let bundle_metadata = state.get_actor_bundle_metadata()?;
304 if expected_bundle_metadata != bundle_metadata {
305 let current_epoch = head.epoch();
306 let target_head = self
307 .chain_index()
308 .load_required_tipset_by_height(
309 (expected_height_info.epoch - 1).max(0),
310 head,
311 ResolveNullTipset::TakeOlder,
312 )
313 .await?;
314 let target_epoch = target_head.epoch();
315 let bundle_version = &bundle_metadata.version;
316 let expected_bundle_version = &expected_bundle_metadata.version;
317 if target_epoch < current_epoch {
318 tracing::warn!(
319 "rewinding chain head from {current_epoch} to {target_epoch}, actor bundle: {bundle_version}, expected: {expected_bundle_version}"
320 );
321 if self.db().has(target_head.parent_state())? {
322 self.chain_store().set_heaviest_tipset(target_head)?;
323 return Ok(true);
324 } else {
325 anyhow::bail!(
326 "failed to rewind, state tree @ {target_epoch} is missing from blockstore: {}",
327 target_head.parent_state()
328 );
329 }
330 }
331 }
332 }
333 Ok(false)
334 }
335
336 pub fn beacon_schedule(&self) -> &Arc<BeaconSchedule> {
337 &self.beacon
338 }
339
340 pub fn get_network_version(&self, epoch: ChainEpoch) -> NetworkVersion {
342 self.chain_config().network_version(epoch)
343 }
344
345 pub fn get_state_tree(&self, state_cid: &Cid) -> anyhow::Result<StateTree<DbImpl>> {
347 StateTree::new_from_root(self.chain_index().db(), state_cid)
348 }
349
350 pub fn get_actor(&self, addr: &Address, state_cid: Cid) -> anyhow::Result<Option<ActorState>> {
352 let state = self.get_state_tree(&state_cid)?;
353 state.get_actor(addr)
354 }
355
356 pub fn get_actor_state<S: LoadActorStateFromBlockstore>(
358 &self,
359 ts: &Tipset,
360 ) -> anyhow::Result<S> {
361 let state_tree = self.get_state_tree(ts.parent_state())?;
362 state_tree.get_actor_state()
363 }
364
365 pub fn get_actor_state_from_address<S: LoadActorStateFromBlockstore>(
367 &self,
368 ts: &Tipset,
369 actor_address: &Address,
370 ) -> anyhow::Result<S> {
371 let state_tree = self.get_state_tree(ts.parent_state())?;
372 state_tree.get_actor_state_from_address(actor_address)
373 }
374
375 pub fn get_required_actor(&self, addr: &Address, state_cid: Cid) -> anyhow::Result<ActorState> {
377 let state = self.get_state_tree(&state_cid)?;
378 state.get_actor(addr)?.with_context(|| {
379 format!("Failed to load actor with addr={addr}, state_cid={state_cid}")
380 })
381 }
382
383 pub fn db(&self) -> &DbImpl {
385 self.cs.db()
386 }
387
388 pub fn db_owned(&self) -> DbImpl {
389 self.cs.db_owned()
390 }
391
392 pub fn chain_store(&self) -> &ChainStore {
394 &self.cs
395 }
396
397 pub fn chain_index(&self) -> &ChainIndex {
399 self.cs.chain_index()
400 }
401
402 pub fn chain_config(&self) -> &Arc<ChainConfig> {
404 self.cs.chain_config()
405 }
406
407 pub fn chain_rand(&self, tipset: Tipset) -> ChainRand {
408 ChainRand::new(
409 self.chain_config().shallow_clone(),
410 tipset,
411 self.chain_index().shallow_clone(),
412 self.beacon.shallow_clone(),
413 )
414 }
415
416 pub fn get_network_state_name(
418 &self,
419 state_cid: Cid,
420 ) -> anyhow::Result<crate::networks::StateNetworkName> {
421 let init_act = self
422 .get_actor(&init::ADDRESS.into(), state_cid)?
423 .ok_or_else(|| Error::state("Init actor address could not be resolved"))?;
424 Ok(State::load(self.db(), init_act.code, init_act.state)?
425 .into_network_name()
426 .into())
427 }
428
429 pub fn is_miner_slashed(&self, addr: &Address, state_cid: &Cid) -> anyhow::Result<bool, Error> {
431 let actor = self
432 .get_actor(&Address::POWER_ACTOR, *state_cid)?
433 .ok_or_else(|| Error::state("Power actor address could not be resolved"))?;
434
435 let spas = power::State::load(self.db(), actor.code, actor.state)?;
436
437 Ok(spas.miner_power(self.db(), addr)?.is_none())
438 }
439
440 pub fn get_miner_work_addr(&self, state_cid: Cid, addr: &Address) -> Result<Address, Error> {
442 let state = StateTree::new_from_root(self.db(), &state_cid).map_err(Error::other)?;
443 let ms: miner::State = state.get_actor_state_from_address(addr)?;
444 let info = ms.info(self.db()).map_err(|e| e.to_string())?;
445 let addr = state.resolve_to_deterministic_address(self.db(), info.worker())?;
446 Ok(addr)
447 }
448
449 pub fn get_power(
452 &self,
453 state_cid: &Cid,
454 addr: Option<&Address>,
455 ) -> anyhow::Result<Option<(power::Claim, power::Claim)>, Error> {
456 let actor = self
457 .get_actor(&Address::POWER_ACTOR, *state_cid)?
458 .ok_or_else(|| Error::state("Power actor address could not be resolved"))?;
459
460 let spas = power::State::load(self.db(), actor.code, actor.state)?;
461
462 let t_pow = spas.total_power();
463
464 if let Some(maddr) = addr {
465 let m_pow = spas
466 .miner_power(self.db(), maddr)?
467 .ok_or_else(|| Error::state(format!("Miner for address {maddr} not found")))?;
468
469 let min_pow = spas.miner_nominal_power_meets_consensus_minimum(
470 &self.chain_config().policy,
471 self.db(),
472 maddr,
473 )?;
474 if min_pow {
475 return Ok(Some((m_pow, t_pow)));
476 }
477 }
478
479 Ok(None)
480 }
481
482 pub fn get_all_sectors(
484 &self,
485 addr: &Address,
486 ts: &Tipset,
487 ) -> anyhow::Result<Vec<SectorOnChainInfo>> {
488 let actor = self
489 .get_actor(addr, *ts.parent_state())?
490 .ok_or_else(|| Error::state(format!("Miner actor {addr} not found")))?;
491 let state = miner::State::load(self.db(), actor.code, actor.state)?;
492 state.load_sectors_ext(self.db(), None)
493 }
494}