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