1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT
// ported from commit hash b622af

// The FVM crates only support state tree versions 3,4 and 5. This module
// contains read-only support for state tree version 0. This version is required
// to parse genesis states. Ideally, we would have a library that supports _all_
// state tree versions.

use cid::Cid;
use fil_actors_shared::fvm_ipld_hamt::Hamtv0 as Hamt;
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::CborStore;

use super::state_tree::StateRoot;
use super::state_tree::StateTreeVersion;
use crate::shim::address::Address;
pub use fvm2::state_tree::ActorState as ActorStateV2;

const HAMTV0_BIT_WIDTH: u32 = 5;

// This is a read-only version of the earliest state trees.
/// State tree implementation using HAMT. This structure is not thread safe and should only be used
/// in sync contexts.
pub struct StateTreeV0<S> {
    hamt: Hamt<S, ActorStateV2>,
}

impl<S> StateTreeV0<S>
where
    S: Blockstore,
{
    /// Constructor for a HAMT state tree given an IPLD store
    pub fn new_from_root(store: S, c: &Cid) -> anyhow::Result<Self> {
        // Try to load state root, if versioned
        let (version, actors) = if let Ok(Some(StateRoot {
            version, actors, ..
        })) = store.get_cbor(c)
        {
            (StateTreeVersion::from(version), actors)
        } else {
            // Fallback to v0 state tree if retrieval fails
            (StateTreeVersion::V0, *c)
        };

        match version {
            StateTreeVersion::V0 => {
                let hamt = Hamt::load_with_bit_width(&actors, store, HAMTV0_BIT_WIDTH)?;
                Ok(Self { hamt })
            }
            _ => anyhow::bail!("unsupported state tree version: {:?}", version),
        }
    }

    /// Retrieve store reference to modify db.
    pub fn store(&self) -> &S {
        self.hamt.store()
    }

    /// Get actor state from an address. Will be resolved to ID address.
    pub fn get_actor(&self, addr: &Address) -> anyhow::Result<Option<ActorStateV2>> {
        let addr = match self.lookup_id(addr)? {
            Some(addr) => addr,
            None => return Ok(None),
        };

        // if state doesn't exist, find using hamt
        let act = self.hamt.get(&addr.to_bytes())?.cloned();

        Ok(act)
    }

    /// Get an ID address from any Address
    pub fn lookup_id(&self, addr: &Address) -> anyhow::Result<Option<Address>> {
        if addr.protocol() == fvm_shared4::address::Protocol::ID {
            return Ok(Some(*addr));
        }
        anyhow::bail!("StateTreeV0::lookup_id is only defined for ID addresses")
    }
}