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