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}
184
185impl ShallowClone for StateManager {
186 fn shallow_clone(&self) -> Self {
187 Self {
188 cs: self.cs.shallow_clone(),
189 cache: self.cache.shallow_clone(),
190 trace_cache: self.trace_cache.shallow_clone(),
191 id_to_deterministic_address_cache: self
192 .id_to_deterministic_address_cache
193 .as_ref()
194 .map(ShallowClone::shallow_clone),
195 beacon: self.beacon.shallow_clone(),
196 engine: self.engine.shallow_clone(),
197 }
198 }
199}
200
201#[allow(clippy::type_complexity)]
202pub const NO_CALLBACK: Option<fn(MessageCallbackCtx<'_>) -> anyhow::Result<()>> = None;
203
204#[derive(Debug, Copy, Clone, Default)]
206pub enum VMFlush {
207 Flush,
208 #[default]
209 Skip,
210}
211
212impl StateManager {
213 pub fn new(cs: ChainStore) -> anyhow::Result<Self> {
214 Self::new_with_engine(cs, GLOBAL_MULTI_ENGINE.clone())
215 }
216
217 pub fn new_with_engine(cs: ChainStore, engine: Arc<MultiEngine>) -> anyhow::Result<Self> {
218 let genesis = cs.genesis_block_header();
219 let beacon = Arc::new(cs.chain_config().get_beacon_schedule(genesis.timestamp));
220
221 Ok(Self {
222 cs,
223 cache: ForestCache::new("tipset_state_executed_tipset"), trace_cache: ForestCache::with_size("tipset_trace", DEFAULT_TRACE_CACHE_SIZE),
225 beacon,
226 engine,
227 id_to_deterministic_address_cache: Some(SizeTrackingCache::new_with_metrics(
228 "id_to_deterministic_address",
229 DEFAULT_ID_TO_DETERMINISTIC_ADDRESS_CACHE_SIZE,
230 )),
231 })
232 }
233
234 pub fn with_id_address_cache_disabled(mut self) -> Self {
237 self.id_to_deterministic_address_cache = None;
238 self
239 }
240
241 #[cfg(test)]
243 pub(crate) fn id_to_deterministic_address_cache(&self) -> Option<&IdToAddressCache> {
244 self.id_to_deterministic_address_cache.as_ref()
245 }
246
247 pub fn heaviest_tipset(&self) -> Tipset {
249 self.chain_store().heaviest_tipset()
250 }
251
252 pub async fn maybe_rewind_heaviest_tipset(&self) -> anyhow::Result<()> {
257 while self.maybe_rewind_heaviest_tipset_once().await? {}
258 Ok(())
259 }
260
261 async fn maybe_rewind_heaviest_tipset_once(&self) -> anyhow::Result<bool> {
262 let head = self.heaviest_tipset();
263 if let Some(info) = self
264 .chain_config()
265 .network_height_with_actor_bundle(head.epoch())
266 {
267 let expected_height_info = info.info;
268 let expected_bundle = info.manifest(self.db())?;
269 let expected_bundle_metadata = expected_bundle.metadata()?;
270 let state = self.get_state_tree(head.parent_state())?;
271 let bundle_metadata = state.get_actor_bundle_metadata()?;
272 if expected_bundle_metadata != bundle_metadata {
273 let current_epoch = head.epoch();
274 let target_head = self
275 .chain_index()
276 .load_required_tipset_by_height(
277 (expected_height_info.epoch - 1).max(0),
278 head,
279 ResolveNullTipset::TakeOlder,
280 )
281 .await?;
282 let target_epoch = target_head.epoch();
283 let bundle_version = &bundle_metadata.version;
284 let expected_bundle_version = &expected_bundle_metadata.version;
285 if target_epoch < current_epoch {
286 tracing::warn!(
287 "rewinding chain head from {current_epoch} to {target_epoch}, actor bundle: {bundle_version}, expected: {expected_bundle_version}"
288 );
289 if self.db().has(target_head.parent_state())? {
290 self.chain_store().set_heaviest_tipset(target_head)?;
291 return Ok(true);
292 } else {
293 anyhow::bail!(
294 "failed to rewind, state tree @ {target_epoch} is missing from blockstore: {}",
295 target_head.parent_state()
296 );
297 }
298 }
299 }
300 }
301 Ok(false)
302 }
303
304 pub fn beacon_schedule(&self) -> &Arc<BeaconSchedule> {
305 &self.beacon
306 }
307
308 pub fn get_network_version(&self, epoch: ChainEpoch) -> NetworkVersion {
310 self.chain_config().network_version(epoch)
311 }
312
313 pub fn get_state_tree(&self, state_cid: &Cid) -> anyhow::Result<StateTree<DbImpl>> {
315 StateTree::new_from_root(self.chain_index().db(), state_cid)
316 }
317
318 pub fn get_actor(&self, addr: &Address, state_cid: Cid) -> anyhow::Result<Option<ActorState>> {
320 let state = self.get_state_tree(&state_cid)?;
321 state.get_actor(addr)
322 }
323
324 pub fn get_actor_state<S: LoadActorStateFromBlockstore>(
326 &self,
327 ts: &Tipset,
328 ) -> anyhow::Result<S> {
329 let state_tree = self.get_state_tree(ts.parent_state())?;
330 state_tree.get_actor_state()
331 }
332
333 pub fn get_actor_state_from_address<S: LoadActorStateFromBlockstore>(
335 &self,
336 ts: &Tipset,
337 actor_address: &Address,
338 ) -> anyhow::Result<S> {
339 let state_tree = self.get_state_tree(ts.parent_state())?;
340 state_tree.get_actor_state_from_address(actor_address)
341 }
342
343 pub fn get_required_actor(&self, addr: &Address, state_cid: Cid) -> anyhow::Result<ActorState> {
345 let state = self.get_state_tree(&state_cid)?;
346 state.get_actor(addr)?.with_context(|| {
347 format!("Failed to load actor with addr={addr}, state_cid={state_cid}")
348 })
349 }
350
351 pub fn db(&self) -> &DbImpl {
353 self.cs.db()
354 }
355
356 pub fn db_owned(&self) -> DbImpl {
357 self.cs.db_owned()
358 }
359
360 pub fn chain_store(&self) -> &ChainStore {
362 &self.cs
363 }
364
365 pub fn chain_index(&self) -> &ChainIndex {
367 self.cs.chain_index()
368 }
369
370 pub fn chain_config(&self) -> &Arc<ChainConfig> {
372 self.cs.chain_config()
373 }
374
375 pub fn chain_rand(&self, tipset: Tipset) -> ChainRand {
376 ChainRand::new(
377 self.chain_config().shallow_clone(),
378 tipset,
379 self.chain_index().shallow_clone(),
380 self.beacon.shallow_clone(),
381 )
382 }
383
384 pub fn get_network_state_name(
386 &self,
387 state_cid: Cid,
388 ) -> anyhow::Result<crate::networks::StateNetworkName> {
389 let init_act = self
390 .get_actor(&init::ADDRESS.into(), state_cid)?
391 .ok_or_else(|| Error::state("Init actor address could not be resolved"))?;
392 Ok(State::load(self.db(), init_act.code, init_act.state)?
393 .into_network_name()
394 .into())
395 }
396
397 pub fn is_miner_slashed(&self, addr: &Address, state_cid: &Cid) -> anyhow::Result<bool, Error> {
399 let actor = self
400 .get_actor(&Address::POWER_ACTOR, *state_cid)?
401 .ok_or_else(|| Error::state("Power actor address could not be resolved"))?;
402
403 let spas = power::State::load(self.db(), actor.code, actor.state)?;
404
405 Ok(spas.miner_power(self.db(), addr)?.is_none())
406 }
407
408 pub fn get_miner_work_addr(&self, state_cid: Cid, addr: &Address) -> Result<Address, Error> {
410 let state = StateTree::new_from_root(self.db(), &state_cid).map_err(Error::other)?;
411 let ms: miner::State = state.get_actor_state_from_address(addr)?;
412 let info = ms.info(self.db()).map_err(|e| e.to_string())?;
413 let addr = state.resolve_to_deterministic_address(self.db(), info.worker())?;
414 Ok(addr)
415 }
416
417 pub fn get_power(
420 &self,
421 state_cid: &Cid,
422 addr: Option<&Address>,
423 ) -> anyhow::Result<Option<(power::Claim, power::Claim)>, Error> {
424 let actor = self
425 .get_actor(&Address::POWER_ACTOR, *state_cid)?
426 .ok_or_else(|| Error::state("Power actor address could not be resolved"))?;
427
428 let spas = power::State::load(self.db(), actor.code, actor.state)?;
429
430 let t_pow = spas.total_power();
431
432 if let Some(maddr) = addr {
433 let m_pow = spas
434 .miner_power(self.db(), maddr)?
435 .ok_or_else(|| Error::state(format!("Miner for address {maddr} not found")))?;
436
437 let min_pow = spas.miner_nominal_power_meets_consensus_minimum(
438 &self.chain_config().policy,
439 self.db(),
440 maddr,
441 )?;
442 if min_pow {
443 return Ok(Some((m_pow, t_pow)));
444 }
445 }
446
447 Ok(None)
448 }
449
450 pub fn get_all_sectors(
452 &self,
453 addr: &Address,
454 ts: &Tipset,
455 ) -> anyhow::Result<Vec<SectorOnChainInfo>> {
456 let actor = self
457 .get_actor(addr, *ts.parent_state())?
458 .ok_or_else(|| Error::state(format!("Miner actor {addr} not found")))?;
459 let state = miner::State::load(self.db(), actor.code, actor.state)?;
460 state.load_sectors_ext(self.db(), None)
461 }
462}