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 && let Ok(address) = state.resolve_to_deterministic_address(self.db(), address)
108 {
109 return Ok(address);
110 }
111 let TipsetState { state_root, .. } = self.load_tipset_state(ts).await?;
113 let state = self.get_state_tree(&state_root)?;
114 state.resolve_to_deterministic_address(self.db(), address)
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use crate::blocks::{CachingBlockHeader, RawBlockHeader, Tipset};
122 use crate::chain::ChainStore;
123 use crate::db::{DbImpl, MemoryDB};
124 use crate::networks::ChainConfig;
125 use crate::shim::state_tree::{ActorState, StateTree, StateTreeVersion};
126 use crate::test_utils::dummy_ticket;
127 use crate::utils::db::CborStoreExt as _;
128
129 const OLD_ACTOR: u64 = 300;
132 const YOUNG_ACTOR: u64 = 400;
134
135 fn setup_with_finality(chain_finality: ChainEpoch) -> (StateManager, Tipset, Address, Address) {
140 let db: DbImpl = Arc::new(MemoryDB::default()).into();
141
142 let mut cfg = ChainConfig::default();
143 cfg.policy.chain_finality = chain_finality;
144 let cfg = Arc::new(cfg);
145
146 let bls_a = Address::new_bls(&[8u8; 48]).unwrap();
147 let bls_b = Address::new_bls(&[9u8; 48]).unwrap();
148
149 let mut st_a = StateTree::new(&db, StateTreeVersion::V5).unwrap();
150 st_a.set_actor(
151 &Address::new_id(OLD_ACTOR),
152 ActorState::new_empty(Cid::default(), Some(bls_a)),
153 )
154 .unwrap();
155 let root_a = st_a.flush().unwrap();
156
157 let mut st_b = StateTree::new_from_root(&db, &root_a).unwrap();
162 st_b.set_actor(
163 &Address::new_id(YOUNG_ACTOR),
164 ActorState::new_empty(Cid::default(), Some(bls_b)),
165 )
166 .unwrap();
167 let root_b = st_b.flush().unwrap();
168
169 let genesis = Tipset::from(CachingBlockHeader::new(RawBlockHeader {
170 ticket: dummy_ticket(0),
171 state_root: root_a,
172 timestamp: 1,
175 ..Default::default()
176 }));
177 db.put_cbor_default(genesis.block_headers().first())
178 .unwrap();
179
180 let ts1 = Tipset::from(CachingBlockHeader::new(RawBlockHeader {
181 parents: genesis.key().clone(),
182 ticket: dummy_ticket(1),
183 epoch: 1,
184 state_root: root_a,
185 timestamp: 1,
186 ..Default::default()
187 }));
188 db.put_cbor_default(ts1.block_headers().first()).unwrap();
189
190 let head = Tipset::from(CachingBlockHeader::new(RawBlockHeader {
191 parents: ts1.key().clone(),
192 ticket: dummy_ticket(2),
193 epoch: 2,
194 state_root: root_b,
195 timestamp: 2,
196 ..Default::default()
197 }));
198 db.put_cbor_default(head.block_headers().first()).unwrap();
199
200 let cs = ChainStore::new(db, cfg, genesis.block_headers().first().clone()).unwrap();
201 let sm = StateManager::new(cs).unwrap();
202 (sm, head, bls_a, bls_b)
203 }
204
205 #[tokio::test]
206 async fn caches_resolution_witnessed_at_finality_lookback() {
207 let (sm, head, bls_a, bls_b) = setup_with_finality(1);
208 let resolved = sm
209 .resolve_to_deterministic_address(Address::new_id(OLD_ACTOR), &head)
210 .await
211 .unwrap();
212 assert_eq!(resolved, bls_a);
213 let cache = sm.id_to_deterministic_address_cache().unwrap();
214 assert_eq!(cache.get(&OLD_ACTOR), Some(bls_a));
215
216 cache.insert(OLD_ACTOR, bls_b);
219 let resolved = sm
220 .resolve_to_deterministic_address(Address::new_id(OLD_ACTOR), &head)
221 .await
222 .unwrap();
223 assert_eq!(resolved, bls_b);
224 }
225
226 #[tokio::test]
227 async fn does_not_cache_actor_younger_than_finality() {
228 let (sm, head, _bls_a, bls_b) = setup_with_finality(1);
229 let resolved = sm
231 .resolve_to_deterministic_address(Address::new_id(YOUNG_ACTOR), &head)
232 .await
233 .unwrap();
234 assert_eq!(resolved, bls_b);
235 let cache = sm.id_to_deterministic_address_cache().unwrap();
236 assert_eq!(cache.get(&YOUNG_ACTOR), None);
237 }
238
239 #[tokio::test]
240 async fn does_not_cache_on_chain_younger_than_finality() {
241 let (sm, head, _bls_a, bls_b) = setup_with_finality(900);
244 let resolved = sm
245 .resolve_to_deterministic_address(Address::new_id(YOUNG_ACTOR), &head)
246 .await
247 .unwrap();
248 assert_eq!(resolved, bls_b);
249 assert_eq!(
250 sm.id_to_deterministic_address_cache().unwrap().len(),
251 0,
252 "nothing may be cached without a finality-deep witness"
253 );
254 }
255
256 #[tokio::test]
257 async fn does_not_cache_at_exact_finality_boundary() {
258 let (sm, head, bls_a, _bls_b) = setup_with_finality(2);
264 let resolved = sm
265 .resolve_to_deterministic_address(Address::new_id(OLD_ACTOR), &head)
266 .await
267 .unwrap();
268 assert_eq!(resolved, bls_a);
269 assert_eq!(
270 sm.id_to_deterministic_address_cache().unwrap().len(),
271 0,
272 "epoch == chain_finality is not finality-deep and must not be cached"
273 );
274 }
275
276 #[tokio::test]
277 async fn non_id_addresses_bypass_cache_and_lookback() {
278 let (sm, head, bls_a, _bls_b) = setup_with_finality(1);
279 let resolved = sm
280 .resolve_to_deterministic_address(bls_a, &head)
281 .await
282 .unwrap();
283 assert_eq!(resolved, bls_a);
284 assert_eq!(sm.id_to_deterministic_address_cache().unwrap().len(), 0);
285 }
286}