1use super::actors::LoadActorStateFromBlockstore;
4pub use super::fvm_shared_latest::{ActorID, state::StateRoot};
5use crate::{
6 blocks::Tipset,
7 networks::{ACTOR_BUNDLES_METADATA, ActorBundleMetadata},
8 prelude::*,
9 shim::{
10 actors::{AccountActorStateLoad as _, account},
11 address::Address,
12 econ::TokenAmount,
13 },
14 utils::get_size::big_int_heap_size_helper,
15};
16use anyhow::bail;
17use fvm_ipld_encoding::{
18 CborStore as _,
19 repr::{Deserialize_repr, Serialize_repr},
20};
21use fvm_shared2::state::StateTreeVersion as StateTreeVersionV2;
22use fvm_shared3::state::StateTreeVersion as StateTreeVersionV3;
23use fvm_shared4::state::StateTreeVersion as StateTreeVersionV4;
24pub use fvm2::state_tree::{ActorState as ActorStateV2, StateTree as StateTreeV2};
25pub use fvm3::state_tree::{ActorState as ActorStateV3, StateTree as StateTreeV3};
26pub use fvm4::state_tree::{
27 ActorState as ActorStateV4, ActorState as ActorState_latest, StateTree as StateTreeV4,
28};
29use get_size2::GetSize;
30use num::FromPrimitive;
31use num_derive::FromPrimitive;
32use serde::{Deserialize, Serialize};
33use spire_enum::prelude::delegated_enum;
34
35#[derive(
36 Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Serialize_repr, Deserialize_repr, FromPrimitive,
37)]
38#[repr(u64)]
39pub enum StateTreeVersion {
40 V0,
41 V1,
42 V2,
43 V3,
44 V4,
45 V5,
46}
47
48impl From<StateTreeVersionV4> for StateTreeVersion {
49 fn from(value: StateTreeVersionV4) -> Self {
50 match value {
51 StateTreeVersionV4::V0 => Self::V0,
52 StateTreeVersionV4::V1 => Self::V1,
53 StateTreeVersionV4::V2 => Self::V2,
54 StateTreeVersionV4::V3 => Self::V3,
55 StateTreeVersionV4::V4 => Self::V4,
56 StateTreeVersionV4::V5 => Self::V5,
57 }
58 }
59}
60
61impl From<StateTreeVersionV3> for StateTreeVersion {
62 fn from(value: StateTreeVersionV3) -> Self {
63 match value {
64 StateTreeVersionV3::V0 => Self::V0,
65 StateTreeVersionV3::V1 => Self::V1,
66 StateTreeVersionV3::V2 => Self::V2,
67 StateTreeVersionV3::V3 => Self::V3,
68 StateTreeVersionV3::V4 => Self::V4,
69 StateTreeVersionV3::V5 => Self::V5,
70 }
71 }
72}
73
74impl TryFrom<StateTreeVersionV2> for StateTreeVersion {
75 type Error = anyhow::Error;
76 fn try_from(value: StateTreeVersionV2) -> anyhow::Result<Self> {
77 if let Some(v) = FromPrimitive::from_u32(value as u32) {
78 Ok(v)
79 } else {
80 bail!("Invalid conversion");
81 }
82 }
83}
84
85impl TryFrom<StateTreeVersion> for StateTreeVersionV2 {
86 type Error = anyhow::Error;
87
88 fn try_from(value: StateTreeVersion) -> anyhow::Result<Self> {
89 Ok(match value {
90 StateTreeVersion::V0 => Self::V0,
91 StateTreeVersion::V1 => Self::V1,
92 StateTreeVersion::V2 => Self::V2,
93 StateTreeVersion::V3 => Self::V3,
94 StateTreeVersion::V4 => Self::V4,
95 StateTreeVersion::V5 => bail!("Impossible conversion"),
96 })
97 }
98}
99
100impl TryFrom<StateTreeVersion> for StateTreeVersionV3 {
101 type Error = anyhow::Error;
102
103 fn try_from(value: StateTreeVersion) -> anyhow::Result<Self> {
104 Ok(match value {
105 StateTreeVersion::V0 => Self::V0,
106 StateTreeVersion::V1 => Self::V1,
107 StateTreeVersion::V2 => Self::V2,
108 StateTreeVersion::V3 => Self::V3,
109 StateTreeVersion::V4 => Self::V4,
110 StateTreeVersion::V5 => Self::V5,
111 })
112 }
113}
114
115impl TryFrom<StateTreeVersion> for StateTreeVersionV4 {
116 type Error = anyhow::Error;
117
118 fn try_from(value: StateTreeVersion) -> anyhow::Result<Self> {
119 Ok(match value {
120 StateTreeVersion::V0 => Self::V0,
121 StateTreeVersion::V1 => Self::V1,
122 StateTreeVersion::V2 => Self::V2,
123 StateTreeVersion::V3 => Self::V3,
124 StateTreeVersion::V4 => Self::V4,
125 StateTreeVersion::V5 => Self::V5,
126 })
127 }
128}
129
130#[delegated_enum(impl_conversions)]
138pub enum StateTree<S> {
139 V0(super::state_tree_v0::StateTreeV0<S>),
141 FvmV2(StateTreeV2<S>),
143 FvmV3(StateTreeV3<S>),
145 FvmV4(StateTreeV4<S>),
147}
148
149impl<S> StateTree<S>
150where
151 S: Blockstore + ShallowClone,
152{
153 pub fn new(store: &S, version: StateTreeVersion) -> anyhow::Result<Self> {
155 if let Ok(st) = StateTreeV4::new(store.shallow_clone(), version.try_into()?) {
156 Ok(StateTree::FvmV4(st))
157 } else if let Ok(st) = StateTreeV3::new(store.shallow_clone(), version.try_into()?) {
158 Ok(StateTree::FvmV3(st))
159 } else if let Ok(st) = StateTreeV2::new(store.shallow_clone(), version.try_into()?) {
160 Ok(StateTree::FvmV2(st))
161 } else {
162 bail!("Can't create a valid state tree for the given version.");
163 }
164 }
165
166 pub fn new_from_root(store: &S, c: &Cid) -> anyhow::Result<Self> {
167 if let Ok(st) = StateTreeV4::new_from_root(store.shallow_clone(), c) {
168 Ok(StateTree::FvmV4(st))
169 } else if let Ok(st) = StateTreeV3::new_from_root(store.shallow_clone(), c) {
170 Ok(StateTree::FvmV3(st))
171 } else if let Ok(st) = StateTreeV2::new_from_root(store.shallow_clone(), c) {
172 Ok(StateTree::FvmV2(st))
173 } else if let Ok(st) =
174 super::state_tree_v0::StateTreeV0::new_from_root(store.shallow_clone(), c)
175 {
176 Ok(StateTree::V0(st))
177 } else if !store.has(c)? {
178 bail!("No state tree exists for the root {c}.")
179 } else {
180 let state_root = store.get_cbor::<StateRoot>(c).ok().flatten();
181 let state_root_version = state_root
182 .map(|sr| format!("{:?}", sr.version))
183 .unwrap_or_else(|| "unknown".into());
184 bail!(
185 "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}"
186 )
187 }
188 }
189
190 pub fn new_from_tipset(store: &S, ts: &Tipset) -> anyhow::Result<Self> {
191 Self::new_from_root(store, ts.parent_state())
192 }
193}
194
195impl<S> StateTree<S>
196where
197 S: Blockstore,
198{
199 pub fn get_required_actor(&self, addr: &Address) -> anyhow::Result<ActorState> {
201 self.get_actor(addr)?
202 .with_context(|| format!("Actor not found: addr={addr}"))
203 }
204
205 pub fn get_actor_bundle_metadata(&self) -> anyhow::Result<&ActorBundleMetadata> {
207 let system_actor_code = self.get_required_actor(&Address::SYSTEM_ACTOR)?.code;
208 ACTOR_BUNDLES_METADATA
209 .values()
210 .find(|v| v.manifest.get_system() == system_actor_code)
211 .with_context(|| format!("actor bundle not found for system actor {system_actor_code}"))
212 }
213
214 pub fn get_actor(&self, addr: &Address) -> anyhow::Result<Option<ActorState>> {
216 match self {
217 StateTree::FvmV2(st) => {
218 anyhow::ensure!(
219 addr.protocol() != crate::shim::address::Protocol::Delegated,
220 "Delegated addresses are not supported in FVMv2 state trees"
221 );
222 Ok(st.get_actor(&addr.into())?.map(Into::into))
223 }
224 StateTree::FvmV3(st) => {
225 let id = st.lookup_id(&addr.into())?;
226 if let Some(id) = id {
227 Ok(st.get_actor(id)?.map(Into::into))
228 } else {
229 Ok(None)
230 }
231 }
232 StateTree::FvmV4(st) => {
233 let id = st.lookup_id(addr)?;
234 if let Some(id) = id {
235 Ok(st.get_actor(id)?.map(Into::into))
236 } else {
237 Ok(None)
238 }
239 }
240 StateTree::V0(st) => {
241 let id = st.lookup_id(addr)?;
242 if let Some(id) = id {
243 Ok(st.get_actor(&id)?.map(Into::into))
244 } else {
245 Ok(None)
246 }
247 }
248 }
249 }
250
251 pub fn get_actor_state<STATE: LoadActorStateFromBlockstore>(&self) -> anyhow::Result<STATE> {
253 let address = STATE::ACTOR.with_context(|| {
254 format!(
255 "No associated actor address for {}, use `get_actor_state_from_address` instead.",
256 std::any::type_name::<STATE>()
257 )
258 })?;
259 let actor = self.get_required_actor(&address)?;
260 STATE::load_from_blockstore(self.store(), &actor)
261 }
262
263 pub fn get_actor_state_from_address<STATE: LoadActorStateFromBlockstore>(
265 &self,
266 actor_address: &Address,
267 ) -> anyhow::Result<STATE> {
268 let actor = self.get_required_actor(actor_address)?;
269 STATE::load_from_blockstore(self.store(), &actor)
270 }
271
272 pub fn store(&self) -> &S {
274 delegate_state_tree!(self.store())
275 }
276
277 pub fn lookup_id(&self, addr: &Address) -> anyhow::Result<Option<ActorID>> {
279 match self {
280 StateTree::FvmV2(st) => {
281 anyhow::ensure!(
284 addr.protocol() != crate::shim::address::Protocol::Delegated,
285 "Delegated addresses are not supported in FVMv2 state trees"
286 );
287 Ok(st.lookup_id(&addr.into())?)
288 }
289 StateTree::FvmV3(st) => Ok(st.lookup_id(&addr.into())?),
290 StateTree::FvmV4(st) => Ok(st.lookup_id(&addr.into())?),
291 StateTree::V0(_) => bail!("StateTree::lookup_id not supported on old state trees"),
292 }
293 }
294
295 pub fn lookup_required_id(&self, addr: &Address) -> anyhow::Result<ActorID> {
297 self.lookup_id(addr)?
298 .with_context(|| format!("actor id not found for address {addr}"))
299 }
300
301 pub fn for_each<F>(&self, mut f: F) -> anyhow::Result<()>
304 where
305 F: FnMut(Address, &ActorState) -> anyhow::Result<()>,
306 {
307 match self {
308 StateTree::FvmV2(st) => {
309 st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
310 }
311 StateTree::FvmV3(st) => {
312 st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
313 }
314 StateTree::FvmV4(st) => {
315 st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
316 }
317 StateTree::V0(_) => bail!("StateTree::for_each not supported on old state trees"),
318 }
319 }
320
321 pub fn for_each_cacheless<F>(&self, mut f: F) -> anyhow::Result<()>
323 where
324 F: FnMut(Address, &ActorState) -> anyhow::Result<()>,
325 {
326 match self {
327 StateTree::FvmV2(st) => {
328 st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
329 }
330 StateTree::FvmV3(st) => {
331 st.for_each(|address, actor_state| f(address.into(), &actor_state.into()))
332 }
333 StateTree::FvmV4(st) => {
334 st.for_each_cacheless(|address, actor_state| f(address.into(), &actor_state.into()))
335 }
336 StateTree::V0(_) => {
337 bail!("StateTree::for_each_cacheless not supported on old state trees")
338 }
339 }
340 }
341
342 pub fn flush(&mut self) -> anyhow::Result<Cid> {
344 match self {
345 StateTree::FvmV2(st) => Ok(st.flush()?),
346 StateTree::FvmV3(st) => Ok(st.flush()?),
347 StateTree::FvmV4(st) => Ok(st.flush()?),
348 StateTree::V0(_) => bail!("StateTree::flush not supported on old state trees"),
349 }
350 }
351
352 pub fn set_actor(&mut self, addr: &Address, actor: ActorState) -> anyhow::Result<()> {
354 match self {
355 StateTree::FvmV2(st) => {
356 st.set_actor(&addr.into(), actor.into())?;
357 Ok(())
358 }
359 StateTree::FvmV3(st) => {
360 let id = st
361 .lookup_id(&addr.into())?
362 .context("couldn't find actor id")?;
363 st.set_actor(id, actor.into());
364 Ok(())
365 }
366 StateTree::FvmV4(st) => {
367 let id = st
368 .lookup_id(&addr.into())?
369 .context("couldn't find actor id")?;
370 st.set_actor(id, actor.into());
371 Ok(())
372 }
373 StateTree::V0(_) => bail!("StateTree::set_actor not supported on old state trees"),
374 }
375 }
376
377 pub fn resolve_to_deterministic_address(
381 &self,
382 store: &impl Blockstore,
383 addr: Address,
384 ) -> anyhow::Result<Address> {
385 use crate::shim::address::Protocol::*;
386 match addr.protocol() {
387 BLS | Secp256k1 | Delegated => Ok(addr),
388 _ => {
389 let actor = self
390 .get_actor(&addr)?
391 .with_context(|| format!("failed to find actor: {addr}"))?;
392 if let Some(address) = actor.delegated_address {
393 Ok(address.into())
394 } else {
395 let account_state = account::State::load(store, actor.code, actor.state)?;
396 Ok(account_state.pubkey_address())
397 }
398 }
399 }
400 }
401}
402
403#[derive(
429 PartialEq, Eq, Clone, Debug, Serialize, Deserialize, derive_more::Deref, derive_more::DerefMut,
430)]
431#[serde(transparent)]
432#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
433pub struct ActorState(ActorState_latest);
434
435impl ActorState {
436 pub fn new(
437 code: Cid,
438 state: Cid,
439 balance: TokenAmount,
440 sequence: u64,
441 address: Option<Address>,
442 ) -> Self {
443 Self(ActorState_latest::new(
444 code,
445 state,
446 balance.into(),
447 sequence,
448 address.map(Into::into),
449 ))
450 }
451 pub fn new_empty(code: Cid, delegated_address: Option<Address>) -> Self {
453 Self(ActorState_latest::new_empty(
454 code,
455 delegated_address.map(Into::into),
456 ))
457 }
458}
459
460impl GetSize for ActorState {
461 fn get_heap_size_with_tracker<T: get_size2::GetSizeTracker>(&self, tracker: T) -> (usize, T) {
462 (big_int_heap_size_helper(self.balance.atto()), tracker)
463 }
464}
465
466impl From<&ActorStateV2> for ActorState {
467 fn from(value: &ActorStateV2) -> Self {
468 Self(ActorState_latest {
469 code: value.code,
470 state: value.state,
471 sequence: value.sequence,
472 balance: TokenAmount::from(&value.balance).into(),
473 delegated_address: None,
474 })
475 }
476}
477
478impl From<ActorStateV2> for ActorState {
479 fn from(value: ActorStateV2) -> Self {
480 (&value).into()
481 }
482}
483
484impl From<ActorStateV3> for ActorState {
485 fn from(value: ActorStateV3) -> Self {
486 Self(ActorState_latest {
487 code: value.code,
488 state: value.state,
489 sequence: value.sequence,
490 balance: TokenAmount::from(value.balance).into(),
491 delegated_address: value
492 .delegated_address
493 .map(|addr| Address::from(addr).into()),
494 })
495 }
496}
497
498impl From<&ActorStateV3> for ActorState {
499 fn from(value: &ActorStateV3) -> Self {
500 value.clone().into()
501 }
502}
503
504impl From<ActorStateV4> for ActorState {
505 fn from(value: ActorStateV4) -> Self {
506 ActorState(value)
507 }
508}
509
510impl From<&ActorStateV4> for ActorState {
511 fn from(value: &ActorStateV4) -> Self {
512 value.clone().into()
513 }
514}
515
516impl From<ActorState> for ActorStateV2 {
517 fn from(other: ActorState) -> ActorStateV2 {
518 Self {
519 code: other.code,
520 state: other.state,
521 sequence: other.sequence,
522 balance: TokenAmount::from(&other.balance).into(),
523 }
524 }
525}
526
527impl From<&ActorState> for ActorStateV2 {
528 fn from(other: &ActorState) -> ActorStateV2 {
529 Self {
530 code: other.code,
531 state: other.state,
532 sequence: other.sequence,
533 balance: TokenAmount::from(&other.balance).into(),
534 }
535 }
536}
537
538impl From<ActorState> for ActorStateV3 {
539 fn from(other: ActorState) -> Self {
540 Self {
541 code: other.code,
542 state: other.state,
543 sequence: other.sequence,
544 balance: TokenAmount::from(&other.balance).into(),
545 delegated_address: other
546 .delegated_address
547 .map(|addr| Address::from(addr).into()),
548 }
549 }
550}
551
552impl From<ActorState> for ActorStateV4 {
553 fn from(other: ActorState) -> Self {
554 other.0
555 }
556}
557
558#[cfg(test)]
559mod tests {
560 use super::StateTree;
561 use crate::blocks::CachingBlockHeader;
562 use crate::db::car::AnyCar;
563 use crate::networks::{calibnet, mainnet};
564 use crate::shim::actors::init;
565 use cid::Cid;
566 use std::sync::Arc;
567
568 fn get_network_name(car: &'static [u8], genesis_cid: Cid) -> String {
570 let forest_car = Arc::new(AnyCar::new(car).unwrap());
571 let genesis_block = CachingBlockHeader::load(&forest_car, genesis_cid)
572 .unwrap()
573 .unwrap();
574 let state_tree = StateTree::new_from_root(&forest_car, &genesis_block.state_root).unwrap();
575 let state: init::State = state_tree.get_actor_state().unwrap();
576 state.into_network_name()
577 }
578
579 #[test]
580 fn calibnet_network_name() {
581 assert_eq!(
582 get_network_name(calibnet::DEFAULT_GENESIS, *calibnet::GENESIS_CID),
583 "calibrationnet"
584 );
585 }
586
587 #[test]
588 fn mainnet_network_name() {
589 assert_eq!(
591 get_network_name(mainnet::DEFAULT_GENESIS, *mainnet::GENESIS_CID),
592 "testnetnet"
593 );
594 }
595}