Skip to main content

forest/state_manager/
address_resolution.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use super::*;
5use crate::db::EthMappingsStore;
6use crate::shim::address::{Payload, Protocol};
7use anyhow::Context as _;
8use bls_signatures::{PublicKey as BlsPublicKey, Serialize as _};
9
10impl<DB> StateManager<DB>
11where
12    DB: Blockstore + Send + Sync + 'static,
13{
14    /// Returns a BLS public key from provided address
15    pub fn get_bls_public_key(
16        db: &Arc<DB>,
17        addr: &Address,
18        state_cid: Cid,
19    ) -> Result<BlsPublicKey, Error> {
20        let state = StateTree::new_from_root(Arc::clone(db), &state_cid)
21            .map_err(|e| Error::Other(e.to_string()))?;
22        let kaddr =
23            resolve_to_key_addr(&state, db, addr).context("Failed to resolve key address")?;
24
25        match kaddr.into_payload() {
26            Payload::BLS(key) => BlsPublicKey::from_bytes(&key)
27                .context("Failed to construct bls public key")
28                .map_err(Error::from),
29            _ => Err(Error::state(
30                "Address must be BLS address to load bls public key",
31            )),
32        }
33    }
34
35    /// Looks up ID [Address] from the state at the given [Tipset].
36    pub fn lookup_id(&self, addr: &Address, ts: &Tipset) -> Result<Option<Address>, Error> {
37        let state_tree = StateTree::new_from_root(self.blockstore_owned(), ts.parent_state())
38            .map_err(|e| format!("{e:?}"))?;
39        Ok(state_tree
40            .lookup_id(addr)
41            .map_err(|e| Error::Other(e.to_string()))?
42            .map(Address::new_id))
43    }
44
45    /// Looks up required ID [Address] from the state at the given [Tipset].
46    pub fn lookup_required_id(&self, addr: &Address, ts: &Tipset) -> Result<Address, Error> {
47        self.lookup_id(addr, ts)?
48            .ok_or_else(|| Error::Other(format!("Failed to lookup the id address {addr}")))
49    }
50
51    /// Similar to `resolve_to_key_addr` in the `forest_vm` [`crate::state_manager`] but does not
52    /// allow `Actor` type of addresses. Uses `ts` to generate the VM state.
53    pub async fn resolve_to_key_addr(
54        self: &Arc<Self>,
55        addr: &Address,
56        ts: &Tipset,
57    ) -> anyhow::Result<Address>
58    where
59        DB: EthMappingsStore,
60    {
61        match addr.protocol() {
62            Protocol::BLS | Protocol::Secp256k1 | Protocol::Delegated => return Ok(*addr),
63            Protocol::Actor => {
64                return Err(Error::Other(
65                    "cannot resolve actor address to key address".to_string(),
66                )
67                .into());
68            }
69            _ => {}
70        };
71
72        // First try to resolve the actor in the parent state, so we don't have to
73        // compute anything.
74        let state = StateTree::new_from_root(self.blockstore_owned(), ts.parent_state())?;
75        if let Ok(addr) = resolve_to_key_addr(&state, self.blockstore(), addr) {
76            return Ok(addr);
77        }
78
79        // If that fails, compute the tip-set and try again.
80        let TipsetState { state_root, .. } = self.load_tipset_state(ts).await?;
81        let state = StateTree::new_from_root(self.blockstore_owned(), &state_root)?;
82
83        resolve_to_key_addr(&state, self.blockstore(), addr)
84    }
85
86    /// Similar to [`StateTree::resolve_to_deterministic_addr`] but does not allow [`crate::shim::address::Protocol::Actor`] type of addresses.
87    /// Uses the [`Tipset`] `ts` to generate the VM state.
88    pub async fn resolve_to_deterministic_address(
89        self: &Arc<Self>,
90        address: Address,
91        ts: &Tipset,
92    ) -> anyhow::Result<Address>
93    where
94        DB: EthMappingsStore,
95    {
96        use crate::shim::address::Protocol::*;
97        match address.protocol() {
98            BLS | Secp256k1 | Delegated => Ok(address),
99            Actor => anyhow::bail!("cannot resolve actor address to key address"),
100            ID => {
101                let id = address.id()?;
102                if let Some(cached) = self.id_to_deterministic_address_cache.get_cloned(&id) {
103                    return Ok(cached);
104                }
105                // First try to resolve the actor in the parent state, so we don't have to compute anything.
106                let resolved = if let Ok(state) =
107                    StateTree::new_from_root(self.blockstore_owned(), ts.parent_state())
108                    && let Ok(address) = state
109                        .resolve_to_deterministic_addr(self.chain_store().blockstore(), address)
110                {
111                    address
112                } else {
113                    // If that fails, compute the tip-set and try again.
114                    let TipsetState { state_root, .. } = self.load_tipset_state(ts).await?;
115                    let state = StateTree::new_from_root(self.blockstore_owned(), &state_root)?;
116                    state.resolve_to_deterministic_addr(self.chain_store().blockstore(), address)?
117                };
118                self.id_to_deterministic_address_cache.push(id, resolved);
119                Ok(resolved)
120            }
121        }
122    }
123}