Skip to main content

forest/rpc/methods/
state.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4mod types;
5use futures::stream::FuturesOrdered;
6pub use types::*;
7
8use super::chain::ChainGetTipSetV2;
9use crate::beacon::Beacon as _;
10use crate::blocks::{Tipset, TipsetKey};
11use crate::chain::index::ResolveNullTipset;
12use crate::cid_collections::CidHashSet;
13use crate::eth::EthChainId;
14use crate::interpreter::{MessageCallbackCtx, VMTrace};
15use crate::libp2p::NetworkMessage;
16use crate::lotus_json::{LotusJson, lotus_json_with_self};
17use crate::networks::ChainConfig;
18use crate::rpc::registry::actors_reg::load_and_serialize_actor_state;
19use crate::shim::actors::market::DealState;
20use crate::shim::actors::market::ext::MarketStateExt as _;
21use crate::shim::actors::miner::ext::DeadlineExt;
22use crate::shim::actors::state_load::*;
23use crate::shim::actors::verifreg::ext::VerifiedRegistryStateExt as _;
24use crate::shim::actors::verifreg::{Allocation, AllocationID, Claim};
25use crate::shim::actors::{init, system};
26use crate::shim::actors::{
27    market, miner,
28    miner::{MinerInfo, MinerPower},
29    power, reward, verifreg,
30};
31use crate::shim::actors::{
32    market::ext::BalanceTableExt as _, miner::ext::MinerStateExt as _,
33    power::ext::PowerStateExt as _,
34};
35use crate::shim::address::Payload;
36use crate::shim::machine::BuiltinActorManifest;
37use crate::shim::message::{Message, MethodNum};
38use crate::shim::sector::{SectorNumber, SectorSize};
39use crate::shim::state_tree::{ActorID, StateTree};
40use crate::shim::{
41    address::Address, clock::ChainEpoch, deal::DealID, econ::TokenAmount, executor::Receipt,
42    state_tree::ActorState, version::NetworkVersion,
43};
44use crate::state_manager::{ExecutedTipset, NO_CALLBACK};
45use crate::state_manager::{
46    MarketBalance, StateManager, circulating_supply::GenesisInfo, utils::structured,
47};
48use crate::utils::ShallowClone as _;
49use crate::utils::db::car_stream::{CarBlock, CarWriter};
50use crate::{
51    beacon::BeaconEntry,
52    rpc::{ApiPaths, Ctx, Permission, RpcMethod, ServerError, types::*},
53};
54use ahash::{HashMap, HashMapExt, HashSet};
55use anyhow::Context;
56use anyhow::Result;
57use cid::Cid;
58use enumflags2::{BitFlags, make_bitflags};
59use fil_actor_miner_state::v10::{qa_power_for_weight, qa_power_max};
60use fil_actor_verifreg_state::v13::ClaimID;
61use fil_actors_shared::fvm_ipld_amt::Amt;
62use fil_actors_shared::fvm_ipld_bitfield::BitField;
63use futures::{StreamExt as _, TryStreamExt as _};
64use fvm_ipld_blockstore::Blockstore;
65use fvm_ipld_encoding::{CborStore, DAG_CBOR};
66pub use fvm_shared3::sector::StoragePower;
67use ipld_core::ipld::Ipld;
68use jsonrpsee::types::error::ErrorObject;
69use num_bigint::BigInt;
70use num_traits::Euclid;
71use nunny::vec as nonempty;
72use parking_lot::Mutex;
73use schemars::JsonSchema;
74use serde::{Deserialize, Serialize};
75use std::num::NonZeroUsize;
76use std::ops::Mul;
77use std::path::PathBuf;
78use std::{sync::Arc, time::Duration};
79use tokio::task::JoinSet;
80
81const INITIAL_PLEDGE_NUM: u64 = 110;
82const INITIAL_PLEDGE_DEN: u64 = 100;
83
84pub enum StateCall {}
85
86impl StateCall {
87    pub fn run<DB: Blockstore + Send + Sync + 'static>(
88        state_manager: &StateManager<DB>,
89        message: &Message,
90        tsk: Option<TipsetKey>,
91    ) -> anyhow::Result<ApiInvocResult> {
92        let tipset = state_manager
93            .chain_store()
94            .load_required_tipset_or_heaviest(&tsk)?;
95        Ok(state_manager.call(message, Some(tipset))?)
96    }
97}
98
99impl RpcMethod<2> for StateCall {
100    const NAME: &'static str = "Filecoin.StateCall";
101    const PARAM_NAMES: [&'static str; 2] = ["message", "tipsetKey"];
102    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
103    const PERMISSION: Permission = Permission::Read;
104    const DESCRIPTION: Option<&'static str> = Some(
105        "Runs the given message and returns its result without persisting changes. The message is applied to the tipset's parent state.",
106    );
107
108    type Params = (Message, ApiTipsetKey);
109    type Ok = ApiInvocResult;
110
111    async fn handle(
112        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
113        (message, ApiTipsetKey(tsk)): Self::Params,
114        _: &http::Extensions,
115    ) -> Result<Self::Ok, ServerError> {
116        Ok(Self::run(&ctx.state_manager, &message, tsk)?)
117    }
118}
119
120pub enum StateReplay {}
121impl RpcMethod<2> for StateReplay {
122    const NAME: &'static str = "Filecoin.StateReplay";
123    const PARAM_NAMES: [&'static str; 2] = ["tipsetKey", "messageCid"];
124    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
125    const PERMISSION: Permission = Permission::Read;
126    const DESCRIPTION: Option<&'static str> = Some(
127        "Replays a given message, assuming it was included in a block in the specified tipset.",
128    );
129
130    type Params = (ApiTipsetKey, Cid);
131    type Ok = ApiInvocResult;
132
133    /// returns the result of executing the indicated message, assuming it was
134    /// executed in the indicated tipset.
135    async fn handle(
136        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
137        (ApiTipsetKey(tsk), message_cid): Self::Params,
138        _: &http::Extensions,
139    ) -> Result<Self::Ok, ServerError> {
140        let tipset = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
141        Ok(ctx.state_manager.replay(tipset, message_cid).await?)
142    }
143}
144
145pub enum StateNetworkName {}
146impl RpcMethod<0> for StateNetworkName {
147    const NAME: &'static str = "Filecoin.StateNetworkName";
148    const PARAM_NAMES: [&'static str; 0] = [];
149    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
150    const PERMISSION: Permission = Permission::Read;
151
152    type Params = ();
153    type Ok = String;
154
155    async fn handle(
156        ctx: Ctx<impl Blockstore>,
157        (): Self::Params,
158        _: &http::Extensions,
159    ) -> Result<Self::Ok, ServerError> {
160        let heaviest_tipset = ctx.chain_store().heaviest_tipset();
161        Ok(ctx
162            .state_manager
163            .get_network_state_name(*heaviest_tipset.parent_state())?
164            .into())
165    }
166}
167
168pub enum StateNetworkVersion {}
169impl RpcMethod<1> for StateNetworkVersion {
170    const NAME: &'static str = "Filecoin.StateNetworkVersion";
171    const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"];
172    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
173    const PERMISSION: Permission = Permission::Read;
174    const DESCRIPTION: Option<&'static str> =
175        Some("Returns the network version at the given tipset.");
176
177    type Params = (ApiTipsetKey,);
178    type Ok = NetworkVersion;
179
180    async fn handle(
181        ctx: Ctx<impl Blockstore>,
182        (ApiTipsetKey(tsk),): Self::Params,
183        _: &http::Extensions,
184    ) -> Result<Self::Ok, ServerError> {
185        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
186        Ok(ctx.state_manager.get_network_version(ts.epoch()))
187    }
188}
189
190/// gets the public key address of the given ID address
191/// See <https://github.com/filecoin-project/lotus/blob/master/documentation/en/api-methods-v0-deprecated.md#StateAccountKey>
192pub enum StateAccountKey {}
193
194impl RpcMethod<2> for StateAccountKey {
195    const NAME: &'static str = "Filecoin.StateAccountKey";
196    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetKey"];
197    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
198    const PERMISSION: Permission = Permission::Read;
199    const DESCRIPTION: Option<&'static str> =
200        Some("Returns the public key address for the given ID address (secp and bls accounts).");
201
202    type Params = (Address, ApiTipsetKey);
203    type Ok = Address;
204
205    async fn handle(
206        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
207        (address, ApiTipsetKey(tsk)): Self::Params,
208        _: &http::Extensions,
209    ) -> Result<Self::Ok, ServerError> {
210        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
211        Ok(ctx
212            .state_manager
213            .resolve_to_deterministic_address(address, &ts)
214            .await?)
215    }
216}
217
218/// retrieves the ID address of the given address
219/// See <https://github.com/filecoin-project/lotus/blob/master/documentation/en/api-methods-v0-deprecated.md#StateLookupID>
220pub enum StateLookupID {}
221
222impl RpcMethod<2> for StateLookupID {
223    const NAME: &'static str = "Filecoin.StateLookupID";
224    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetKey"];
225    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
226    const PERMISSION: Permission = Permission::Read;
227    const DESCRIPTION: Option<&'static str> =
228        Some("Retrieves the ID address of the given address.");
229
230    type Params = (Address, ApiTipsetKey);
231    type Ok = Address;
232
233    async fn handle(
234        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
235        (address, ApiTipsetKey(tsk)): Self::Params,
236        _: &http::Extensions,
237    ) -> Result<Self::Ok, ServerError> {
238        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
239        Ok(ctx.state_manager.lookup_required_id(&address, &ts)?)
240    }
241}
242
243/// `StateVerifiedRegistryRootKey` returns the address of the Verified Registry's root key
244pub enum StateVerifiedRegistryRootKey {}
245
246impl RpcMethod<1> for StateVerifiedRegistryRootKey {
247    const NAME: &'static str = "Filecoin.StateVerifiedRegistryRootKey";
248    const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"];
249    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
250    const PERMISSION: Permission = Permission::Read;
251    const DESCRIPTION: Option<&'static str> =
252        Some("Returns the address of the Verified Registry's root key.");
253
254    type Params = (ApiTipsetKey,);
255    type Ok = Address;
256
257    async fn handle(
258        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
259        (ApiTipsetKey(tsk),): Self::Params,
260        _: &http::Extensions,
261    ) -> Result<Self::Ok, ServerError> {
262        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
263        let state: verifreg::State = ctx.state_manager.get_actor_state(&ts)?;
264        Ok(state.root_key())
265    }
266}
267
268// StateVerifiedClientStatus returns the data cap for the given address.
269// Returns zero if there is no entry in the data cap table for the address.
270pub enum StateVerifierStatus {}
271
272impl RpcMethod<2> for StateVerifierStatus {
273    const NAME: &'static str = "Filecoin.StateVerifierStatus";
274    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetKey"];
275    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
276    const PERMISSION: Permission = Permission::Read;
277    const DESCRIPTION: Option<&'static str> = Some("Returns the data cap for the given address.");
278
279    type Params = (Address, ApiTipsetKey);
280    type Ok = Option<StoragePower>;
281
282    async fn handle(
283        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
284        (address, ApiTipsetKey(tsk)): Self::Params,
285        _: &http::Extensions,
286    ) -> Result<Self::Ok, ServerError> {
287        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
288        let aid = ctx.state_manager.lookup_required_id(&address, &ts)?;
289        let verifreg_state: verifreg::State = ctx.state_manager.get_actor_state(&ts)?;
290        Ok(verifreg_state.verifier_data_cap(ctx.store(), aid)?)
291    }
292}
293
294pub enum StateGetActor {}
295
296impl RpcMethod<2> for StateGetActor {
297    const NAME: &'static str = "Filecoin.StateGetActor";
298    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetKey"];
299    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
300    const PERMISSION: Permission = Permission::Read;
301    const DESCRIPTION: Option<&'static str> =
302        Some("Returns the nonce and balance for the specified actor.");
303
304    type Params = (Address, ApiTipsetKey);
305    type Ok = Option<ActorState>;
306
307    async fn handle(
308        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
309        (address, ApiTipsetKey(tsk)): Self::Params,
310        _: &http::Extensions,
311    ) -> Result<Self::Ok, ServerError> {
312        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
313        let state = ctx.state_manager.get_actor(&address, *ts.parent_state())?;
314        Ok(state)
315    }
316}
317
318pub enum StateGetActorV2 {}
319
320impl RpcMethod<2> for StateGetActorV2 {
321    const NAME: &'static str = "Filecoin.StateGetActor";
322    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetSelector"];
323    const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::{ V2 });
324    const PERMISSION: Permission = Permission::Read;
325    const DESCRIPTION: Option<&'static str> =
326        Some("Returns the nonce and balance for the specified actor.");
327
328    type Params = (Address, TipsetSelector);
329    type Ok = Option<ActorState>;
330
331    async fn handle(
332        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
333        (address, selector): Self::Params,
334        _: &http::Extensions,
335    ) -> Result<Self::Ok, ServerError> {
336        let ts = ChainGetTipSetV2::get_tipset(&ctx, &selector).await?;
337        Ok(ctx.state_manager.get_actor(&address, *ts.parent_state())?)
338    }
339}
340
341pub enum StateGetID {}
342
343impl RpcMethod<2> for StateGetID {
344    const NAME: &'static str = "Filecoin.StateGetID";
345    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetSelector"];
346    const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::{ V2 });
347    const PERMISSION: Permission = Permission::Read;
348    const DESCRIPTION: Option<&'static str> =
349        Some("Retrieves the ID address for the specified address at the selected tipset.");
350
351    type Params = (Address, TipsetSelector);
352    type Ok = Address;
353
354    async fn handle(
355        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
356        (address, selector): Self::Params,
357        _: &http::Extensions,
358    ) -> Result<Self::Ok, ServerError> {
359        let ts = ChainGetTipSetV2::get_tipset(&ctx, &selector).await?;
360        Ok(ctx.state_manager.lookup_required_id(&address, &ts)?)
361    }
362}
363
364pub enum StateLookupRobustAddress {}
365
366macro_rules! get_robust_address {
367    ($store:expr, $id_addr_decoded:expr, $state:expr, $make_map_with_root:path, $robust_addr:expr) => {{
368        let map = $make_map_with_root(&$state.address_map, &$store)?;
369        map.for_each(|addr, v| {
370            if *v == $id_addr_decoded {
371                $robust_addr = Address::from_bytes(addr)?;
372                return Ok(());
373            }
374            Ok(())
375        })?;
376        Ok($robust_addr)
377    }};
378}
379
380impl RpcMethod<2> for StateLookupRobustAddress {
381    const NAME: &'static str = "Filecoin.StateLookupRobustAddress";
382    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetKey"];
383    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
384    const PERMISSION: Permission = Permission::Read;
385    const DESCRIPTION: Option<&'static str> =
386        Some("Returns the public key address for non-account addresses (e.g., multisig, miners).");
387
388    type Params = (Address, ApiTipsetKey);
389    type Ok = Address;
390
391    async fn handle(
392        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
393        (addr, ApiTipsetKey(tsk)): Self::Params,
394        _: &http::Extensions,
395    ) -> Result<Self::Ok, ServerError> {
396        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
397        let store = ctx.store();
398        let state_tree = StateTree::new_from_root(ctx.store_owned(), ts.parent_state())?;
399        if let &Payload::ID(id_addr_decoded) = addr.payload() {
400            let init_state: init::State = state_tree.get_actor_state()?;
401            let mut robust_addr = Address::default();
402            match init_state {
403                init::State::V0(_) => Err(ServerError::internal_error(
404                    "StateLookupRobustAddress is not implemented for init state v0",
405                    None,
406                )),
407                init::State::V8(state) => get_robust_address!(
408                    store,
409                    id_addr_decoded,
410                    state,
411                    fil_actors_shared::v8::make_map_with_root::<_, ActorID>,
412                    robust_addr
413                ),
414                init::State::V9(state) => get_robust_address!(
415                    store,
416                    id_addr_decoded,
417                    state,
418                    fil_actors_shared::v9::make_map_with_root::<_, ActorID>,
419                    robust_addr
420                ),
421                init::State::V10(state) => get_robust_address!(
422                    store,
423                    id_addr_decoded,
424                    state,
425                    fil_actors_shared::v10::make_map_with_root::<_, ActorID>,
426                    robust_addr
427                ),
428                init::State::V11(state) => get_robust_address!(
429                    store,
430                    id_addr_decoded,
431                    state,
432                    fil_actors_shared::v11::make_map_with_root::<_, ActorID>,
433                    robust_addr
434                ),
435                init::State::V12(state) => get_robust_address!(
436                    store,
437                    id_addr_decoded,
438                    state,
439                    fil_actors_shared::v12::make_map_with_root::<_, ActorID>,
440                    robust_addr
441                ),
442                init::State::V13(state) => get_robust_address!(
443                    store,
444                    id_addr_decoded,
445                    state,
446                    fil_actors_shared::v13::make_map_with_root::<_, ActorID>,
447                    robust_addr
448                ),
449                init::State::V14(state) => {
450                    let map = fil_actor_init_state::v14::AddressMap::load(
451                        &store,
452                        &state.address_map,
453                        fil_actors_shared::v14::DEFAULT_HAMT_CONFIG,
454                        "address_map",
455                    )
456                    .context("Failed to load address map")?;
457                    map.for_each(|addr, v| {
458                        if *v == id_addr_decoded {
459                            robust_addr = addr.into();
460                            return Ok(());
461                        }
462                        Ok(())
463                    })
464                    .context("Robust address not found")?;
465                    Ok(robust_addr)
466                }
467                init::State::V15(state) => {
468                    let map = fil_actor_init_state::v15::AddressMap::load(
469                        &store,
470                        &state.address_map,
471                        fil_actors_shared::v15::DEFAULT_HAMT_CONFIG,
472                        "address_map",
473                    )
474                    .context("Failed to load address map")?;
475                    map.for_each(|addr, v| {
476                        if *v == id_addr_decoded {
477                            robust_addr = addr.into();
478                            return Ok(());
479                        }
480                        Ok(())
481                    })
482                    .context("Robust address not found")?;
483                    Ok(robust_addr)
484                }
485                init::State::V16(state) => {
486                    let map = fil_actor_init_state::v16::AddressMap::load(
487                        &store,
488                        &state.address_map,
489                        fil_actors_shared::v16::DEFAULT_HAMT_CONFIG,
490                        "address_map",
491                    )
492                    .context("Failed to load address map")?;
493                    map.for_each(|addr, v| {
494                        if *v == id_addr_decoded {
495                            robust_addr = addr.into();
496                            return Ok(());
497                        }
498                        Ok(())
499                    })
500                    .context("Robust address not found")?;
501                    Ok(robust_addr)
502                }
503                init::State::V17(state) => {
504                    let map = fil_actor_init_state::v17::AddressMap::load(
505                        &store,
506                        &state.address_map,
507                        fil_actors_shared::v17::DEFAULT_HAMT_CONFIG,
508                        "address_map",
509                    )
510                    .context("Failed to load address map")?;
511                    map.for_each(|addr, v| {
512                        if *v == id_addr_decoded {
513                            robust_addr = addr.into();
514                            return Ok(());
515                        }
516                        Ok(())
517                    })
518                    .context("Robust address not found")?;
519                    Ok(robust_addr)
520                }
521                init::State::V18(state) => {
522                    let map = fil_actor_init_state::v18::AddressMap::load(
523                        &store,
524                        &state.address_map,
525                        fil_actors_shared::v18::DEFAULT_HAMT_CONFIG,
526                        "address_map",
527                    )
528                    .context("Failed to load address map")?;
529                    map.for_each(|addr, v| {
530                        if *v == id_addr_decoded {
531                            robust_addr = addr.into();
532                            return Ok(());
533                        }
534                        Ok(())
535                    })
536                    .context("Robust address not found")?;
537                    Ok(robust_addr)
538                }
539            }
540        } else {
541            Ok(Address::default())
542        }
543    }
544}
545
546/// looks up the Escrow and Locked balances of the given address in the Storage
547/// Market
548pub enum StateMarketBalance {}
549
550impl RpcMethod<2> for StateMarketBalance {
551    const NAME: &'static str = "Filecoin.StateMarketBalance";
552    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetKey"];
553    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
554    const PERMISSION: Permission = Permission::Read;
555    const DESCRIPTION: Option<&'static str> = Some(
556        "Returns the Escrow and Locked balances of the specified address in the Storage Market.",
557    );
558
559    type Params = (Address, ApiTipsetKey);
560    type Ok = MarketBalance;
561
562    async fn handle(
563        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
564        (address, ApiTipsetKey(tsk)): Self::Params,
565        _: &http::Extensions,
566    ) -> Result<Self::Ok, ServerError> {
567        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
568        ctx.state_manager
569            .market_balance(&address, &ts)
570            .map_err(From::from)
571    }
572}
573
574pub enum StateMarketDeals {}
575
576impl RpcMethod<1> for StateMarketDeals {
577    const NAME: &'static str = "Filecoin.StateMarketDeals";
578    const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"];
579    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
580    const PERMISSION: Permission = Permission::Read;
581    const DESCRIPTION: Option<&'static str> =
582        Some("Returns information about every deal in the Storage Market.");
583
584    type Params = (ApiTipsetKey,);
585    type Ok = HashMap<String, ApiMarketDeal>;
586
587    async fn handle(
588        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
589        (ApiTipsetKey(tsk),): Self::Params,
590        _: &http::Extensions,
591    ) -> Result<Self::Ok, ServerError> {
592        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
593        let market_state: market::State = ctx.state_manager.get_actor_state(&ts)?;
594
595        let da = market_state.proposals(ctx.store())?;
596        let sa = market_state.states(ctx.store())?;
597
598        let mut out = HashMap::new();
599        da.for_each(|deal_id, d| {
600            let s = sa.get(deal_id)?.unwrap_or(market::DealState {
601                sector_start_epoch: -1,
602                last_updated_epoch: -1,
603                slash_epoch: -1,
604                verified_claim: 0,
605                sector_number: 0,
606            });
607            out.insert(
608                deal_id.to_string(),
609                MarketDeal {
610                    proposal: d?,
611                    state: s,
612                }
613                .into(),
614            );
615            Ok(())
616        })?;
617        Ok(out)
618    }
619}
620
621/// looks up the miner info of the given address.
622pub enum StateMinerInfo {}
623
624impl RpcMethod<2> for StateMinerInfo {
625    const NAME: &'static str = "Filecoin.StateMinerInfo";
626    const PARAM_NAMES: [&'static str; 2] = ["minerAddress", "tipsetKey"];
627    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
628    const PERMISSION: Permission = Permission::Read;
629    const DESCRIPTION: Option<&'static str> =
630        Some("Returns information about the specified miner.");
631
632    type Params = (Address, ApiTipsetKey);
633    type Ok = MinerInfo;
634
635    async fn handle(
636        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
637        (address, ApiTipsetKey(tsk)): Self::Params,
638        _: &http::Extensions,
639    ) -> Result<Self::Ok, ServerError> {
640        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
641        Ok(ctx.state_manager.miner_info(&address, &ts)?)
642    }
643}
644
645pub enum StateMinerActiveSectors {}
646
647impl RpcMethod<2> for StateMinerActiveSectors {
648    const NAME: &'static str = "Filecoin.StateMinerActiveSectors";
649    const PARAM_NAMES: [&'static str; 2] = ["minerAddress", "tipsetKey"];
650    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
651    const PERMISSION: Permission = Permission::Read;
652    const DESCRIPTION: Option<&'static str> =
653        Some("Returns information about sectors actively proven by a given miner.");
654
655    type Params = (Address, ApiTipsetKey);
656    type Ok = Vec<SectorOnChainInfo>;
657
658    async fn handle(
659        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
660        (address, ApiTipsetKey(tsk)): Self::Params,
661        _: &http::Extensions,
662    ) -> Result<Self::Ok, ServerError> {
663        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
664        let policy = &ctx.chain_config().policy;
665        let miner_state: miner::State = ctx
666            .state_manager
667            .get_actor_state_from_address(&ts, &address)?;
668        // Collect active sectors from each partition in each deadline.
669        let mut active_sectors = vec![];
670        miner_state.for_each_deadline(policy, ctx.store(), |_dlidx, deadline| {
671            deadline.for_each(ctx.store(), |_partidx, partition| {
672                active_sectors.push(partition.active_sectors());
673                Ok(())
674            })
675        })?;
676        let sectors =
677            miner_state.load_sectors_ext(ctx.store(), Some(&BitField::union(&active_sectors)))?;
678        Ok(sectors)
679    }
680}
681
682/// Returns a bitfield containing all sector numbers marked as allocated in miner state
683pub enum StateMinerAllocated {}
684
685impl RpcMethod<2> for StateMinerAllocated {
686    const NAME: &'static str = "Filecoin.StateMinerAllocated";
687    const PARAM_NAMES: [&'static str; 2] = ["minerAddress", "tipsetKey"];
688    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
689    const PERMISSION: Permission = Permission::Read;
690    const DESCRIPTION: Option<&'static str> = Some(
691        "Returns a bitfield containing all sector numbers marked as allocated to the provided miner ID.",
692    );
693
694    type Params = (Address, ApiTipsetKey);
695    type Ok = BitField;
696
697    async fn handle(
698        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
699        (address, ApiTipsetKey(tsk)): Self::Params,
700        _: &http::Extensions,
701    ) -> Result<Self::Ok, ServerError> {
702        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
703        let miner_state: miner::State = ctx
704            .state_manager
705            .get_actor_state_from_address(&ts, &address)?;
706        Ok(miner_state.load_allocated_sector_numbers(ctx.store())?)
707    }
708}
709
710/// Return all partitions in the specified deadline
711pub enum StateMinerPartitions {}
712
713impl RpcMethod<3> for StateMinerPartitions {
714    const NAME: &'static str = "Filecoin.StateMinerPartitions";
715    const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "deadlineIndex", "tipsetKey"];
716    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
717    const PERMISSION: Permission = Permission::Read;
718    const DESCRIPTION: Option<&'static str> =
719        Some("Returns all partitions in the specified deadline.");
720
721    type Params = (Address, u64, ApiTipsetKey);
722    type Ok = Vec<MinerPartitions>;
723
724    async fn handle(
725        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
726        (address, dl_idx, ApiTipsetKey(tsk)): Self::Params,
727        _: &http::Extensions,
728    ) -> Result<Self::Ok, ServerError> {
729        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
730        let policy = &ctx.chain_config().policy;
731        let miner_state: miner::State = ctx
732            .state_manager
733            .get_actor_state_from_address(&ts, &address)?;
734        let deadline = miner_state.load_deadline(policy, ctx.store(), dl_idx)?;
735        let mut all_partitions = Vec::new();
736        deadline.for_each(ctx.store(), |_partidx, partition| {
737            all_partitions.push(MinerPartitions::new(
738                partition.all_sectors(),
739                partition.faulty_sectors(),
740                partition.recovering_sectors(),
741                partition.live_sectors(),
742                partition.active_sectors(),
743            ));
744            Ok(())
745        })?;
746        Ok(all_partitions)
747    }
748}
749
750pub enum StateMinerSectors {}
751
752impl RpcMethod<3> for StateMinerSectors {
753    const NAME: &'static str = "Filecoin.StateMinerSectors";
754    const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "sectors", "tipsetKey"];
755    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
756    const PERMISSION: Permission = Permission::Read;
757    const DESCRIPTION: Option<&'static str> = Some(
758        "Returns information about the given miner's sectors. If no filter is provided, all sectors are included.",
759    );
760
761    type Params = (Address, Option<BitField>, ApiTipsetKey);
762    type Ok = Vec<SectorOnChainInfo>;
763
764    async fn handle(
765        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
766        (address, sectors, ApiTipsetKey(tsk)): Self::Params,
767        _: &http::Extensions,
768    ) -> Result<Self::Ok, ServerError> {
769        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
770        let miner_state: miner::State = ctx
771            .state_manager
772            .get_actor_state_from_address(&ts, &address)?;
773        Ok(miner_state.load_sectors_ext(ctx.store(), sectors.as_ref())?)
774    }
775}
776
777/// Returns the number of sectors in a miner's sector set and proving set
778pub enum StateMinerSectorCount {}
779
780impl RpcMethod<2> for StateMinerSectorCount {
781    const NAME: &'static str = "Filecoin.StateMinerSectorCount";
782    const PARAM_NAMES: [&'static str; 2] = ["minerAddress", "tipsetKey"];
783    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
784    const PERMISSION: Permission = Permission::Read;
785    const DESCRIPTION: Option<&'static str> =
786        Some("Returns the number of sectors in a miner's sector and proving sets.");
787
788    type Params = (Address, ApiTipsetKey);
789    type Ok = MinerSectors;
790
791    async fn handle(
792        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
793        (address, ApiTipsetKey(tsk)): Self::Params,
794        _: &http::Extensions,
795    ) -> Result<Self::Ok, ServerError> {
796        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
797        let policy = &ctx.chain_config().policy;
798        let miner_state: miner::State = ctx
799            .state_manager
800            .get_actor_state_from_address(&ts, &address)?;
801        // Collect live, active and faulty sectors count from each partition in each deadline.
802        let mut live_count = 0;
803        let mut active_count = 0;
804        let mut faulty_count = 0;
805        miner_state.for_each_deadline(policy, ctx.store(), |_dlidx, deadline| {
806            deadline.for_each(ctx.store(), |_partidx, partition| {
807                live_count += partition.live_sectors().len();
808                active_count += partition.active_sectors().len();
809                faulty_count += partition.faulty_sectors().len();
810                Ok(())
811            })
812        })?;
813        Ok(MinerSectors::new(live_count, active_count, faulty_count))
814    }
815}
816
817/// Checks if a sector is allocated
818pub enum StateMinerSectorAllocated {}
819
820impl RpcMethod<3> for StateMinerSectorAllocated {
821    const NAME: &'static str = "Filecoin.StateMinerSectorAllocated";
822    const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "sectorNumber", "tipsetKey"];
823    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
824    const PERMISSION: Permission = Permission::Read;
825    const DESCRIPTION: Option<&'static str> =
826        Some("Checks if a sector number is marked as allocated.");
827
828    type Params = (Address, SectorNumber, ApiTipsetKey);
829    type Ok = bool;
830
831    async fn handle(
832        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
833        (miner_address, sector_number, ApiTipsetKey(tsk)): Self::Params,
834        _: &http::Extensions,
835    ) -> Result<Self::Ok, ServerError> {
836        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
837        let miner_state: miner::State = ctx
838            .state_manager
839            .get_actor_state_from_address(&ts, &miner_address)?;
840        let allocated_sector_numbers: BitField =
841            miner_state.load_allocated_sector_numbers(ctx.store())?;
842        Ok(allocated_sector_numbers.get(sector_number))
843    }
844}
845
846/// looks up the miner power of the given address.
847pub enum StateMinerPower {}
848
849impl RpcMethod<2> for StateMinerPower {
850    const NAME: &'static str = "Filecoin.StateMinerPower";
851    const PARAM_NAMES: [&'static str; 2] = ["minerAddress", "tipsetKey"];
852    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
853    const PERMISSION: Permission = Permission::Read;
854    const DESCRIPTION: Option<&'static str> = Some("Returns the power of the specified miner.");
855
856    type Params = (Address, ApiTipsetKey);
857    type Ok = MinerPower;
858
859    async fn handle(
860        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
861        (address, ApiTipsetKey(tsk)): Self::Params,
862        _: &http::Extensions,
863    ) -> Result<Self::Ok, ServerError> {
864        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
865        ctx.state_manager
866            .miner_power(&address, &ts)
867            .map_err(From::from)
868    }
869}
870
871pub enum StateMinerDeadlines {}
872
873impl RpcMethod<2> for StateMinerDeadlines {
874    const NAME: &'static str = "Filecoin.StateMinerDeadlines";
875    const PARAM_NAMES: [&'static str; 2] = ["minerAddress", "tipsetKey"];
876    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
877    const PERMISSION: Permission = Permission::Read;
878    const DESCRIPTION: Option<&'static str> =
879        Some("Returns all proving deadlines for the given miner.");
880
881    type Params = (Address, ApiTipsetKey);
882    type Ok = Vec<ApiDeadline>;
883
884    async fn handle(
885        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
886        (address, ApiTipsetKey(tsk)): Self::Params,
887        _: &http::Extensions,
888    ) -> Result<Self::Ok, ServerError> {
889        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
890        let policy = &ctx.chain_config().policy;
891        let state: miner::State = ctx
892            .state_manager
893            .get_actor_state_from_address(&ts, &address)?;
894        let mut res = Vec::new();
895        state.for_each_deadline(policy, ctx.store(), |_idx, deadline| {
896            res.push(ApiDeadline {
897                post_submissions: deadline.partitions_posted(),
898                disputable_proof_count: deadline.disputable_proof_count(ctx.store())?,
899                daily_fee: deadline.daily_fee(),
900            });
901            Ok(())
902        })?;
903        Ok(res)
904    }
905}
906
907pub enum StateMinerProvingDeadline {}
908
909impl RpcMethod<2> for StateMinerProvingDeadline {
910    const NAME: &'static str = "Filecoin.StateMinerProvingDeadline";
911    const PARAM_NAMES: [&'static str; 2] = ["minerAddress", "tipsetKey"];
912    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
913    const PERMISSION: Permission = Permission::Read;
914    const DESCRIPTION: Option<&'static str> = Some(
915        "Calculates the deadline and related details for a given epoch during a proving period.",
916    );
917
918    type Params = (Address, ApiTipsetKey);
919    type Ok = ApiDeadlineInfo;
920
921    async fn handle(
922        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
923        (address, ApiTipsetKey(tsk)): Self::Params,
924        _: &http::Extensions,
925    ) -> Result<Self::Ok, ServerError> {
926        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
927        let policy = &ctx.chain_config().policy;
928        let state: miner::State = ctx
929            .state_manager
930            .get_actor_state_from_address(&ts, &address)?;
931        Ok(ApiDeadlineInfo(
932            state
933                .recorded_deadline_info(policy, ts.epoch())
934                .next_not_elapsed(),
935        ))
936    }
937}
938
939/// looks up the miner power of the given address.
940pub enum StateMinerFaults {}
941
942impl RpcMethod<2> for StateMinerFaults {
943    const NAME: &'static str = "Filecoin.StateMinerFaults";
944    const PARAM_NAMES: [&'static str; 2] = ["minerAddress", "tipsetKey"];
945    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
946    const PERMISSION: Permission = Permission::Read;
947    const DESCRIPTION: Option<&'static str> =
948        Some("Returns a bitfield of the faulty sectors for the given miner.");
949
950    type Params = (Address, ApiTipsetKey);
951    type Ok = BitField;
952
953    async fn handle(
954        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
955        (address, ApiTipsetKey(tsk)): Self::Params,
956        _: &http::Extensions,
957    ) -> Result<Self::Ok, ServerError> {
958        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
959        ctx.state_manager
960            .miner_faults(&address, &ts)
961            .map_err(From::from)
962    }
963}
964
965pub enum StateMinerRecoveries {}
966
967impl RpcMethod<2> for StateMinerRecoveries {
968    const NAME: &'static str = "Filecoin.StateMinerRecoveries";
969    const PARAM_NAMES: [&'static str; 2] = ["minerAddress", "tipsetKey"];
970    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
971    const PERMISSION: Permission = Permission::Read;
972    const DESCRIPTION: Option<&'static str> =
973        Some("Returns a bitfield of recovering sectors for the given miner.");
974
975    type Params = (Address, ApiTipsetKey);
976    type Ok = BitField;
977
978    async fn handle(
979        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
980        (address, ApiTipsetKey(tsk)): Self::Params,
981        _: &http::Extensions,
982    ) -> Result<Self::Ok, ServerError> {
983        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
984        ctx.state_manager
985            .miner_recoveries(&address, &ts)
986            .map_err(From::from)
987    }
988}
989
990pub enum StateMinerAvailableBalance {}
991
992impl RpcMethod<2> for StateMinerAvailableBalance {
993    const NAME: &'static str = "Filecoin.StateMinerAvailableBalance";
994    const PARAM_NAMES: [&'static str; 2] = ["minerAddress", "tipsetKey"];
995    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
996    const PERMISSION: Permission = Permission::Read;
997    const DESCRIPTION: Option<&'static str> =
998        Some("Returns the portion of a miner's balance available for withdrawal or spending.");
999
1000    type Params = (Address, ApiTipsetKey);
1001    type Ok = TokenAmount;
1002
1003    async fn handle(
1004        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1005        (address, ApiTipsetKey(tsk)): Self::Params,
1006        _: &http::Extensions,
1007    ) -> Result<Self::Ok, ServerError> {
1008        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1009        let actor = ctx
1010            .state_manager
1011            .get_required_actor(&address, *ts.parent_state())?;
1012        let state = miner::State::load(ctx.store(), actor.code, actor.state)?;
1013        let actor_balance: TokenAmount = actor.balance.clone().into();
1014        let (vested, available): (TokenAmount, TokenAmount) = match &state {
1015            miner::State::V18(s) => (
1016                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1017                s.get_available_balance(&actor_balance.into())?.into(),
1018            ),
1019            miner::State::V17(s) => (
1020                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1021                s.get_available_balance(&actor_balance.into())?.into(),
1022            ),
1023            miner::State::V16(s) => (
1024                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1025                s.get_available_balance(&actor_balance.into())?.into(),
1026            ),
1027            miner::State::V15(s) => (
1028                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1029                s.get_available_balance(&actor_balance.into())?.into(),
1030            ),
1031            miner::State::V14(s) => (
1032                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1033                s.get_available_balance(&actor_balance.into())?.into(),
1034            ),
1035            miner::State::V13(s) => (
1036                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1037                s.get_available_balance(&actor_balance.into())?.into(),
1038            ),
1039            miner::State::V12(s) => (
1040                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1041                s.get_available_balance(&actor_balance.into())?.into(),
1042            ),
1043            miner::State::V11(s) => (
1044                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1045                s.get_available_balance(&actor_balance.into())?.into(),
1046            ),
1047            miner::State::V10(s) => (
1048                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1049                s.get_available_balance(&actor_balance.into())?.into(),
1050            ),
1051            miner::State::V9(s) => (
1052                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1053                s.get_available_balance(&actor_balance.into())?.into(),
1054            ),
1055            miner::State::V8(s) => (
1056                s.check_vested_funds(ctx.store(), ts.epoch())?.into(),
1057                s.get_available_balance(&actor_balance.into())?.into(),
1058            ),
1059        };
1060
1061        Ok(vested + available)
1062    }
1063}
1064
1065pub enum StateMinerInitialPledgeCollateral {}
1066
1067impl RpcMethod<3> for StateMinerInitialPledgeCollateral {
1068    const NAME: &'static str = "Filecoin.StateMinerInitialPledgeCollateral";
1069    const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "sectorPreCommitInfo", "tipsetKey"];
1070    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1071    const PERMISSION: Permission = Permission::Read;
1072    const DESCRIPTION: Option<&'static str> =
1073        Some("Returns the initial pledge collateral for the specified miner's sector.");
1074
1075    type Params = (Address, SectorPreCommitInfo, ApiTipsetKey);
1076    type Ok = TokenAmount;
1077
1078    async fn handle(
1079        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1080        (address, pci, ApiTipsetKey(tsk)): Self::Params,
1081        _: &http::Extensions,
1082    ) -> Result<Self::Ok, ServerError> {
1083        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1084
1085        let sector_size = pci
1086            .seal_proof
1087            .sector_size()
1088            .map_err(|e| anyhow::anyhow!("failed to get resolve size: {e}"))?;
1089
1090        let market_state: market::State = ctx.state_manager.get_actor_state(&ts)?;
1091        let (w, vw) = market_state.verify_deals_for_activation(
1092            ctx.store(),
1093            address,
1094            pci.deal_ids,
1095            ts.epoch(),
1096            pci.expiration,
1097        )?;
1098        let duration = pci.expiration - ts.epoch();
1099        let sector_weight =
1100            qa_power_for_weight(SectorSize::from(sector_size).into(), duration, &w, &vw);
1101
1102        let power_state: power::State = ctx.state_manager.get_actor_state(&ts)?;
1103        let power_smoothed = power_state.total_power_smoothed();
1104        let pledge_collateral = power_state.total_locked();
1105
1106        let reward_state: reward::State = ctx.state_manager.get_actor_state(&ts)?;
1107        let genesis_info = GenesisInfo::from_chain_config(ctx.chain_config().clone());
1108        let circ_supply = genesis_info.get_vm_circulating_supply_detailed(
1109            ts.epoch(),
1110            &Arc::new(ctx.store()),
1111            ts.parent_state(),
1112        )?;
1113        let initial_pledge = reward_state.initial_pledge_for_power(
1114            &sector_weight,
1115            pledge_collateral,
1116            power_smoothed,
1117            &circ_supply.fil_circulating,
1118            power_state.ramp_start_epoch(),
1119            power_state.ramp_duration_epochs(),
1120        )?;
1121
1122        let (q, _) = (initial_pledge * INITIAL_PLEDGE_NUM).div_rem(INITIAL_PLEDGE_DEN);
1123        Ok(q)
1124    }
1125}
1126
1127pub enum StateMinerPreCommitDepositForPower {}
1128
1129impl RpcMethod<3> for StateMinerPreCommitDepositForPower {
1130    const NAME: &'static str = "Filecoin.StateMinerPreCommitDepositForPower";
1131    const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "sectorPreCommitInfo", "tipsetKey"];
1132    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1133    const PERMISSION: Permission = Permission::Read;
1134    const DESCRIPTION: Option<&'static str> =
1135        Some("Returns the sector precommit deposit for the specified miner.");
1136
1137    type Params = (Address, SectorPreCommitInfo, ApiTipsetKey);
1138    type Ok = TokenAmount;
1139
1140    async fn handle(
1141        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1142        (address, pci, ApiTipsetKey(tsk)): Self::Params,
1143        _: &http::Extensions,
1144    ) -> Result<Self::Ok, ServerError> {
1145        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1146
1147        let sector_size = pci
1148            .seal_proof
1149            .sector_size()
1150            .map_err(|e| anyhow::anyhow!("failed to get resolve size: {e}"))?;
1151
1152        let market_state: market::State = ctx.state_manager.get_actor_state(&ts)?;
1153        let (w, vw) = market_state.verify_deals_for_activation(
1154            ctx.store(),
1155            address,
1156            pci.deal_ids,
1157            ts.epoch(),
1158            pci.expiration,
1159        )?;
1160        let duration = pci.expiration - ts.epoch();
1161        let sector_size = SectorSize::from(sector_size).into();
1162        let sector_weight =
1163            if ctx.state_manager.get_network_version(ts.epoch()) < NetworkVersion::V16 {
1164                qa_power_for_weight(sector_size, duration, &w, &vw)
1165            } else {
1166                qa_power_max(sector_size)
1167            };
1168
1169        let power_state: power::State = ctx.state_manager.get_actor_state(&ts)?;
1170        let power_smoothed = power_state.total_power_smoothed();
1171
1172        let reward_state: reward::State = ctx.state_manager.get_actor_state(&ts)?;
1173        let deposit: TokenAmount =
1174            reward_state.pre_commit_deposit_for_power(power_smoothed, sector_weight)?;
1175        let (value, _) = (deposit * INITIAL_PLEDGE_NUM).div_rem(INITIAL_PLEDGE_DEN);
1176        Ok(value)
1177    }
1178}
1179
1180/// returns the message receipt for the given message
1181pub enum StateGetReceipt {}
1182
1183impl RpcMethod<2> for StateGetReceipt {
1184    const NAME: &'static str = "Filecoin.StateGetReceipt";
1185    const PARAM_NAMES: [&'static str; 2] = ["cid", "tipset_key"];
1186    const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::V0); // deprecated in V1
1187    const PERMISSION: Permission = Permission::Read;
1188
1189    type Params = (Cid, ApiTipsetKey);
1190    type Ok = Receipt;
1191
1192    async fn handle(
1193        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1194        (cid, ApiTipsetKey(tsk)): Self::Params,
1195        _: &http::Extensions,
1196    ) -> Result<Self::Ok, ServerError> {
1197        let tipset = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1198        ctx.state_manager
1199            .get_receipt(tipset, cid)
1200            .map_err(From::from)
1201    }
1202}
1203
1204/// looks back in the chain for a message. If not found, it blocks until the
1205/// message arrives on chain, and gets to the indicated confidence depth.
1206pub enum StateWaitMsgV0 {}
1207
1208impl RpcMethod<2> for StateWaitMsgV0 {
1209    const NAME: &'static str = "Filecoin.StateWaitMsg";
1210    const PARAM_NAMES: [&'static str; 2] = ["messageCid", "confidence"];
1211    const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::V0); // Changed in V1
1212    const PERMISSION: Permission = Permission::Read;
1213
1214    type Params = (Cid, i64);
1215    type Ok = MessageLookup;
1216
1217    async fn handle(
1218        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1219        (message_cid, confidence): Self::Params,
1220        _: &http::Extensions,
1221    ) -> Result<Self::Ok, ServerError> {
1222        let (tipset, receipt) = ctx
1223            .state_manager
1224            .wait_for_message(message_cid, confidence, None, None)
1225            .await?;
1226        let tipset = tipset.context("wait for msg returned empty tuple")?;
1227        let receipt = receipt.context("wait for msg returned empty receipt")?;
1228        let ipld = receipt.return_data().deserialize().unwrap_or(Ipld::Null);
1229        Ok(MessageLookup {
1230            receipt,
1231            tipset: tipset.key().clone(),
1232            height: tipset.epoch(),
1233            message: message_cid,
1234            return_dec: ipld,
1235        })
1236    }
1237}
1238
1239/// looks back in the chain for a message. If not found, it blocks until the
1240/// message arrives on chain, and gets to the indicated confidence depth.
1241pub enum StateWaitMsg {}
1242
1243impl RpcMethod<4> for StateWaitMsg {
1244    const NAME: &'static str = "Filecoin.StateWaitMsg";
1245    const PARAM_NAMES: [&'static str; 4] =
1246        ["messageCid", "confidence", "lookbackLimit", "allowReplaced"];
1247    const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::V1); // Changed in V1
1248    const PERMISSION: Permission = Permission::Read;
1249    const DESCRIPTION: Option<&'static str> = Some(
1250        "StateWaitMsg searches up to limit epochs for a message in the chain. If not found, it blocks until the message appears on-chain and reaches the required confidence depth.",
1251    );
1252
1253    type Params = (Cid, i64, ChainEpoch, bool);
1254    type Ok = MessageLookup;
1255
1256    async fn handle(
1257        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1258        (message_cid, confidence, look_back_limit, allow_replaced): Self::Params,
1259        _: &http::Extensions,
1260    ) -> Result<Self::Ok, ServerError> {
1261        let (tipset, receipt) = ctx
1262            .state_manager
1263            .wait_for_message(
1264                message_cid,
1265                confidence,
1266                Some(look_back_limit),
1267                Some(allow_replaced),
1268            )
1269            .await?;
1270        let tipset = tipset.context("wait for msg returned empty tuple")?;
1271        let receipt = receipt.context("wait for msg returned empty receipt")?;
1272        let ipld = receipt.return_data().deserialize().unwrap_or(Ipld::Null);
1273        Ok(MessageLookup {
1274            receipt,
1275            tipset: tipset.key().clone(),
1276            height: tipset.epoch(),
1277            message: message_cid,
1278            return_dec: ipld,
1279        })
1280    }
1281}
1282
1283/// Searches for a message in the chain, and returns its receipt and the tipset where it was executed.
1284/// See <https://github.com/filecoin-project/lotus/blob/master/documentation/en/api-methods-v1-stable.md#StateSearchMsg>
1285pub enum StateSearchMsg {}
1286
1287impl RpcMethod<4> for StateSearchMsg {
1288    const NAME: &'static str = "Filecoin.StateSearchMsg";
1289    const PARAM_NAMES: [&'static str; 4] =
1290        ["tipsetKey", "messageCid", "lookBackLimit", "allowReplaced"];
1291    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1292    const PERMISSION: Permission = Permission::Read;
1293    const DESCRIPTION: Option<&'static str> = Some(
1294        "Returns the receipt and tipset the specified message was included in, or null if the message was not found.",
1295    );
1296
1297    type Params = (ApiTipsetKey, Cid, i64, bool);
1298    type Ok = Option<MessageLookup>;
1299
1300    async fn handle(
1301        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1302        (ApiTipsetKey(tsk), message_cid, look_back_limit, allow_replaced): Self::Params,
1303        _: &http::Extensions,
1304    ) -> Result<Self::Ok, ServerError> {
1305        let from = tsk
1306            .map(|k| ctx.chain_index().load_required_tipset(&k))
1307            .transpose()?;
1308        let Some((tipset, receipt)) = ctx
1309            .state_manager
1310            .search_for_message(
1311                from,
1312                message_cid,
1313                Some(look_back_limit),
1314                Some(allow_replaced),
1315            )
1316            .await?
1317        else {
1318            return Ok(None);
1319        };
1320        let ipld = receipt.return_data().deserialize().unwrap_or(Ipld::Null);
1321        Ok(Some(MessageLookup {
1322            receipt,
1323            tipset: tipset.key().clone(),
1324            height: tipset.epoch(),
1325            message: message_cid,
1326            return_dec: ipld,
1327        }))
1328    }
1329}
1330
1331/// See <https://github.com/filecoin-project/lotus/blob/master/documentation/en/api-methods-v0-deprecated.md#StateSearchMsgLimited>
1332pub enum StateSearchMsgLimited {}
1333
1334impl RpcMethod<2> for StateSearchMsgLimited {
1335    const NAME: &'static str = "Filecoin.StateSearchMsgLimited";
1336    const PARAM_NAMES: [&'static str; 2] = ["message_cid", "look_back_limit"];
1337    const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::V0); // Not supported in V1
1338    const PERMISSION: Permission = Permission::Read;
1339    const DESCRIPTION: Option<&'static str> = Some(
1340        "Looks back up to limit epochs in the chain for a message, and returns its receipt and the tipset where it was executed, or null if it was not found.",
1341    );
1342    type Params = (Cid, i64);
1343    type Ok = Option<MessageLookup>;
1344
1345    async fn handle(
1346        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1347        (message_cid, look_back_limit): Self::Params,
1348        _: &http::Extensions,
1349    ) -> Result<Self::Ok, ServerError> {
1350        let Some((tipset, receipt)) = ctx
1351            .state_manager
1352            .search_for_message(None, message_cid, Some(look_back_limit), None)
1353            .await?
1354        else {
1355            return Ok(None);
1356        };
1357        let ipld = receipt.return_data().deserialize().unwrap_or(Ipld::Null);
1358        Ok(Some(MessageLookup {
1359            receipt,
1360            tipset: tipset.key().clone(),
1361            height: tipset.epoch(),
1362            message: message_cid,
1363            return_dec: ipld,
1364        }))
1365    }
1366}
1367
1368// Sample CIDs (useful for testing):
1369//   Mainnet:
1370//     1,594,681 bafy2bzaceaclaz3jvmbjg3piazaq5dcesoyv26cdpoozlkzdiwnsvdvm2qoqm OhSnap upgrade
1371//     1_960_320 bafy2bzacec43okhmihmnwmgqspyrkuivqtxv75rpymsdbulq6lgsdq2vkwkcg Skyr upgrade
1372//     2,833,266 bafy2bzacecaydufxqo5vtouuysmg3tqik6onyuezm6lyviycriohgfnzfslm2
1373//     2,933,266 bafy2bzacebyp6cmbshtzzuogzk7icf24pt6s5veyq5zkkqbn3sbbvswtptuuu
1374//   Calibnet:
1375//     242,150 bafy2bzaceb522vvt3wo7xhleo2dvb7wb7pyydmzlahc4aqd7lmvg3afreejiw
1376//     630,932 bafy2bzacedidwdsd7ds73t3z76hcjfsaisoxrangkxsqlzih67ulqgtxnypqk
1377//
1378/// Traverse an IPLD directed acyclic graph and use libp2p-bitswap to request any missing nodes.
1379/// This function has two primary uses: (1) Downloading specific state-roots when Forest deviates
1380/// from the mainline blockchain, (2) fetching historical state-trees to verify past versions of the
1381/// consensus rules.
1382pub enum StateFetchRoot {}
1383
1384impl RpcMethod<2> for StateFetchRoot {
1385    const NAME: &'static str = "Forest.StateFetchRoot";
1386    const PARAM_NAMES: [&'static str; 2] = ["root_cid", "save_to_file"];
1387    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1388    const PERMISSION: Permission = Permission::Read;
1389
1390    type Params = (Cid, Option<PathBuf>);
1391    type Ok = String;
1392
1393    async fn handle(
1394        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1395        (root_cid, save_to_file): Self::Params,
1396        _: &http::Extensions,
1397    ) -> Result<Self::Ok, ServerError> {
1398        let network_send = ctx.network_send().clone();
1399        let db = ctx.store_owned();
1400
1401        let (car_tx, car_handle) = if let Some(save_to_file) = save_to_file {
1402            let (car_tx, car_rx) = flume::bounded(100);
1403            let roots = nonempty![root_cid];
1404            let file = tokio::fs::File::create(save_to_file).await?;
1405
1406            let car_handle = tokio::spawn(async move {
1407                car_rx
1408                    .stream()
1409                    .map(Ok)
1410                    .forward(CarWriter::new_carv1(roots, file)?)
1411                    .await
1412            });
1413
1414            (Some(car_tx), Some(car_handle))
1415        } else {
1416            (None, None)
1417        };
1418
1419        const MAX_CONCURRENT_REQUESTS: usize = 64;
1420        const REQUEST_TIMEOUT: Duration = Duration::from_secs(10);
1421
1422        let mut seen: CidHashSet = CidHashSet::default();
1423        let mut counter: usize = 0;
1424        let mut fetched: usize = 0;
1425        let mut failures: usize = 0;
1426        let mut task_set = JoinSet::new();
1427
1428        fn handle_worker(fetched: &mut usize, failures: &mut usize, ret: anyhow::Result<()>) {
1429            match ret {
1430                Ok(()) => *fetched += 1,
1431                Err(msg) => {
1432                    *failures += 1;
1433                    tracing::debug!("Request failed: {msg}");
1434                }
1435            }
1436        }
1437
1438        // When walking an Ipld graph, we're only interested in the DAG_CBOR encoded nodes.
1439        let mut get_ipld_link = |ipld: &Ipld| match ipld {
1440            &Ipld::Link(cid) if cid.codec() == DAG_CBOR && seen.insert(cid) => Some(cid),
1441            _ => None,
1442        };
1443
1444        // Do a depth-first-search of the IPLD graph (DAG). Nodes that are _not_ present in our database
1445        // are fetched in background tasks. If the number of tasks reaches MAX_CONCURRENT_REQUESTS, the
1446        // depth-first-search pauses until one of the work tasks returns. The memory usage of this
1447        // algorithm is dominated by the set of seen CIDs and the 'dfs' stack is not expected to grow to
1448        // more than 1000 elements (even when walking tens of millions of nodes).
1449        let dfs = Arc::new(Mutex::new(vec![Ipld::Link(root_cid)]));
1450        let mut to_be_fetched = vec![];
1451
1452        // Loop until: No more items in `dfs` AND no running worker tasks.
1453        loop {
1454            while let Some(ipld) = lock_pop(&dfs) {
1455                {
1456                    let mut dfs_guard = dfs.lock();
1457                    // Scan for unseen CIDs. Available IPLD nodes are pushed to the depth-first-search
1458                    // stack, unavailable nodes will be requested in worker tasks.
1459                    for new_cid in ipld.iter().filter_map(&mut get_ipld_link) {
1460                        counter += 1;
1461                        if counter.is_multiple_of(1_000) {
1462                            // set RUST_LOG=forest::rpc::state_api=debug to enable these printouts.
1463                            tracing::debug!(
1464                                "Graph walk: CIDs: {counter}, Fetched: {fetched}, Failures: {failures}, dfs: {}, Concurrent: {}",
1465                                dfs_guard.len(),
1466                                task_set.len()
1467                            );
1468                        }
1469
1470                        if let Some(next_ipld) = db.get_cbor(&new_cid)? {
1471                            dfs_guard.push(next_ipld);
1472                            if let Some(car_tx) = &car_tx {
1473                                car_tx.send(CarBlock {
1474                                    cid: new_cid,
1475                                    data: db.get(&new_cid)?.with_context(|| {
1476                                        format!("Failed to get cid {new_cid} from block store")
1477                                    })?,
1478                                })?;
1479                            }
1480                        } else {
1481                            to_be_fetched.push(new_cid);
1482                        }
1483                    }
1484                }
1485
1486                while let Some(cid) = to_be_fetched.pop() {
1487                    if task_set.len() == MAX_CONCURRENT_REQUESTS
1488                        && let Some(ret) = task_set.join_next().await
1489                    {
1490                        handle_worker(&mut fetched, &mut failures, ret?)
1491                    }
1492                    task_set.spawn_blocking({
1493                        let network_send = network_send.clone();
1494                        let db = db.clone();
1495                        let dfs_vec = Arc::clone(&dfs);
1496                        let car_tx = car_tx.clone();
1497                        move || {
1498                            let (tx, rx) = flume::bounded(1);
1499                            network_send.send(NetworkMessage::BitswapRequest {
1500                                cid,
1501                                response_channel: tx,
1502                            })?;
1503                            // Bitswap requests do not fail. They are just ignored if no-one has
1504                            // the requested data. Here we arbitrary decide to only wait for
1505                            // REQUEST_TIMEOUT before judging that the data is unavailable.
1506                            let _ignore = rx.recv_timeout(REQUEST_TIMEOUT);
1507
1508                            let new_ipld = db
1509                                .get_cbor::<Ipld>(&cid)?
1510                                .with_context(|| format!("Request failed: {cid}"))?;
1511                            dfs_vec.lock().push(new_ipld);
1512                            if let Some(car_tx) = &car_tx {
1513                                car_tx.send(CarBlock {
1514                                    cid,
1515                                    data: db.get(&cid)?.with_context(|| {
1516                                        format!("Failed to get cid {cid} from block store")
1517                                    })?,
1518                                })?;
1519                            }
1520
1521                            Ok(())
1522                        }
1523                    });
1524                }
1525                tokio::task::yield_now().await;
1526            }
1527            if let Some(ret) = task_set.join_next().await {
1528                handle_worker(&mut fetched, &mut failures, ret?)
1529            } else {
1530                // We are out of work items (dfs) and all worker threads have finished, this means
1531                // the entire graph has been walked and fetched.
1532                break;
1533            }
1534        }
1535
1536        drop(car_tx);
1537        if let Some(car_handle) = car_handle {
1538            car_handle.await??;
1539        }
1540
1541        Ok(format!(
1542            "IPLD graph traversed! CIDs: {counter}, fetched: {fetched}, failures: {failures}."
1543        ))
1544    }
1545}
1546
1547pub enum ForestStateCompute {}
1548
1549impl RpcMethod<3> for ForestStateCompute {
1550    const NAME: &'static str = "Forest.StateCompute";
1551    const N_REQUIRED_PARAMS: usize = 1;
1552    const PARAM_NAMES: [&'static str; 3] = ["epoch", "n_epochs", "force_recompute"];
1553    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1554    const PERMISSION: Permission = Permission::Read;
1555    const DESCRIPTION: Option<&'static str> = Some(
1556        "Forest-specific RPC method that recomputes tipset state over an epoch range. It reuses cached executed tipsets only when the cached state root is loadable; otherwise it recomputes. Unlike Filecoin.StateCompute, it does not apply caller-supplied messages or return execution traces.",
1557    );
1558
1559    type Params = (ChainEpoch, Option<NonZeroUsize>, Option<bool>);
1560    type Ok = Vec<ForestComputeStateOutput>;
1561
1562    async fn handle(
1563        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1564        (from_epoch, n_epochs, force_recompute): Self::Params,
1565        _: &http::Extensions,
1566    ) -> Result<Self::Ok, ServerError> {
1567        let force_recompute = force_recompute.unwrap_or_default();
1568        let n_epochs = n_epochs.map(|n| n.get()).unwrap_or(1) as ChainEpoch;
1569        let to_epoch = from_epoch + n_epochs - 1;
1570        let to_ts = ctx.chain_index().tipset_by_height(
1571            to_epoch,
1572            ctx.chain_store().heaviest_tipset(),
1573            ResolveNullTipset::TakeOlder,
1574        )?;
1575        let from_ts = if from_epoch >= to_ts.epoch() {
1576            // When `from_epoch` is a null epoch or `n_epochs` is 1,
1577            // `to_ts.epoch()` could be less than or equal to `from_epoch`
1578            to_ts.shallow_clone()
1579        } else {
1580            ctx.chain_index().tipset_by_height(
1581                from_epoch,
1582                to_ts.shallow_clone(),
1583                ResolveNullTipset::TakeOlder,
1584            )?
1585        };
1586
1587        let mut futures = FuturesOrdered::new();
1588        for ts in to_ts
1589            .chain(ctx.store())
1590            .take_while(|ts| ts.epoch() >= from_ts.epoch())
1591        {
1592            let chain_store = ctx.chain_store().clone();
1593            let network_context = ctx.sync_network_context.shallow_clone();
1594            futures.push_front(async move {
1595                if crate::chain_sync::load_full_tipset(&chain_store, ts.key()).is_err() {
1596                    // Backfill full tipset from the network
1597                    const MAX_RETRIES: usize = 5;
1598                    let fts = 'retry_loop: {
1599                        for i in 1..=MAX_RETRIES {
1600                            match network_context.chain_exchange_messages(None, &ts).await {
1601                                Ok(fts) => break 'retry_loop Ok(fts),
1602                                Err(e) if i >= MAX_RETRIES => break 'retry_loop Err(e),
1603                                Err(_) => continue,
1604                            }
1605                        }
1606                        anyhow::bail!("unreachable chain exchange error in ForestStateCompute")
1607                    }
1608                    .with_context(|| format!("failed to download messages@{}", ts.epoch()))?;
1609                    fts.persist(chain_store.blockstore())?;
1610                }
1611                anyhow::Ok(ts)
1612            });
1613        }
1614
1615        let mut results = Vec::with_capacity(n_epochs as _);
1616        while let Some(ts) = futures.try_next().await? {
1617            let epoch = ts.epoch();
1618            let tipset_key = ts.key().clone();
1619            if !force_recompute {
1620                let ExecutedTipset { state_root, .. } =
1621                    ctx.state_manager.load_executed_tipset(&ts).await?;
1622                // Verify the state tree is loadable as the root CID could present due to some bad or wrong diff snapshot import
1623                if StateTree::new_from_root(ctx.store_owned(), &state_root).is_ok() {
1624                    results.push(ForestComputeStateOutput {
1625                        state_root,
1626                        epoch,
1627                        tipset_key,
1628                    });
1629                    continue;
1630                }
1631            }
1632
1633            let ExecutedTipset { state_root, .. } = ctx
1634                .state_manager
1635                .compute_tipset_state(ts, NO_CALLBACK, VMTrace::NotTraced)
1636                .await?;
1637            // Verify the result state tree
1638            StateTree::new_from_root(ctx.store_owned(), &state_root).with_context(|| format!("failed to load the result state tree, root: {state_root}, epoch: {epoch}, tipset key: {tipset_key}"))?;
1639            results.push(ForestComputeStateOutput {
1640                state_root,
1641                epoch,
1642                tipset_key,
1643            });
1644        }
1645        Ok(results)
1646    }
1647}
1648
1649pub enum StateCompute {}
1650
1651impl RpcMethod<3> for StateCompute {
1652    const NAME: &'static str = "Filecoin.StateCompute";
1653    const PARAM_NAMES: [&'static str; 3] = ["height", "messages", "tipsetKey"];
1654    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1655    const PERMISSION: Permission = Permission::Read;
1656    const DESCRIPTION: Option<&'static str> =
1657        Some("Applies the given messages on the given tipset");
1658
1659    type Params = (ChainEpoch, Vec<Message>, ApiTipsetKey);
1660    type Ok = ComputeStateOutput;
1661
1662    async fn handle(
1663        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1664        (height, messages, ApiTipsetKey(tsk)): Self::Params,
1665        _: &http::Extensions,
1666    ) -> Result<Self::Ok, ServerError> {
1667        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1668        let (tx, rx) = flume::unbounded();
1669        let callback = move |ctx: MessageCallbackCtx<'_>| {
1670            tx.send(ApiInvocResult {
1671                msg_cid: ctx.message.cid(),
1672                msg: ctx.message.message().clone(),
1673                msg_rct: Some(ctx.apply_ret.msg_receipt()),
1674                error: ctx.apply_ret.failure_info().unwrap_or_default(),
1675                duration: ctx.duration.as_nanos().clamp(0, u128::from(u64::MAX)) as u64,
1676                gas_cost: MessageGasCost::new(ctx.message.message(), ctx.apply_ret)?,
1677                execution_trace: structured::parse_events(ctx.apply_ret.exec_trace())
1678                    .unwrap_or_default(),
1679            })?;
1680            Ok(())
1681        };
1682        let ExecutedTipset { state_root, .. } = ctx
1683            .state_manager
1684            .compute_state(height, messages, ts, Some(callback), VMTrace::Traced)
1685            .await?;
1686        let mut trace = vec![];
1687        while let Ok(v) = rx.try_recv() {
1688            trace.push(v);
1689        }
1690        Ok(ComputeStateOutput {
1691            root: state_root,
1692            trace,
1693        })
1694    }
1695}
1696
1697// Convenience function for locking and popping a value out of a vector. If this function is
1698// inlined, the mutex guard isn't dropped early enough.
1699fn lock_pop<T>(mutex: &Mutex<Vec<T>>) -> Option<T> {
1700    mutex.lock().pop()
1701}
1702
1703/// Get randomness from tickets
1704pub enum StateGetRandomnessFromTickets {}
1705
1706impl RpcMethod<4> for StateGetRandomnessFromTickets {
1707    const NAME: &'static str = "Filecoin.StateGetRandomnessFromTickets";
1708    const PARAM_NAMES: [&'static str; 4] = ["personalization", "randEpoch", "entropy", "tipsetKey"];
1709    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1710    const PERMISSION: Permission = Permission::Read;
1711    const DESCRIPTION: Option<&'static str> = Some("Samples the chain for randomness.");
1712
1713    type Params = (i64, ChainEpoch, Vec<u8>, ApiTipsetKey);
1714    type Ok = Vec<u8>;
1715
1716    async fn handle(
1717        ctx: Ctx<impl Blockstore>,
1718        (personalization, rand_epoch, entropy, ApiTipsetKey(tsk)): Self::Params,
1719        _: &http::Extensions,
1720    ) -> Result<Self::Ok, ServerError> {
1721        let tipset = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1722        let chain_rand = ctx.state_manager.chain_rand(tipset);
1723        let digest = chain_rand.get_chain_randomness(rand_epoch, false)?;
1724        let value = crate::state_manager::chain_rand::draw_randomness_from_digest(
1725            &digest,
1726            personalization,
1727            rand_epoch,
1728            &entropy,
1729        )?;
1730        Ok(value.to_vec())
1731    }
1732}
1733
1734pub enum StateGetRandomnessDigestFromTickets {}
1735
1736impl RpcMethod<2> for StateGetRandomnessDigestFromTickets {
1737    const NAME: &'static str = "Filecoin.StateGetRandomnessDigestFromTickets";
1738    const PARAM_NAMES: [&'static str; 2] = ["randEpoch", "tipsetKey"];
1739    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1740    const PERMISSION: Permission = Permission::Read;
1741    const DESCRIPTION: Option<&'static str> = Some("Samples the chain for randomness.");
1742
1743    type Params = (ChainEpoch, ApiTipsetKey);
1744    type Ok = Vec<u8>;
1745
1746    async fn handle(
1747        ctx: Ctx<impl Blockstore>,
1748        (rand_epoch, ApiTipsetKey(tsk)): Self::Params,
1749        _: &http::Extensions,
1750    ) -> Result<Self::Ok, ServerError> {
1751        let tipset = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1752        let chain_rand = ctx.state_manager.chain_rand(tipset);
1753        let digest = chain_rand.get_chain_randomness(rand_epoch, false)?;
1754        Ok(digest.to_vec())
1755    }
1756}
1757
1758/// Get randomness from beacon
1759pub enum StateGetRandomnessFromBeacon {}
1760
1761impl RpcMethod<4> for StateGetRandomnessFromBeacon {
1762    const NAME: &'static str = "Filecoin.StateGetRandomnessFromBeacon";
1763    const PARAM_NAMES: [&'static str; 4] = ["personalization", "randEpoch", "entropy", "tipsetKey"];
1764    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1765    const PERMISSION: Permission = Permission::Read;
1766    const DESCRIPTION: Option<&'static str> = Some(
1767        "Returns the beacon entry for the specified Filecoin epoch. If unavailable, the call blocks until it becomes available.",
1768    );
1769
1770    type Params = (i64, ChainEpoch, Vec<u8>, ApiTipsetKey);
1771    type Ok = Vec<u8>;
1772
1773    async fn handle(
1774        ctx: Ctx<impl Blockstore>,
1775        (personalization, rand_epoch, entropy, ApiTipsetKey(tsk)): Self::Params,
1776        _: &http::Extensions,
1777    ) -> Result<Self::Ok, ServerError> {
1778        let tipset = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1779        let chain_rand = ctx.state_manager.chain_rand(tipset);
1780        let digest = chain_rand.get_beacon_randomness_v3(rand_epoch)?;
1781        let value = crate::state_manager::chain_rand::draw_randomness_from_digest(
1782            &digest,
1783            personalization,
1784            rand_epoch,
1785            &entropy,
1786        )?;
1787        Ok(value.to_vec())
1788    }
1789}
1790
1791pub enum StateGetRandomnessDigestFromBeacon {}
1792
1793impl RpcMethod<2> for StateGetRandomnessDigestFromBeacon {
1794    const NAME: &'static str = "Filecoin.StateGetRandomnessDigestFromBeacon";
1795    const PARAM_NAMES: [&'static str; 2] = ["randEpoch", "tipsetKey"];
1796    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1797    const PERMISSION: Permission = Permission::Read;
1798    const DESCRIPTION: Option<&'static str> = Some("Samples the beacon for randomness.");
1799
1800    type Params = (ChainEpoch, ApiTipsetKey);
1801    type Ok = Vec<u8>;
1802
1803    async fn handle(
1804        ctx: Ctx<impl Blockstore>,
1805        (rand_epoch, ApiTipsetKey(tsk)): Self::Params,
1806        _: &http::Extensions,
1807    ) -> Result<Self::Ok, ServerError> {
1808        let tipset = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1809        let chain_rand = ctx.state_manager.chain_rand(tipset);
1810        let digest = chain_rand.get_beacon_randomness_v3(rand_epoch)?;
1811        Ok(digest.to_vec())
1812    }
1813}
1814
1815/// Get read state
1816pub enum StateReadState {}
1817
1818impl RpcMethod<2> for StateReadState {
1819    const NAME: &'static str = "Filecoin.StateReadState";
1820    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetKey"];
1821    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1822    const PERMISSION: Permission = Permission::Read;
1823    const DESCRIPTION: Option<&'static str> = Some("Returns the state of the specified actor.");
1824
1825    type Params = (Address, ApiTipsetKey);
1826    type Ok = ApiActorState;
1827
1828    async fn handle(
1829        ctx: Ctx<impl Blockstore>,
1830        (address, ApiTipsetKey(tsk)): Self::Params,
1831        _: &http::Extensions,
1832    ) -> Result<Self::Ok, ServerError> {
1833        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1834        let actor = ctx
1835            .state_manager
1836            .get_required_actor(&address, *ts.parent_state())?;
1837        let state_json = load_and_serialize_actor_state(ctx.store(), &actor.code, &actor.state)
1838            .map_err(|e| anyhow::anyhow!("Failed to load actor state: {}", e))?;
1839        Ok(ApiActorState {
1840            balance: actor.balance.clone().into(),
1841            code: actor.code,
1842            state: state_json,
1843        })
1844    }
1845}
1846
1847pub enum StateDecodeParams {}
1848impl RpcMethod<4> for StateDecodeParams {
1849    const NAME: &'static str = "Filecoin.StateDecodeParams";
1850    const PARAM_NAMES: [&'static str; 4] = ["address", "method", "params", "tipsetKey"];
1851    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1852    const PERMISSION: Permission = Permission::Read;
1853    const DESCRIPTION: Option<&'static str> = Some("Decode the provided method params.");
1854
1855    type Params = (Address, MethodNum, Vec<u8>, ApiTipsetKey);
1856    type Ok = serde_json::Value;
1857
1858    async fn handle(
1859        ctx: Ctx<impl Blockstore>,
1860        (address, method, params, ApiTipsetKey(tsk)): Self::Params,
1861        _: &http::Extensions,
1862    ) -> Result<Self::Ok, ServerError> {
1863        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1864        let actor = ctx
1865            .state_manager
1866            .get_required_actor(&address, *ts.parent_state())?;
1867
1868        let res = crate::rpc::registry::methods_reg::deserialize_params(
1869            &actor.code,
1870            method,
1871            params.as_slice(),
1872        )?;
1873        Ok(res.into())
1874    }
1875}
1876
1877pub enum StateCirculatingSupply {}
1878
1879impl RpcMethod<1> for StateCirculatingSupply {
1880    const NAME: &'static str = "Filecoin.StateCirculatingSupply";
1881    const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"];
1882    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1883    const PERMISSION: Permission = Permission::Read;
1884    const DESCRIPTION: Option<&'static str> =
1885        Some("Returns the exact circulating supply of Filecoin at the given tipset.");
1886
1887    type Params = (ApiTipsetKey,);
1888    type Ok = TokenAmount;
1889
1890    async fn handle(
1891        ctx: Ctx<impl Blockstore>,
1892        (ApiTipsetKey(tsk),): Self::Params,
1893        _: &http::Extensions,
1894    ) -> Result<Self::Ok, ServerError> {
1895        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1896        let height = ts.epoch();
1897        let root = ts.parent_state();
1898        let genesis_info = GenesisInfo::from_chain_config(ctx.chain_config().clone());
1899        let supply =
1900            genesis_info.get_state_circulating_supply(height - 1, &ctx.store_owned(), root)?;
1901        Ok(supply)
1902    }
1903}
1904
1905pub enum StateVerifiedClientStatus {}
1906
1907impl RpcMethod<2> for StateVerifiedClientStatus {
1908    const NAME: &'static str = "Filecoin.StateVerifiedClientStatus";
1909    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetKey"];
1910    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1911    const PERMISSION: Permission = Permission::Read;
1912    const DESCRIPTION: Option<&'static str> = Some(
1913        "Returns the data cap for the given address. Returns null if no entry exists in the data cap table.",
1914    );
1915
1916    type Params = (Address, ApiTipsetKey);
1917    type Ok = Option<BigInt>;
1918
1919    async fn handle(
1920        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1921        (address, ApiTipsetKey(tsk)): Self::Params,
1922        _: &http::Extensions,
1923    ) -> Result<Self::Ok, ServerError> {
1924        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1925        let status = ctx.state_manager.verified_client_status(&address, &ts)?;
1926        Ok(status)
1927    }
1928}
1929
1930pub enum StateVMCirculatingSupplyInternal {}
1931
1932impl RpcMethod<1> for StateVMCirculatingSupplyInternal {
1933    const NAME: &'static str = "Filecoin.StateVMCirculatingSupplyInternal";
1934    const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"];
1935    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1936    const PERMISSION: Permission = Permission::Read;
1937    const DESCRIPTION: Option<&'static str> =
1938        Some("Returns an approximation of Filecoin's circulating supply at the given tipset.");
1939
1940    type Params = (ApiTipsetKey,);
1941    type Ok = CirculatingSupply;
1942
1943    async fn handle(
1944        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1945        (ApiTipsetKey(tsk),): Self::Params,
1946        _: &http::Extensions,
1947    ) -> Result<Self::Ok, ServerError> {
1948        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1949        let genesis_info = GenesisInfo::from_chain_config(ctx.chain_config().clone());
1950        Ok(genesis_info.get_vm_circulating_supply_detailed(
1951            ts.epoch(),
1952            &ctx.store_owned(),
1953            ts.parent_state(),
1954        )?)
1955    }
1956}
1957
1958pub enum StateListMiners {}
1959
1960impl RpcMethod<1> for StateListMiners {
1961    const NAME: &'static str = "Filecoin.StateListMiners";
1962    const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"];
1963    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1964    const PERMISSION: Permission = Permission::Read;
1965    const DESCRIPTION: Option<&'static str> =
1966        Some("Returns the addresses of every miner with claimed power in the Power Actor.");
1967
1968    type Params = (ApiTipsetKey,);
1969    type Ok = Vec<Address>;
1970
1971    async fn handle(
1972        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1973        (ApiTipsetKey(tsk),): Self::Params,
1974        _: &http::Extensions,
1975    ) -> Result<Self::Ok, ServerError> {
1976        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
1977        let state: power::State = ctx.state_manager.get_actor_state(&ts)?;
1978        let miners = state.list_all_miners(ctx.store())?;
1979        Ok(miners)
1980    }
1981}
1982
1983pub enum StateListActors {}
1984
1985impl RpcMethod<1> for StateListActors {
1986    const NAME: &'static str = "Filecoin.StateListActors";
1987    const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"];
1988    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
1989    const PERMISSION: Permission = Permission::Read;
1990    const DESCRIPTION: Option<&'static str> =
1991        Some("Returns the addresses of every actor in the state.");
1992
1993    type Params = (ApiTipsetKey,);
1994    type Ok = Vec<Address>;
1995
1996    async fn handle(
1997        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
1998        (ApiTipsetKey(tsk),): Self::Params,
1999        _: &http::Extensions,
2000    ) -> Result<Self::Ok, ServerError> {
2001        let mut actors = vec![];
2002        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2003        let state_tree = ctx.state_manager.get_state_tree(ts.parent_state())?;
2004        state_tree.for_each(|addr, _state| {
2005            actors.push(addr);
2006            Ok(())
2007        })?;
2008        Ok(actors)
2009    }
2010}
2011
2012pub enum StateMarketStorageDeal {}
2013
2014impl RpcMethod<2> for StateMarketStorageDeal {
2015    const NAME: &'static str = "Filecoin.StateMarketStorageDeal";
2016    const PARAM_NAMES: [&'static str; 2] = ["dealId", "tipsetKey"];
2017    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2018    const PERMISSION: Permission = Permission::Read;
2019    const DESCRIPTION: Option<&'static str> = Some("Returns information about the specified deal.");
2020
2021    type Params = (DealID, ApiTipsetKey);
2022    type Ok = ApiMarketDeal;
2023
2024    async fn handle(
2025        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
2026        (deal_id, ApiTipsetKey(tsk)): Self::Params,
2027        _: &http::Extensions,
2028    ) -> Result<Self::Ok, ServerError> {
2029        let store = ctx.store();
2030        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2031        let market_state: market::State = ctx.state_manager.get_actor_state(&ts)?;
2032        let proposals = market_state.proposals(store)?;
2033        let proposal = proposals.get(deal_id)?.ok_or_else(|| anyhow::anyhow!("deal {deal_id} not found - deal may not have completed sealing before deal proposal start epoch, or deal may have been slashed"))?;
2034
2035        let states = market_state.states(store)?;
2036        let state = states.get(deal_id)?.unwrap_or_else(DealState::empty);
2037
2038        Ok(MarketDeal { proposal, state }.into())
2039    }
2040}
2041
2042pub enum StateMarketParticipants {}
2043
2044impl RpcMethod<1> for StateMarketParticipants {
2045    const NAME: &'static str = "Filecoin.StateMarketParticipants";
2046    const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"];
2047    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2048    const PERMISSION: Permission = Permission::Read;
2049    const DESCRIPTION: Option<&'static str> =
2050        Some("Returns the Escrow and Locked balances of all participants in the Storage Market.");
2051
2052    type Params = (ApiTipsetKey,);
2053    type Ok = HashMap<String, MarketBalance>;
2054
2055    async fn handle(
2056        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
2057        (ApiTipsetKey(tsk),): Self::Params,
2058        _: &http::Extensions,
2059    ) -> Result<Self::Ok, ServerError> {
2060        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2061        let market_state = ctx.state_manager.market_state(&ts)?;
2062        let escrow_table = market_state.escrow_table(ctx.store())?;
2063        let locked_table = market_state.locked_table(ctx.store())?;
2064        let mut result = HashMap::new();
2065        escrow_table.for_each(|address, escrow| {
2066            let locked = locked_table.get(address)?;
2067            result.insert(
2068                address.to_string(),
2069                MarketBalance {
2070                    escrow: escrow.clone(),
2071                    locked,
2072                },
2073            );
2074            Ok(())
2075        })?;
2076        Ok(result)
2077    }
2078}
2079
2080pub enum StateDealProviderCollateralBounds {}
2081
2082impl RpcMethod<3> for StateDealProviderCollateralBounds {
2083    const NAME: &'static str = "Filecoin.StateDealProviderCollateralBounds";
2084    const PARAM_NAMES: [&'static str; 3] = ["size", "verified", "tipsetKey"];
2085    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2086    const PERMISSION: Permission = Permission::Read;
2087    const DESCRIPTION: Option<&'static str> = Some(
2088        "Returns the minimum and maximum collateral a storage provider can issue, based on deal size and verified status.",
2089    );
2090
2091    type Params = (u64, bool, ApiTipsetKey);
2092    type Ok = DealCollateralBounds;
2093
2094    async fn handle(
2095        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
2096        (size, verified, ApiTipsetKey(tsk)): Self::Params,
2097        _: &http::Extensions,
2098    ) -> Result<Self::Ok, ServerError> {
2099        let deal_provider_collateral_num = BigInt::from(110);
2100        let deal_provider_collateral_denom = BigInt::from(100);
2101
2102        // This is more eloquent than giving the whole match pattern a type.
2103        let _: bool = verified;
2104
2105        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2106
2107        let power_state: power::State = ctx.state_manager.get_actor_state(&ts)?;
2108        let reward_state: reward::State = ctx.state_manager.get_actor_state(&ts)?;
2109
2110        let genesis_info = GenesisInfo::from_chain_config(ctx.chain_config().clone());
2111
2112        let supply = genesis_info.get_vm_circulating_supply(
2113            ts.epoch(),
2114            &ctx.store_owned(),
2115            ts.parent_state(),
2116        )?;
2117
2118        let power_claim = power_state.total_power();
2119
2120        let policy = &ctx.chain_config().policy;
2121
2122        let baseline_power = reward_state.this_epoch_baseline_power();
2123
2124        let (min, max) = reward_state.deal_provider_collateral_bounds(
2125            policy,
2126            size.into(),
2127            &power_claim.raw_byte_power,
2128            baseline_power,
2129            &supply,
2130        );
2131
2132        let min = min
2133            .atto()
2134            .mul(deal_provider_collateral_num)
2135            .div_euclid(&deal_provider_collateral_denom);
2136
2137        Ok(DealCollateralBounds {
2138            max,
2139            min: TokenAmount::from_atto(min),
2140        })
2141    }
2142}
2143
2144pub enum StateGetBeaconEntry {}
2145
2146impl RpcMethod<1> for StateGetBeaconEntry {
2147    const NAME: &'static str = "Filecoin.StateGetBeaconEntry";
2148    const PARAM_NAMES: [&'static str; 1] = ["epoch"];
2149    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2150    const PERMISSION: Permission = Permission::Read;
2151    const DESCRIPTION: Option<&'static str> =
2152        Some("Returns the beacon entries for the specified epoch.");
2153
2154    type Params = (ChainEpoch,);
2155    type Ok = BeaconEntry;
2156
2157    async fn handle(
2158        ctx: Ctx<impl Blockstore>,
2159        (epoch,): Self::Params,
2160        _: &http::Extensions,
2161    ) -> Result<Self::Ok, ServerError> {
2162        {
2163            let genesis_timestamp = ctx.chain_store().genesis_block_header().timestamp as i64;
2164            let block_delay = i64::from(ctx.chain_config().block_delay_secs);
2165            // Give it a 1s clock drift buffer
2166            let epoch_timestamp = genesis_timestamp + block_delay * epoch + 1;
2167            let now_timestamp = chrono::Utc::now().timestamp();
2168            match epoch_timestamp.saturating_sub(now_timestamp) {
2169                diff if diff > 0 => {
2170                    tokio::time::sleep(Duration::from_secs(diff as u64)).await;
2171                }
2172                _ => {}
2173            };
2174        }
2175
2176        let (_, beacon) = ctx.beacon().beacon_for_epoch(epoch)?;
2177        let network_version = ctx.state_manager.get_network_version(epoch);
2178        let round = beacon.max_beacon_round_for_epoch(network_version, epoch);
2179        let entry = beacon.entry(round).await?;
2180        Ok(entry)
2181    }
2182}
2183
2184pub enum StateSectorPreCommitInfoV0 {}
2185
2186impl RpcMethod<3> for StateSectorPreCommitInfoV0 {
2187    const NAME: &'static str = "Filecoin.StateSectorPreCommitInfo";
2188    const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "sectorNumber", "tipsetKey"];
2189    const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::V0); // Changed in V1
2190    const PERMISSION: Permission = Permission::Read;
2191
2192    type Params = (Address, u64, ApiTipsetKey);
2193    type Ok = SectorPreCommitOnChainInfo;
2194
2195    async fn handle(
2196        ctx: Ctx<impl Blockstore>,
2197        (miner_address, sector_number, ApiTipsetKey(tsk)): Self::Params,
2198        _: &http::Extensions,
2199    ) -> Result<Self::Ok, ServerError> {
2200        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2201        let state: miner::State = ctx
2202            .state_manager
2203            .get_actor_state_from_address(&ts, &miner_address)?;
2204        Ok(state
2205            .load_precommit_on_chain_info(ctx.store(), sector_number)?
2206            .context("precommit info does not exist")?)
2207    }
2208}
2209
2210pub enum StateSectorPreCommitInfo {}
2211
2212impl RpcMethod<3> for StateSectorPreCommitInfo {
2213    const NAME: &'static str = "Filecoin.StateSectorPreCommitInfo";
2214    const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "sectorNumber", "tipsetKey"];
2215    const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::V1); // Changed in V1
2216    const PERMISSION: Permission = Permission::Read;
2217    const DESCRIPTION: Option<&'static str> = Some(
2218        "Returns the PreCommit information for the specified miner's sector. Returns null if not precommitted.",
2219    );
2220
2221    type Params = (Address, u64, ApiTipsetKey);
2222    type Ok = Option<SectorPreCommitOnChainInfo>;
2223
2224    async fn handle(
2225        ctx: Ctx<impl Blockstore>,
2226        (miner_address, sector_number, ApiTipsetKey(tsk)): Self::Params,
2227        _: &http::Extensions,
2228    ) -> Result<Self::Ok, ServerError> {
2229        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2230        let state: miner::State = ctx
2231            .state_manager
2232            .get_actor_state_from_address(&ts, &miner_address)?;
2233        Ok(state.load_precommit_on_chain_info(ctx.store(), sector_number)?)
2234    }
2235}
2236
2237impl StateSectorPreCommitInfo {
2238    pub fn get_sectors(
2239        store: &Arc<impl Blockstore>,
2240        miner_address: &Address,
2241        tipset: &Tipset,
2242    ) -> anyhow::Result<Vec<u64>> {
2243        let mut sectors = vec![];
2244        let state_tree = StateTree::new_from_root(store.clone(), tipset.parent_state())?;
2245        let state: miner::State = state_tree.get_actor_state_from_address(miner_address)?;
2246        match &state {
2247            miner::State::V8(s) => {
2248                let precommitted = fil_actors_shared::v8::make_map_with_root::<
2249                    _,
2250                    fil_actor_miner_state::v8::SectorPreCommitOnChainInfo,
2251                >(&s.pre_committed_sectors, store)?;
2252                precommitted
2253                    .for_each(|_k, v| {
2254                        sectors.push(v.info.sector_number);
2255                        Ok(())
2256                    })
2257                    .context("failed to iterate over precommitted sectors")
2258            }
2259            miner::State::V9(s) => {
2260                let precommitted = fil_actors_shared::v9::make_map_with_root::<
2261                    _,
2262                    fil_actor_miner_state::v9::SectorPreCommitOnChainInfo,
2263                >(&s.pre_committed_sectors, store)?;
2264                precommitted
2265                    .for_each(|_k, v| {
2266                        sectors.push(v.info.sector_number);
2267                        Ok(())
2268                    })
2269                    .context("failed to iterate over precommitted sectors")
2270            }
2271            miner::State::V10(s) => {
2272                let precommitted = fil_actors_shared::v10::make_map_with_root::<
2273                    _,
2274                    fil_actor_miner_state::v10::SectorPreCommitOnChainInfo,
2275                >(&s.pre_committed_sectors, store)?;
2276                precommitted
2277                    .for_each(|_k, v| {
2278                        sectors.push(v.info.sector_number);
2279                        Ok(())
2280                    })
2281                    .context("failed to iterate over precommitted sectors")
2282            }
2283            miner::State::V11(s) => {
2284                let precommitted = fil_actors_shared::v11::make_map_with_root::<
2285                    _,
2286                    fil_actor_miner_state::v11::SectorPreCommitOnChainInfo,
2287                >(&s.pre_committed_sectors, store)?;
2288                precommitted
2289                    .for_each(|_k, v| {
2290                        sectors.push(v.info.sector_number);
2291                        Ok(())
2292                    })
2293                    .context("failed to iterate over precommitted sectors")
2294            }
2295            miner::State::V12(s) => {
2296                let precommitted = fil_actors_shared::v12::make_map_with_root::<
2297                    _,
2298                    fil_actor_miner_state::v12::SectorPreCommitOnChainInfo,
2299                >(&s.pre_committed_sectors, store)?;
2300                precommitted
2301                    .for_each(|_k, v| {
2302                        sectors.push(v.info.sector_number);
2303                        Ok(())
2304                    })
2305                    .context("failed to iterate over precommitted sectors")
2306            }
2307            miner::State::V13(s) => {
2308                let precommitted = fil_actors_shared::v13::make_map_with_root::<
2309                    _,
2310                    fil_actor_miner_state::v13::SectorPreCommitOnChainInfo,
2311                >(&s.pre_committed_sectors, store)?;
2312                precommitted
2313                    .for_each(|_k, v| {
2314                        sectors.push(v.info.sector_number);
2315                        Ok(())
2316                    })
2317                    .context("failed to iterate over precommitted sectors")
2318            }
2319            miner::State::V14(s) => {
2320                let precommitted = fil_actor_miner_state::v14::PreCommitMap::load(
2321                    store,
2322                    &s.pre_committed_sectors,
2323                    fil_actor_miner_state::v14::PRECOMMIT_CONFIG,
2324                    "precommits",
2325                )?;
2326                precommitted
2327                    .for_each(|_k, v| {
2328                        sectors.push(v.info.sector_number);
2329                        Ok(())
2330                    })
2331                    .context("failed to iterate over precommitted sectors")
2332            }
2333            miner::State::V15(s) => {
2334                let precommitted = fil_actor_miner_state::v15::PreCommitMap::load(
2335                    store,
2336                    &s.pre_committed_sectors,
2337                    fil_actor_miner_state::v15::PRECOMMIT_CONFIG,
2338                    "precommits",
2339                )?;
2340                precommitted
2341                    .for_each(|_k, v| {
2342                        sectors.push(v.info.sector_number);
2343                        Ok(())
2344                    })
2345                    .context("failed to iterate over precommitted sectors")
2346            }
2347            miner::State::V16(s) => {
2348                let precommitted = fil_actor_miner_state::v16::PreCommitMap::load(
2349                    store,
2350                    &s.pre_committed_sectors,
2351                    fil_actor_miner_state::v16::PRECOMMIT_CONFIG,
2352                    "precommits",
2353                )?;
2354                precommitted
2355                    .for_each(|_k, v| {
2356                        sectors.push(v.info.sector_number);
2357                        Ok(())
2358                    })
2359                    .context("failed to iterate over precommitted sectors")
2360            }
2361            miner::State::V17(s) => {
2362                let precommitted = fil_actor_miner_state::v17::PreCommitMap::load(
2363                    store,
2364                    &s.pre_committed_sectors,
2365                    fil_actor_miner_state::v17::PRECOMMIT_CONFIG,
2366                    "precommits",
2367                )?;
2368                precommitted
2369                    .for_each(|_k, v| {
2370                        sectors.push(v.info.sector_number);
2371                        Ok(())
2372                    })
2373                    .context("failed to iterate over precommitted sectors")
2374            }
2375            miner::State::V18(s) => {
2376                let precommitted = fil_actor_miner_state::v18::PreCommitMap::load(
2377                    store,
2378                    &s.pre_committed_sectors,
2379                    fil_actor_miner_state::v18::PRECOMMIT_CONFIG,
2380                    "precommits",
2381                )?;
2382                precommitted
2383                    .for_each(|_k, v| {
2384                        sectors.push(v.info.sector_number);
2385                        Ok(())
2386                    })
2387                    .context("failed to iterate over precommitted sectors")
2388            }
2389        }?;
2390
2391        Ok(sectors)
2392    }
2393
2394    pub fn get_sector_pre_commit_infos(
2395        store: &Arc<impl Blockstore>,
2396        miner_address: &Address,
2397        tipset: &Tipset,
2398    ) -> anyhow::Result<Vec<SectorPreCommitInfo>> {
2399        let mut infos = vec![];
2400        let state_tree = StateTree::new_from_root(store.clone(), tipset.parent_state())?;
2401        let state: miner::State = state_tree.get_actor_state_from_address(miner_address)?;
2402        match &state {
2403            miner::State::V8(s) => {
2404                let precommitted = fil_actors_shared::v8::make_map_with_root::<
2405                    _,
2406                    fil_actor_miner_state::v8::SectorPreCommitOnChainInfo,
2407                >(&s.pre_committed_sectors, store)?;
2408                precommitted
2409                    .for_each(|_k, v| {
2410                        infos.push(v.info.clone().into());
2411                        Ok(())
2412                    })
2413                    .context("failed to iterate over precommitted sectors")
2414            }
2415            miner::State::V9(s) => {
2416                let precommitted = fil_actors_shared::v9::make_map_with_root::<
2417                    _,
2418                    fil_actor_miner_state::v9::SectorPreCommitOnChainInfo,
2419                >(&s.pre_committed_sectors, store)?;
2420                precommitted
2421                    .for_each(|_k, v| {
2422                        infos.push(v.info.clone().into());
2423                        Ok(())
2424                    })
2425                    .context("failed to iterate over precommitted sectors")
2426            }
2427            miner::State::V10(s) => {
2428                let precommitted = fil_actors_shared::v10::make_map_with_root::<
2429                    _,
2430                    fil_actor_miner_state::v10::SectorPreCommitOnChainInfo,
2431                >(&s.pre_committed_sectors, store)?;
2432                precommitted
2433                    .for_each(|_k, v| {
2434                        infos.push(v.info.clone().into());
2435                        Ok(())
2436                    })
2437                    .context("failed to iterate over precommitted sectors")
2438            }
2439            miner::State::V11(s) => {
2440                let precommitted = fil_actors_shared::v11::make_map_with_root::<
2441                    _,
2442                    fil_actor_miner_state::v11::SectorPreCommitOnChainInfo,
2443                >(&s.pre_committed_sectors, store)?;
2444                precommitted
2445                    .for_each(|_k, v| {
2446                        infos.push(v.info.clone().into());
2447                        Ok(())
2448                    })
2449                    .context("failed to iterate over precommitted sectors")
2450            }
2451            miner::State::V12(s) => {
2452                let precommitted = fil_actors_shared::v12::make_map_with_root::<
2453                    _,
2454                    fil_actor_miner_state::v12::SectorPreCommitOnChainInfo,
2455                >(&s.pre_committed_sectors, store)?;
2456                precommitted
2457                    .for_each(|_k, v| {
2458                        infos.push(v.info.clone().into());
2459                        Ok(())
2460                    })
2461                    .context("failed to iterate over precommitted sectors")
2462            }
2463            miner::State::V13(s) => {
2464                let precommitted = fil_actors_shared::v13::make_map_with_root::<
2465                    _,
2466                    fil_actor_miner_state::v13::SectorPreCommitOnChainInfo,
2467                >(&s.pre_committed_sectors, store)?;
2468                precommitted
2469                    .for_each(|_k, v| {
2470                        infos.push(v.info.clone().into());
2471                        Ok(())
2472                    })
2473                    .context("failed to iterate over precommitted sectors")
2474            }
2475            miner::State::V14(s) => {
2476                let precommitted = fil_actor_miner_state::v14::PreCommitMap::load(
2477                    store,
2478                    &s.pre_committed_sectors,
2479                    fil_actor_miner_state::v14::PRECOMMIT_CONFIG,
2480                    "precommits",
2481                )?;
2482                precommitted
2483                    .for_each(|_k, v| {
2484                        infos.push(v.info.clone().into());
2485                        Ok(())
2486                    })
2487                    .context("failed to iterate over precommitted sectors")
2488            }
2489            miner::State::V15(s) => {
2490                let precommitted = fil_actor_miner_state::v15::PreCommitMap::load(
2491                    store,
2492                    &s.pre_committed_sectors,
2493                    fil_actor_miner_state::v15::PRECOMMIT_CONFIG,
2494                    "precommits",
2495                )?;
2496                precommitted
2497                    .for_each(|_k, v| {
2498                        infos.push(v.info.clone().into());
2499                        Ok(())
2500                    })
2501                    .context("failed to iterate over precommitted sectors")
2502            }
2503            miner::State::V16(s) => {
2504                let precommitted = fil_actor_miner_state::v16::PreCommitMap::load(
2505                    store,
2506                    &s.pre_committed_sectors,
2507                    fil_actor_miner_state::v16::PRECOMMIT_CONFIG,
2508                    "precommits",
2509                )?;
2510                precommitted
2511                    .for_each(|_k, v| {
2512                        infos.push(v.info.clone().into());
2513                        Ok(())
2514                    })
2515                    .context("failed to iterate over precommitted sectors")
2516            }
2517            miner::State::V17(s) => {
2518                let precommitted = fil_actor_miner_state::v17::PreCommitMap::load(
2519                    store,
2520                    &s.pre_committed_sectors,
2521                    fil_actor_miner_state::v17::PRECOMMIT_CONFIG,
2522                    "precommits",
2523                )?;
2524                precommitted
2525                    .for_each(|_k, v| {
2526                        infos.push(v.info.clone().into());
2527                        Ok(())
2528                    })
2529                    .context("failed to iterate over precommitted sectors")
2530            }
2531            miner::State::V18(s) => {
2532                let precommitted = fil_actor_miner_state::v18::PreCommitMap::load(
2533                    store,
2534                    &s.pre_committed_sectors,
2535                    fil_actor_miner_state::v18::PRECOMMIT_CONFIG,
2536                    "precommits",
2537                )?;
2538                precommitted
2539                    .for_each(|_k, v| {
2540                        infos.push(v.info.clone().into());
2541                        Ok(())
2542                    })
2543                    .context("failed to iterate over precommitted sectors")
2544            }
2545        }?;
2546
2547        Ok(infos)
2548    }
2549}
2550
2551pub enum StateSectorGetInfo {}
2552
2553impl RpcMethod<3> for StateSectorGetInfo {
2554    const NAME: &'static str = "Filecoin.StateSectorGetInfo";
2555    const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "sectorNumber", "tipsetKey"];
2556    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2557    const PERMISSION: Permission = Permission::Read;
2558    const DESCRIPTION: Option<&'static str> = Some(
2559        "Returns on-chain information for the specified miner's sector. Returns null if not found. Use StateSectorExpiration for accurate expiration epochs.",
2560    );
2561
2562    type Params = (Address, u64, ApiTipsetKey);
2563    type Ok = Option<SectorOnChainInfo>;
2564
2565    async fn handle(
2566        ctx: Ctx<impl Blockstore>,
2567        (miner_address, sector_number, ApiTipsetKey(tsk)): Self::Params,
2568        _: &http::Extensions,
2569    ) -> Result<Self::Ok, ServerError> {
2570        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2571        Ok(ctx
2572            .state_manager
2573            .get_all_sectors(&miner_address, &ts)?
2574            .into_iter()
2575            .find(|info| info.sector_number == sector_number))
2576    }
2577}
2578
2579impl StateSectorGetInfo {
2580    pub fn get_sectors(
2581        store: &Arc<impl Blockstore>,
2582        miner_address: &Address,
2583        tipset: &Tipset,
2584    ) -> anyhow::Result<Vec<u64>> {
2585        let state_tree = StateTree::new_from_root(store.clone(), tipset.parent_state())?;
2586        let state: miner::State = state_tree.get_actor_state_from_address(miner_address)?;
2587        Ok(state
2588            .load_sectors(store, None)?
2589            .into_iter()
2590            .map(|s| s.sector_number)
2591            .collect())
2592    }
2593}
2594
2595pub enum StateSectorExpiration {}
2596
2597impl RpcMethod<3> for StateSectorExpiration {
2598    const NAME: &'static str = "Filecoin.StateSectorExpiration";
2599    const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "sectorNumber", "tipsetKey"];
2600    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2601    const PERMISSION: Permission = Permission::Read;
2602    const DESCRIPTION: Option<&'static str> =
2603        Some("Returns the epoch at which the specified sector will expire.");
2604
2605    type Params = (Address, u64, ApiTipsetKey);
2606    type Ok = SectorExpiration;
2607
2608    async fn handle(
2609        ctx: Ctx<impl Blockstore>,
2610        (miner_address, sector_number, ApiTipsetKey(tsk)): Self::Params,
2611        _: &http::Extensions,
2612    ) -> Result<Self::Ok, ServerError> {
2613        let store = ctx.store();
2614        let policy = &ctx.chain_config().policy;
2615        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2616        let state: miner::State = ctx
2617            .state_manager
2618            .get_actor_state_from_address(&ts, &miner_address)?;
2619        let mut early = 0;
2620        let mut on_time = 0;
2621        let mut terminated = false;
2622        state.for_each_deadline(policy, store, |_deadline_index, deadline| {
2623            deadline.for_each(store, |_partition_index, partition| {
2624                if !terminated && partition.all_sectors().get(sector_number) {
2625                    if partition.terminated().get(sector_number) {
2626                        terminated = true;
2627                        early = 0;
2628                        on_time = 0;
2629                        return Ok(());
2630                    }
2631                    let expirations: Amt<fil_actor_miner_state::v16::ExpirationSet, _> =
2632                        Amt::load(&partition.expirations_epochs(), store)?;
2633                    expirations.for_each(|epoch, expiration| {
2634                        if expiration.early_sectors.get(sector_number) {
2635                            early = epoch as _;
2636                        }
2637                        if expiration.on_time_sectors.get(sector_number) {
2638                            on_time = epoch as _;
2639                        }
2640                        Ok(())
2641                    })?;
2642                }
2643
2644                Ok(())
2645            })?;
2646            Ok(())
2647        })?;
2648        if early == 0 && on_time == 0 {
2649            Err(anyhow::anyhow!("failed to find sector {sector_number}").into())
2650        } else {
2651            Ok(SectorExpiration { early, on_time })
2652        }
2653    }
2654}
2655
2656pub enum StateSectorPartition {}
2657
2658impl RpcMethod<3> for StateSectorPartition {
2659    const NAME: &'static str = "Filecoin.StateSectorPartition";
2660    const PARAM_NAMES: [&'static str; 3] = ["minerAddress", "sectorNumber", "tipsetKey"];
2661    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2662    const PERMISSION: Permission = Permission::Read;
2663    const DESCRIPTION: Option<&'static str> =
2664        Some("Finds the deadline/partition for the specified sector.");
2665
2666    type Params = (Address, u64, ApiTipsetKey);
2667    type Ok = SectorLocation;
2668
2669    async fn handle(
2670        ctx: Ctx<impl Blockstore>,
2671        (miner_address, sector_number, ApiTipsetKey(tsk)): Self::Params,
2672        _: &http::Extensions,
2673    ) -> Result<Self::Ok, ServerError> {
2674        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2675        let state: miner::State = ctx
2676            .state_manager
2677            .get_actor_state_from_address(&ts, &miner_address)?;
2678        let (deadline, partition) =
2679            state.find_sector(ctx.store(), sector_number, &ctx.chain_config().policy)?;
2680        Ok(SectorLocation {
2681            deadline,
2682            partition,
2683        })
2684    }
2685}
2686
2687/// Looks back and returns all messages with a matching to or from address, stopping at the given height.
2688pub enum StateListMessages {}
2689
2690impl RpcMethod<3> for StateListMessages {
2691    const NAME: &'static str = "Filecoin.StateListMessages";
2692    const PARAM_NAMES: [&'static str; 3] = ["messageFilter", "tipsetKey", "maxHeight"];
2693    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2694    const PERMISSION: Permission = Permission::Read;
2695    const DESCRIPTION: Option<&'static str> =
2696        Some("Returns all messages with a matching to or from address up to the given height.");
2697
2698    type Params = (MessageFilter, ApiTipsetKey, i64);
2699    type Ok = Vec<Cid>;
2700
2701    async fn handle(
2702        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
2703        (from_to, ApiTipsetKey(tsk), max_height): Self::Params,
2704        _: &http::Extensions,
2705    ) -> Result<Self::Ok, ServerError> {
2706        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2707        if from_to.is_empty() {
2708            return Err(ErrorObject::owned(
2709                1,
2710                "must specify at least To or From in message filter",
2711                Some(from_to),
2712            )
2713            .into());
2714        } else if let Some(to) = from_to.to {
2715            // this is following lotus logic, it probably should be `if let` instead of `else if let`
2716            // see <https://github.com/ChainSafe/forest/pull/3827#discussion_r1462691005>
2717            if ctx.state_manager.lookup_id(&to, &ts)?.is_none() {
2718                return Ok(vec![]);
2719            }
2720        } else if let Some(from) = from_to.from
2721            && ctx.state_manager.lookup_id(&from, &ts)?.is_none()
2722        {
2723            return Ok(vec![]);
2724        }
2725
2726        let mut out = Vec::new();
2727        let mut cur_ts = ts.shallow_clone();
2728
2729        while cur_ts.epoch() >= max_height {
2730            let msgs = ctx.chain_store().messages_for_tipset(&cur_ts)?;
2731
2732            for msg in msgs.iter() {
2733                if from_to.matches(msg.message()) {
2734                    out.push(msg.cid());
2735                }
2736            }
2737
2738            if cur_ts.epoch() == 0 {
2739                break;
2740            }
2741
2742            let next = ctx.chain_index().load_required_tipset(cur_ts.parents())?;
2743            cur_ts = next;
2744        }
2745
2746        Ok(out)
2747    }
2748}
2749
2750pub enum StateGetClaim {}
2751
2752impl RpcMethod<3> for StateGetClaim {
2753    const NAME: &'static str = "Filecoin.StateGetClaim";
2754    const PARAM_NAMES: [&'static str; 3] = ["address", "claimId", "tipsetKey"];
2755    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2756    const PERMISSION: Permission = Permission::Read;
2757    const DESCRIPTION: Option<&'static str> =
2758        Some("Returns the claim for a given address and claim ID.");
2759
2760    type Params = (Address, ClaimID, ApiTipsetKey);
2761    type Ok = Option<Claim>;
2762
2763    async fn handle(
2764        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
2765        (address, claim_id, ApiTipsetKey(tsk)): Self::Params,
2766        _: &http::Extensions,
2767    ) -> Result<Self::Ok, ServerError> {
2768        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2769        Ok(ctx.state_manager.get_claim(&address, &ts, claim_id)?)
2770    }
2771}
2772
2773pub enum StateGetClaims {}
2774
2775impl RpcMethod<2> for StateGetClaims {
2776    const NAME: &'static str = "Filecoin.StateGetClaims";
2777    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetKey"];
2778    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2779    const PERMISSION: Permission = Permission::Read;
2780    const DESCRIPTION: Option<&'static str> = Some("Returns all claims for a given provider.");
2781
2782    type Params = (Address, ApiTipsetKey);
2783    type Ok = HashMap<ClaimID, Claim>;
2784
2785    async fn handle(
2786        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
2787        (address, ApiTipsetKey(tsk)): Self::Params,
2788        _: &http::Extensions,
2789    ) -> Result<Self::Ok, ServerError> {
2790        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2791        Ok(Self::get_claims(&ctx.store_owned(), &address, &ts)?)
2792    }
2793}
2794
2795impl StateGetClaims {
2796    pub fn get_claims(
2797        store: &Arc<impl Blockstore>,
2798        address: &Address,
2799        tipset: &Tipset,
2800    ) -> anyhow::Result<HashMap<ClaimID, Claim>> {
2801        let state_tree = StateTree::new_from_tipset(store.clone(), tipset)?;
2802        let state: verifreg::State = state_tree.get_actor_state()?;
2803        let actor_id = state_tree.lookup_required_id(address)?;
2804        let actor_id_address = Address::new_id(actor_id);
2805        state.get_claims(store, &actor_id_address)
2806    }
2807}
2808
2809pub enum StateGetAllClaims {}
2810
2811impl RpcMethod<1> for StateGetAllClaims {
2812    const NAME: &'static str = "Filecoin.StateGetAllClaims";
2813    const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"];
2814    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2815    const PERMISSION: Permission = Permission::Read;
2816    const DESCRIPTION: Option<&'static str> =
2817        Some("Returns all claims available in the verified registry actor.");
2818
2819    type Params = (ApiTipsetKey,);
2820    type Ok = HashMap<ClaimID, Claim>;
2821
2822    async fn handle(
2823        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
2824        (ApiTipsetKey(tsk),): Self::Params,
2825        _: &http::Extensions,
2826    ) -> Result<Self::Ok, ServerError> {
2827        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2828        Ok(ctx.state_manager.get_all_claims(&ts)?)
2829    }
2830}
2831
2832pub enum StateGetAllocation {}
2833
2834impl RpcMethod<3> for StateGetAllocation {
2835    const NAME: &'static str = "Filecoin.StateGetAllocation";
2836    const PARAM_NAMES: [&'static str; 3] = ["address", "allocationId", "tipsetKey"];
2837    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2838    const PERMISSION: Permission = Permission::Read;
2839    const DESCRIPTION: Option<&'static str> =
2840        Some("Returns the allocation for a given address and allocation ID.");
2841
2842    type Params = (Address, AllocationID, ApiTipsetKey);
2843    type Ok = Option<Allocation>;
2844
2845    async fn handle(
2846        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
2847        (address, allocation_id, ApiTipsetKey(tsk)): Self::Params,
2848        _: &http::Extensions,
2849    ) -> Result<Self::Ok, ServerError> {
2850        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2851        Ok(ctx
2852            .state_manager
2853            .get_allocation(&address, &ts, allocation_id)?)
2854    }
2855}
2856
2857pub enum StateGetAllocations {}
2858
2859impl RpcMethod<2> for StateGetAllocations {
2860    const NAME: &'static str = "Filecoin.StateGetAllocations";
2861    const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetKey"];
2862    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
2863    const PERMISSION: Permission = Permission::Read;
2864    const DESCRIPTION: Option<&'static str> = Some("Returns all allocations for a given client.");
2865
2866    type Params = (Address, ApiTipsetKey);
2867    type Ok = HashMap<AllocationID, Allocation>;
2868
2869    async fn handle(
2870        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
2871        (address, ApiTipsetKey(tsk)): Self::Params,
2872        _: &http::Extensions,
2873    ) -> Result<Self::Ok, ServerError> {
2874        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
2875        Ok(Self::get_allocations(&ctx.store_owned(), &address, &ts)?)
2876    }
2877}
2878
2879impl StateGetAllocations {
2880    // For testing
2881    pub fn get_valid_actor_addresses<'a>(
2882        store: &'a Arc<impl Blockstore>,
2883        tipset: &'a Tipset,
2884    ) -> anyhow::Result<impl Iterator<Item = Address> + 'a> {
2885        let mut addresses = HashSet::default();
2886        let state_tree = StateTree::new_from_tipset(store.clone(), tipset)?;
2887        let verifreg_state: verifreg::State = state_tree.get_actor_state()?;
2888        match verifreg_state {
2889            verifreg::State::V13(s) => {
2890                let map = s.load_allocs(store)?;
2891                map.for_each(|k, _| {
2892                    let actor_id = fil_actors_shared::v13::parse_uint_key(k)?;
2893                    addresses.insert(Address::new_id(actor_id));
2894                    Ok(())
2895                })?;
2896            }
2897            verifreg::State::V12(s) => {
2898                let map = s.load_allocs(store)?;
2899                map.for_each(|k, _| {
2900                    let actor_id = fil_actors_shared::v12::parse_uint_key(k)?;
2901                    addresses.insert(Address::new_id(actor_id));
2902                    Ok(())
2903                })?;
2904            }
2905            _ => (),
2906        };
2907
2908        if addresses.is_empty() {
2909            let init_state: init::State = state_tree.get_actor_state()?;
2910            match init_state {
2911                init::State::V0(_) => {
2912                    anyhow::bail!("StateGetAllocations is not implemented for init state v0");
2913                }
2914                init::State::V8(s) => {
2915                    let map =
2916                        fil_actors_shared::v8::make_map_with_root::<_, u64>(&s.address_map, store)?;
2917                    map.for_each(|_k, v| {
2918                        addresses.insert(Address::new_id(*v));
2919                        Ok(())
2920                    })?;
2921                }
2922                init::State::V9(s) => {
2923                    let map =
2924                        fil_actors_shared::v9::make_map_with_root::<_, u64>(&s.address_map, store)?;
2925                    map.for_each(|_k, v| {
2926                        addresses.insert(Address::new_id(*v));
2927                        Ok(())
2928                    })?;
2929                }
2930                init::State::V10(s) => {
2931                    let map = fil_actors_shared::v10::make_map_with_root::<_, u64>(
2932                        &s.address_map,
2933                        store,
2934                    )?;
2935                    map.for_each(|_k, v| {
2936                        addresses.insert(Address::new_id(*v));
2937                        Ok(())
2938                    })?;
2939                }
2940                init::State::V11(s) => {
2941                    let map = fil_actors_shared::v11::make_map_with_root::<_, u64>(
2942                        &s.address_map,
2943                        store,
2944                    )?;
2945                    map.for_each(|_k, v| {
2946                        addresses.insert(Address::new_id(*v));
2947                        Ok(())
2948                    })?;
2949                }
2950                init::State::V12(s) => {
2951                    let map = fil_actors_shared::v12::make_map_with_root::<_, u64>(
2952                        &s.address_map,
2953                        store,
2954                    )?;
2955                    map.for_each(|_k, v| {
2956                        addresses.insert(Address::new_id(*v));
2957                        Ok(())
2958                    })?;
2959                }
2960                init::State::V13(s) => {
2961                    let map = fil_actors_shared::v13::make_map_with_root::<_, u64>(
2962                        &s.address_map,
2963                        store,
2964                    )?;
2965                    map.for_each(|_k, v| {
2966                        addresses.insert(Address::new_id(*v));
2967                        Ok(())
2968                    })?;
2969                }
2970                init::State::V14(s) => {
2971                    let map = fil_actor_init_state::v14::AddressMap::load(
2972                        store,
2973                        &s.address_map,
2974                        fil_actors_shared::v14::DEFAULT_HAMT_CONFIG,
2975                        "address_map",
2976                    )?;
2977                    map.for_each(|_k, v| {
2978                        addresses.insert(Address::new_id(*v));
2979                        Ok(())
2980                    })?;
2981                }
2982                init::State::V15(s) => {
2983                    let map = fil_actor_init_state::v15::AddressMap::load(
2984                        store,
2985                        &s.address_map,
2986                        fil_actors_shared::v15::DEFAULT_HAMT_CONFIG,
2987                        "address_map",
2988                    )?;
2989                    map.for_each(|_k, v| {
2990                        addresses.insert(Address::new_id(*v));
2991                        Ok(())
2992                    })?;
2993                }
2994                init::State::V16(s) => {
2995                    let map = fil_actor_init_state::v16::AddressMap::load(
2996                        store,
2997                        &s.address_map,
2998                        fil_actors_shared::v16::DEFAULT_HAMT_CONFIG,
2999                        "address_map",
3000                    )?;
3001                    map.for_each(|_k, v| {
3002                        addresses.insert(Address::new_id(*v));
3003                        Ok(())
3004                    })?;
3005                }
3006                init::State::V17(s) => {
3007                    let map = fil_actor_init_state::v17::AddressMap::load(
3008                        store,
3009                        &s.address_map,
3010                        fil_actors_shared::v17::DEFAULT_HAMT_CONFIG,
3011                        "address_map",
3012                    )?;
3013                    map.for_each(|_k, v| {
3014                        addresses.insert(Address::new_id(*v));
3015                        Ok(())
3016                    })?;
3017                }
3018                init::State::V18(s) => {
3019                    let map = fil_actor_init_state::v18::AddressMap::load(
3020                        store,
3021                        &s.address_map,
3022                        fil_actors_shared::v18::DEFAULT_HAMT_CONFIG,
3023                        "address_map",
3024                    )?;
3025                    map.for_each(|_k, v| {
3026                        addresses.insert(Address::new_id(*v));
3027                        Ok(())
3028                    })?;
3029                }
3030            };
3031        }
3032
3033        Ok(addresses
3034            .into_iter()
3035            .filter(|addr| match Self::get_allocations(store, addr, tipset) {
3036                Ok(r) => !r.is_empty(),
3037                _ => false,
3038            }))
3039    }
3040
3041    pub fn get_allocations(
3042        store: &Arc<impl Blockstore>,
3043        address: &Address,
3044        tipset: &Tipset,
3045    ) -> anyhow::Result<HashMap<AllocationID, Allocation>> {
3046        let state_tree = StateTree::new_from_tipset(store.clone(), tipset)?;
3047        let state: verifreg::State = state_tree.get_actor_state()?;
3048        state.get_allocations(store, address)
3049    }
3050}
3051
3052pub enum StateGetAllAllocations {}
3053
3054impl RpcMethod<1> for crate::rpc::prelude::StateGetAllAllocations {
3055    const NAME: &'static str = "Filecoin.StateGetAllAllocations";
3056    const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"];
3057    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
3058    const PERMISSION: Permission = Permission::Read;
3059    const DESCRIPTION: Option<&'static str> =
3060        Some("Returns all allocations available in the verified registry actor.");
3061
3062    type Params = (ApiTipsetKey,);
3063    type Ok = HashMap<AllocationID, Allocation>;
3064
3065    async fn handle(
3066        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
3067        (ApiTipsetKey(tsk),): Self::Params,
3068        _: &http::Extensions,
3069    ) -> Result<Self::Ok, ServerError> {
3070        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
3071        Ok(ctx.state_manager.get_all_allocations(&ts)?)
3072    }
3073}
3074
3075pub enum StateGetAllocationIdForPendingDeal {}
3076
3077impl RpcMethod<2> for StateGetAllocationIdForPendingDeal {
3078    const NAME: &'static str = "Filecoin.StateGetAllocationIdForPendingDeal";
3079    const PARAM_NAMES: [&'static str; 2] = ["dealId", "tipsetKey"];
3080    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
3081    const PERMISSION: Permission = Permission::Read;
3082    const DESCRIPTION: Option<&'static str> =
3083        Some("Returns the allocation ID for the specified pending deal.");
3084
3085    type Params = (DealID, ApiTipsetKey);
3086    type Ok = AllocationID;
3087
3088    async fn handle(
3089        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
3090        (deal_id, ApiTipsetKey(tsk)): Self::Params,
3091        _: &http::Extensions,
3092    ) -> Result<Self::Ok, ServerError> {
3093        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
3094        let state_tree = StateTree::new_from_tipset(ctx.store_owned(), &ts)?;
3095        let market_state: market::State = state_tree.get_actor_state()?;
3096        Ok(market_state.get_allocation_id_for_pending_deal(ctx.store(), &deal_id)?)
3097    }
3098}
3099
3100impl StateGetAllocationIdForPendingDeal {
3101    pub fn get_allocations_for_pending_deals(
3102        store: &Arc<impl Blockstore>,
3103        tipset: &Tipset,
3104    ) -> anyhow::Result<HashMap<DealID, AllocationID>> {
3105        let state_tree = StateTree::new_from_tipset(store.clone(), tipset)?;
3106        let state: market::State = state_tree.get_actor_state()?;
3107        state.get_allocations_for_pending_deals(store)
3108    }
3109}
3110
3111pub enum StateGetAllocationForPendingDeal {}
3112
3113impl RpcMethod<2> for StateGetAllocationForPendingDeal {
3114    const NAME: &'static str = "Filecoin.StateGetAllocationForPendingDeal";
3115    const PARAM_NAMES: [&'static str; 2] = ["dealId", "tipsetKey"];
3116    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
3117    const PERMISSION: Permission = Permission::Read;
3118    const DESCRIPTION: Option<&'static str> = Some(
3119        "Returns the allocation for the specified pending deal. Returns null if no pending allocation is found.",
3120    );
3121
3122    type Params = (DealID, ApiTipsetKey);
3123    type Ok = Option<Allocation>;
3124
3125    async fn handle(
3126        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
3127        (deal_id, tsk): Self::Params,
3128        ext: &http::Extensions,
3129    ) -> Result<Self::Ok, ServerError> {
3130        let allocation_id =
3131            StateGetAllocationIdForPendingDeal::handle(ctx.clone(), (deal_id, tsk.clone()), ext)
3132                .await?;
3133        if allocation_id == fil_actor_market_state::v14::NO_ALLOCATION_ID {
3134            return Ok(None);
3135        }
3136        let deal = StateMarketStorageDeal::handle(ctx.clone(), (deal_id, tsk.clone()), ext).await?;
3137        StateGetAllocation::handle(ctx.clone(), (deal.proposal.client, allocation_id, tsk), ext)
3138            .await
3139    }
3140}
3141
3142pub enum StateGetNetworkParams {}
3143
3144impl RpcMethod<0> for StateGetNetworkParams {
3145    const NAME: &'static str = "Filecoin.StateGetNetworkParams";
3146    const PARAM_NAMES: [&'static str; 0] = [];
3147    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
3148    const PERMISSION: Permission = Permission::Read;
3149    const DESCRIPTION: Option<&'static str> = Some("Returns current network parameters.");
3150
3151    type Params = ();
3152    type Ok = NetworkParams;
3153
3154    async fn handle(
3155        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
3156        (): Self::Params,
3157        _: &http::Extensions,
3158    ) -> Result<Self::Ok, ServerError> {
3159        let config = ctx.chain_config().as_ref();
3160        let heaviest_tipset = ctx.chain_store().heaviest_tipset();
3161        let network_name = ctx
3162            .state_manager
3163            .get_network_state_name(*heaviest_tipset.parent_state())?
3164            .into();
3165        let policy = &config.policy;
3166
3167        let params = NetworkParams {
3168            network_name,
3169            block_delay_secs: u64::from(config.block_delay_secs),
3170            consensus_miner_min_power: policy.minimum_consensus_power.clone(),
3171            pre_commit_challenge_delay: policy.pre_commit_challenge_delay,
3172            fork_upgrade_params: ForkUpgradeParams::try_from(config)
3173                .context("Failed to get fork upgrade params")?,
3174            eip155_chain_id: config.eth_chain_id,
3175        };
3176
3177        Ok(params)
3178    }
3179}
3180
3181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)]
3182#[serde(rename_all = "PascalCase")]
3183pub struct NetworkParams {
3184    network_name: String,
3185    block_delay_secs: u64,
3186    #[schemars(with = "crate::lotus_json::LotusJson<BigInt>")]
3187    #[serde(with = "crate::lotus_json")]
3188    consensus_miner_min_power: BigInt,
3189    pre_commit_challenge_delay: ChainEpoch,
3190    fork_upgrade_params: ForkUpgradeParams,
3191    #[serde(rename = "Eip155ChainID")]
3192    eip155_chain_id: EthChainId,
3193}
3194
3195lotus_json_with_self!(NetworkParams);
3196
3197#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)]
3198#[serde(rename_all = "PascalCase")]
3199pub struct ForkUpgradeParams {
3200    upgrade_smoke_height: ChainEpoch,
3201    upgrade_breeze_height: ChainEpoch,
3202    upgrade_ignition_height: ChainEpoch,
3203    upgrade_liftoff_height: ChainEpoch,
3204    upgrade_assembly_height: ChainEpoch,
3205    upgrade_refuel_height: ChainEpoch,
3206    upgrade_tape_height: ChainEpoch,
3207    upgrade_kumquat_height: ChainEpoch,
3208    breeze_gas_tamping_duration: ChainEpoch,
3209    upgrade_calico_height: ChainEpoch,
3210    upgrade_persian_height: ChainEpoch,
3211    upgrade_orange_height: ChainEpoch,
3212    upgrade_claus_height: ChainEpoch,
3213    upgrade_trust_height: ChainEpoch,
3214    upgrade_norwegian_height: ChainEpoch,
3215    upgrade_turbo_height: ChainEpoch,
3216    upgrade_hyperdrive_height: ChainEpoch,
3217    upgrade_chocolate_height: ChainEpoch,
3218    upgrade_oh_snap_height: ChainEpoch,
3219    upgrade_skyr_height: ChainEpoch,
3220    upgrade_shark_height: ChainEpoch,
3221    upgrade_hygge_height: ChainEpoch,
3222    upgrade_lightning_height: ChainEpoch,
3223    upgrade_thunder_height: ChainEpoch,
3224    upgrade_watermelon_height: ChainEpoch,
3225    upgrade_dragon_height: ChainEpoch,
3226    upgrade_phoenix_height: ChainEpoch,
3227    upgrade_waffle_height: ChainEpoch,
3228    upgrade_tuktuk_height: ChainEpoch,
3229    upgrade_teep_height: ChainEpoch,
3230    upgrade_tock_height: ChainEpoch,
3231    upgrade_golden_week_height: ChainEpoch,
3232    //upgrade_xxx_height: ChainEpoch,
3233}
3234
3235impl TryFrom<&ChainConfig> for ForkUpgradeParams {
3236    type Error = anyhow::Error;
3237    fn try_from(config: &ChainConfig) -> anyhow::Result<Self> {
3238        let height_infos = &config.height_infos;
3239        let get_height = |height| -> anyhow::Result<ChainEpoch> {
3240            let height = height_infos
3241                .get(&height)
3242                .context(format!("Height info for {height} not found"))?
3243                .epoch;
3244            Ok(height)
3245        };
3246
3247        use crate::networks::Height::*;
3248        Ok(ForkUpgradeParams {
3249            upgrade_smoke_height: get_height(Smoke)?,
3250            upgrade_breeze_height: get_height(Breeze)?,
3251            upgrade_ignition_height: get_height(Ignition)?,
3252            upgrade_liftoff_height: get_height(Liftoff)?,
3253            upgrade_assembly_height: get_height(Assembly)?,
3254            upgrade_refuel_height: get_height(Refuel)?,
3255            upgrade_tape_height: get_height(Tape)?,
3256            upgrade_kumquat_height: get_height(Kumquat)?,
3257            breeze_gas_tamping_duration: config.breeze_gas_tamping_duration,
3258            upgrade_calico_height: get_height(Calico)?,
3259            upgrade_persian_height: get_height(Persian)?,
3260            upgrade_orange_height: get_height(Orange)?,
3261            upgrade_claus_height: get_height(Claus)?,
3262            upgrade_trust_height: get_height(Trust)?,
3263            upgrade_norwegian_height: get_height(Norwegian)?,
3264            upgrade_turbo_height: get_height(Turbo)?,
3265            upgrade_hyperdrive_height: get_height(Hyperdrive)?,
3266            upgrade_chocolate_height: get_height(Chocolate)?,
3267            upgrade_oh_snap_height: get_height(OhSnap)?,
3268            upgrade_skyr_height: get_height(Skyr)?,
3269            upgrade_shark_height: get_height(Shark)?,
3270            upgrade_hygge_height: get_height(Hygge)?,
3271            upgrade_lightning_height: get_height(Lightning)?,
3272            upgrade_thunder_height: get_height(Thunder)?,
3273            upgrade_watermelon_height: get_height(Watermelon)?,
3274            upgrade_dragon_height: get_height(Dragon)?,
3275            upgrade_phoenix_height: get_height(Phoenix)?,
3276            upgrade_waffle_height: get_height(Waffle)?,
3277            upgrade_tuktuk_height: get_height(TukTuk)?,
3278            upgrade_teep_height: get_height(Teep)?,
3279            upgrade_tock_height: get_height(Tock)?,
3280            upgrade_golden_week_height: get_height(GoldenWeek)?,
3281            //upgrade_firehorse_height: get_height(FireHorse)?,
3282        })
3283    }
3284}
3285
3286pub enum StateMinerInitialPledgeForSector {}
3287impl RpcMethod<4> for StateMinerInitialPledgeForSector {
3288    const NAME: &'static str = "Filecoin.StateMinerInitialPledgeForSector";
3289    const PARAM_NAMES: [&'static str; 4] = [
3290        "sector_duration",
3291        "sector_size",
3292        "verified_size",
3293        "tipset_key",
3294    ];
3295    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
3296    const PERMISSION: Permission = Permission::Read;
3297
3298    type Params = (ChainEpoch, SectorSize, u64, ApiTipsetKey);
3299    type Ok = TokenAmount;
3300
3301    async fn handle(
3302        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
3303        (sector_duration, sector_size, verified_size, ApiTipsetKey(tsk)): Self::Params,
3304        _: &http::Extensions,
3305    ) -> Result<Self::Ok, ServerError> {
3306        if sector_duration <= 0 {
3307            return Err(anyhow::anyhow!("sector duration must be greater than 0").into());
3308        }
3309        if verified_size > sector_size as u64 {
3310            return Err(
3311                anyhow::anyhow!("verified deal size cannot be larger than sector size").into(),
3312            );
3313        }
3314
3315        let ts = ctx.chain_store().load_required_tipset_or_heaviest(&tsk)?;
3316
3317        let power_state: power::State = ctx.state_manager.get_actor_state(&ts)?;
3318        let power_smoothed = power_state.total_power_smoothed();
3319        let pledge_collateral = power_state.total_locked();
3320
3321        let reward_state: reward::State = ctx.state_manager.get_actor_state(&ts)?;
3322
3323        let genesis_info = GenesisInfo::from_chain_config(ctx.chain_config().clone());
3324        let circ_supply = genesis_info.get_vm_circulating_supply_detailed(
3325            ts.epoch(),
3326            &ctx.store_owned(),
3327            ts.parent_state(),
3328        )?;
3329
3330        let deal_weight = BigInt::from(0);
3331        let verified_deal_weight = BigInt::from(verified_size) * sector_duration;
3332        let sector_weight = qa_power_for_weight(
3333            sector_size.into(),
3334            sector_duration,
3335            &deal_weight,
3336            &verified_deal_weight,
3337        );
3338
3339        let (epochs_since_start, duration) = get_pledge_ramp_params(&ctx, ts.epoch(), &ts)?;
3340
3341        let initial_pledge = reward_state.initial_pledge_for_power(
3342            &sector_weight,
3343            pledge_collateral,
3344            power_smoothed,
3345            &circ_supply.fil_circulating,
3346            epochs_since_start,
3347            duration,
3348        )?;
3349
3350        let (value, _) = (initial_pledge * INITIAL_PLEDGE_NUM).div_rem(INITIAL_PLEDGE_DEN);
3351        Ok(value)
3352    }
3353}
3354
3355fn get_pledge_ramp_params(
3356    ctx: &Ctx<impl Blockstore + Send + Sync + 'static>,
3357    height: ChainEpoch,
3358    ts: &Tipset,
3359) -> anyhow::Result<(ChainEpoch, u64)> {
3360    let state_tree = ctx.state_manager.get_state_tree(ts.parent_state())?;
3361
3362    let power_state: power::State = state_tree
3363        .get_actor_state()
3364        .context("loading power actor state")?;
3365
3366    if power_state.ramp_start_epoch() > 0 {
3367        Ok((
3368            height - power_state.ramp_start_epoch(),
3369            power_state.ramp_duration_epochs(),
3370        ))
3371    } else {
3372        Ok((0, 0))
3373    }
3374}
3375
3376#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)]
3377#[serde(rename_all = "PascalCase")]
3378pub struct StateActorCodeCidsOutput {
3379    pub network_version: NetworkVersion,
3380    pub network_version_revision: i64,
3381    pub actor_version: String,
3382    #[serde(with = "crate::lotus_json")]
3383    #[schemars(with = "LotusJson<Cid>")]
3384    pub manifest: Cid,
3385    #[serde(with = "crate::lotus_json")]
3386    #[schemars(with = "LotusJson<Cid>")]
3387    pub bundle: Cid,
3388    #[serde(with = "crate::lotus_json")]
3389    #[schemars(with = "LotusJson<HashMap<String, Cid>>")]
3390    pub actor_cids: HashMap<String, Cid>,
3391}
3392lotus_json_with_self!(StateActorCodeCidsOutput);
3393
3394impl std::fmt::Display for StateActorCodeCidsOutput {
3395    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3396        writeln!(f, "Network Version: {}", self.network_version)?;
3397        writeln!(
3398            f,
3399            "Network Version Revision: {}",
3400            self.network_version_revision
3401        )?;
3402        writeln!(f, "Actor Version: {}", self.actor_version)?;
3403        writeln!(f, "Manifest CID: {}", self.manifest)?;
3404        writeln!(f, "Bundle CID: {}", self.bundle)?;
3405        writeln!(f, "Actor CIDs:")?;
3406        let longest_name = self
3407            .actor_cids
3408            .keys()
3409            .map(|name| name.len())
3410            .max()
3411            .unwrap_or(0);
3412        for (name, cid) in &self.actor_cids {
3413            writeln!(f, "  {:width$} : {}", name, cid, width = longest_name)?;
3414        }
3415        Ok(())
3416    }
3417}
3418
3419pub enum StateActorInfo {}
3420
3421impl RpcMethod<0> for StateActorInfo {
3422    const NAME: &'static str = "Forest.StateActorInfo";
3423    const PARAM_NAMES: [&'static str; 0] = [];
3424    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
3425    const PERMISSION: Permission = Permission::Read;
3426    const DESCRIPTION: Option<&'static str> =
3427        Some("Returns the builtin actor information for the current network.");
3428
3429    type Params = ();
3430    type Ok = StateActorCodeCidsOutput;
3431
3432    async fn handle(
3433        ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
3434        (): Self::Params,
3435        _: &http::Extensions,
3436    ) -> Result<Self::Ok, ServerError> {
3437        let ts = ctx.chain_store().load_required_tipset_or_heaviest(None)?;
3438        let state_tree = StateTree::new_from_tipset(ctx.store_owned(), &ts)?;
3439        let bundle = state_tree.get_actor_bundle_metadata()?;
3440        let system_state: system::State = state_tree.get_actor_state()?;
3441        let actors = system_state.builtin_actors_cid();
3442
3443        let current_manifest = BuiltinActorManifest::load_v1_actor_list(ctx.store(), actors)?;
3444
3445        // Sanity check: the command would normally be used only for diagnostics, so we want to be
3446        // sure the data is consistent.
3447        if current_manifest != bundle.manifest {
3448            return Err(anyhow::anyhow!("Actor bundle manifest does not match the manifest in the state tree. This indicates that the node is misconfigured or is running an unsupported network.")
3449            .into());
3450        }
3451
3452        let network_version = ctx.chain_config().network_version(ts.epoch() - 1);
3453        let network_version_revision = ctx.chain_config().network_version_revision(ts.epoch() - 1);
3454        let result = StateActorCodeCidsOutput {
3455            network_version,
3456            network_version_revision,
3457            actor_version: bundle.version.to_owned(),
3458            manifest: current_manifest.actor_list_cid,
3459            bundle: bundle.bundle_cid,
3460            actor_cids: current_manifest
3461                .builtin_actors()
3462                .map(|(a, c)| (a.name().to_string(), c))
3463                .collect(),
3464        };
3465
3466        Ok(result)
3467    }
3468}