1use super::*;
5use crate::chain::AtFinalityResolution;
6use crate::prelude::*;
7use crate::shim::address::Payload;
8use bls_signatures::{PublicKey as BlsPublicKey, Serialize as _};
9
10impl StateManager {
11 pub fn get_bls_public_key(
13 db: &(impl Blockstore + ShallowClone),
14 addr: Address,
15 state_cid: Cid,
16 ) -> Result<BlsPublicKey, Error> {
17 let state =
18 StateTree::new_from_root(db, &state_cid).map_err(|e| Error::Other(e.to_string()))?;
19 let kaddr = state
20 .resolve_to_deterministic_address(db, addr)
21 .context("Failed to resolve key address")?;
22
23 match kaddr.into_payload() {
24 Payload::BLS(key) => BlsPublicKey::from_bytes(&key)
25 .context("Failed to construct bls public key")
26 .map_err(Error::from),
27 _ => Err(Error::state(
28 "Address must be BLS address to load bls public key",
29 )),
30 }
31 }
32
33 pub fn lookup_id(&self, addr: &Address, ts: &Tipset) -> Result<Option<Address>, Error> {
35 let state_tree =
36 StateTree::new_from_root(self.db(), ts.parent_state()).map_err(|e| format!("{e:?}"))?;
37 Ok(state_tree
38 .lookup_id(addr)
39 .map_err(|e| Error::Other(e.to_string()))?
40 .map(Address::new_id))
41 }
42
43 pub fn lookup_required_id(&self, addr: &Address, ts: &Tipset) -> Result<Address, Error> {
45 self.lookup_id(addr, ts)?
46 .ok_or_else(|| Error::Other(format!("Failed to lookup the id address {addr}")))
47 }
48
49 pub async fn resolve_to_deterministic_address(
52 &self,
53 address: Address,
54 ts: &Tipset,
55 ) -> anyhow::Result<Address> {
56 use crate::shim::address::Protocol::*;
57 match address.protocol() {
58 BLS | Secp256k1 | Delegated => Ok(address),
59 Actor => anyhow::bail!("cannot resolve actor address to key address"),
60 ID => {
61 let id = address.id()?;
62 let Some(cache) = &self.id_to_deterministic_address_cache else {
65 return self.resolve_id_address_at_tipset(address, ts).await;
66 };
67 if let Some(resolved) = cache.get(&id) {
68 return Ok(resolved);
69 }
70 let at_finality = {
76 let cs = self.chain_store().shallow_clone();
77 let ts = ts.clone();
78 tokio::task::spawn_blocking(move || {
79 cs.resolve_to_deterministic_address_at_finality(&address, &ts)
80 })
81 .await
82 .context("tokio join error")?
83 };
84 if let Ok(resolution) = at_finality {
85 return Ok(match resolution {
86 AtFinalityResolution::ReorgStable(resolved) => {
87 cache.insert(id, resolved);
88 resolved
89 }
90 AtFinalityResolution::Unstable(resolved) => resolved,
91 });
92 }
93 self.resolve_id_address_at_tipset(address, ts).await
94 }
95 }
96 }
97
98 async fn resolve_id_address_at_tipset(
101 &self,
102 address: Address,
103 ts: &Tipset,
104 ) -> anyhow::Result<Address> {
105 if let Ok(state) = self.get_state_tree(ts.parent_state()) {
107 match state.resolve_to_deterministic_address(self.db(), address) {
108 Ok(resolved) => return Ok(resolved),
109 Err(e)
114 if matches!(state.get_actor(&address), Ok(Some(_)))
115 && !self.computes_across_migration(ts) =>
116 {
117 return Err(e);
118 }
119 Err(_) => {}
120 }
121 }
122 let TipsetState { state_root, .. } = self.load_tipset_state(ts).await?;
124 let state = self.get_state_tree(&state_root)?;
125 state.resolve_to_deterministic_address(self.db(), address)
126 }
127
128 fn computes_across_migration(&self, ts: &Tipset) -> bool {
133 let Ok(parent) = self.chain_index().load_required_tipset(ts.parents()) else {
134 return true;
135 };
136 self.chain_config()
137 .has_expensive_fork_between(parent.epoch(), ts.epoch())
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::blocks::{CachingBlockHeader, RawBlockHeader, Tipset};
145 use crate::chain::ChainStore;
146 use crate::db::{DbImpl, MemoryDB};
147 use crate::networks::ChainConfig;
148 use crate::shim::state_tree::{ActorState, StateTree, StateTreeVersion};
149 use crate::test_utils::dummy_ticket;
150 use crate::utils::db::CborStoreExt as _;
151
152 const OLD_ACTOR: u64 = 300;
155 const YOUNG_ACTOR: u64 = 400;
157 const NON_ACCOUNT_ACTOR: u64 = 500;
161
162 fn setup_with_finality(chain_finality: ChainEpoch) -> (StateManager, Tipset, Address, Address) {
167 let db: DbImpl = Arc::new(MemoryDB::default()).into();
168
169 let mut cfg = ChainConfig::default();
170 cfg.policy.chain_finality = chain_finality;
171 let cfg = Arc::new(cfg);
172
173 let bls_a = Address::new_bls(&[8u8; 48]).unwrap();
174 let bls_b = Address::new_bls(&[9u8; 48]).unwrap();
175
176 let mut st_a = StateTree::new(&db, StateTreeVersion::V5).unwrap();
177 st_a.set_actor(
178 &Address::new_id(OLD_ACTOR),
179 ActorState::new_empty(Cid::default(), Some(bls_a)),
180 )
181 .unwrap();
182 st_a.set_actor(
183 &Address::new_id(NON_ACCOUNT_ACTOR),
184 ActorState::new_empty(Cid::default(), None),
185 )
186 .unwrap();
187 let root_a = st_a.flush().unwrap();
188
189 let mut st_b = StateTree::new_from_root(&db, &root_a).unwrap();
194 st_b.set_actor(
195 &Address::new_id(YOUNG_ACTOR),
196 ActorState::new_empty(Cid::default(), Some(bls_b)),
197 )
198 .unwrap();
199 let root_b = st_b.flush().unwrap();
200
201 let genesis = Tipset::from(CachingBlockHeader::new(RawBlockHeader {
202 ticket: dummy_ticket(0),
203 state_root: root_a,
204 timestamp: 1,
207 ..Default::default()
208 }));
209 db.put_cbor_default(genesis.block_headers().first())
210 .unwrap();
211
212 let ts1 = Tipset::from(CachingBlockHeader::new(RawBlockHeader {
213 parents: genesis.key().clone(),
214 ticket: dummy_ticket(1),
215 epoch: 1,
216 state_root: root_a,
217 timestamp: 1,
218 ..Default::default()
219 }));
220 db.put_cbor_default(ts1.block_headers().first()).unwrap();
221
222 let head = Tipset::from(CachingBlockHeader::new(RawBlockHeader {
223 parents: ts1.key().clone(),
224 ticket: dummy_ticket(2),
225 epoch: 2,
226 state_root: root_b,
227 timestamp: 2,
228 ..Default::default()
229 }));
230 db.put_cbor_default(head.block_headers().first()).unwrap();
231
232 let cs = ChainStore::new(db, cfg, genesis.block_headers().first().clone()).unwrap();
233 let sm = StateManager::new(cs).unwrap();
234 (sm, head, bls_a, bls_b)
235 }
236
237 #[tokio::test]
238 async fn caches_resolution_witnessed_at_finality_lookback() {
239 let (sm, head, bls_a, bls_b) = setup_with_finality(1);
240 let resolved = sm
241 .resolve_to_deterministic_address(Address::new_id(OLD_ACTOR), &head)
242 .await
243 .unwrap();
244 assert_eq!(resolved, bls_a);
245 let cache = sm.id_to_deterministic_address_cache().unwrap();
246 assert_eq!(cache.get(&OLD_ACTOR), Some(bls_a));
247
248 cache.insert(OLD_ACTOR, bls_b);
251 let resolved = sm
252 .resolve_to_deterministic_address(Address::new_id(OLD_ACTOR), &head)
253 .await
254 .unwrap();
255 assert_eq!(resolved, bls_b);
256 }
257
258 #[tokio::test]
259 async fn does_not_cache_actor_younger_than_finality() {
260 let (sm, head, _bls_a, bls_b) = setup_with_finality(1);
261 let resolved = sm
263 .resolve_to_deterministic_address(Address::new_id(YOUNG_ACTOR), &head)
264 .await
265 .unwrap();
266 assert_eq!(resolved, bls_b);
267 let cache = sm.id_to_deterministic_address_cache().unwrap();
268 assert_eq!(cache.get(&YOUNG_ACTOR), None);
269 }
270
271 #[tokio::test]
272 async fn does_not_cache_on_chain_younger_than_finality() {
273 let (sm, head, _bls_a, bls_b) = setup_with_finality(900);
276 let resolved = sm
277 .resolve_to_deterministic_address(Address::new_id(YOUNG_ACTOR), &head)
278 .await
279 .unwrap();
280 assert_eq!(resolved, bls_b);
281 assert_eq!(
282 sm.id_to_deterministic_address_cache().unwrap().len(),
283 0,
284 "nothing may be cached without a finality-deep witness"
285 );
286 }
287
288 #[tokio::test]
289 async fn does_not_cache_at_exact_finality_boundary() {
290 let (sm, head, bls_a, _bls_b) = setup_with_finality(2);
296 let resolved = sm
297 .resolve_to_deterministic_address(Address::new_id(OLD_ACTOR), &head)
298 .await
299 .unwrap();
300 assert_eq!(resolved, bls_a);
301 assert_eq!(
302 sm.id_to_deterministic_address_cache().unwrap().len(),
303 0,
304 "epoch == chain_finality is not finality-deep and must not be cached"
305 );
306 }
307
308 #[tokio::test]
309 async fn present_non_account_actor_errors_without_caching() {
310 let (sm, head, _bls_a, _bls_b) = setup_with_finality(1);
311 let resolved = sm
312 .resolve_to_deterministic_address(Address::new_id(NON_ACCOUNT_ACTOR), &head)
313 .await;
314 assert!(
315 resolved.is_err(),
316 "an actor that is present but is not an account has no key address"
317 );
318 assert_eq!(
319 sm.id_to_deterministic_address_cache().unwrap().len(),
320 0,
321 "an unresolvable actor must not be cached"
322 );
323 }
324
325 #[tokio::test]
326 async fn non_id_addresses_bypass_cache_and_lookback() {
327 let (sm, head, bls_a, _bls_b) = setup_with_finality(1);
328 let resolved = sm
329 .resolve_to_deterministic_address(bls_a, &head)
330 .await
331 .unwrap();
332 assert_eq!(resolved, bls_a);
333 assert_eq!(sm.id_to_deterministic_address_cache().unwrap().len(), 0);
334 }
335}