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