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