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 _, anyhow, 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
215                    .get_actor(&addr.into())
216                    .map_err(|e| anyhow!("{e}"))?
217                    .map(Into::into))
218            }
219            StateTree::FvmV3(st) => {
220                let id = st.lookup_id(&addr.into())?;
221                if let Some(id) = id {
222                    Ok(st
223                        .get_actor(id)
224                        .map_err(|e| anyhow!("{e}"))?
225                        .map(Into::into))
226                } else {
227                    Ok(None)
228                }
229            }
230            StateTree::FvmV4(st) => {
231                let id = st.lookup_id(addr)?;
232                if let Some(id) = id {
233                    Ok(st
234                        .get_actor(id)
235                        .map_err(|e| anyhow!("{e}"))?
236                        .map(Into::into))
237                } else {
238                    Ok(None)
239                }
240            }
241            StateTree::V0(st) => {
242                let id = st.lookup_id(addr)?;
243                if let Some(id) = id {
244                    Ok(st
245                        .get_actor(&id)
246                        .map_err(|e| anyhow!("{e}"))?
247                        .map(Into::into))
248                } else {
249                    Ok(None)
250                }
251            }
252        }
253    }
254
255    /// Gets actor state from implicit actor address
256    pub fn get_actor_state<STATE: LoadActorStateFromBlockstore>(&self) -> anyhow::Result<STATE> {
257        let address = STATE::ACTOR.with_context(|| {
258            format!(
259                "No associated actor address for {}, use `get_actor_state_from_address` instead.",
260                std::any::type_name::<STATE>()
261            )
262        })?;
263        let actor = self.get_required_actor(&address)?;
264        STATE::load_from_blockstore(self.store(), &actor)
265    }
266
267    /// Gets actor state from explicit actor address
268    pub fn get_actor_state_from_address<STATE: LoadActorStateFromBlockstore>(
269        &self,
270        actor_address: &Address,
271    ) -> anyhow::Result<STATE> {
272        let actor = self.get_required_actor(actor_address)?;
273        STATE::load_from_blockstore(self.store(), &actor)
274    }
275
276    /// Retrieve store reference to modify db.
277    pub fn store(&self) -> &S {
278        delegate_state_tree!(self.store())
279    }
280
281    /// Get an ID address from any Address
282    pub fn lookup_id(&self, addr: &Address) -> anyhow::Result<Option<ActorID>> {
283        match self {
284            StateTree::FvmV2(st) => st.lookup_id(&addr.into()).map_err(|e| anyhow!("{e}")),
285            StateTree::FvmV3(st) => Ok(st.lookup_id(&addr.into())?),
286            StateTree::FvmV4(st) => Ok(st.lookup_id(&addr.into())?),
287            StateTree::V0(_) => bail!("StateTree::lookup_id not supported on old state trees"),
288        }
289    }
290
291    /// Get an required ID address from any Address
292    pub fn lookup_required_id(&self, addr: &Address) -> anyhow::Result<ActorID> {
293        self.lookup_id(addr)?
294            .with_context(|| format!("actor id not found for address {addr}"))
295    }
296
297    pub fn for_each<F>(&self, mut f: F) -> anyhow::Result<()>
298    where
299        F: FnMut(Address, &ActorState) -> anyhow::Result<()>,
300    {
301        match self {
302            StateTree::FvmV2(st) => {
303                st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
304            }
305            StateTree::FvmV3(st) => {
306                st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
307            }
308            StateTree::FvmV4(st) => {
309                st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
310            }
311            StateTree::V0(_) => bail!("StateTree::for_each not supported on old state trees"),
312        }
313    }
314
315    /// Flush state tree and return Cid root.
316    pub fn flush(&mut self) -> anyhow::Result<Cid> {
317        match self {
318            StateTree::FvmV2(st) => st.flush().map_err(|e| anyhow!("{e}")),
319            StateTree::FvmV3(st) => Ok(st.flush()?),
320            StateTree::FvmV4(st) => Ok(st.flush()?),
321            StateTree::V0(_) => bail!("StateTree::flush not supported on old state trees"),
322        }
323    }
324
325    /// Set actor state with an actor ID.
326    pub fn set_actor(&mut self, addr: &Address, actor: ActorState) -> anyhow::Result<()> {
327        match self {
328            StateTree::FvmV2(st) => st
329                .set_actor(&addr.into(), actor.into())
330                .map_err(|e| anyhow!("{e}")),
331            StateTree::FvmV3(st) => {
332                let id = st
333                    .lookup_id(&addr.into())?
334                    .context("couldn't find actor id")?;
335                st.set_actor(id, actor.into());
336                Ok(())
337            }
338            StateTree::FvmV4(st) => {
339                let id = st
340                    .lookup_id(&addr.into())?
341                    .context("couldn't find actor id")?;
342                st.set_actor(id, actor.into());
343                Ok(())
344            }
345            StateTree::V0(_) => bail!("StateTree::set_actor not supported on old state trees"),
346        }
347    }
348
349    /// Returns the public key type of
350    /// address(`BLS`/`SECP256K1`) of an actor identified by `addr`,
351    /// or its delegated address.
352    pub fn resolve_to_deterministic_addr(
353        &self,
354        store: &impl Blockstore,
355        addr: Address,
356    ) -> anyhow::Result<Address> {
357        use crate::shim::address::Protocol::*;
358        match addr.protocol() {
359            BLS | Secp256k1 | Delegated => Ok(addr),
360            _ => {
361                let actor = self
362                    .get_actor(&addr)?
363                    .with_context(|| format!("failed to find actor: {addr}"))?;
364
365                // A workaround to implement `if state.Version() >= types.StateTreeVersion5`
366                // When state tree version is not available in rust APIs
367                if !matches!(self, Self::FvmV2(_) | Self::V0(_))
368                    && let Some(address) = actor.delegated_address
369                {
370                    return Ok(address.into());
371                }
372
373                let account_state = account::State::load(store, actor.code, actor.state)?;
374                Ok(account_state.pubkey_address())
375            }
376        }
377    }
378}
379
380/// `Newtype` to wrap different versions of `fvm::state_tree::ActorState`
381///
382/// # Examples
383/// ```
384/// # use forest::doctest_private::ActorState;
385/// use cid::Cid;
386///
387/// // Create FVM2 ActorState normally
388/// let fvm2_actor_state = fvm2::state_tree::ActorState::new(Cid::default(), Cid::default(),
389/// fvm_shared2::econ::TokenAmount::from_atto(42), 0);
390///
391/// // Create a correspndoning FVM3 ActorState
392/// let fvm3_actor_state = fvm3::state_tree::ActorState::new(Cid::default(), Cid::default(),
393/// fvm_shared3::econ::TokenAmount::from_atto(42), 0, None);
394///
395/// // Create a correspndoning FVM4 ActorState
396/// let fvm4_actor_state = fvm4::state_tree::ActorState::new(Cid::default(), Cid::default(),
397/// fvm_shared4::econ::TokenAmount::from_atto(42), 0, None);
398///
399/// // Create a shim out of fvm2 state, ensure conversions are correct
400/// let state_shim = ActorState::from(fvm2_actor_state.clone());
401/// assert_eq!(fvm4_actor_state, *state_shim);
402/// assert_eq!(fvm3_actor_state, state_shim.clone().into());
403/// assert_eq!(fvm2_actor_state, state_shim.into());
404/// ```
405#[derive(
406    PartialEq, Eq, Clone, Debug, Serialize, Deserialize, derive_more::Deref, derive_more::DerefMut,
407)]
408#[serde(transparent)]
409#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
410pub struct ActorState(ActorState_latest);
411
412impl ActorState {
413    pub fn new(
414        code: Cid,
415        state: Cid,
416        balance: TokenAmount,
417        sequence: u64,
418        address: Option<Address>,
419    ) -> Self {
420        Self(ActorState_latest::new(
421            code,
422            state,
423            balance.into(),
424            sequence,
425            address.map(Into::into),
426        ))
427    }
428    /// Construct a new empty actor with the specified code.
429    pub fn new_empty(code: Cid, delegated_address: Option<Address>) -> Self {
430        Self(ActorState_latest::new_empty(
431            code,
432            delegated_address.map(Into::into),
433        ))
434    }
435}
436
437impl From<&ActorStateV2> for ActorState {
438    fn from(value: &ActorStateV2) -> Self {
439        Self(ActorState_latest {
440            code: value.code,
441            state: value.state,
442            sequence: value.sequence,
443            balance: TokenAmount::from(&value.balance).into(),
444            delegated_address: None,
445        })
446    }
447}
448
449impl From<ActorStateV2> for ActorState {
450    fn from(value: ActorStateV2) -> Self {
451        (&value).into()
452    }
453}
454
455impl From<ActorStateV3> for ActorState {
456    fn from(value: ActorStateV3) -> Self {
457        Self(ActorState_latest {
458            code: value.code,
459            state: value.state,
460            sequence: value.sequence,
461            balance: TokenAmount::from(value.balance).into(),
462            delegated_address: value
463                .delegated_address
464                .map(|addr| Address::from(addr).into()),
465        })
466    }
467}
468
469impl From<&ActorStateV3> for ActorState {
470    fn from(value: &ActorStateV3) -> Self {
471        value.clone().into()
472    }
473}
474
475impl From<ActorStateV4> for ActorState {
476    fn from(value: ActorStateV4) -> Self {
477        ActorState(value)
478    }
479}
480
481impl From<&ActorStateV4> for ActorState {
482    fn from(value: &ActorStateV4) -> Self {
483        value.clone().into()
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 ActorStateV2 {
499    fn from(other: &ActorState) -> ActorStateV2 {
500        Self {
501            code: other.code,
502            state: other.state,
503            sequence: other.sequence,
504            balance: TokenAmount::from(&other.balance).into(),
505        }
506    }
507}
508
509impl From<ActorState> for ActorStateV3 {
510    fn from(other: ActorState) -> Self {
511        Self {
512            code: other.code,
513            state: other.state,
514            sequence: other.sequence,
515            balance: TokenAmount::from(&other.balance).into(),
516            delegated_address: other
517                .delegated_address
518                .map(|addr| Address::from(addr).into()),
519        }
520    }
521}
522
523impl From<ActorState> for ActorStateV4 {
524    fn from(other: ActorState) -> Self {
525        other.0
526    }
527}
528
529#[cfg(test)]
530mod tests {
531    use super::StateTree;
532    use crate::blocks::CachingBlockHeader;
533    use crate::db::car::AnyCar;
534    use crate::networks::{calibnet, mainnet};
535    use crate::shim::actors::init;
536    use cid::Cid;
537    use std::sync::Arc;
538
539    // refactored from `StateManager::get_network_name`
540    fn get_network_name(car: &'static [u8], genesis_cid: Cid) -> String {
541        let forest_car = AnyCar::new(car).unwrap();
542        let genesis_block = CachingBlockHeader::load(&forest_car, genesis_cid)
543            .unwrap()
544            .unwrap();
545        let state_tree =
546            StateTree::new_from_root(Arc::new(&forest_car), &genesis_block.state_root).unwrap();
547        let state: init::State = state_tree.get_actor_state().unwrap();
548        state.into_network_name()
549    }
550
551    #[test]
552    fn calibnet_network_name() {
553        assert_eq!(
554            get_network_name(calibnet::DEFAULT_GENESIS, *calibnet::GENESIS_CID),
555            "calibrationnet"
556        );
557    }
558
559    #[test]
560    fn mainnet_network_name() {
561        // Yes, the name of `mainnet` in the genesis block really is `testnetnet`.
562        assert_eq!(
563            get_network_name(mainnet::DEFAULT_GENESIS, *mainnet::GENESIS_CID),
564            "testnetnet"
565        );
566    }
567}