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