Skip to content

Commit

Permalink
refactor: migrate beacon_api to use trait RpcMethod (#4155)
Browse files Browse the repository at this point in the history
  • Loading branch information
aatifsyed authored Apr 8, 2024
1 parent 070202b commit c46d82a
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 99 deletions.
4 changes: 3 additions & 1 deletion src/lotus_json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,9 @@ where
}

/// A domain struct that is (de) serialized through its lotus JSON representation.
#[derive(Debug, Serialize, Deserialize, From, Default, Clone)]
#[derive(
Debug, Serialize, Deserialize, From, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[serde(bound = "T: HasLotusJson + Clone")]
pub struct LotusJson<T>(#[serde(with = "self")] pub T);

Expand Down
2 changes: 1 addition & 1 deletion src/rpc/auth_layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ static ACCESS_MAP: Lazy<HashMap<&str, Access>> = Lazy::new(|| {
access.insert(auth_api::AuthVerify::NAME, Access::Read);

// Beacon API
access.insert(beacon_api::BEACON_GET_ENTRY, Access::Read);
access.insert(beacon_api::BeaconGetEntry::NAME, Access::Read);

// Chain API
access.insert(chain_api::CHAIN_GET_MESSAGE, Access::Read);
Expand Down
41 changes: 26 additions & 15 deletions src/rpc/beacon_api.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use crate::rpc::error::JsonRpcError;
use crate::rpc::Ctx;
use crate::rpc::{
reflect::SelfDescribingRpcModule, ApiVersion, Ctx, JsonRpcError, RPCState, RpcMethod,
RpcMethodExt as _,
};
use crate::{beacon::BeaconEntry, lotus_json::LotusJson, shim::clock::ChainEpoch};
use anyhow::Result;
use fvm_ipld_blockstore::Blockstore;
use jsonrpsee::types::Params;

pub const BEACON_GET_ENTRY: &str = "Filecoin.BeaconGetEntry";
pub fn register_all(
module: &mut SelfDescribingRpcModule<RPCState<impl Blockstore + Send + Sync + 'static>>,
) {
BeaconGetEntry::register(module);
}

/// `BeaconGetEntry` returns the beacon entry for the given Filecoin epoch. If
/// the entry has not yet been produced, the call will block until the entry
/// becomes available
pub async fn beacon_get_entry<DB: Blockstore>(
params: Params<'_>,
data: Ctx<DB>,
) -> Result<LotusJson<BeaconEntry>, JsonRpcError> {
let (first,): (ChainEpoch,) = params.parse()?;

let (_, beacon) = data.beacon.beacon_for_epoch(first)?;
let rr =
beacon.max_beacon_round_for_epoch(data.state_manager.get_network_version(first), first);
let e = beacon.entry(rr).await?;
Ok(e.into())
pub enum BeaconGetEntry {}
impl RpcMethod<1> for BeaconGetEntry {
const NAME: &'static str = "Filecoin.BeaconGetEntry";
const PARAM_NAMES: [&'static str; 1] = ["first"];
const API_VERSION: ApiVersion = ApiVersion::V0;
type Params = (ChainEpoch,);
type Ok = LotusJson<BeaconEntry>;
async fn handle(
ctx: Ctx<impl Blockstore>,
(first,): Self::Params,
) -> Result<Self::Ok, JsonRpcError> {
let (_, beacon) = ctx.beacon.beacon_for_epoch(first)?;
let rr =
beacon.max_beacon_round_for_epoch(ctx.state_manager.get_network_version(first), first);
let e = beacon.entry(rr).await?;
Ok(e.into())
}
}
4 changes: 1 addition & 3 deletions src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ where
ChainGetPath::register(&mut module);
mpool_api::register_all(&mut module);
auth_api::register_all(&mut module);
beacon_api::register_all(&mut module);
module.finish()
}

Expand All @@ -181,7 +182,6 @@ fn register_methods<DB>(
where
DB: Blockstore + Send + Sync + 'static,
{
use beacon_api::*;
use chain_api::*;
use common_api::*;
use eth_api::*;
Expand All @@ -191,8 +191,6 @@ where
use sync_api::*;
use wallet_api::*;

// Beacon API
module.register_async_method(BEACON_GET_ENTRY, beacon_get_entry::<DB>)?;
// Chain API
module.register_async_method(CHAIN_GET_MESSAGE, chain_get_message::<DB>)?;
module.register_async_method(CHAIN_EXPORT, chain_export::<DB>)?;
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/reflect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ pub trait RpcMethodExt<const ARITY: usize>: RpcMethod<ARITY> {
// TODO(aatifsyed): https://github.com/ChainSafe/forest/issues/4032
// Client::call has an inappropriate HasLotusJson
// bound, work around it for now.
let json = client.call(Self::request(params)?.lower()).await?;
let json = client.call(Self::request(params)?.map_ty()).await?;
Ok(serde_json::from_value(json)?)
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/rpc/snapshots/forest_filecoin__rpc__tests__openrpc.snap
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,27 @@ methods:
items:
type: string
required: true
- name: Filecoin.BeaconGetEntry
params:
- name: first
schema:
type: integer
format: int64
required: true
paramStructure: by-position
result:
name: "Filecoin.BeaconGetEntry::Result"
schema:
type: object
required:
- Data
- Round
properties:
Data:
$ref: "#/components/schemas/VecU8LotusJson2"
Round:
$ref: "#/components/schemas/uint64"
required: true
components:
schemas:
CidLotusJsonGeneric_for_64:
Expand Down
12 changes: 0 additions & 12 deletions src/rpc_client/beacon_ops.rs

This file was deleted.

9 changes: 6 additions & 3 deletions src/rpc_client/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

pub mod beacon_ops;
pub mod chain_ops;
pub mod common_ops;
pub mod eth_ops;
Expand Down Expand Up @@ -170,8 +169,8 @@ impl<T> RpcRequest<T> {
self
}

// Discard type information about the response.
pub fn lower(self) -> RpcRequest {
/// Map type information about the response.
pub fn map_ty<U>(self) -> RpcRequest<U> {
RpcRequest {
method_name: self.method_name,
params: self.params,
Expand All @@ -180,6 +179,10 @@ impl<T> RpcRequest<T> {
timeout: self.timeout,
}
}
/// Discard type information about the response.
pub fn lower(self) -> RpcRequest {
self.map_ty()
}
}

impl<T> ToRpcParams for RpcRequest<T> {
Expand Down
113 changes: 50 additions & 63 deletions src/tool/subcommands/api_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::lotus_json::{HasLotusJson, LotusJson};
use crate::message::Message as _;
use crate::message_pool::{MessagePool, MpoolRpcProvider};
use crate::networks::{parse_bootstrap_peers, ChainConfig, NetworkChain};
use crate::rpc::beacon_api::BeaconGetEntry;
use crate::rpc::eth_api::Address as EthAddress;
use crate::rpc::eth_api::*;
use crate::rpc::types::{ApiTipsetKey, MessageFilter, MessageLookup};
Expand Down Expand Up @@ -212,36 +213,22 @@ struct RpcTest {
ignore: Option<&'static str>,
}

/// Duplication between `<method>` and `<method>_raw` is a temporary measure, and
/// should be removed when <https://github.com/ChainSafe/forest/issues/4032> is
/// completed.
impl RpcTest {
// Check that an endpoint exist and that both the Lotus and Forest JSON
// response follows the same schema.
fn basic<T>(request: RpcRequest<T>) -> RpcTest
/// Check that an endpoint exists and that both the Lotus and Forest JSON
/// response follows the same schema.
fn basic<T>(request: RpcRequest<T>) -> Self
where
T: HasLotusJson,
{
RpcTest {
request: request.lower(),
check_syntax: Arc::new(
|value| match serde_json::from_value::<T::LotusJson>(value) {
Ok(_) => true,
Err(e) => {
debug!("{e}");
false
}
},
),
check_semantics: Arc::new(|_, _| true),
ignore: None,
}
Self::basic_raw(request.map_ty::<T::LotusJson>())
}
/// This is [`Self::basic`] without the [`HasLotusJson`] bound, for methods
/// where the return type does not need converting via a third type.
///
/// This is a temporary measure, and should be removed when
/// <https://github.com/ChainSafe/forest/issues/4032> is completed.
/// See [Self::basic], and note on this `impl` block.
fn basic_raw<T: DeserializeOwned>(request: RpcRequest<T>) -> Self {
Self {
request: request.lower(),
request: request.map_ty(),
check_syntax: Arc::new(|it| match serde_json::from_value::<T>(it) {
Ok(_) => true,
Err(e) => {
Expand All @@ -253,37 +240,36 @@ impl RpcTest {
ignore: None,
}
}

// Check that an endpoint exist, has the same JSON schema, and do custom
// validation over both responses.
fn validate<T>(
/// Check that an endpoint exists, has the same JSON schema, and do custom
/// validation over both responses.
fn validate<T: HasLotusJson>(
request: RpcRequest<T>,
validate: impl Fn(T, T) -> bool + Send + Sync + 'static,
) -> RpcTest
where
T: HasLotusJson,
T::LotusJson: DeserializeOwned,
{
RpcTest {
request: request.lower(),
check_syntax: Arc::new(
|value| match serde_json::from_value::<T::LotusJson>(value) {
Ok(_) => true,
Err(e) => {
debug!("{e}");
false
}
},
),
) -> Self {
Self::validate_raw(request.map_ty::<T::LotusJson>(), move |l, r| {
validate(T::from_lotus_json(l), T::from_lotus_json(r))
})
}
/// See [Self::validate], and note on this `impl` block.
fn validate_raw<T: DeserializeOwned>(
request: RpcRequest<T>,
validate: impl Fn(T, T) -> bool + Send + Sync + 'static,
) -> Self {
Self {
request: request.map_ty(),
check_syntax: Arc::new(|value| match serde_json::from_value::<T>(value) {
Ok(_) => true,
Err(e) => {
debug!("{e}");
false
}
}),
check_semantics: Arc::new(move |forest_json, lotus_json| {
match (
serde_json::from_value::<T::LotusJson>(forest_json),
serde_json::from_value::<T::LotusJson>(lotus_json),
serde_json::from_value::<T>(forest_json),
serde_json::from_value::<T>(lotus_json),
) {
(Ok(forest), Ok(lotus)) => validate(
HasLotusJson::from_lotus_json(forest),
HasLotusJson::from_lotus_json(lotus),
),
(Ok(forest), Ok(lotus)) => validate(forest, lotus),
(forest, lotus) => {
if let Err(e) = forest {
debug!("[forest] invalid json: {e}");
Expand All @@ -298,27 +284,26 @@ impl RpcTest {
ignore: None,
}
}

fn ignore(mut self, msg: &'static str) -> Self {
self.ignore = Some(msg);
self
/// Check that an endpoint exists and that Forest returns exactly the same
/// JSON as Lotus.
fn identity<T: PartialEq + HasLotusJson>(request: RpcRequest<T>) -> RpcTest {
Self::validate(request, |forest, lotus| forest == lotus)
}

// Check that an endpoint exist and that Forest returns exactly the same
// JSON as Lotus.
fn identity<T: PartialEq>(request: RpcRequest<T>) -> RpcTest
where
T: HasLotusJson,
T::LotusJson: DeserializeOwned,
{
RpcTest::validate(request, |forest, lotus| forest == lotus)
/// See [Self::identity], and note on this `impl` block.
fn identity_raw<T: PartialEq + DeserializeOwned>(request: RpcRequest<T>) -> Self {
Self::validate_raw(request, |l, r| l == r)
}

fn with_timeout(mut self, timeout: Duration) -> Self {
self.request.set_timeout(timeout);
self
}

fn ignore(mut self, msg: &'static str) -> Self {
self.ignore = Some(msg);
self
}

async fn run(
&self,
forest_api: &ApiInfo,
Expand Down Expand Up @@ -382,7 +367,9 @@ fn auth_tests() -> Vec<RpcTest> {
}

fn beacon_tests() -> Vec<RpcTest> {
vec![RpcTest::identity(ApiInfo::beacon_get_entry_req(10101))]
vec![RpcTest::identity_raw(
BeaconGetEntry::request((10101,)).unwrap(),
)]
}

fn chain_tests() -> Vec<RpcTest> {
Expand Down

0 comments on commit c46d82a

Please sign in to comment.