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