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