Skip to main content

forest/shim/
state_tree.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3use super::actors::LoadActorStateFromBlockstore;
4pub use super::fvm_shared_latest::{ActorID, state::StateRoot};
5use crate::{
6    blocks::Tipset,
7    networks::{ACTOR_BUNDLES_METADATA, ActorBundleMetadata},
8    prelude::*,
9    shim::{
10        actors::{AccountActorStateLoad as _, account},
11        address::Address,
12        econ::TokenAmount,
13    },
14    utils::get_size::big_int_heap_size_helper,
15};
16use anyhow::bail;
17use fvm_ipld_encoding::{
18    CborStore as _,
19    repr::{Deserialize_repr, Serialize_repr},
20};
21use fvm_shared2::state::StateTreeVersion as StateTreeVersionV2;
22use fvm_shared3::state::StateTreeVersion as StateTreeVersionV3;
23use fvm_shared4::state::StateTreeVersion as StateTreeVersionV4;
24pub use fvm2::state_tree::{ActorState as ActorStateV2, StateTree as StateTreeV2};
25pub use fvm3::state_tree::{ActorState as ActorStateV3, StateTree as StateTreeV3};
26pub use fvm4::state_tree::{
27    ActorState as ActorStateV4, ActorState as ActorState_latest, StateTree as StateTreeV4,
28};
29use get_size2::GetSize;
30use num::FromPrimitive;
31use num_derive::FromPrimitive;
32use serde::{Deserialize, Serialize};
33use spire_enum::prelude::delegated_enum;
34
35#[derive(
36    Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Serialize_repr, Deserialize_repr, FromPrimitive,
37)]
38#[repr(u64)]
39pub enum StateTreeVersion {
40    V0,
41    V1,
42    V2,
43    V3,
44    V4,
45    V5,
46}
47
48impl From<StateTreeVersionV4> for StateTreeVersion {
49    fn from(value: StateTreeVersionV4) -> Self {
50        match value {
51            StateTreeVersionV4::V0 => Self::V0,
52            StateTreeVersionV4::V1 => Self::V1,
53            StateTreeVersionV4::V2 => Self::V2,
54            StateTreeVersionV4::V3 => Self::V3,
55            StateTreeVersionV4::V4 => Self::V4,
56            StateTreeVersionV4::V5 => Self::V5,
57        }
58    }
59}
60
61impl From<StateTreeVersionV3> for StateTreeVersion {
62    fn from(value: StateTreeVersionV3) -> Self {
63        match value {
64            StateTreeVersionV3::V0 => Self::V0,
65            StateTreeVersionV3::V1 => Self::V1,
66            StateTreeVersionV3::V2 => Self::V2,
67            StateTreeVersionV3::V3 => Self::V3,
68            StateTreeVersionV3::V4 => Self::V4,
69            StateTreeVersionV3::V5 => Self::V5,
70        }
71    }
72}
73
74impl TryFrom<StateTreeVersionV2> for StateTreeVersion {
75    type Error = anyhow::Error;
76    fn try_from(value: StateTreeVersionV2) -> anyhow::Result<Self> {
77        if let Some(v) = FromPrimitive::from_u32(value as u32) {
78            Ok(v)
79        } else {
80            bail!("Invalid conversion");
81        }
82    }
83}
84
85impl TryFrom<StateTreeVersion> for StateTreeVersionV2 {
86    type Error = anyhow::Error;
87
88    fn try_from(value: StateTreeVersion) -> anyhow::Result<Self> {
89        Ok(match value {
90            StateTreeVersion::V0 => Self::V0,
91            StateTreeVersion::V1 => Self::V1,
92            StateTreeVersion::V2 => Self::V2,
93            StateTreeVersion::V3 => Self::V3,
94            StateTreeVersion::V4 => Self::V4,
95            StateTreeVersion::V5 => bail!("Impossible conversion"),
96        })
97    }
98}
99
100impl TryFrom<StateTreeVersion> for StateTreeVersionV3 {
101    type Error = anyhow::Error;
102
103    fn try_from(value: StateTreeVersion) -> anyhow::Result<Self> {
104        Ok(match value {
105            StateTreeVersion::V0 => Self::V0,
106            StateTreeVersion::V1 => Self::V1,
107            StateTreeVersion::V2 => Self::V2,
108            StateTreeVersion::V3 => Self::V3,
109            StateTreeVersion::V4 => Self::V4,
110            StateTreeVersion::V5 => Self::V5,
111        })
112    }
113}
114
115impl TryFrom<StateTreeVersion> for StateTreeVersionV4 {
116    type Error = anyhow::Error;
117
118    fn try_from(value: StateTreeVersion) -> anyhow::Result<Self> {
119        Ok(match value {
120            StateTreeVersion::V0 => Self::V0,
121            StateTreeVersion::V1 => Self::V1,
122            StateTreeVersion::V2 => Self::V2,
123            StateTreeVersion::V3 => Self::V3,
124            StateTreeVersion::V4 => Self::V4,
125            StateTreeVersion::V5 => Self::V5,
126        })
127    }
128}
129
130/// FVM `StateTree` variant. The `new_from_root` constructor will try to resolve
131/// to a valid `StateTree` version or fail if we don't support it at the moment.
132/// Other methods usage should be transparent (using shimmed versions of
133/// structures introduced in this crate::shim.
134///
135/// Not all the inner methods are implemented, only those that are needed. Feel
136/// free to add those when necessary.
137#[delegated_enum(impl_conversions)]
138pub enum StateTree<S> {
139    // Version 0 is used to parse the genesis block.
140    V0(super::state_tree_v0::StateTreeV0<S>),
141    // fvm-2 support state tree versions 3 and 4.
142    FvmV2(StateTreeV2<S>),
143    // fvm-3 support state tree versions 5.
144    FvmV3(StateTreeV3<S>),
145    // fvm-4 support state tree versions *.
146    FvmV4(StateTreeV4<S>),
147}
148
149impl<S> StateTree<S>
150where
151    S: Blockstore + ShallowClone,
152{
153    /// Constructor for a HAMT state tree given an IPLD store
154    pub fn new(store: &S, version: StateTreeVersion) -> anyhow::Result<Self> {
155        if let Ok(st) = StateTreeV4::new(store.shallow_clone(), version.try_into()?) {
156            Ok(StateTree::FvmV4(st))
157        } else if let Ok(st) = StateTreeV3::new(store.shallow_clone(), version.try_into()?) {
158            Ok(StateTree::FvmV3(st))
159        } else if let Ok(st) = StateTreeV2::new(store.shallow_clone(), version.try_into()?) {
160            Ok(StateTree::FvmV2(st))
161        } else {
162            bail!("Can't create a valid state tree for the given version.");
163        }
164    }
165
166    pub fn new_from_root(store: &S, c: &Cid) -> anyhow::Result<Self> {
167        if let Ok(st) = StateTreeV4::new_from_root(store.shallow_clone(), c) {
168            Ok(StateTree::FvmV4(st))
169        } else if let Ok(st) = StateTreeV3::new_from_root(store.shallow_clone(), c) {
170            Ok(StateTree::FvmV3(st))
171        } else if let Ok(st) = StateTreeV2::new_from_root(store.shallow_clone(), c) {
172            Ok(StateTree::FvmV2(st))
173        } else if let Ok(st) =
174            super::state_tree_v0::StateTreeV0::new_from_root(store.shallow_clone(), c)
175        {
176            Ok(StateTree::V0(st))
177        } else if !store.has(c)? {
178            bail!("No state tree exists for the root {c}.")
179        } else {
180            let state_root = store.get_cbor::<StateRoot>(c).ok().flatten();
181            let state_root_version = state_root
182                .map(|sr| format!("{:?}", sr.version))
183                .unwrap_or_else(|| "unknown".into());
184            bail!(
185                "Can't create a valid state tree from the given root. This error may indicate unsupported version. state_root_cid={c}, state_root_version={state_root_version}"
186            )
187        }
188    }
189
190    pub fn new_from_tipset(store: &S, ts: &Tipset) -> anyhow::Result<Self> {
191        Self::new_from_root(store, ts.parent_state())
192    }
193}
194
195impl<S> StateTree<S>
196where
197    S: Blockstore,
198{
199    /// Get required actor state from an address. Will be resolved to ID address.
200    pub fn get_required_actor(&self, addr: &Address) -> anyhow::Result<ActorState> {
201        self.get_actor(addr)?
202            .with_context(|| format!("Actor not found: addr={addr}"))
203    }
204
205    /// Get the actor bundle metadata
206    pub fn get_actor_bundle_metadata(&self) -> anyhow::Result<&ActorBundleMetadata> {
207        let system_actor_code = self.get_required_actor(&Address::SYSTEM_ACTOR)?.code;
208        ACTOR_BUNDLES_METADATA
209            .values()
210            .find(|v| v.manifest.get_system() == system_actor_code)
211            .with_context(|| format!("actor bundle not found for system actor {system_actor_code}"))
212    }
213
214    /// Get actor state from an address. Will be resolved to ID address.
215    pub fn get_actor(&self, addr: &Address) -> anyhow::Result<Option<ActorState>> {
216        match self {
217            StateTree::FvmV2(st) => {
218                anyhow::ensure!(
219                    addr.protocol() != crate::shim::address::Protocol::Delegated,
220                    "Delegated addresses are not supported in FVMv2 state trees"
221                );
222                Ok(st.get_actor(&addr.into())?.map(Into::into))
223            }
224            StateTree::FvmV3(st) => {
225                let id = st.lookup_id(&addr.into())?;
226                if let Some(id) = id {
227                    Ok(st.get_actor(id)?.map(Into::into))
228                } else {
229                    Ok(None)
230                }
231            }
232            StateTree::FvmV4(st) => {
233                let id = st.lookup_id(addr)?;
234                if let Some(id) = id {
235                    Ok(st.get_actor(id)?.map(Into::into))
236                } else {
237                    Ok(None)
238                }
239            }
240            StateTree::V0(st) => {
241                let id = st.lookup_id(addr)?;
242                if let Some(id) = id {
243                    Ok(st.get_actor(&id)?.map(Into::into))
244                } else {
245                    Ok(None)
246                }
247            }
248        }
249    }
250
251    /// Gets actor state from implicit actor address
252    pub fn get_actor_state<STATE: LoadActorStateFromBlockstore>(&self) -> anyhow::Result<STATE> {
253        let address = STATE::ACTOR.with_context(|| {
254            format!(
255                "No associated actor address for {}, use `get_actor_state_from_address` instead.",
256                std::any::type_name::<STATE>()
257            )
258        })?;
259        let actor = self.get_required_actor(&address)?;
260        STATE::load_from_blockstore(self.store(), &actor)
261    }
262
263    /// Gets actor state from explicit actor address
264    pub fn get_actor_state_from_address<STATE: LoadActorStateFromBlockstore>(
265        &self,
266        actor_address: &Address,
267    ) -> anyhow::Result<STATE> {
268        let actor = self.get_required_actor(actor_address)?;
269        STATE::load_from_blockstore(self.store(), &actor)
270    }
271
272    /// Retrieve store reference to modify db.
273    pub fn store(&self) -> &S {
274        delegate_state_tree!(self.store())
275    }
276
277    /// Get an ID address from any Address
278    pub fn lookup_id(&self, addr: &Address) -> anyhow::Result<Option<ActorID>> {
279        match self {
280            StateTree::FvmV2(st) => {
281                // Same guard as `get_actor` above: FVM2 has no `Delegated` protocol, so converting
282                // an `f4` address would panic.
283                anyhow::ensure!(
284                    addr.protocol() != crate::shim::address::Protocol::Delegated,
285                    "Delegated addresses are not supported in FVMv2 state trees"
286                );
287                Ok(st.lookup_id(&addr.into())?)
288            }
289            StateTree::FvmV3(st) => Ok(st.lookup_id(&addr.into())?),
290            StateTree::FvmV4(st) => Ok(st.lookup_id(&addr.into())?),
291            StateTree::V0(_) => bail!("StateTree::lookup_id not supported on old state trees"),
292        }
293    }
294
295    /// Get an required ID address from any Address
296    pub fn lookup_required_id(&self, addr: &Address) -> anyhow::Result<ActorID> {
297        self.lookup_id(addr)?
298            .with_context(|| format!("actor id not found for address {addr}"))
299    }
300
301    /// Use [`Self::for_each_cacheless`] instead unless cache is really needed.
302    /// Note that this method caches all HAMT nodes and can be memory-intensive.
303    pub fn for_each<F>(&self, mut f: F) -> anyhow::Result<()>
304    where
305        F: FnMut(Address, &ActorState) -> anyhow::Result<()>,
306    {
307        match self {
308            StateTree::FvmV2(st) => {
309                st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
310            }
311            StateTree::FvmV3(st) => {
312                st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
313            }
314            StateTree::FvmV4(st) => {
315                st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
316            }
317            StateTree::V0(_) => bail!("StateTree::for_each not supported on old state trees"),
318        }
319    }
320
321    /// Iterate on all actors
322    pub fn for_each_cacheless<F>(&self, mut f: F) -> anyhow::Result<()>
323    where
324        F: FnMut(Address, &ActorState) -> anyhow::Result<()>,
325    {
326        match self {
327            StateTree::FvmV2(st) => {
328                st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
329            }
330            StateTree::FvmV3(st) => {
331                st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
332            }
333            StateTree::FvmV4(st) => {
334                st.for_each_cacheless(|address, actor_state| f(address.into(), &actor_state.into()))
335            }
336            StateTree::V0(_) => {
337                bail!("StateTree::for_each_cacheless not supported on old state trees")
338            }
339        }
340    }
341
342    /// Flush state tree and return Cid root.
343    pub fn flush(&mut self) -> anyhow::Result<Cid> {
344        match self {
345            StateTree::FvmV2(st) => Ok(st.flush()?),
346            StateTree::FvmV3(st) => Ok(st.flush()?),
347            StateTree::FvmV4(st) => Ok(st.flush()?),
348            StateTree::V0(_) => bail!("StateTree::flush not supported on old state trees"),
349        }
350    }
351
352    /// Set actor state with an actor ID.
353    pub fn set_actor(&mut self, addr: &Address, actor: ActorState) -> anyhow::Result<()> {
354        match self {
355            StateTree::FvmV2(st) => {
356                st.set_actor(&addr.into(), actor.into())?;
357                Ok(())
358            }
359            StateTree::FvmV3(st) => {
360                let id = st
361                    .lookup_id(&addr.into())?
362                    .context("couldn't find actor id")?;
363                st.set_actor(id, actor.into());
364                Ok(())
365            }
366            StateTree::FvmV4(st) => {
367                let id = st
368                    .lookup_id(&addr.into())?
369                    .context("couldn't find actor id")?;
370                st.set_actor(id, actor.into());
371                Ok(())
372            }
373            StateTree::V0(_) => bail!("StateTree::set_actor not supported on old state trees"),
374        }
375    }
376
377    /// Returns the public key type of
378    /// address(`BLS`/`SECP256K1`) of an actor identified by `addr`,
379    /// or its delegated address.
380    pub fn resolve_to_deterministic_address(
381        &self,
382        store: &impl Blockstore,
383        addr: Address,
384    ) -> anyhow::Result<Address> {
385        use crate::shim::address::Protocol::*;
386        match addr.protocol() {
387            BLS | Secp256k1 | Delegated => Ok(addr),
388            _ => {
389                let actor = self
390                    .get_actor(&addr)?
391                    .with_context(|| format!("failed to find actor: {addr}"))?;
392                if let Some(address) = actor.delegated_address {
393                    Ok(address.into())
394                } else {
395                    let account_state = account::State::load(store, actor.code, actor.state)?;
396                    Ok(account_state.pubkey_address())
397                }
398            }
399        }
400    }
401}
402
403/// `Newtype` to wrap different versions of `fvm::state_tree::ActorState`
404///
405/// # Examples
406/// ```
407/// # use forest::doctest_private::ActorState;
408/// use cid::Cid;
409///
410/// // Create FVM2 ActorState normally
411/// let fvm2_actor_state = fvm2::state_tree::ActorState::new(Cid::default(), Cid::default(),
412/// fvm_shared2::econ::TokenAmount::from_atto(42), 0);
413///
414/// // Create a correspndoning FVM3 ActorState
415/// let fvm3_actor_state = fvm3::state_tree::ActorState::new(Cid::default(), Cid::default(),
416/// fvm_shared3::econ::TokenAmount::from_atto(42), 0, None);
417///
418/// // Create a correspndoning FVM4 ActorState
419/// let fvm4_actor_state = fvm4::state_tree::ActorState::new(Cid::default(), Cid::default(),
420/// fvm_shared4::econ::TokenAmount::from_atto(42), 0, None);
421///
422/// // Create a shim out of fvm2 state, ensure conversions are correct
423/// let state_shim = ActorState::from(fvm2_actor_state.clone());
424/// assert_eq!(fvm4_actor_state, *state_shim);
425/// assert_eq!(fvm3_actor_state, state_shim.clone().into());
426/// assert_eq!(fvm2_actor_state, state_shim.into());
427/// ```
428#[derive(
429    PartialEq, Eq, Clone, Debug, Serialize, Deserialize, derive_more::Deref, derive_more::DerefMut,
430)]
431#[serde(transparent)]
432#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
433pub struct ActorState(ActorState_latest);
434
435impl ActorState {
436    pub fn new(
437        code: Cid,
438        state: Cid,
439        balance: TokenAmount,
440        sequence: u64,
441        address: Option<Address>,
442    ) -> Self {
443        Self(ActorState_latest::new(
444            code,
445            state,
446            balance.into(),
447            sequence,
448            address.map(Into::into),
449        ))
450    }
451    /// Construct a new empty actor with the specified code.
452    pub fn new_empty(code: Cid, delegated_address: Option<Address>) -> Self {
453        Self(ActorState_latest::new_empty(
454            code,
455            delegated_address.map(Into::into),
456        ))
457    }
458}
459
460impl GetSize for ActorState {
461    fn get_heap_size_with_tracker<T: get_size2::GetSizeTracker>(&self, tracker: T) -> (usize, T) {
462        (big_int_heap_size_helper(self.balance.atto()), tracker)
463    }
464}
465
466impl From<&ActorStateV2> for ActorState {
467    fn from(value: &ActorStateV2) -> Self {
468        Self(ActorState_latest {
469            code: value.code,
470            state: value.state,
471            sequence: value.sequence,
472            balance: TokenAmount::from(&value.balance).into(),
473            delegated_address: None,
474        })
475    }
476}
477
478impl From<ActorStateV2> for ActorState {
479    fn from(value: ActorStateV2) -> Self {
480        (&value).into()
481    }
482}
483
484impl From<ActorStateV3> for ActorState {
485    fn from(value: ActorStateV3) -> Self {
486        Self(ActorState_latest {
487            code: value.code,
488            state: value.state,
489            sequence: value.sequence,
490            balance: TokenAmount::from(value.balance).into(),
491            delegated_address: value
492                .delegated_address
493                .map(|addr| Address::from(addr).into()),
494        })
495    }
496}
497
498impl From<&ActorStateV3> for ActorState {
499    fn from(value: &ActorStateV3) -> Self {
500        value.clone().into()
501    }
502}
503
504impl From<ActorStateV4> for ActorState {
505    fn from(value: ActorStateV4) -> Self {
506        ActorState(value)
507    }
508}
509
510impl From<&ActorStateV4> for ActorState {
511    fn from(value: &ActorStateV4) -> Self {
512        value.clone().into()
513    }
514}
515
516impl From<ActorState> for ActorStateV2 {
517    fn from(other: ActorState) -> ActorStateV2 {
518        Self {
519            code: other.code,
520            state: other.state,
521            sequence: other.sequence,
522            balance: TokenAmount::from(&other.balance).into(),
523        }
524    }
525}
526
527impl From<&ActorState> for ActorStateV2 {
528    fn from(other: &ActorState) -> ActorStateV2 {
529        Self {
530            code: other.code,
531            state: other.state,
532            sequence: other.sequence,
533            balance: TokenAmount::from(&other.balance).into(),
534        }
535    }
536}
537
538impl From<ActorState> for ActorStateV3 {
539    fn from(other: ActorState) -> Self {
540        Self {
541            code: other.code,
542            state: other.state,
543            sequence: other.sequence,
544            balance: TokenAmount::from(&other.balance).into(),
545            delegated_address: other
546                .delegated_address
547                .map(|addr| Address::from(addr).into()),
548        }
549    }
550}
551
552impl From<ActorState> for ActorStateV4 {
553    fn from(other: ActorState) -> Self {
554        other.0
555    }
556}
557
558#[cfg(test)]
559mod tests {
560    use super::StateTree;
561    use crate::blocks::CachingBlockHeader;
562    use crate::db::car::AnyCar;
563    use crate::networks::{calibnet, mainnet};
564    use crate::shim::actors::init;
565    use cid::Cid;
566    use std::sync::Arc;
567
568    // refactored from `StateManager::get_network_name`
569    fn get_network_name(car: &'static [u8], genesis_cid: Cid) -> String {
570        let forest_car = Arc::new(AnyCar::new(car).unwrap());
571        let genesis_block = CachingBlockHeader::load(&forest_car, genesis_cid)
572            .unwrap()
573            .unwrap();
574        let state_tree = StateTree::new_from_root(&forest_car, &genesis_block.state_root).unwrap();
575        let state: init::State = state_tree.get_actor_state().unwrap();
576        state.into_network_name()
577    }
578
579    #[test]
580    fn calibnet_network_name() {
581        assert_eq!(
582            get_network_name(calibnet::DEFAULT_GENESIS, *calibnet::GENESIS_CID),
583            "calibrationnet"
584        );
585    }
586
587    #[test]
588    fn mainnet_network_name() {
589        // Yes, the name of `mainnet` in the genesis block really is `testnetnet`.
590        assert_eq!(
591            get_network_name(mainnet::DEFAULT_GENESIS, *mainnet::GENESIS_CID),
592            "testnetnet"
593        );
594    }
595}