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 pub fn heaviest_tipset(&self) -> Tipset {
243 self.chain_store().heaviest_tipset()
244 }
245
246 pub async fn maybe_rewind_heaviest_tipset(&self) -> anyhow::Result<()> {
251 while self.maybe_rewind_heaviest_tipset_once().await? {}
252 Ok(())
253 }
254
255 async fn maybe_rewind_heaviest_tipset_once(&self) -> anyhow::Result<bool> {
256 let head = self.heaviest_tipset();
257 if let Some(info) = self
258 .chain_config()
259 .network_height_with_actor_bundle(head.epoch())
260 {
261 let expected_height_info = info.info;
262 let expected_bundle = info.manifest(self.db())?;
263 let expected_bundle_metadata = expected_bundle.metadata()?;
264 let state = self.get_state_tree(head.parent_state())?;
265 let bundle_metadata = state.get_actor_bundle_metadata()?;
266 if expected_bundle_metadata != bundle_metadata {
267 let current_epoch = head.epoch();
268 let target_head = self
269 .chain_index()
270 .load_required_tipset_by_height(
271 (expected_height_info.epoch - 1).max(0),
272 head,
273 ResolveNullTipset::TakeOlder,
274 )
275 .await?;
276 let target_epoch = target_head.epoch();
277 let bundle_version = &bundle_metadata.version;
278 let expected_bundle_version = &expected_bundle_metadata.version;
279 if target_epoch < current_epoch {
280 tracing::warn!(
281 "rewinding chain head from {current_epoch} to {target_epoch}, actor bundle: {bundle_version}, expected: {expected_bundle_version}"
282 );
283 if self.db().has(target_head.parent_state())? {
284 self.chain_store().set_heaviest_tipset(target_head)?;
285 return Ok(true);
286 } else {
287 anyhow::bail!(
288 "failed to rewind, state tree @ {target_epoch} is missing from blockstore: {}",
289 target_head.parent_state()
290 );
291 }
292 }
293 }
294 }
295 Ok(false)
296 }
297
298 pub fn beacon_schedule(&self) -> &Arc<BeaconSchedule> {
299 &self.beacon
300 }
301
302 pub fn get_network_version(&self, epoch: ChainEpoch) -> NetworkVersion {
304 self.chain_config().network_version(epoch)
305 }
306
307 pub fn get_state_tree(&self, state_cid: &Cid) -> anyhow::Result<StateTree<DbImpl>> {
309 StateTree::new_from_root(self.chain_index().db(), state_cid)
310 }
311
312 pub fn get_actor(&self, addr: &Address, state_cid: Cid) -> anyhow::Result<Option<ActorState>> {
314 let state = self.get_state_tree(&state_cid)?;
315 state.get_actor(addr)
316 }
317
318 pub fn get_actor_state<S: LoadActorStateFromBlockstore>(
320 &self,
321 ts: &Tipset,
322 ) -> anyhow::Result<S> {
323 let state_tree = self.get_state_tree(ts.parent_state())?;
324 state_tree.get_actor_state()
325 }
326
327 pub fn get_actor_state_from_address<S: LoadActorStateFromBlockstore>(
329 &self,
330 ts: &Tipset,
331 actor_address: &Address,
332 ) -> anyhow::Result<S> {
333 let state_tree = self.get_state_tree(ts.parent_state())?;
334 state_tree.get_actor_state_from_address(actor_address)
335 }
336
337 pub fn get_required_actor(&self, addr: &Address, state_cid: Cid) -> anyhow::Result<ActorState> {
339 let state = self.get_state_tree(&state_cid)?;
340 state.get_actor(addr)?.with_context(|| {
341 format!("Failed to load actor with addr={addr}, state_cid={state_cid}")
342 })
343 }
344
345 pub fn db(&self) -> &DbImpl {
347 self.cs.db()
348 }
349
350 pub fn db_owned(&self) -> DbImpl {
351 self.cs.db_owned()
352 }
353
354 pub fn chain_store(&self) -> &ChainStore {
356 &self.cs
357 }
358
359 pub fn chain_index(&self) -> &ChainIndex {
361 self.cs.chain_index()
362 }
363
364 pub fn chain_config(&self) -> &Arc<ChainConfig> {
366 self.cs.chain_config()
367 }
368
369 pub fn chain_rand(&self, tipset: Tipset) -> ChainRand {
370 ChainRand::new(
371 self.chain_config().shallow_clone(),
372 tipset,
373 self.chain_index().shallow_clone(),
374 self.beacon.shallow_clone(),
375 )
376 }
377
378 pub fn get_network_state_name(
380 &self,
381 state_cid: Cid,
382 ) -> anyhow::Result<crate::networks::StateNetworkName> {
383 let init_act = self
384 .get_actor(&init::ADDRESS.into(), state_cid)?
385 .ok_or_else(|| Error::state("Init actor address could not be resolved"))?;
386 Ok(State::load(self.db(), init_act.code, init_act.state)?
387 .into_network_name()
388 .into())
389 }
390
391 pub fn is_miner_slashed(&self, addr: &Address, state_cid: &Cid) -> anyhow::Result<bool, Error> {
393 let actor = self
394 .get_actor(&Address::POWER_ACTOR, *state_cid)?
395 .ok_or_else(|| Error::state("Power actor address could not be resolved"))?;
396
397 let spas = power::State::load(self.db(), actor.code, actor.state)?;
398
399 Ok(spas.miner_power(self.db(), addr)?.is_none())
400 }
401
402 pub fn get_miner_work_addr(&self, state_cid: Cid, addr: &Address) -> Result<Address, Error> {
404 let state = StateTree::new_from_root(self.db(), &state_cid).map_err(Error::other)?;
405 let ms: miner::State = state.get_actor_state_from_address(addr)?;
406 let info = ms.info(self.db()).map_err(|e| e.to_string())?;
407 let addr = state.resolve_to_deterministic_address(self.db(), info.worker())?;
408 Ok(addr)
409 }
410
411 pub fn get_power(
414 &self,
415 state_cid: &Cid,
416 addr: Option<&Address>,
417 ) -> anyhow::Result<Option<(power::Claim, power::Claim)>, Error> {
418 let actor = self
419 .get_actor(&Address::POWER_ACTOR, *state_cid)?
420 .ok_or_else(|| Error::state("Power actor address could not be resolved"))?;
421
422 let spas = power::State::load(self.db(), actor.code, actor.state)?;
423
424 let t_pow = spas.total_power();
425
426 if let Some(maddr) = addr {
427 let m_pow = spas
428 .miner_power(self.db(), maddr)?
429 .ok_or_else(|| Error::state(format!("Miner for address {maddr} not found")))?;
430
431 let min_pow = spas.miner_nominal_power_meets_consensus_minimum(
432 &self.chain_config().policy,
433 self.db(),
434 maddr,
435 )?;
436 if min_pow {
437 return Ok(Some((m_pow, t_pow)));
438 }
439 }
440
441 Ok(None)
442 }
443
444 pub fn get_all_sectors(
446 &self,
447 addr: &Address,
448 ts: &Tipset,
449 ) -> anyhow::Result<Vec<SectorOnChainInfo>> {
450 let actor = self
451 .get_actor(addr, *ts.parent_state())?
452 .ok_or_else(|| Error::state(format!("Miner actor {addr} not found")))?;
453 let state = miner::State::load(self.db(), actor.code, actor.state)?;
454 state.load_sectors_ext(self.db(), None)
455 }
456}