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