Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@

- [#6231](https://github.com/ChainSafe/forest/pull/6231) Implemented `Filecoin.ChainGetTipSet` for API v2.

- [#6312](https://github.com/ChainSafe/forest/pull/6312) Implemented `Filecoin.StateGetActor` for API v2.

- [#6312](https://github.com/ChainSafe/forest/pull/6312) Implemented `Filecoin.StateGetID` for API v2.

### Changed

### Removed
Expand Down
54 changes: 35 additions & 19 deletions src/rpc/methods/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::lotus_json::{assert_all_snapshots, assert_unchanged_via_json};
use crate::message::{ChainMessage, SignedMessage};
use crate::rpc::eth::{EthLog, eth_logs_with_filter, types::ApiHeaders, types::EthFilterSpec};
use crate::rpc::f3::F3ExportLatestSnapshot;
use crate::rpc::types::{ApiExportResult, ApiExportStatus, ApiTipsetKey, Event};
use crate::rpc::types::*;
use crate::rpc::{ApiPaths, Ctx, EthEventHandler, Permission, RpcMethod, ServerError};
use crate::shim::clock::ChainEpoch;
use crate::shim::error::ExitCode;
Expand Down Expand Up @@ -1101,22 +1101,11 @@ impl ChainGetTipSetV2 {
Ok(None)
}
}
}

impl RpcMethod<1> for ChainGetTipSetV2 {
const NAME: &'static str = "Filecoin.ChainGetTipSet";
const PARAM_NAMES: [&'static str; 1] = ["tipsetSelector"];
const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::{ V2 });
const PERMISSION: Permission = Permission::Read;
const DESCRIPTION: Option<&'static str> = Some("Returns the tipset with the specified CID.");

type Params = (TipsetSelector,);
type Ok = Option<Tipset>;

async fn handle(
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(selector,): Self::Params,
) -> Result<Self::Ok, ServerError> {
pub async fn get_tipset(
ctx: &Ctx<impl Blockstore + Send + Sync + 'static>,
selector: &TipsetSelector,
) -> anyhow::Result<Option<Tipset>> {
selector.validate()?;
// Get tipset by key.
if let ApiTipsetKey(Some(tsk)) = &selector.key {
Expand All @@ -1125,7 +1114,7 @@ impl RpcMethod<1> for ChainGetTipSetV2 {
}
// Get tipset by height.
if let Some(height) = &selector.height {
let anchor = Self::get_tipset_by_anchor(&ctx, &height.anchor).await?;
let anchor = Self::get_tipset_by_anchor(ctx, &height.anchor).await?;
let ts = ctx.chain_index().tipset_by_height(
height.at,
anchor.unwrap_or_else(|| ctx.chain_store().heaviest_tipset()),
Expand All @@ -1135,10 +1124,37 @@ impl RpcMethod<1> for ChainGetTipSetV2 {
}
// Get tipset by tag, either latest or finalized.
if let Some(tag) = &selector.tag {
let ts = Self::get_tipset_by_tag(&ctx, *tag).await?;
let ts = Self::get_tipset_by_tag(ctx, *tag).await?;
return Ok(ts);
}
Err(anyhow::anyhow!("no tipset found for selector").into())
anyhow::bail!("no tipset found for selector")
}

pub async fn get_required_tipset(
ctx: &Ctx<impl Blockstore + Send + Sync + 'static>,
selector: &TipsetSelector,
) -> anyhow::Result<Tipset> {
Self::get_tipset(ctx, selector)
.await?
.context("failed to select a tipset")
}
}

impl RpcMethod<1> for ChainGetTipSetV2 {
const NAME: &'static str = "Filecoin.ChainGetTipSet";
const PARAM_NAMES: [&'static str; 1] = ["tipsetSelector"];
const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::{ V2 });
const PERMISSION: Permission = Permission::Read;
const DESCRIPTION: Option<&'static str> = Some("Returns the tipset with the specified CID.");

type Params = (TipsetSelector,);
type Ok = Option<Tipset>;

async fn handle(
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(selector,): Self::Params,
) -> Result<Self::Ok, ServerError> {
Ok(Self::get_tipset(&ctx, &selector).await?)
}
}

Expand Down
124 changes: 0 additions & 124 deletions src/rpc/methods/chain/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,134 +3,10 @@

use super::*;

#[cfg(test)]
mod tests;

#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, Eq, PartialEq, Default)]
#[serde(rename_all = "PascalCase")]
pub struct ObjStat {
pub size: usize,
pub links: usize,
}
lotus_json_with_self!(ObjStat);

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
pub struct TipsetSelector {
#[serde(
with = "crate::lotus_json",
skip_serializing_if = "ApiTipsetKey::is_none",
default
)]
#[schemars(with = "LotusJson<TipsetKey>")]
pub key: ApiTipsetKey,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub height: Option<TipsetHeight>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub tag: Option<TipsetTag>,
}
lotus_json_with_self!(TipsetSelector);

impl TipsetSelector {
/// Validate ensures that the [`TipsetSelector`] is valid. It checks that only one of
/// the selection criteria is specified.
pub fn validate(&self) -> anyhow::Result<()> {
match (&self.key.0, &self.tag, &self.height) {
(Some(_), None, None) | (None, Some(_), None) => {}
(None, None, Some(height)) => {
height.validate()?;
}
_ => {
let criteria = [
self.key.0.is_some(),
self.tag.is_some(),
self.height.is_some(),
]
.into_iter()
.filter(|&b| b)
.count();
anyhow::bail!(
"exactly one tipset selection criteria must be specified, found: {criteria}"
)
}
}
Ok(())
}
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
pub struct TipsetHeight {
pub at: ChainEpoch,
pub previous: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub anchor: Option<TipsetAnchor>,
}
lotus_json_with_self!(TipsetHeight);

impl TipsetHeight {
/// Ensures that the [`TipsetHeight`] is valid. It checks that the height is
/// not negative and the anchor is valid.
pub fn validate(&self) -> anyhow::Result<()> {
anyhow::ensure!(
self.at >= 0,
"invalid tipset height: epoch cannot be less than zero"
);
TipsetAnchor::validate(&self.anchor)?;
Ok(())
}

pub fn resolve_null_tipset_policy(&self) -> ResolveNullTipset {
if self.previous {
ResolveNullTipset::TakeOlder
} else {
ResolveNullTipset::TakeNewer
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
pub struct TipsetAnchor {
#[serde(with = "crate::lotus_json")]
#[schemars(with = "LotusJson<TipsetKey>")]
pub key: ApiTipsetKey,
#[serde(skip_serializing_if = "Option::is_none")]
pub tag: Option<TipsetTag>,
}
lotus_json_with_self!(TipsetAnchor);

impl TipsetAnchor {
/// Validate ensures that the [`TipsetAnchor`] is valid. It checks that at most one
/// of [`TipsetKey`] or [`TipsetTag`] is specified. Otherwise, it returns an error.
///
/// Note that a [`None`] anchor is valid, and is considered to be
/// equivalent to the default anchor, which is the tipset tagged as [`TipsetTag::Finalized`].
pub fn validate(anchor: &Option<Self>) -> anyhow::Result<()> {
if let Some(anchor) = anchor {
anyhow::ensure!(
anchor.key.0.is_none() || anchor.tag.is_none(),
"invalid tipset anchor: at most one of key or tag must be specified"
);
}
// None anchor is valid, and considered to be an equivalent to whatever the API decides the default to be.
Ok(())
}
}

#[derive(
Debug,
Clone,
Copy,
strum::Display,
strum::EnumString,
Serialize,
Deserialize,
PartialEq,
Eq,
JsonSchema,
)]
#[strum(serialize_all = "lowercase")]
#[serde(rename_all = "lowercase")]
pub enum TipsetTag {
Latest,
Finalized,
Safe,
}
45 changes: 45 additions & 0 deletions src/rpc/methods/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod types;
use futures::stream::FuturesOrdered;
pub use types::*;

use super::chain::ChainGetTipSetV2;
use crate::blocks::{Tipset, TipsetKey};
Comment thread
coderabbitai[bot] marked this conversation as resolved.
use crate::chain::index::ResolveNullTipset;
use crate::cid_collections::CidHashSet;
Expand Down Expand Up @@ -301,6 +302,50 @@ impl RpcMethod<2> for StateGetActor {
}
}

pub enum StateGetActorV2 {}

impl RpcMethod<2> for StateGetActorV2 {
const NAME: &'static str = "Filecoin.StateGetActor";
const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetSelector"];
const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::{ V2 });
const PERMISSION: Permission = Permission::Read;
const DESCRIPTION: Option<&'static str> =
Some("Returns the nonce and balance for the specified actor.");

type Params = (Address, TipsetSelector);
type Ok = Option<ActorState>;

async fn handle(
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(address, selector): Self::Params,
) -> Result<Self::Ok, ServerError> {
let ts = ChainGetTipSetV2::get_required_tipset(&ctx, &selector).await?;
Ok(ctx.state_manager.get_actor(&address, *ts.parent_state())?)
}
}

pub enum StateGetID {}

impl RpcMethod<2> for StateGetID {
const NAME: &'static str = "Filecoin.StateGetID";
const PARAM_NAMES: [&'static str; 2] = ["address", "tipsetSelector"];
const API_PATHS: BitFlags<ApiPaths> = make_bitflags!(ApiPaths::{ V2 });
const PERMISSION: Permission = Permission::Read;
const DESCRIPTION: Option<&'static str> =
Some("Retrieves the ID address for the specified address at the selected tipset.");

type Params = (Address, TipsetSelector);
type Ok = Address;

async fn handle(
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(address, selector): Self::Params,
) -> Result<Self::Ok, ServerError> {
let ts = ChainGetTipSetV2::get_required_tipset(&ctx, &selector).await?;
Ok(ctx.state_manager.lookup_required_id(&address, &ts)?)
}
}

pub enum StateLookupRobustAddress {}

macro_rules! get_robust_address {
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ macro_rules! for_each_rpc_method {
$callback!($crate::rpc::state::StateDealProviderCollateralBounds);
$callback!($crate::rpc::state::StateFetchRoot);
$callback!($crate::rpc::state::StateGetActor);
$callback!($crate::rpc::state::StateGetActorV2);
$callback!($crate::rpc::state::StateGetID);
$callback!($crate::rpc::state::StateGetAllAllocations);
$callback!($crate::rpc::state::StateGetAllClaims);
$callback!($crate::rpc::state::StateGetAllocation);
Expand Down
3 changes: 3 additions & 0 deletions src/rpc/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
//!
//! If a type here is used by only one API, it should be relocated.

mod tipset_selector;
pub use tipset_selector::*;

mod address_impl;
mod deal_impl;
mod sector_impl;
Expand Down
Loading
Loading