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