1#[cfg(test)]
5mod tests;
6
7mod actor_queries;
8mod address_resolution;
9mod 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, validate_tipsets};
22use crate::beacon::BeaconSchedule;
23use crate::blocks::Tipset;
24use crate::chain::{
25 ChainStore,
26 index::{ChainIndex, ResolveNullTipset},
27};
28use crate::db::EthMappingsStore;
29use crate::interpreter::MessageCallbackCtx;
30use crate::interpreter::resolve_to_key_addr;
31use crate::lotus_json::{LotusJson, lotus_json_with_self};
32use crate::message::ChainMessage;
33use crate::networks::ChainConfig;
34use crate::rpc::types::SectorOnChainInfo;
35use crate::shim::actors::init::{self, State};
36use crate::shim::actors::*;
37use crate::shim::address::AddressId;
38use crate::shim::{
39 actors::{LoadActorStateFromBlockstore, miner::ext::MinerStateExt as _},
40 executor::{Receipt, StampedEvent},
41};
42use crate::shim::{
43 address::Address,
44 clock::ChainEpoch,
45 econ::TokenAmount,
46 machine::{GLOBAL_MULTI_ENGINE, MultiEngine},
47 state_tree::{ActorState, StateTree},
48 version::NetworkVersion,
49};
50use crate::state_manager::cache::TipsetStateCache;
51use crate::utils::ShallowClone as _;
52use crate::utils::cache::SizeTrackingLruCache;
53use crate::utils::get_size::{GetSize, vec_heap_size_helper};
54use anyhow::Context as _;
55use chain_rand::ChainRand;
56use cid::Cid;
57use fvm_ipld_blockstore::Blockstore;
58use nonzero_ext::nonzero;
59use schemars::JsonSchema;
60use serde::{Deserialize, Serialize};
61use std::{num::NonZeroUsize, sync::Arc};
62use tracing::warn;
63
64const DEFAULT_TIPSET_CACHE_SIZE: NonZeroUsize = nonzero!(1024usize);
65const DEFAULT_ID_TO_DETERMINISTIC_ADDRESS_CACHE_SIZE: NonZeroUsize = nonzero!(1024usize);
66pub const EVENTS_AMT_BITWIDTH: u32 = 5;
67pub type IdToAddressCache = SizeTrackingLruCache<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(&self) -> usize {
82 self.message.get_heap_size()
83 + self.receipt.get_heap_size()
84 + self
85 .events
86 .as_ref()
87 .map(vec_heap_size_helper)
88 .unwrap_or_default()
89 }
90}
91
92#[derive(Debug, Clone, GetSize)]
94pub struct ExecutedTipset {
95 #[get_size(ignore)]
97 pub state_root: Cid,
98 #[get_size(ignore)]
100 pub receipt_root: Cid,
101 pub executed_messages: Arc<Vec<ExecutedMessage>>,
104}
105
106#[derive(Debug, Clone, GetSize)]
108pub struct TipsetState {
109 #[get_size(ignore)]
111 pub state_root: Cid,
112 #[allow(dead_code)]
114 #[get_size(ignore)]
115 pub receipt_root: Cid,
116}
117
118impl From<ExecutedTipset> for TipsetState {
119 fn from(
120 ExecutedTipset {
121 state_root,
122 receipt_root,
123 ..
124 }: ExecutedTipset,
125 ) -> Self {
126 Self {
127 state_root,
128 receipt_root,
129 }
130 }
131}
132
133impl From<&ExecutedTipset> for TipsetState {
134 fn from(
135 ExecutedTipset {
136 state_root,
137 receipt_root,
138 ..
139 }: &ExecutedTipset,
140 ) -> Self {
141 Self {
142 state_root: *state_root,
143 receipt_root: *receipt_root,
144 }
145 }
146}
147
148#[derive(
150 Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, JsonSchema,
151)]
152#[serde(rename_all = "PascalCase")]
153pub struct MarketBalance {
154 #[schemars(with = "LotusJson<TokenAmount>")]
155 #[serde(with = "crate::lotus_json")]
156 pub escrow: TokenAmount,
157 #[schemars(with = "LotusJson<TokenAmount>")]
158 #[serde(with = "crate::lotus_json")]
159 pub locked: TokenAmount,
160}
161lotus_json_with_self!(MarketBalance);
162
163pub struct StateManager<DB> {
169 cs: Arc<ChainStore<DB>>,
171 cache: TipsetStateCache<ExecutedTipset>,
173 id_to_deterministic_address_cache: IdToAddressCache,
174 beacon: Arc<crate::beacon::BeaconSchedule>,
175 engine: Arc<MultiEngine>,
176}
177
178#[allow(clippy::type_complexity)]
179pub const NO_CALLBACK: Option<fn(MessageCallbackCtx<'_>) -> anyhow::Result<()>> = None;
180
181#[derive(Debug, Copy, Clone, Default)]
183pub enum VMFlush {
184 Flush,
185 #[default]
186 Skip,
187}
188
189impl<DB> StateManager<DB>
190where
191 DB: Blockstore,
192{
193 pub fn new(cs: Arc<ChainStore<DB>>) -> anyhow::Result<Self> {
194 Self::new_with_engine(cs, GLOBAL_MULTI_ENGINE.clone())
195 }
196
197 pub fn new_with_engine(
198 cs: Arc<ChainStore<DB>>,
199 engine: Arc<MultiEngine>,
200 ) -> anyhow::Result<Self> {
201 let genesis = cs.genesis_block_header();
202 let beacon = Arc::new(cs.chain_config().get_beacon_schedule(genesis.timestamp));
203
204 Ok(Self {
205 cs,
206 cache: TipsetStateCache::new("executed_tipset"), beacon,
208 engine,
209 id_to_deterministic_address_cache: SizeTrackingLruCache::new_with_metrics(
210 "id_to_deterministic_address".into(),
211 DEFAULT_ID_TO_DETERMINISTIC_ADDRESS_CACHE_SIZE,
212 ),
213 })
214 }
215
216 pub fn heaviest_tipset(&self) -> Tipset {
218 self.chain_store().heaviest_tipset()
219 }
220
221 pub fn maybe_rewind_heaviest_tipset(&self) -> anyhow::Result<()>
226 where
227 DB: EthMappingsStore,
228 {
229 while self.maybe_rewind_heaviest_tipset_once()? {}
230 Ok(())
231 }
232
233 fn maybe_rewind_heaviest_tipset_once(&self) -> anyhow::Result<bool>
234 where
235 DB: EthMappingsStore,
236 {
237 let head = self.heaviest_tipset();
238 if let Some(info) = self
239 .chain_config()
240 .network_height_with_actor_bundle(head.epoch())
241 {
242 let expected_height_info = info.info;
243 let expected_bundle = info.manifest(self.blockstore())?;
244 let expected_bundle_metadata = expected_bundle.metadata()?;
245 let state = self.get_state_tree(head.parent_state())?;
246 let bundle_metadata = state.get_actor_bundle_metadata()?;
247 if expected_bundle_metadata != bundle_metadata {
248 let current_epoch = head.epoch();
249 let target_head = self.chain_index().load_required_tipset_by_height(
250 (expected_height_info.epoch - 1).max(0),
251 head,
252 ResolveNullTipset::TakeOlder,
253 )?;
254 let target_epoch = target_head.epoch();
255 let bundle_version = &bundle_metadata.version;
256 let expected_bundle_version = &expected_bundle_metadata.version;
257 if target_epoch < current_epoch {
258 tracing::warn!(
259 "rewinding chain head from {current_epoch} to {target_epoch}, actor bundle: {bundle_version}, expected: {expected_bundle_version}"
260 );
261 if self.blockstore().has(target_head.parent_state())? {
262 self.chain_store().set_heaviest_tipset(target_head)?;
263 return Ok(true);
264 } else {
265 anyhow::bail!(
266 "failed to rewind, state tree @ {target_epoch} is missing from blockstore: {}",
267 target_head.parent_state()
268 );
269 }
270 }
271 }
272 }
273 Ok(false)
274 }
275
276 pub fn beacon_schedule(&self) -> &Arc<BeaconSchedule> {
277 &self.beacon
278 }
279
280 pub fn get_network_version(&self, epoch: ChainEpoch) -> NetworkVersion {
282 self.chain_config().network_version(epoch)
283 }
284
285 pub fn get_state_tree(&self, state_cid: &Cid) -> anyhow::Result<StateTree<DB>> {
287 StateTree::new_from_root(self.blockstore_owned(), state_cid)
288 }
289
290 pub fn get_actor(&self, addr: &Address, state_cid: Cid) -> anyhow::Result<Option<ActorState>> {
292 let state = self.get_state_tree(&state_cid)?;
293 state.get_actor(addr)
294 }
295
296 pub fn get_actor_state<S: LoadActorStateFromBlockstore>(
298 &self,
299 ts: &Tipset,
300 ) -> anyhow::Result<S> {
301 let state_tree = self.get_state_tree(ts.parent_state())?;
302 state_tree.get_actor_state()
303 }
304
305 pub fn get_actor_state_from_address<S: LoadActorStateFromBlockstore>(
307 &self,
308 ts: &Tipset,
309 actor_address: &Address,
310 ) -> anyhow::Result<S> {
311 let state_tree = self.get_state_tree(ts.parent_state())?;
312 state_tree.get_actor_state_from_address(actor_address)
313 }
314
315 pub fn get_required_actor(&self, addr: &Address, state_cid: Cid) -> anyhow::Result<ActorState> {
317 let state = self.get_state_tree(&state_cid)?;
318 state.get_actor(addr)?.with_context(|| {
319 format!("Failed to load actor with addr={addr}, state_cid={state_cid}")
320 })
321 }
322
323 pub fn blockstore(&self) -> &Arc<DB> {
325 self.cs.blockstore()
326 }
327
328 pub fn blockstore_owned(&self) -> Arc<DB> {
329 self.blockstore().clone()
330 }
331
332 pub fn chain_store(&self) -> &Arc<ChainStore<DB>> {
334 &self.cs
335 }
336
337 pub fn chain_index(&self) -> &ChainIndex<DB> {
339 self.cs.chain_index()
340 }
341
342 pub fn chain_config(&self) -> &Arc<ChainConfig> {
344 self.cs.chain_config()
345 }
346
347 pub fn chain_rand(&self, tipset: Tipset) -> ChainRand<DB> {
348 ChainRand::new(
349 self.chain_config().shallow_clone(),
350 tipset,
351 self.chain_index().shallow_clone(),
352 self.beacon.shallow_clone(),
353 )
354 }
355
356 pub fn get_network_state_name(
358 &self,
359 state_cid: Cid,
360 ) -> anyhow::Result<crate::networks::StateNetworkName> {
361 let init_act = self
362 .get_actor(&init::ADDRESS.into(), state_cid)?
363 .ok_or_else(|| Error::state("Init actor address could not be resolved"))?;
364 Ok(
365 State::load(self.blockstore(), init_act.code, init_act.state)?
366 .into_network_name()
367 .into(),
368 )
369 }
370
371 pub fn is_miner_slashed(&self, addr: &Address, state_cid: &Cid) -> anyhow::Result<bool, Error> {
373 let actor = self
374 .get_actor(&Address::POWER_ACTOR, *state_cid)?
375 .ok_or_else(|| Error::state("Power actor address could not be resolved"))?;
376
377 let spas = power::State::load(self.blockstore(), actor.code, actor.state)?;
378
379 Ok(spas.miner_power(self.blockstore(), addr)?.is_none())
380 }
381
382 pub fn get_miner_work_addr(&self, state_cid: Cid, addr: &Address) -> Result<Address, Error> {
384 let state =
385 StateTree::new_from_root(self.blockstore_owned(), &state_cid).map_err(Error::other)?;
386 let ms: miner::State = state.get_actor_state_from_address(addr)?;
387 let info = ms.info(self.blockstore()).map_err(|e| e.to_string())?;
388 let addr = resolve_to_key_addr(&state, self.blockstore(), &info.worker())?;
389 Ok(addr)
390 }
391
392 pub fn get_power(
395 &self,
396 state_cid: &Cid,
397 addr: Option<&Address>,
398 ) -> anyhow::Result<Option<(power::Claim, power::Claim)>, 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.blockstore(), actor.code, actor.state)?;
404
405 let t_pow = spas.total_power();
406
407 if let Some(maddr) = addr {
408 let m_pow = spas
409 .miner_power(self.blockstore(), maddr)?
410 .ok_or_else(|| Error::state(format!("Miner for address {maddr} not found")))?;
411
412 let min_pow = spas.miner_nominal_power_meets_consensus_minimum(
413 &self.chain_config().policy,
414 self.blockstore(),
415 maddr,
416 )?;
417 if min_pow {
418 return Ok(Some((m_pow, t_pow)));
419 }
420 }
421
422 Ok(None)
423 }
424
425 pub fn get_all_sectors(
427 &self,
428 addr: &Address,
429 ts: &Tipset,
430 ) -> anyhow::Result<Vec<SectorOnChainInfo>> {
431 let actor = self
432 .get_actor(addr, *ts.parent_state())?
433 .ok_or_else(|| Error::state(format!("Miner actor {addr} not found")))?;
434 let state = miner::State::load(self.blockstore(), actor.code, actor.state)?;
435 state.load_sectors_ext(self.blockstore(), None)
436 }
437}