Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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("Retrieves the ID address for the specified address at the selected tipset.");

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