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