diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b7304df76b..2c68a3986f4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ ### Changed +- [#6631](https://github.com/ChainSafe/forest/issues/6631): Backported F3 finality resolution to ETH v1 RPC methods. + ### Removed - [#6681](https://github.com/ChainSafe/forest/pull/6681): Removed `tracing-chrome` feature and all related code as it was deemed unused. If you didn't set `CHROME_TRACE_FILE` manually before, you shouldn't be affected by this change. If you were using this feature, reach out. diff --git a/docs/docs/users/reference/env_variables.md b/docs/docs/users/reference/env_variables.md index f0c77ccc6979..d5b75cc66a5b 100644 --- a/docs/docs/users/reference/env_variables.md +++ b/docs/docs/users/reference/env_variables.md @@ -58,6 +58,7 @@ process. | `FOREST_STRICT_JSON` | 1 or true | false | 1 | Enable strict JSON validation to detect duplicate keys in RPC requests | | `FOREST_AUTO_DOWNLOAD_SNAPSHOT_PATH` | URL or file path | empty | `/var/tmp/forest_snapshot_calibnet.forest.car.zst` | Override snapshot path for `--auto-download-snapshot` | | `FOREST_DOWNLOAD_CONNECTIONS` | positive integer | 5 | 10 | Number of parallel HTTP connections for downloading snapshots | +| `FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION` | 1 or true | empty | 1 | Whether or not to disable F3 finality resolution in Eth `v1` RPC methods | ### `FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT` diff --git a/src/eth/mod.rs b/src/eth/mod.rs index a2402a1ad85d..0dba2f3eaf77 100644 --- a/src/eth/mod.rs +++ b/src/eth/mod.rs @@ -32,10 +32,3 @@ pub const LEGACY_V_VALUE_27: u64 = 27; pub const LEGACY_V_VALUE_28: u64 = 28; pub const ETH_LEGACY_HOMESTEAD_TX_CHAIN_ID: u64 = 0; - -/// From Lotus: -/// > Research into Filecoin chain behavior suggests that probabilistic finality -/// > generally approaches the intended stability guarantee at, or near, 30 epochs. -/// > Although a strictly "finalized" safe recommendation remains 900 epochs. -/// > See -pub const SAFE_EPOCH_DELAY: i64 = 30; diff --git a/src/rpc/client.rs b/src/rpc/client.rs index 1cba246afbb6..880337ac7568 100644 --- a/src/rpc/client.rs +++ b/src/rpc/client.rs @@ -92,9 +92,7 @@ impl Client { &self, req: Request, ) -> Result { - let max_api_path = req - .api_path() - .map_err(|e| ClientError::Custom(e.to_string()))?; + let api_path = req.api_path; let Request { method_name, params, @@ -102,7 +100,7 @@ impl Client { .. } = req; let method_name = method_name.as_ref(); - let client = self.get_or_init_client(max_api_path).await?; + let client = self.get_or_init_client(api_path).await?; let span = tracing::debug_span!("request", method = %method_name, url = %client.url); let work = async { // jsonrpsee's clients have a global `timeout`, but not a per-request timeout, which diff --git a/src/rpc/methods/chain.rs b/src/rpc/methods/chain.rs index 8ff8a8c87904..a3e5df878b94 100644 --- a/src/rpc/methods/chain.rs +++ b/src/rpc/methods/chain.rs @@ -68,7 +68,7 @@ const HEAD_CHANNEL_CAPACITY: usize = 10; /// Discussion on this current value and a tracking item to document the /// probabilistic impact of various values is in /// https://github.com/filecoin-project/go-f3/issues/944 -const SAFE_HEIGHT_DISTANCE: ChainEpoch = 200; +pub const SAFE_HEIGHT_DISTANCE: ChainEpoch = 200; static CHAIN_EXPORT_LOCK: LazyLock>> = LazyLock::new(|| Mutex::new(None)); @@ -1029,6 +1029,7 @@ impl RpcMethod<1> for ChainGetBlock { } pub enum ChainGetTipSet {} + impl RpcMethod<1> for ChainGetTipSet { const NAME: &'static str = "Filecoin.ChainGetTipSet"; const PARAM_NAMES: [&'static str; 1] = ["tipsetKey"]; @@ -1063,13 +1064,13 @@ impl ChainGetTipSetV2 { pub async fn get_tipset_by_anchor( ctx: &Ctx, anchor: &Option, - ) -> anyhow::Result> { + ) -> anyhow::Result { if let Some(anchor) = anchor { match (&anchor.key.0, &anchor.tag) { // Anchor is zero-valued. Fall back to heaviest tipset. - (None, None) => Ok(Some(ctx.state_manager.heaviest_tipset())), + (None, None) => Ok(ctx.state_manager.heaviest_tipset()), // Get tipset at the specified key. - (Some(tsk), None) => Ok(Some(ctx.chain_index().load_required_tipset(tsk)?)), + (Some(tsk), None) => Ok(ctx.chain_index().load_required_tipset(tsk)?), (None, Some(tag)) => Self::get_tipset_by_tag(ctx, *tag).await, _ => { anyhow::bail!("invalid anchor") @@ -1084,11 +1085,11 @@ impl ChainGetTipSetV2 { pub async fn get_tipset_by_tag( ctx: &Ctx, tag: TipsetTag, - ) -> anyhow::Result> { + ) -> anyhow::Result { match tag { - TipsetTag::Latest => Ok(Some(ctx.state_manager.heaviest_tipset())), + TipsetTag::Latest => Ok(ctx.state_manager.heaviest_tipset()), TipsetTag::Finalized => Self::get_latest_finalized_tipset(ctx).await, - TipsetTag::Safe => Some(Self::get_latest_safe_tipset(ctx).await).transpose(), + TipsetTag::Safe => Self::get_latest_safe_tipset(ctx).await, } } @@ -1098,9 +1099,7 @@ impl ChainGetTipSetV2 { let finalized = Self::get_latest_finalized_tipset(ctx).await?; let head = ctx.chain_store().heaviest_tipset(); let safe_height = (head.epoch() - SAFE_HEIGHT_DISTANCE).max(0); - if let Some(finalized) = finalized - && finalized.epoch() >= safe_height - { + if finalized.epoch() >= safe_height { Ok(finalized) } else { Ok(ctx.chain_index().tipset_by_height( @@ -1113,7 +1112,7 @@ impl ChainGetTipSetV2 { pub async fn get_latest_finalized_tipset( ctx: &Ctx, - ) -> anyhow::Result> { + ) -> anyhow::Result { let Ok(f3_finalized_cert) = crate::rpc::f3::F3GetLatestCertificate::get().await else { return Self::get_ec_finalized_tipset(ctx); }; @@ -1135,43 +1134,38 @@ impl ChainGetTipSetV2 { f3_finalized_head.key, ) })?; - Ok(Some(ts)) + Ok(ts) } - pub fn get_ec_finalized_tipset(ctx: &Ctx) -> anyhow::Result> { + pub fn get_ec_finalized_tipset(ctx: &Ctx) -> anyhow::Result { let head = ctx.chain_store().heaviest_tipset(); - let ec_finality_epoch = head.epoch() - ctx.chain_config().policy.chain_finality; - if ec_finality_epoch >= 0 { - let ts = ctx.chain_index().tipset_by_height( - ec_finality_epoch, - head, - ResolveNullTipset::TakeOlder, - )?; - Ok(Some(ts)) - } else { - Ok(None) - } + let ec_finality_epoch = (head.epoch() - ctx.chain_config().policy.chain_finality).max(0); + Ok(ctx.chain_index().tipset_by_height( + ec_finality_epoch, + head, + ResolveNullTipset::TakeOlder, + )?) } pub async fn get_tipset( ctx: &Ctx, selector: &TipsetSelector, - ) -> anyhow::Result> { + ) -> anyhow::Result { selector.validate()?; // Get tipset by key. if let ApiTipsetKey(Some(tsk)) = &selector.key { let ts = ctx.chain_index().load_required_tipset(tsk)?; - return Ok(Some(ts)); + return Ok(ts); } // Get tipset by height. if let Some(height) = &selector.height { 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()), + anchor, height.resolve_null_tipset_policy(), )?; - return Ok(Some(ts)); + return Ok(ts); } // Get tipset by tag, either latest or finalized. if let Some(tag) = &selector.tag { @@ -1180,15 +1174,6 @@ impl ChainGetTipSetV2 { } anyhow::bail!("no tipset found for selector") } - - pub async fn get_required_tipset( - ctx: &Ctx, - selector: &TipsetSelector, - ) -> anyhow::Result { - Self::get_tipset(ctx, selector) - .await? - .context("failed to select a tipset") - } } impl RpcMethod<1> for ChainGetTipSetV2 { @@ -1199,7 +1184,7 @@ impl RpcMethod<1> for ChainGetTipSetV2 { const DESCRIPTION: Option<&'static str> = Some("Returns the tipset with the specified CID."); type Params = (TipsetSelector,); - type Ok = Option; + type Ok = Tipset; async fn handle( ctx: Ctx, diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs index fddab489e05a..13a0ae4ec3f5 100644 --- a/src/rpc/methods/eth.rs +++ b/src/rpc/methods/eth.rs @@ -6,9 +6,11 @@ mod eth_tx; pub mod filter; pub mod pubsub; pub(crate) mod pubsub_trait; +mod tipset_resolver; mod trace; pub mod types; mod utils; +pub use tipset_resolver::TipsetResolver; use self::eth_tx::*; use self::filter::hex_str_to_epoch; @@ -20,29 +22,25 @@ use crate::chain_sync::NodeSyncStatus; use crate::cid_collections::CidHashSet; use crate::eth::{ EAMMethod, EVMMethod, EthChainId as EthChainIdType, EthEip1559TxArgs, EthLegacyEip155TxArgs, - EthLegacyHomesteadTxArgs, + EthLegacyHomesteadTxArgs, parse_eth_transaction, }; -use crate::eth::{SAFE_EPOCH_DELAY, parse_eth_transaction}; use crate::interpreter::VMTrace; use crate::lotus_json::{HasLotusJson, lotus_json_with_self}; use crate::message::{ChainMessage, Message as _, SignedMessage}; -use crate::rpc::error::ServerError; -use crate::rpc::eth::errors::EthErrors; -use crate::rpc::eth::filter::{ - SkipEvent, event::EventFilter, mempool::MempoolFilter, tipset::TipSetFilter, +use crate::rpc::{ + ApiPaths, Ctx, EthEventHandler, LOOKBACK_NO_LIMIT, Permission, RpcMethod, RpcMethodExt as _, + error::ServerError, + eth::{ + errors::EthErrors, + filter::{SkipEvent, event::EventFilter, mempool::MempoolFilter, tipset::TipSetFilter}, + types::{EthBlockTrace, EthTrace}, + utils::decode_revert_reason, + }, + methods::chain::ChainGetTipSetV2, + state::ApiInvocResult, + types::{ApiTipsetKey, EventEntry, MessageLookup}, }; -use crate::rpc::eth::types::{EthBlockTrace, EthTrace}; -use crate::rpc::eth::utils::decode_revert_reason; -use crate::rpc::methods::chain::ChainGetTipSetV2; -use crate::rpc::state::ApiInvocResult; -use crate::rpc::types::{ApiTipsetKey, EventEntry, MessageLookup}; -use crate::rpc::{ApiPaths, Ctx, Permission, RpcMethod}; -use crate::rpc::{EthEventHandler, LOOKBACK_NO_LIMIT}; -use crate::shim::actors::EVMActorStateLoad as _; -use crate::shim::actors::eam; -use crate::shim::actors::evm; -use crate::shim::actors::is_evm_actor; -use crate::shim::actors::system; +use crate::shim::actors::{EVMActorStateLoad as _, eam, evm, is_evm_actor, system}; use crate::shim::address::{Address as FilecoinAddress, Protocol}; use crate::shim::crypto::Signature; use crate::shim::econ::{BLOCK_GAS_LIMIT, TokenAmount}; @@ -59,7 +57,7 @@ use crate::utils::cache::SizeTrackingLruCache; use crate::utils::db::BlockstoreExt as _; use crate::utils::encoding::from_slice_with_fallback; use crate::utils::get_size::{CidWrapper, big_int_heap_size_helper}; -use crate::utils::misc::env::env_or_default; +use crate::utils::misc::env::{env_or_default, is_env_truthy}; use crate::utils::multihash::prelude::*; use ahash::HashSet; use anyhow::{Context, Error, Result, anyhow, bail, ensure}; @@ -257,6 +255,7 @@ impl EthUint64 { derive_more::From, derive_more::Into, derive_more::Deref, + GetSize, )] pub struct EthInt64( #[schemars(with = "String")] @@ -301,6 +300,7 @@ impl From<[u8; EVM_WORD_LENGTH]> for EthHash { PartialEq, Debug, Clone, + Copy, Serialize, Deserialize, Default, @@ -315,42 +315,10 @@ pub enum Predefined { Pending, #[default] Latest, -} - -#[derive( - PartialEq, - Debug, - Clone, - Serialize, - Deserialize, - Default, - JsonSchema, - strum::Display, - strum::EnumString, -)] -#[strum(serialize_all = "lowercase")] -#[serde(rename_all = "lowercase")] -pub enum ExtPredefined { - Earliest, - Pending, - #[default] - Latest, Safe, Finalized, } -impl TryFrom<&ExtPredefined> for Predefined { - type Error = (); - fn try_from(ext: &ExtPredefined) -> Result { - match ext { - ExtPredefined::Earliest => Ok(Predefined::Earliest), - ExtPredefined::Pending => Ok(Predefined::Pending), - ExtPredefined::Latest => Ok(Predefined::Latest), - _ => Err(()), - } - } -} - #[derive(PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct BlockNumber { @@ -365,7 +333,9 @@ pub struct BlockHash { require_canonical: bool, } -#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[derive( + PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema, strum::Display, derive_more::From, +)] #[serde(untagged)] pub enum BlockNumberOrHash { #[schemars(with = "String")] @@ -375,22 +345,13 @@ pub enum BlockNumberOrHash { BlockNumberObject(BlockNumber), BlockHashObject(BlockHash), } - lotus_json_with_self!(BlockNumberOrHash); impl BlockNumberOrHash { - pub fn from_predefined(predefined: Predefined) -> Self { - Self::PredefinedBlock(predefined) - } - pub fn from_block_number(number: i64) -> Self { Self::BlockNumber(EthInt64(number)) } - pub fn from_block_hash(hash: EthHash) -> Self { - Self::BlockHash(hash) - } - /// Construct a block number using EIP-1898 Object scheme. /// /// For details see @@ -416,65 +377,8 @@ impl BlockNumberOrHash { return Ok(BlockNumberOrHash::from_block_number(epoch)); } s.parse::() - .map(BlockNumberOrHash::from_predefined) - .map_err(|_| anyhow!("Invalid block identifier")) - } -} - -#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[serde(untagged)] -pub enum ExtBlockNumberOrHash { - #[schemars(with = "String")] - PredefinedBlock(ExtPredefined), - BlockNumber(EthInt64), - BlockHash(EthHash), - BlockNumberObject(BlockNumber), - BlockHashObject(BlockHash), -} - -lotus_json_with_self!(ExtBlockNumberOrHash); - -#[allow(dead_code)] -impl ExtBlockNumberOrHash { - pub fn from_predefined(ext_predefined: ExtPredefined) -> Self { - Self::PredefinedBlock(ext_predefined) - } - - pub fn from_block_number(number: i64) -> Self { - Self::BlockNumber(EthInt64(number)) - } - - pub fn from_block_hash(hash: EthHash) -> Self { - Self::BlockHash(hash) - } - - /// Construct a block number using EIP-1898 Object scheme. - /// - /// For details see - pub fn from_block_number_object(number: i64) -> Self { - Self::BlockNumberObject(BlockNumber { - block_number: EthInt64(number), - }) - } - - /// Construct a block hash using EIP-1898 Object scheme. - /// - /// For details see - pub fn from_block_hash_object(hash: EthHash, require_canonical: bool) -> Self { - Self::BlockHashObject(BlockHash { - block_hash: hash, - require_canonical, - }) - } - - pub fn from_str(s: &str) -> Result { - if s.starts_with("0x") { - let epoch = hex_str_to_epoch(s)?; - return Ok(ExtBlockNumberOrHash::from_block_number(epoch)); - } - s.parse::() - .map(ExtBlockNumberOrHash::from_predefined) .map_err(|_| anyhow!("Invalid block identifier")) + .map(BlockNumberOrHash::from) } } @@ -554,7 +458,7 @@ pub struct Block { pub logs_bloom: Bloom, pub difficulty: EthUint64, pub total_difficulty: EthUint64, - pub number: EthUint64, + pub number: EthInt64, pub gas_limit: EthUint64, pub gas_used: EthUint64, pub timestamp: EthUint64, @@ -621,7 +525,7 @@ impl Block { b } else { let parent_cid = tipset.parents().cid()?; - let block_number = EthUint64(tipset.epoch() as u64); + let block_number = EthInt64(tipset.epoch()); let block_hash: EthHash = block_cid.into(); let (state_root, msgs_and_receipts) = execute_tipset(&ctx, &tipset).await?; @@ -691,7 +595,7 @@ pub struct ApiEthTx { pub nonce: EthUint64, pub hash: EthHash, pub block_hash: EthHash, - pub block_number: EthUint64, + pub block_number: EthInt64, pub transaction_index: EthUint64, pub from: EthAddress, #[serde(skip_serializing_if = "Option::is_none", default)] @@ -816,7 +720,7 @@ pub struct EthTxReceipt { transaction_hash: EthHash, transaction_index: EthUint64, block_hash: EthHash, - block_number: EthUint64, + block_number: EthInt64, from: EthAddress, to: Option, root: EthHash, @@ -1002,7 +906,7 @@ impl RpcMethod<2> for EthGetBalance { const NAME: &'static str = "Filecoin.EthGetBalance"; const NAME_ALIAS: Option<&'static str> = Some("eth_getBalance"); const PARAM_NAMES: [&'static str; 2] = ["address", "blockParam"]; - const API_PATHS: BitFlags = ApiPaths::all(); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some("Returns the balance of an Ethereum address at the specified block state"); @@ -1013,37 +917,11 @@ impl RpcMethod<2> for EthGetBalance { async fn handle( ctx: Ctx, (address, block_param): Self::Params, - _: &http::Extensions, - ) -> Result { - let ts = tipset_by_block_number_or_hash( - ctx.chain_store(), - block_param, - ResolveNullTipset::TakeOlder, - )?; - let balance = eth_get_balance(&ctx, &address, &ts).await?; - Ok(balance) - } -} - -pub enum EthGetBalanceV2 {} -impl RpcMethod<2> for EthGetBalanceV2 { - const NAME: &'static str = "Filecoin.EthGetBalance"; - const NAME_ALIAS: Option<&'static str> = Some("eth_getBalance"); - const PARAM_NAMES: [&'static str; 2] = ["address", "blockParam"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = - Some("Returns the balance of an Ethereum address at the specified block state"); - - type Params = (EthAddress, ExtBlockNumberOrHash); - type Ok = EthBigInt; - - async fn handle( - ctx: Ctx, - (address, block_param): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash_v2(&ctx, block_param, ResolveNullTipset::TakeOlder) + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) .await?; let balance = eth_get_balance(&ctx, &address, &ts).await?; Ok(balance) @@ -1075,75 +953,12 @@ fn get_tipset_from_hash( Tipset::load_required(chain_store.blockstore(), &tsk) } -fn resolve_predefined_tipset( - chain: &ChainStore, - head: Tipset, - predefined: Predefined, -) -> anyhow::Result { - match predefined { - Predefined::Earliest => bail!("block param \"earliest\" is not supported"), - Predefined::Pending => Ok(head), - Predefined::Latest => Ok(chain.chain_index().load_required_tipset(head.parents())?), - } -} - -async fn resolve_predefined_tipset_v2( - ctx: &Ctx, - head: Tipset, - tag: ExtPredefined, -) -> anyhow::Result { - if let Ok(common) = Predefined::try_from(&tag) { - resolve_predefined_tipset(ctx.chain_store(), head, common) - } else { - match tag { - ExtPredefined::Safe => Ok(ChainGetTipSetV2::get_latest_safe_tipset(ctx).await?), - ExtPredefined::Finalized => Ok(ChainGetTipSetV2::get_latest_finalized_tipset(ctx) - .await? - .unwrap_or(ctx.chain_index().tipset_by_height( - 0, - head, - ResolveNullTipset::TakeOlder, - )?)), - _ => bail!("unknown block tag: {:?}", tag), - } - } -} - -fn resolve_ext_predefined_tipset( - chain: &ChainStore, - head: Tipset, - ext_predefined: ExtPredefined, - resolve: ResolveNullTipset, -) -> anyhow::Result { - if let Ok(common) = Predefined::try_from(&ext_predefined) { - resolve_predefined_tipset(chain, head, common) - } else { - let latest_height = head.epoch() - 1; - // Matches all `ExtPredefined` variants outside `Predefined`. - match ext_predefined { - ExtPredefined::Safe => { - let safe_height = latest_height - SAFE_EPOCH_DELAY; - Ok(chain - .chain_index() - .tipset_by_height(safe_height, head, resolve)?) - } - ExtPredefined::Finalized => { - let finality_height = latest_height - chain.chain_config().policy.chain_finality; - Ok(chain - .chain_index() - .tipset_by_height(finality_height, head, resolve)?) - } - _ => bail!("Unhandled ExtPredefined variant: {:?}", ext_predefined), - } - } -} - fn resolve_block_number_tipset( chain: &ChainStore, - head: Tipset, block_number: EthInt64, resolve: ResolveNullTipset, ) -> anyhow::Result { + let head = chain.heaviest_tipset(); let height = ChainEpoch::from(block_number.0); if height > head.epoch() - 1 { bail!("requested a future epoch (beyond \"latest\")"); @@ -1155,7 +970,6 @@ fn resolve_block_number_tipset( fn resolve_block_hash_tipset( chain: &ChainStore, - head: Tipset, block_hash: &EthHash, require_canonical: bool, resolve: ResolveNullTipset, @@ -1164,9 +978,10 @@ fn resolve_block_hash_tipset( // verify that the tipset is in the canonical chain if require_canonical { // walk up the current chain (our head) until we reach ts.epoch() - let walk_ts = chain - .chain_index() - .tipset_by_height(ts.epoch(), head, resolve)?; + let walk_ts = + chain + .chain_index() + .tipset_by_height(ts.epoch(), chain.heaviest_tipset(), resolve)?; // verify that it equals the expected tipset if walk_ts != ts { bail!("tipset is not canonical"); @@ -1175,79 +990,6 @@ fn resolve_block_hash_tipset( Ok(ts) } -fn tipset_by_block_number_or_hash( - chain: &ChainStore, - block_param: BlockNumberOrHash, - resolve: ResolveNullTipset, -) -> anyhow::Result { - let head = chain.heaviest_tipset(); - match block_param { - BlockNumberOrHash::PredefinedBlock(predefined) => { - resolve_predefined_tipset(chain, head, predefined) - } - BlockNumberOrHash::BlockNumber(block_number) - | BlockNumberOrHash::BlockNumberObject(BlockNumber { block_number }) => { - resolve_block_number_tipset(chain, head, block_number, resolve) - } - BlockNumberOrHash::BlockHash(block_hash) => { - resolve_block_hash_tipset(chain, head, &block_hash, false, resolve) - } - BlockNumberOrHash::BlockHashObject(BlockHash { - block_hash, - require_canonical, - }) => resolve_block_hash_tipset(chain, head, &block_hash, require_canonical, resolve), - } -} - -async fn tipset_by_block_number_or_hash_v2( - ctx: &Ctx, - block_param: ExtBlockNumberOrHash, - resolve: ResolveNullTipset, -) -> anyhow::Result { - let chain = ctx.chain_store(); - let head = chain.heaviest_tipset(); - match block_param { - ExtBlockNumberOrHash::PredefinedBlock(predefined) => { - resolve_predefined_tipset_v2(ctx, head, predefined).await - } - ExtBlockNumberOrHash::BlockNumber(block_number) - | ExtBlockNumberOrHash::BlockNumberObject(BlockNumber { block_number }) => { - resolve_block_number_tipset(chain, head, block_number, resolve) - } - ExtBlockNumberOrHash::BlockHash(block_hash) => { - resolve_block_hash_tipset(chain, head, &block_hash, false, resolve) - } - ExtBlockNumberOrHash::BlockHashObject(BlockHash { - block_hash, - require_canonical, - }) => resolve_block_hash_tipset(chain, head, &block_hash, require_canonical, resolve), - } -} - -fn tipset_by_ext_block_number_or_hash( - chain: &ChainStore, - block_param: ExtBlockNumberOrHash, - resolve: ResolveNullTipset, -) -> anyhow::Result { - let head = chain.heaviest_tipset(); - match block_param { - ExtBlockNumberOrHash::PredefinedBlock(ext_predefined) => { - resolve_ext_predefined_tipset(chain, head, ext_predefined, resolve) - } - ExtBlockNumberOrHash::BlockNumber(block_number) - | ExtBlockNumberOrHash::BlockNumberObject(BlockNumber { block_number }) => { - resolve_block_number_tipset(chain, head, block_number, resolve) - } - ExtBlockNumberOrHash::BlockHash(block_hash) => { - resolve_block_hash_tipset(chain, head, &block_hash, false, resolve) - } - ExtBlockNumberOrHash::BlockHashObject(BlockHash { - block_hash, - require_canonical, - }) => resolve_block_hash_tipset(chain, head, &block_hash, require_canonical, resolve), - } -} - async fn execute_tipset( data: &Ctx, tipset: &Tipset, @@ -1496,7 +1238,7 @@ fn new_eth_tx_from_message_lookup( Ok(ApiEthTx { block_hash: parent_ts_cid.into(), - block_number: (parent_ts.epoch() as u64).into(), + block_number: parent_ts.epoch().into(), transaction_index: tx_index.into(), ..new_eth_tx_from_signed_message(&smsg, &state, ctx.chain_config().eth_chain_id)? }) @@ -1515,7 +1257,7 @@ fn new_eth_tx( Ok(ApiEthTx { block_hash: (*msg_tipset_cid).into(), - block_number: (block_height as u64).into(), + block_number: block_height.into(), transaction_index: tx_index.into(), ..tx }) @@ -1657,13 +1399,12 @@ impl RpcMethod<2> for EthGetBlockByHash { async fn handle( ctx: Ctx, (block_hash, full_tx_info): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash( - ctx.chain_store(), - BlockNumberOrHash::from_block_hash(block_hash), - ResolveNullTipset::TakeOlder, - )?; + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_hash, ResolveNullTipset::TakeOlder) + .await?; Block::from_filecoin_tipset(ctx, ts, full_tx_info.into()) .await .map_err(ServerError::from) @@ -1675,36 +1416,7 @@ impl RpcMethod<2> for EthGetBlockByNumber { const NAME: &'static str = "Filecoin.EthGetBlockByNumber"; const NAME_ALIAS: Option<&'static str> = Some("eth_getBlockByNumber"); const PARAM_NAMES: [&'static str; 2] = ["blockParam", "fullTxInfo"]; - const API_PATHS: BitFlags = ApiPaths::all(); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = - Some("Retrieves a block by its number or a special tag."); - - type Params = (BlockNumberOrPredefined, bool); - type Ok = Block; - - async fn handle( - ctx: Ctx, - (block_param, full_tx_info): Self::Params, - _: &http::Extensions, - ) -> Result { - let ts = tipset_by_ext_block_number_or_hash( - ctx.chain_store(), - block_param.into(), - ResolveNullTipset::TakeOlder, - )?; - Block::from_filecoin_tipset(ctx, ts, full_tx_info.into()) - .await - .map_err(ServerError::from) - } -} - -pub enum EthGetBlockByNumberV2 {} -impl RpcMethod<2> for EthGetBlockByNumberV2 { - const NAME: &'static str = "Filecoin.EthGetBlockByNumber"; - const NAME_ALIAS: Option<&'static str> = Some("eth_getBlockByNumber"); - const PARAM_NAMES: [&'static str; 2] = ["blockParam", "fullTxInfo"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some("Retrieves a block by its number or a special tag."); @@ -1715,14 +1427,12 @@ impl RpcMethod<2> for EthGetBlockByNumberV2 { async fn handle( ctx: Ctx, (block_param, full_tx_info): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash_v2( - &ctx, - block_param.into(), - ResolveNullTipset::TakeOlder, - ) - .await?; + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) + .await?; Block::from_filecoin_tipset(ctx, ts, full_tx_info.into()) .await .map_err(ServerError::from) @@ -1774,7 +1484,7 @@ impl RpcMethod<1> for EthGetBlockReceipts { const NAME: &'static str = "Filecoin.EthGetBlockReceipts"; const NAME_ALIAS: Option<&'static str> = Some("eth_getBlockReceipts"); const PARAM_NAMES: [&'static str; 1] = ["blockParam"]; - const API_PATHS: BitFlags = ApiPaths::all(); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some( "Retrieves all transaction receipts for a block by its number, hash or a special tag.", @@ -1786,39 +1496,11 @@ impl RpcMethod<1> for EthGetBlockReceipts { async fn handle( ctx: Ctx, (block_param,): Self::Params, - _: &http::Extensions, - ) -> Result { - let ts = tipset_by_block_number_or_hash( - ctx.chain_store(), - block_param, - ResolveNullTipset::TakeOlder, - )?; - get_block_receipts(&ctx, ts, None) - .await - .map_err(ServerError::from) - } -} - -pub enum EthGetBlockReceiptsV2 {} -impl RpcMethod<1> for EthGetBlockReceiptsV2 { - const NAME: &'static str = "Filecoin.EthGetBlockReceipts"; - const NAME_ALIAS: Option<&'static str> = Some("eth_getBlockReceipts"); - const PARAM_NAMES: [&'static str; 1] = ["blockParam"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = Some( - "Retrieves all transaction receipts for a block by its number, hash or a special tag.", - ); - - type Params = (ExtBlockNumberOrHash,); - type Ok = Vec; - - async fn handle( - ctx: Ctx, - (block_param,): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash_v2(&ctx, block_param, ResolveNullTipset::TakeOlder) + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) .await?; get_block_receipts(&ctx, ts, None) .await @@ -1831,7 +1513,7 @@ impl RpcMethod<2> for EthGetBlockReceiptsLimited { const NAME: &'static str = "Filecoin.EthGetBlockReceiptsLimited"; const NAME_ALIAS: Option<&'static str> = Some("eth_getBlockReceiptsLimited"); const PARAM_NAMES: [&'static str; 2] = ["blockParam", "limit"]; - const API_PATHS: BitFlags = ApiPaths::all(); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some( "Retrieves all transaction receipts for a block identified by its number, hash or a special tag along with an optional limit on the chain epoch for state resolution.", @@ -1843,39 +1525,11 @@ impl RpcMethod<2> for EthGetBlockReceiptsLimited { async fn handle( ctx: Ctx, (block_param, limit): Self::Params, - _: &http::Extensions, - ) -> Result { - let ts = tipset_by_block_number_or_hash( - ctx.chain_store(), - block_param, - ResolveNullTipset::TakeOlder, - )?; - get_block_receipts(&ctx, ts, Some(limit)) - .await - .map_err(ServerError::from) - } -} - -pub enum EthGetBlockReceiptsLimitedV2 {} -impl RpcMethod<2> for EthGetBlockReceiptsLimitedV2 { - const NAME: &'static str = "Filecoin.EthGetBlockReceiptsLimited"; - const NAME_ALIAS: Option<&'static str> = Some("eth_getBlockReceiptsLimited"); - const PARAM_NAMES: [&'static str; 2] = ["blockParam", "limit"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = Some( - "Retrieves all transaction receipts for a block identified by its number, hash or a special tag along with an optional limit on the chain epoch for state resolution.", - ); - - type Params = (ExtBlockNumberOrHash, ChainEpoch); - type Ok = Vec; - - async fn handle( - ctx: Ctx, - (block_param, limit): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash_v2(&ctx, block_param, ResolveNullTipset::TakeOlder) + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) .await?; get_block_receipts(&ctx, ts, Some(limit)) .await @@ -1915,38 +1569,7 @@ impl RpcMethod<1> for EthGetBlockTransactionCountByNumber { const NAME: &'static str = "Filecoin.EthGetBlockTransactionCountByNumber"; const NAME_ALIAS: Option<&'static str> = Some("eth_getBlockTransactionCountByNumber"); const PARAM_NAMES: [&'static str; 1] = ["blockNumber"]; - const API_PATHS: BitFlags = ApiPaths::all(); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = - Some("Returns the number of transactions in a block identified by its block number."); - - type Params = (EthInt64,); - type Ok = EthUint64; - - async fn handle( - ctx: Ctx, - (block_number,): Self::Params, - _: &http::Extensions, - ) -> Result { - let height = block_number.0; - let head = ctx.chain_store().heaviest_tipset(); - if height > head.epoch() { - return Err(anyhow::anyhow!("requested a future epoch (beyond \"latest\")").into()); - } - let ts = ctx - .chain_index() - .tipset_by_height(height, head, ResolveNullTipset::TakeOlder)?; - let count = count_messages_in_tipset(ctx.store(), &ts)?; - Ok(EthUint64(count as _)) - } -} - -pub enum EthGetBlockTransactionCountByNumberV2 {} -impl RpcMethod<1> for EthGetBlockTransactionCountByNumberV2 { - const NAME: &'static str = "Filecoin.EthGetBlockTransactionCountByNumber"; - const NAME_ALIAS: Option<&'static str> = Some("eth_getBlockTransactionCountByNumber"); - const PARAM_NAMES: [&'static str; 1] = ["blockNumber"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some( "Returns the number of transactions in a block identified by its block number or a special tag.", @@ -1958,14 +1581,12 @@ impl RpcMethod<1> for EthGetBlockTransactionCountByNumberV2 { async fn handle( ctx: Ctx, (block_number,): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash_v2( - &ctx, - block_number.into(), - ResolveNullTipset::TakeOlder, - ) - .await?; + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_number, ResolveNullTipset::TakeOlder) + .await?; let count = count_messages_in_tipset(ctx.store(), &ts)?; Ok(EthUint64(count as _)) } @@ -2084,7 +1705,7 @@ impl RpcMethod<2> for EthEstimateGas { const NAME_ALIAS: Option<&'static str> = Some("eth_estimateGas"); const N_REQUIRED_PARAMS: usize = 1; const PARAM_NAMES: [&'static str; 2] = ["tx", "blockParam"]; - const API_PATHS: BitFlags = ApiPaths::all(); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; type Params = (EthCallMessage, Option); @@ -2093,41 +1714,12 @@ impl RpcMethod<2> for EthEstimateGas { async fn handle( ctx: Ctx, (tx, block_param): Self::Params, - _: &http::Extensions, - ) -> Result { - let tipset = if let Some(block_param) = block_param { - tipset_by_block_number_or_hash( - ctx.chain_store(), - block_param, - ResolveNullTipset::TakeOlder, - )? - } else { - ctx.chain_store().heaviest_tipset() - }; - eth_estimate_gas(&ctx, tx, tipset).await - } -} - -pub enum EthEstimateGasV2 {} - -impl RpcMethod<2> for EthEstimateGasV2 { - const NAME: &'static str = "Filecoin.EthEstimateGas"; - const NAME_ALIAS: Option<&'static str> = Some("eth_estimateGas"); - const N_REQUIRED_PARAMS: usize = 1; - const PARAM_NAMES: [&'static str; 2] = ["tx", "blockParam"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); - const PERMISSION: Permission = Permission::Read; - - type Params = (EthCallMessage, Option); - type Ok = EthUint64; - - async fn handle( - ctx: Ctx, - (tx, block_param): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { let tipset = if let Some(block_param) = block_param { - tipset_by_block_number_or_hash_v2(&ctx, block_param, ResolveNullTipset::TakeOlder) + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) .await? } else { ctx.chain_store().heaviest_tipset() @@ -2306,66 +1898,35 @@ where high = median; } else { low = median; - } - check_threshold = median / 100; - } - - Ok(high) -} - -pub enum EthFeeHistory {} - -impl RpcMethod<3> for EthFeeHistory { - const NAME: &'static str = "Filecoin.EthFeeHistory"; - const NAME_ALIAS: Option<&'static str> = Some("eth_feeHistory"); - const N_REQUIRED_PARAMS: usize = 2; - const PARAM_NAMES: [&'static str; 3] = ["blockCount", "newestBlockNumber", "rewardPercentiles"]; - const API_PATHS: BitFlags = ApiPaths::all(); - const PERMISSION: Permission = Permission::Read; - - type Params = (EthUint64, BlockNumberOrPredefined, Option>); - type Ok = EthFeeHistoryResult; - - async fn handle( - ctx: Ctx, - (EthUint64(block_count), newest_block_number, reward_percentiles): Self::Params, - _: &http::Extensions, - ) -> Result { - let tipset = tipset_by_ext_block_number_or_hash( - ctx.chain_store(), - newest_block_number.into(), - ResolveNullTipset::TakeOlder, - )?; - - eth_fee_history(ctx, tipset, block_count, reward_percentiles).await + } + check_threshold = median / 100; } + + Ok(high) } -pub enum EthFeeHistoryV2 {} +pub enum EthFeeHistory {} -impl RpcMethod<3> for EthFeeHistoryV2 { +impl RpcMethod<3> for EthFeeHistory { const NAME: &'static str = "Filecoin.EthFeeHistory"; const NAME_ALIAS: Option<&'static str> = Some("eth_feeHistory"); const N_REQUIRED_PARAMS: usize = 2; const PARAM_NAMES: [&'static str; 3] = ["blockCount", "newestBlockNumber", "rewardPercentiles"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; - type Params = (EthUint64, ExtBlockNumberOrHash, Option>); + type Params = (EthUint64, BlockNumberOrPredefined, Option>); type Ok = EthFeeHistoryResult; async fn handle( ctx: Ctx, (EthUint64(block_count), newest_block_number, reward_percentiles): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let tipset = tipset_by_block_number_or_hash_v2( - &ctx, - newest_block_number, - ResolveNullTipset::TakeOlder, - ) - .await?; - + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let tipset = resolver + .tipset_by_block_number_or_hash(newest_block_number, ResolveNullTipset::TakeOlder) + .await?; eth_fee_history(ctx, tipset, block_count, reward_percentiles).await } } @@ -2492,7 +2053,7 @@ impl RpcMethod<2> for EthGetCode { const NAME: &'static str = "Filecoin.EthGetCode"; const NAME_ALIAS: Option<&'static str> = Some("eth_getCode"); const PARAM_NAMES: [&'static str; 2] = ["ethAddress", "blockNumberOrHash"]; - const API_PATHS: BitFlags = ApiPaths::all(); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some( "Retrieves the contract code at a specific address and block state, identified by its number, hash, or a special tag.", @@ -2504,37 +2065,11 @@ impl RpcMethod<2> for EthGetCode { async fn handle( ctx: Ctx, (eth_address, block_param): Self::Params, - _: &http::Extensions, - ) -> Result { - let ts = tipset_by_block_number_or_hash( - ctx.chain_store(), - block_param, - ResolveNullTipset::TakeOlder, - )?; - eth_get_code(&ctx, &ts, ð_address).await - } -} - -pub enum EthGetCodeV2 {} -impl RpcMethod<2> for EthGetCodeV2 { - const NAME: &'static str = "Filecoin.EthGetCode"; - const NAME_ALIAS: Option<&'static str> = Some("eth_getCode"); - const PARAM_NAMES: [&'static str; 2] = ["ethAddress", "blockNumberOrHash"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = Some( - "Retrieves the contract code at a specific address and block state, identified by its number, hash, or a special tag.", - ); - - type Params = (EthAddress, ExtBlockNumberOrHash); - type Ok = EthBytes; - - async fn handle( - ctx: Ctx, - (eth_address, block_param): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash_v2(&ctx, block_param, ResolveNullTipset::TakeOlder) + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) .await?; eth_get_code(&ctx, &ts, ð_address).await } @@ -2612,7 +2147,7 @@ impl RpcMethod<3> for EthGetStorageAt { const NAME: &'static str = "Filecoin.EthGetStorageAt"; const NAME_ALIAS: Option<&'static str> = Some("eth_getStorageAt"); const PARAM_NAMES: [&'static str; 3] = ["ethAddress", "position", "blockNumberOrHash"]; - const API_PATHS: BitFlags = ApiPaths::all(); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some( "Retrieves the storage value at a specific position for a contract @@ -2625,43 +2160,12 @@ impl RpcMethod<3> for EthGetStorageAt { async fn handle( ctx: Ctx, (eth_address, position, block_number_or_hash): Self::Params, - _: &http::Extensions, - ) -> Result { - let ts = tipset_by_block_number_or_hash( - ctx.chain_store(), - block_number_or_hash, - ResolveNullTipset::TakeOlder, - )?; - get_storage_at(&ctx, ts, eth_address, position).await - } -} - -pub enum EthGetStorageAtV2 {} -impl RpcMethod<3> for EthGetStorageAtV2 { - const NAME: &'static str = "Filecoin.EthGetStorageAt"; - const NAME_ALIAS: Option<&'static str> = Some("eth_getStorageAt"); - const PARAM_NAMES: [&'static str; 3] = ["ethAddress", "position", "blockNumberOrHash"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = Some( - "Retrieves the storage value at a specific position for a contract - at a given block state, identified by its number, hash, or a special tag.", - ); - - type Params = (EthAddress, EthBytes, ExtBlockNumberOrHash); - type Ok = EthBytes; - - async fn handle( - ctx: Ctx, - (eth_address, position, block_number_or_hash): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash_v2( - &ctx, - block_number_or_hash, - ResolveNullTipset::TakeOlder, - ) - .await?; + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_number_or_hash, ResolveNullTipset::TakeOlder) + .await?; get_storage_at(&ctx, ts, eth_address, position).await } } @@ -2736,7 +2240,7 @@ impl RpcMethod<2> for EthGetTransactionCount { const NAME: &'static str = "Filecoin.EthGetTransactionCount"; const NAME_ALIAS: Option<&'static str> = Some("eth_getTransactionCount"); const PARAM_NAMES: [&'static str; 2] = ["sender", "blockParam"]; - const API_PATHS: BitFlags = ApiPaths::all(); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; type Params = (EthAddress, BlockNumberOrHash); @@ -2745,7 +2249,7 @@ impl RpcMethod<2> for EthGetTransactionCount { async fn handle( ctx: Ctx, (sender, block_param): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { let addr = sender.to_filecoin_address()?; match block_param { @@ -2753,45 +2257,10 @@ impl RpcMethod<2> for EthGetTransactionCount { Ok(EthUint64(ctx.mpool.get_sequence(&addr)?)) } _ => { - let ts = tipset_by_block_number_or_hash( - ctx.chain_store(), - block_param, - ResolveNullTipset::TakeOlder, - )?; - eth_get_transaction_count(&ctx, &ts, addr).await - } - } - } -} - -pub enum EthGetTransactionCountV2 {} -impl RpcMethod<2> for EthGetTransactionCountV2 { - const NAME: &'static str = "Filecoin.EthGetTransactionCount"; - const NAME_ALIAS: Option<&'static str> = Some("eth_getTransactionCount"); - const PARAM_NAMES: [&'static str; 2] = ["sender", "blockParam"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); - const PERMISSION: Permission = Permission::Read; - - type Params = (EthAddress, ExtBlockNumberOrHash); - type Ok = EthUint64; - - async fn handle( - ctx: Ctx, - (sender, block_param): Self::Params, - _: &http::Extensions, - ) -> Result { - let addr = sender.to_filecoin_address()?; - match block_param { - ExtBlockNumberOrHash::PredefinedBlock(ExtPredefined::Pending) => { - Ok(EthUint64(ctx.mpool.get_sequence(&addr)?)) - } - _ => { - let ts = tipset_by_block_number_or_hash_v2( - &ctx, - block_param, - ResolveNullTipset::TakeOlder, - ) - .await?; + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) + .await?; eth_get_transaction_count(&ctx, &ts, addr).await } } @@ -2878,34 +2347,7 @@ impl RpcMethod<2> for EthGetTransactionByBlockNumberAndIndex { const NAME: &'static str = "Filecoin.EthGetTransactionByBlockNumberAndIndex"; const NAME_ALIAS: Option<&'static str> = Some("eth_getTransactionByBlockNumberAndIndex"); const PARAM_NAMES: [&'static str; 2] = ["blockParam", "txIndex"]; - const API_PATHS: BitFlags = ApiPaths::all(); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = - Some("Retrieves a transaction by its block number and index."); - - type Params = (BlockNumberOrPredefined, EthUint64); - type Ok = Option; - - async fn handle( - ctx: Ctx, - (block_param, tx_index): Self::Params, - _: &http::Extensions, - ) -> Result { - let ts = tipset_by_ext_block_number_or_hash( - ctx.chain_store(), - block_param.into(), - ResolveNullTipset::TakeOlder, - )?; - eth_tx_by_block_num_and_idx(&ctx, &ts, tx_index) - } -} - -pub enum EthGetTransactionByBlockNumberAndIndexV2 {} -impl RpcMethod<2> for EthGetTransactionByBlockNumberAndIndexV2 { - const NAME: &'static str = "Filecoin.EthGetTransactionByBlockNumberAndIndex"; - const NAME_ALIAS: Option<&'static str> = Some("eth_getTransactionByBlockNumberAndIndex"); - const PARAM_NAMES: [&'static str; 2] = ["blockParam", "txIndex"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some("Retrieves a transaction by its block number and index."); @@ -2916,14 +2358,12 @@ impl RpcMethod<2> for EthGetTransactionByBlockNumberAndIndexV2 { async fn handle( ctx: Ctx, (block_param, tx_index): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash_v2( - &ctx, - block_param.into(), - ResolveNullTipset::TakeOlder, - ) - .await?; + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) + .await?; eth_tx_by_block_num_and_idx(&ctx, &ts, tx_index) } } @@ -3141,40 +2581,18 @@ impl RpcMethod<2> for EthCall { const NAME_ALIAS: Option<&'static str> = Some("eth_call"); const N_REQUIRED_PARAMS: usize = 2; const PARAM_NAMES: [&'static str; 2] = ["tx", "blockParam"]; - const API_PATHS: BitFlags = ApiPaths::all(); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; type Params = (EthCallMessage, BlockNumberOrHash); type Ok = EthBytes; async fn handle( ctx: Ctx, (tx, block_param): Self::Params, - _: &http::Extensions, - ) -> Result { - let ts = tipset_by_block_number_or_hash( - ctx.chain_store(), - block_param, - ResolveNullTipset::TakeOlder, - )?; - eth_call(&ctx, tx, ts).await - } -} - -pub enum EthCallV2 {} -impl RpcMethod<2> for EthCallV2 { - const NAME: &'static str = "Filecoin.EthCall"; - const NAME_ALIAS: Option<&'static str> = Some("eth_call"); - const N_REQUIRED_PARAMS: usize = 2; - const PARAM_NAMES: [&'static str; 2] = ["tx", "blockParam"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); - const PERMISSION: Permission = Permission::Read; - type Params = (EthCallMessage, ExtBlockNumberOrHash); - type Ok = EthBytes; - async fn handle( - ctx: Ctx, - (tx, block_param): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash_v2(&ctx, block_param, ResolveNullTipset::TakeOlder) + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) .await?; eth_call(&ctx, tx, ts).await } @@ -3244,7 +2662,6 @@ impl RpcMethod<0> for EthNewPendingTransactionFilter { _: &http::Extensions, ) -> Result { let eth_event_handler = ctx.eth_event_handler.clone(); - Ok(eth_event_handler.eth_new_pending_transaction_filter()?) } } @@ -3378,19 +2795,17 @@ impl RpcMethod<2> for FilecoinAddressToEthAddress { async fn handle( ctx: Ctx, (address, block_param): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { if let Ok(eth_address) = EthAddress::from_filecoin_address(&address) { Ok(eth_address) } else { - let block_param = block_param.unwrap_or(BlockNumberOrPredefined::PredefinedBlock( - ExtPredefined::Finalized, - )); - let ts = tipset_by_ext_block_number_or_hash( - ctx.chain_store(), - block_param.into(), - ResolveNullTipset::TakeOlder, - )?; + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + // Default to Finalized for Lotus parity + let block_param = block_param.unwrap_or_else(|| Predefined::Finalized.into()); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) + .await?; let id_address = ctx.state_manager.lookup_required_id(&address, &ts)?; Ok(EthAddress::from_filecoin_address(&id_address)?) @@ -3962,44 +3377,20 @@ impl RpcMethod<1> for EthTraceBlock { const NAME_ALIAS: Option<&'static str> = Some("trace_block"); const N_REQUIRED_PARAMS: usize = 1; const PARAM_NAMES: [&'static str; 1] = ["blockParam"]; - const API_PATHS: BitFlags = ApiPaths::all(); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = Some("Returns traces created at given block."); - - type Params = (ExtBlockNumberOrHash,); - type Ok = Vec; - async fn handle( - ctx: Ctx, - (block_param,): Self::Params, - ext: &http::Extensions, - ) -> Result { - let ts = tipset_by_ext_block_number_or_hash( - ctx.chain_store(), - block_param, - ResolveNullTipset::TakeOlder, - )?; - eth_trace_block(&ctx, &ts, ext).await - } -} - -pub enum EthTraceBlockV2 {} -impl RpcMethod<1> for EthTraceBlockV2 { - const NAME: &'static str = "Filecoin.EthTraceBlock"; - const NAME_ALIAS: Option<&'static str> = Some("trace_block"); - const N_REQUIRED_PARAMS: usize = 1; - const PARAM_NAMES: [&'static str; 1] = ["blockParam"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some("Returns traces created at given block."); - type Params = (ExtBlockNumberOrHash,); + type Params = (BlockNumberOrHash,); type Ok = Vec; async fn handle( ctx: Ctx, (block_param,): Self::Params, ext: &http::Extensions, ) -> Result { - let ts = tipset_by_block_number_or_hash_v2(&ctx, block_param, ResolveNullTipset::TakeOlder) + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) .await?; eth_trace_block(&ctx, &ts, ext).await } @@ -4073,15 +3464,14 @@ impl RpcMethod<3> for EthTraceCall { async fn handle( ctx: Ctx, (tx, trace_types, block_param): Self::Params, - _: &http::Extensions, + ext: &http::Extensions, ) -> Result { let msg = Message::try_from(tx)?; - let block_param = block_param.unwrap_or(BlockNumberOrHash::from_str("latest")?); - let ts = tipset_by_block_number_or_hash( - ctx.chain_store(), - block_param, - ResolveNullTipset::TakeOlder, - )?; + let block_param = block_param.unwrap_or_else(|| Predefined::Latest.into()); + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) + .await?; let (pre_state_root, _) = ctx .state_manager @@ -4209,11 +3599,10 @@ impl RpcMethod<1> for EthTraceTransaction { .await? .ok_or(ServerError::internal_error("transaction not found", None))?; - let ts = tipset_by_ext_block_number_or_hash( - ctx.chain_store(), - ExtBlockNumberOrHash::from_block_number(eth_txn.block_number.0 as i64), - ResolveNullTipset::TakeOlder, - )?; + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(eth_txn.block_number, ResolveNullTipset::TakeOlder) + .await?; let traces = eth_trace_block(&ctx, &ts, ext) .await? @@ -4230,50 +3619,13 @@ impl RpcMethod<2> for EthTraceReplayBlockTransactions { const NAME: &'static str = "Filecoin.EthTraceReplayBlockTransactions"; const NAME_ALIAS: Option<&'static str> = Some("trace_replayBlockTransactions"); const PARAM_NAMES: [&'static str; 2] = ["blockParam", "traceTypes"]; - const API_PATHS: BitFlags = ApiPaths::all(); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = Some( - "Replays all transactions in a block returning the requested traces for each transaction.", - ); - - type Params = (ExtBlockNumberOrHash, Vec); - type Ok = Vec; - - async fn handle( - ctx: Ctx, - (block_param, trace_types): Self::Params, - ext: &http::Extensions, - ) -> Result { - if trace_types.as_slice() != ["trace"] { - return Err(ServerError::invalid_params( - "only 'trace' is supported", - None, - )); - } - - let ts = tipset_by_ext_block_number_or_hash( - ctx.chain_store(), - block_param, - ResolveNullTipset::TakeOlder, - )?; - - eth_trace_replay_block_transactions(&ctx, &ts, ext).await - } -} - -pub enum EthTraceReplayBlockTransactionsV2 {} -impl RpcMethod<2> for EthTraceReplayBlockTransactionsV2 { - const N_REQUIRED_PARAMS: usize = 2; - const NAME: &'static str = "Filecoin.EthTraceReplayBlockTransactions"; - const NAME_ALIAS: Option<&'static str> = Some("trace_replayBlockTransactions"); - const PARAM_NAMES: [&'static str; 2] = ["blockParam", "traceTypes"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some( "Replays all transactions in a block returning the requested traces for each transaction.", ); - type Params = (ExtBlockNumberOrHash, Vec); + type Params = (BlockNumberOrHash, Vec); type Ok = Vec; async fn handle( @@ -4288,7 +3640,9 @@ impl RpcMethod<2> for EthTraceReplayBlockTransactionsV2 { )); } - let ts = tipset_by_block_number_or_hash_v2(&ctx, block_param, ResolveNullTipset::TakeOlder) + let resolver = TipsetResolver::new(&ctx, Self::api_path(ext)?); + let ts = resolver + .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) .await?; eth_trace_replay_block_transactions(&ctx, &ts, ext).await @@ -4345,31 +3699,20 @@ where Ok(all_traces) } -fn get_eth_block_number_from_string( - chain_store: &ChainStore, - block: Option<&str>, - resolve: ResolveNullTipset, -) -> Result { - let block_param = block - .map(ExtBlockNumberOrHash::from_str) - .transpose()? - .unwrap_or(ExtBlockNumberOrHash::PredefinedBlock(ExtPredefined::Latest)); - Ok(EthUint64( - tipset_by_ext_block_number_or_hash(chain_store, block_param, resolve)?.epoch() as u64, - )) -} - -async fn get_eth_block_number_from_string_v2( +async fn get_eth_block_number_from_string( ctx: &Ctx, block: Option<&str>, resolve: ResolveNullTipset, + api_path: ApiPaths, ) -> Result { let block_param = block - .map(ExtBlockNumberOrHash::from_str) + .map(BlockNumberOrHash::from_str) .transpose()? - .unwrap_or(ExtBlockNumberOrHash::PredefinedBlock(ExtPredefined::Latest)); + .unwrap_or(BlockNumberOrHash::PredefinedBlock(Predefined::Latest)); + let resolver = TipsetResolver::new(ctx, api_path); Ok(EthUint64( - tipset_by_block_number_or_hash_v2(ctx, block_param, resolve) + resolver + .tipset_by_block_number_or_hash(block_param, resolve) .await? .epoch() as u64, )) @@ -4381,7 +3724,7 @@ impl RpcMethod<1> for EthTraceFilter { const NAME: &'static str = "Filecoin.EthTraceFilter"; const NAME_ALIAS: Option<&'static str> = Some("trace_filter"); const PARAM_NAMES: [&'static str; 1] = ["filter"]; - const API_PATHS: BitFlags = ApiPaths::all(); + const API_PATHS: BitFlags = ApiPaths::all_with_v2(); const PERMISSION: Permission = Permission::Read; const DESCRIPTION: Option<&'static str> = Some("Returns the traces for transactions matching the filter criteria."); @@ -4393,54 +3736,21 @@ impl RpcMethod<1> for EthTraceFilter { (filter,): Self::Params, ext: &http::Extensions, ) -> Result { + let api_path = Self::api_path(ext)?; let from_block = get_eth_block_number_from_string( - ctx.chain_store(), - filter.from_block.as_deref(), - ResolveNullTipset::TakeNewer, - ) - .context("cannot parse fromBlock")?; - - let to_block = get_eth_block_number_from_string( - ctx.chain_store(), - filter.to_block.as_deref(), - ResolveNullTipset::TakeOlder, - ) - .context("cannot parse toBlock")?; - - Ok(trace_filter(ctx, filter, from_block, to_block, ext).await?) - } -} - -pub enum EthTraceFilterV2 {} -impl RpcMethod<1> for EthTraceFilterV2 { - const N_REQUIRED_PARAMS: usize = 1; - const NAME: &'static str = "Filecoin.EthTraceFilter"; - const NAME_ALIAS: Option<&'static str> = Some("trace_filter"); - const PARAM_NAMES: [&'static str; 1] = ["filter"]; - const API_PATHS: BitFlags = make_bitflags!(ApiPaths::V2); - const PERMISSION: Permission = Permission::Read; - const DESCRIPTION: Option<&'static str> = - Some("Returns the traces for transactions matching the filter criteria."); - type Params = (EthTraceFilterCriteria,); - type Ok = Vec; - - async fn handle( - ctx: Ctx, - (filter,): Self::Params, - ext: &http::Extensions, - ) -> Result { - let from_block = get_eth_block_number_from_string_v2( &ctx, filter.from_block.as_deref(), ResolveNullTipset::TakeNewer, + api_path, ) .await .context("cannot parse fromBlock")?; - let to_block = get_eth_block_number_from_string_v2( + let to_block = get_eth_block_number_from_string( &ctx, filter.to_block.as_deref(), ResolveNullTipset::TakeOlder, + api_path, ) .await .context("cannot parse toBlock")?; @@ -4473,7 +3783,7 @@ async fn trace_filter( // For BlockNumber, EthTraceBlock and EthTraceBlockV2 are equivalent. let block_traces = EthTraceBlock::handle( ctx.clone(), - (ExtBlockNumberOrHash::from_block_number(blk_num as i64),), + (BlockNumberOrHash::from_block_number(blk_num as i64),), ext, ) .await?; diff --git a/src/rpc/methods/eth/tipset_resolver.rs b/src/rpc/methods/eth/tipset_resolver.rs new file mode 100644 index 000000000000..bccea571b9df --- /dev/null +++ b/src/rpc/methods/eth/tipset_resolver.rs @@ -0,0 +1,180 @@ +// Copyright 2019-2026 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use super::*; +use crate::rpc::chain::SAFE_HEIGHT_DISTANCE; + +pub struct TipsetResolver<'a, DB> +where + DB: Blockstore + Send + Sync + 'static, +{ + ctx: &'a Ctx, + api_version: ApiPaths, +} + +impl<'a, DB> TipsetResolver<'a, DB> +where + DB: Blockstore + Send + Sync + 'static, +{ + /// Creates a TipsetResolver that holds a reference to the given chain context and the API version to use for tipset resolution. + pub fn new(ctx: &'a Ctx, api_version: ApiPaths) -> Self { + Self { ctx, api_version } + } + + /// Resolve a tipset from a block identifier that may be a predefined tag, block height, or block hash. + /// + /// Attempts to resolve the provided `block_param` into a concrete `Tipset`. The parameter may be: + /// - a predefined tag (e.g., `Predefined::Latest`, `Predefined::Safe`, `Predefined::Finalized`), + /// - a block height (number or object form), or + /// - a block hash (raw hash or object form that can require canonicalization). + /// + /// # Parameters + /// + /// - `block_param` — block identifier to resolve; accepts any type convertible to `BlockNumberOrHash`. + /// - `resolve` — rule for how to treat null/unknown tipsets when resolving by height/hash. + /// + /// # Returns + /// + /// The resolved `Tipset` on success. + pub async fn tipset_by_block_number_or_hash( + &self, + block_param: impl Into, + resolve: ResolveNullTipset, + ) -> anyhow::Result { + match block_param.into() { + BlockNumberOrHash::PredefinedBlock(tag) => self.resolve_predefined_tipset(tag).await, + BlockNumberOrHash::BlockNumber(block_number) + | BlockNumberOrHash::BlockNumberObject(BlockNumber { block_number }) => { + resolve_block_number_tipset(self.ctx.chain_store(), block_number, resolve) + } + BlockNumberOrHash::BlockHash(block_hash) => { + resolve_block_hash_tipset(self.ctx.chain_store(), &block_hash, false, resolve) + } + BlockNumberOrHash::BlockHashObject(BlockHash { + block_hash, + require_canonical, + }) => resolve_block_hash_tipset( + self.ctx.chain_store(), + &block_hash, + require_canonical, + resolve, + ), + } + } + + /// Resolve a predefined tipset according to the resolver's API version. + /// + /// # Returns + /// + /// The resolved `Tipset`, or an error if resolution fails. + async fn resolve_predefined_tipset(&self, tag: Predefined) -> anyhow::Result { + match self.api_version { + ApiPaths::V2 => self.resolve_predefined_tipset_v2(tag).await, + ApiPaths::V1 | ApiPaths::V0 => self.resolve_predefined_tipset_v1(tag).await, + } + } + + /// Resolves a predefined tipset using the V1 resolution policy, or delegates to the V2 resolver when the + /// V1 finality-resolution override is not enabled. + /// + /// If the environment variable `FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION` is set to a truthy value, + /// this function first attempts common predefined tag resolution (e.g., Pending, Latest). If that yields + /// no result, the function uses expected-consensus finality to resolve the "safe" or "finalized" tipset + /// for the corresponding `Predefined` tag. When the environment variable is not set or is falsy, + /// resolution is delegated to the V2 resolver. + /// + /// # Errors + /// + /// Returns an error if the requested predefined tag is unknown or if tipset resolution fails. + async fn resolve_predefined_tipset_v1(&self, tag: Predefined) -> anyhow::Result { + const ETH_V1_DISABLE_F3_FINALITY_RESOLUTION_ENV_KEY: &str = + "FOREST_ETH_V1_DISABLE_F3_FINALITY_RESOLUTION"; + static ETH_V1_F3_FINALITY_RESOLUTION_DISABLED: LazyLock = + LazyLock::new(|| is_env_truthy(ETH_V1_DISABLE_F3_FINALITY_RESOLUTION_ENV_KEY)); + + if *ETH_V1_F3_FINALITY_RESOLUTION_DISABLED { + if let Some(ts) = self.resolve_common_predefined_tipset(tag)? { + Ok(ts) + } else { + match tag { + Predefined::Safe => self.get_ec_safe_tipset(), + Predefined::Finalized => self.get_ec_finalized_tipset(), + tag => anyhow::bail!("unknown block tag: {tag}"), + } + } + } else { + self.resolve_predefined_tipset_v2(tag).await + } + } + + /// Resolves a predefined tipset according to the v2 API behavior. + /// + /// Uses a common predefined-tipset lookup first; if that yields no result, resolves + /// `Safe` and `Finalized` tags via the v2 chain getters. Returns an error for unknown tags + /// or on underlying resolution failures. + /// + /// # Returns + /// + /// The resolved `Tipset` on success. + async fn resolve_predefined_tipset_v2(&self, tag: Predefined) -> anyhow::Result { + if let Some(ts) = self.resolve_common_predefined_tipset(tag)? { + Ok(ts) + } else { + match tag { + Predefined::Safe => ChainGetTipSetV2::get_latest_safe_tipset(self.ctx).await, + Predefined::Finalized => { + ChainGetTipSetV2::get_latest_finalized_tipset(self.ctx).await + } + tag => anyhow::bail!("unknown block tag: {tag}"), + } + } + } + + /// Attempt to resolve a predefined block tag to a commonly-handled tipset. + /// + /// Returns `Some(Tipset)` for `Predefined::Pending` (current head) and + /// `Predefined::Latest` (the tipset at the head's parents). Returns `Ok(None)` + /// when the tag is not handled by this common-resolution path (caller should + /// try other resolution strategies). Resolving `Predefined::Earliest` fails + /// with an error. + fn resolve_common_predefined_tipset(&self, tag: Predefined) -> anyhow::Result> { + let head = self.ctx.chain_store().heaviest_tipset(); + match tag { + Predefined::Earliest => bail!("block param \"earliest\" is not supported"), + Predefined::Pending => Ok(Some(head)), + Predefined::Latest => Ok(Some( + self.ctx + .chain_index() + .load_required_tipset(head.parents())?, + )), + Predefined::Safe | Predefined::Finalized => Ok(None), + } + } + + /// Returns the tipset considered "safe" relative to the current heaviest tipset. + /// + /// The safe tipset is the tipset at height `max(head.epoch() - SAFE_HEIGHT_DISTANCE, 0)`. + pub fn get_ec_safe_tipset(&self) -> anyhow::Result { + let head = self.ctx.chain_store().heaviest_tipset(); + let safe_height = (head.epoch() - SAFE_HEIGHT_DISTANCE).max(0); + Ok(self.ctx.chain_index().tipset_by_height( + safe_height, + head, + ResolveNullTipset::TakeOlder, + )?) + } + + /// Returns the tipset considered finalized by expected-consensus finality. + /// + /// The finalized epoch is computed as head.epoch() minus the chain's `policy.chain_finality`, clamped to zero. The tipset at that epoch is returned; when the exact height is unavailable, an older tipset is selected. + pub fn get_ec_finalized_tipset(&self) -> anyhow::Result { + let head = self.ctx.chain_store().heaviest_tipset(); + let ec_finality_epoch = + (head.epoch() - self.ctx.chain_config().policy.chain_finality).max(0); + Ok(self.ctx.chain_index().tipset_by_height( + ec_finality_epoch, + head, + ResolveNullTipset::TakeOlder, + )?) + } +} diff --git a/src/rpc/methods/eth/types.rs b/src/rpc/methods/eth/types.rs index 9ff55728cbcc..ef5759995d9a 100644 --- a/src/rpc/methods/eth/types.rs +++ b/src/rpc/methods/eth/types.rs @@ -267,20 +267,20 @@ impl TryFrom for FilecoinAddress { } } -#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema, derive_more::From)] #[serde(untagged)] pub enum BlockNumberOrPredefined { #[schemars(with = "String")] - PredefinedBlock(ExtPredefined), + PredefinedBlock(Predefined), BlockNumber(EthInt64), } lotus_json_with_self!(BlockNumberOrPredefined); -impl From for ExtBlockNumberOrHash { +impl From for BlockNumberOrHash { fn from(value: BlockNumberOrPredefined) -> Self { match value { - BlockNumberOrPredefined::PredefinedBlock(v) => ExtBlockNumberOrHash::PredefinedBlock(v), - BlockNumberOrPredefined::BlockNumber(v) => ExtBlockNumberOrHash::BlockNumber(v), + BlockNumberOrPredefined::PredefinedBlock(v) => BlockNumberOrHash::PredefinedBlock(v), + BlockNumberOrPredefined::BlockNumber(v) => BlockNumberOrHash::BlockNumber(v), } } } diff --git a/src/rpc/methods/state.rs b/src/rpc/methods/state.rs index 8ca555aefe99..f2e3d35ac9ae 100644 --- a/src/rpc/methods/state.rs +++ b/src/rpc/methods/state.rs @@ -330,7 +330,7 @@ impl RpcMethod<2> for StateGetActorV2 { (address, selector): Self::Params, _: &http::Extensions, ) -> Result { - let ts = ChainGetTipSetV2::get_required_tipset(&ctx, &selector).await?; + let ts = ChainGetTipSetV2::get_tipset(&ctx, &selector).await?; Ok(ctx.state_manager.get_actor(&address, *ts.parent_state())?) } } @@ -353,7 +353,7 @@ impl RpcMethod<2> for StateGetID { (address, selector): Self::Params, _: &http::Extensions, ) -> Result { - let ts = ChainGetTipSetV2::get_required_tipset(&ctx, &selector).await?; + let ts = ChainGetTipSetV2::get_tipset(&ctx, &selector).await?; Ok(ctx.state_manager.lookup_required_id(&address, &ts)?) } } diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 4fcc69066bf3..d759728fbbd5 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -105,40 +105,28 @@ macro_rules! for_each_rpc_method { $callback!($crate::rpc::eth::FilecoinAddressToEthAddress); $callback!($crate::rpc::eth::EthBlockNumber); $callback!($crate::rpc::eth::EthCall); - $callback!($crate::rpc::eth::EthCallV2); $callback!($crate::rpc::eth::EthChainId); $callback!($crate::rpc::eth::EthEstimateGas); - $callback!($crate::rpc::eth::EthEstimateGasV2); $callback!($crate::rpc::eth::EthFeeHistory); - $callback!($crate::rpc::eth::EthFeeHistoryV2); $callback!($crate::rpc::eth::EthGasPrice); $callback!($crate::rpc::eth::EthGetBalance); - $callback!($crate::rpc::eth::EthGetBalanceV2); $callback!($crate::rpc::eth::EthGetBlockByHash); $callback!($crate::rpc::eth::EthGetBlockByNumber); - $callback!($crate::rpc::eth::EthGetBlockByNumberV2); $callback!($crate::rpc::eth::EthGetBlockReceipts); - $callback!($crate::rpc::eth::EthGetBlockReceiptsV2); $callback!($crate::rpc::eth::EthGetBlockReceiptsLimited); - $callback!($crate::rpc::eth::EthGetBlockReceiptsLimitedV2); $callback!($crate::rpc::eth::EthGetBlockTransactionCountByHash); $callback!($crate::rpc::eth::EthGetBlockTransactionCountByNumber); - $callback!($crate::rpc::eth::EthGetBlockTransactionCountByNumberV2); $callback!($crate::rpc::eth::EthGetCode); - $callback!($crate::rpc::eth::EthGetCodeV2); $callback!($crate::rpc::eth::EthGetLogs); $callback!($crate::rpc::eth::EthGetFilterLogs); $callback!($crate::rpc::eth::EthGetFilterChanges); $callback!($crate::rpc::eth::EthGetMessageCidByTransactionHash); $callback!($crate::rpc::eth::EthGetStorageAt); - $callback!($crate::rpc::eth::EthGetStorageAtV2); $callback!($crate::rpc::eth::EthGetTransactionByHash); $callback!($crate::rpc::eth::EthGetTransactionByHashLimited); $callback!($crate::rpc::eth::EthGetTransactionCount); - $callback!($crate::rpc::eth::EthGetTransactionCountV2); $callback!($crate::rpc::eth::EthGetTransactionHashByCid); $callback!($crate::rpc::eth::EthGetTransactionByBlockNumberAndIndex); - $callback!($crate::rpc::eth::EthGetTransactionByBlockNumberAndIndexV2); $callback!($crate::rpc::eth::EthGetTransactionByBlockHashAndIndex); $callback!($crate::rpc::eth::EthMaxPriorityFeePerGas); $callback!($crate::rpc::eth::EthProtocolVersion); @@ -152,13 +140,10 @@ macro_rules! for_each_rpc_method { $callback!($crate::rpc::eth::EthSubscribe); $callback!($crate::rpc::eth::EthSyncing); $callback!($crate::rpc::eth::EthTraceBlock); - $callback!($crate::rpc::eth::EthTraceBlockV2); $callback!($crate::rpc::eth::EthTraceCall); $callback!($crate::rpc::eth::EthTraceFilter); - $callback!($crate::rpc::eth::EthTraceFilterV2); $callback!($crate::rpc::eth::EthTraceTransaction); $callback!($crate::rpc::eth::EthTraceReplayBlockTransactions); - $callback!($crate::rpc::eth::EthTraceReplayBlockTransactionsV2); $callback!($crate::rpc::eth::Web3ClientVersion); $callback!($crate::rpc::eth::EthSendRawTransaction); $callback!($crate::rpc::eth::EthSendRawTransactionUntrusted); diff --git a/src/rpc/reflect/mod.rs b/src/rpc/reflect/mod.rs index 14a9855bf9cc..2aab1b6bdf5a 100644 --- a/src/rpc/reflect/mod.rs +++ b/src/rpc/reflect/mod.rs @@ -286,19 +286,20 @@ pub trait RpcMethodExt: RpcMethod { Ok(()) } /// Returns [`Err`] if any of the parameters fail to serialize. - fn request(params: Self::Params) -> Result, serde_json::Error> { + fn request(params: Self::Params) -> serde_json::Result> { // hardcode calling convention because lotus is by-position only let params = Self::request_params(params)?; Ok(crate::rpc::Request { method_name: Self::NAME.into(), params, result_type: std::marker::PhantomData, - api_paths: Self::API_PATHS, + api_path: crate::rpc::Request::::max_api_path(Self::API_PATHS) + .map_err(serde_json::Error::custom)?, timeout: *crate::rpc::DEFAULT_REQUEST_TIMEOUT, }) } - fn request_params(params: Self::Params) -> Result { + fn request_params(params: Self::Params) -> serde_json::Result { // hardcode calling convention because lotus is by-position only Ok( match Self::build_params(params, ConcreteCallingConvention::ByPosition)? { @@ -335,7 +336,7 @@ pub trait RpcMethodExt: RpcMethod { method_name: name.into(), params, result_type: std::marker::PhantomData, - api_paths: Self::API_PATHS, + api_path: crate::rpc::Request::::max_api_path(Self::API_PATHS)?, timeout: *crate::rpc::DEFAULT_REQUEST_TIMEOUT, }) } @@ -362,6 +363,12 @@ pub trait RpcMethodExt: RpcMethod { .map(Self::Ok::from_lotus_json) } } + + fn api_path(ext: &http::Extensions) -> anyhow::Result { + ext.get::() + .copied() + .context("failed to resolve api path") + } } impl RpcMethodExt for T where T: RpcMethod {} diff --git a/src/rpc/request.rs b/src/rpc/request.rs index 42ab12c40685..1ea25ce4ac94 100644 --- a/src/rpc/request.rs +++ b/src/rpc/request.rs @@ -17,7 +17,7 @@ pub struct Request { #[serde(skip)] pub result_type: PhantomData, #[serde(skip)] - pub api_paths: BitFlags, + pub api_path: ApiPaths, #[serde(skip)] pub timeout: Duration, } @@ -32,13 +32,22 @@ impl Request { self } + pub fn set_api_path(&mut self, api_path: ApiPaths) { + self.api_path = api_path; + } + + pub fn with_api_path(mut self, api_path: ApiPaths) -> Self { + self.set_api_path(api_path); + self + } + /// Map type information about the response. pub fn map_ty(self) -> Request { Request { method_name: self.method_name, params: self.params, result_type: PhantomData, - api_paths: self.api_paths, + api_path: self.api_path, timeout: self.timeout, } } @@ -46,10 +55,6 @@ impl Request { pub fn max_api_path(api_paths: BitFlags) -> anyhow::Result { api_paths.iter().max().context("No supported versions") } - - pub fn api_path(&self) -> anyhow::Result { - Self::max_api_path(self.api_paths) - } } impl ToRpcParams for Request { diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap index 73b0a08377ce..c05c7b15059b 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap @@ -896,12 +896,12 @@ methods: $ref: "#/components/schemas/EthUint64" paramStructure: by-position - name: Filecoin.EthGetBlockTransactionCountByNumber - description: Returns the number of transactions in a block identified by its block number. + description: Returns the number of transactions in a block identified by its block number or a special tag. params: - name: blockNumber required: true schema: - $ref: "#/components/schemas/EthInt64" + $ref: "#/components/schemas/BlockNumberOrPredefined" result: name: Filecoin.EthGetBlockTransactionCountByNumber.Result required: true @@ -909,12 +909,12 @@ methods: $ref: "#/components/schemas/EthUint64" paramStructure: by-position - name: eth_getBlockTransactionCountByNumber - description: Returns the number of transactions in a block identified by its block number. + description: Returns the number of transactions in a block identified by its block number or a special tag. params: - name: blockNumber required: true schema: - $ref: "#/components/schemas/EthInt64" + $ref: "#/components/schemas/BlockNumberOrPredefined" result: name: eth_getBlockTransactionCountByNumber.Result required: true @@ -1531,7 +1531,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthTraceBlock.Result required: false @@ -1548,7 +1548,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: trace_block.Result required: false @@ -1633,7 +1633,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" - name: traceTypes required: true schema: @@ -1658,7 +1658,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" - name: traceTypes required: true schema: @@ -4585,7 +4585,7 @@ components: blockHash: $ref: "#/components/schemas/EthHash" blockNumber: - $ref: "#/components/schemas/EthUint64" + $ref: "#/components/schemas/EthInt64" chainId: $ref: "#/components/schemas/EthUint64" from: @@ -4839,7 +4839,7 @@ components: nonce: $ref: "#/components/schemas/Nonce" number: - $ref: "#/components/schemas/EthUint64" + $ref: "#/components/schemas/EthInt64" parentHash: $ref: "#/components/schemas/EthHash" receiptsRoot: @@ -5641,7 +5641,7 @@ components: blockHash: $ref: "#/components/schemas/EthHash" blockNumber: - $ref: "#/components/schemas/EthUint64" + $ref: "#/components/schemas/EthInt64" contractAddress: anyOf: - $ref: "#/components/schemas/EthAddress" @@ -5752,13 +5752,6 @@ components: type: integer format: uint32 minimum: 0 - ExtBlockNumberOrHash: - anyOf: - - type: string - - $ref: "#/components/schemas/EthInt64" - - $ref: "#/components/schemas/EthHash" - - $ref: "#/components/schemas/BlockNumber" - - $ref: "#/components/schemas/BlockHash" ExtendedSectorInfo: type: object properties: diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap index ea5c02599771..5a769b287320 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap @@ -892,12 +892,12 @@ methods: $ref: "#/components/schemas/EthUint64" paramStructure: by-position - name: Filecoin.EthGetBlockTransactionCountByNumber - description: Returns the number of transactions in a block identified by its block number. + description: Returns the number of transactions in a block identified by its block number or a special tag. params: - name: blockNumber required: true schema: - $ref: "#/components/schemas/EthInt64" + $ref: "#/components/schemas/BlockNumberOrPredefined" result: name: Filecoin.EthGetBlockTransactionCountByNumber.Result required: true @@ -905,12 +905,12 @@ methods: $ref: "#/components/schemas/EthUint64" paramStructure: by-position - name: eth_getBlockTransactionCountByNumber - description: Returns the number of transactions in a block identified by its block number. + description: Returns the number of transactions in a block identified by its block number or a special tag. params: - name: blockNumber required: true schema: - $ref: "#/components/schemas/EthInt64" + $ref: "#/components/schemas/BlockNumberOrPredefined" result: name: eth_getBlockTransactionCountByNumber.Result required: true @@ -1527,7 +1527,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthTraceBlock.Result required: false @@ -1544,7 +1544,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: trace_block.Result required: false @@ -1675,7 +1675,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" - name: traceTypes required: true schema: @@ -1700,7 +1700,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" - name: traceTypes required: true schema: @@ -4622,7 +4622,7 @@ components: blockHash: $ref: "#/components/schemas/EthHash" blockNumber: - $ref: "#/components/schemas/EthUint64" + $ref: "#/components/schemas/EthInt64" chainId: $ref: "#/components/schemas/EthUint64" from: @@ -4876,7 +4876,7 @@ components: nonce: $ref: "#/components/schemas/Nonce" number: - $ref: "#/components/schemas/EthUint64" + $ref: "#/components/schemas/EthInt64" parentHash: $ref: "#/components/schemas/EthHash" receiptsRoot: @@ -5879,7 +5879,7 @@ components: blockHash: $ref: "#/components/schemas/EthHash" blockNumber: - $ref: "#/components/schemas/EthUint64" + $ref: "#/components/schemas/EthInt64" contractAddress: anyOf: - $ref: "#/components/schemas/EthAddress" @@ -5990,13 +5990,6 @@ components: type: integer format: uint32 minimum: 0 - ExtBlockNumberOrHash: - anyOf: - - type: string - - $ref: "#/components/schemas/EthInt64" - - $ref: "#/components/schemas/EthHash" - - $ref: "#/components/schemas/BlockNumber" - - $ref: "#/components/schemas/BlockHash" ExtendedSectorInfo: type: object properties: diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap index 56a1f5085c2c..f7b79ee27fdb 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap @@ -16,11 +16,9 @@ methods: $ref: "#/components/schemas/TipsetSelector" result: name: Filecoin.ChainGetTipSet.Result - required: false + required: true schema: - anyOf: - - $ref: "#/components/schemas/Tipset" - - type: "null" + $ref: "#/components/schemas/Tipset" paramStructure: by-position - name: Forest.ChainExport params: @@ -143,7 +141,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthCall.Result required: true @@ -159,7 +157,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: eth_call.Result required: true @@ -192,7 +190,7 @@ methods: required: true schema: anyOf: - - $ref: "#/components/schemas/ExtBlockNumberOrHash" + - $ref: "#/components/schemas/BlockNumberOrHash" - type: "null" result: name: Filecoin.EthEstimateGas.Result @@ -210,7 +208,7 @@ methods: required: true schema: anyOf: - - $ref: "#/components/schemas/ExtBlockNumberOrHash" + - $ref: "#/components/schemas/BlockNumberOrHash" - type: "null" result: name: eth_estimateGas.Result @@ -227,7 +225,7 @@ methods: - name: newestBlockNumber required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrPredefined" - name: rewardPercentiles required: true schema: @@ -252,7 +250,7 @@ methods: - name: newestBlockNumber required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrPredefined" - name: rewardPercentiles required: true schema: @@ -296,7 +294,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthGetBalance.Result required: true @@ -313,7 +311,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: eth_getBalance.Result required: true @@ -392,7 +390,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthGetBlockReceipts.Result required: false @@ -409,7 +407,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: eth_getBlockReceipts.Result required: false @@ -426,7 +424,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" - name: limit required: true schema: @@ -448,7 +446,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" - name: limit required: true schema: @@ -524,7 +522,7 @@ methods: - name: blockNumberOrHash required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthGetCode.Result required: true @@ -541,7 +539,7 @@ methods: - name: blockNumberOrHash required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: eth_getCode.Result required: true @@ -664,7 +662,7 @@ methods: - name: blockNumberOrHash required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthGetStorageAt.Result required: true @@ -685,7 +683,7 @@ methods: - name: blockNumberOrHash required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: eth_getStorageAt.Result required: true @@ -767,7 +765,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthGetTransactionCount.Result required: true @@ -783,7 +781,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: eth_getTransactionCount.Result required: true @@ -1124,7 +1122,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthTraceBlock.Result required: false @@ -1141,7 +1139,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" result: name: trace_block.Result required: false @@ -1272,7 +1270,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" - name: traceTypes required: true schema: @@ -1297,7 +1295,7 @@ methods: - name: blockParam required: true schema: - $ref: "#/components/schemas/ExtBlockNumberOrHash" + $ref: "#/components/schemas/BlockNumberOrHash" - name: traceTypes required: true schema: @@ -1504,7 +1502,7 @@ components: blockHash: $ref: "#/components/schemas/EthHash" blockNumber: - $ref: "#/components/schemas/EthUint64" + $ref: "#/components/schemas/EthInt64" chainId: $ref: "#/components/schemas/EthUint64" from: @@ -1635,7 +1633,7 @@ components: nonce: $ref: "#/components/schemas/Nonce" number: - $ref: "#/components/schemas/EthUint64" + $ref: "#/components/schemas/EthInt64" parentHash: $ref: "#/components/schemas/EthHash" receiptsRoot: @@ -2352,7 +2350,7 @@ components: blockHash: $ref: "#/components/schemas/EthHash" blockNumber: - $ref: "#/components/schemas/EthUint64" + $ref: "#/components/schemas/EthInt64" contractAddress: anyOf: - $ref: "#/components/schemas/EthAddress" @@ -2401,13 +2399,6 @@ components: - type EthUint64: type: string - ExtBlockNumberOrHash: - anyOf: - - type: string - - $ref: "#/components/schemas/EthInt64" - - $ref: "#/components/schemas/EthHash" - - $ref: "#/components/schemas/BlockNumber" - - $ref: "#/components/schemas/BlockHash" FilecoinSnapshotVersion: type: string enum: diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index be9d42cd9fb8..be17feac6c72 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -439,14 +439,14 @@ impl ApiCommands { rpc::Request { method_name, params, - api_paths, + api_path, .. }, ignore, .. } in api_compare_tests::create_tests(create_tests_args).await? { - if !api_paths.contains(path) { + if api_path != path { continue; } if ignore.is_some() && !include_ignored { diff --git a/src/tool/subcommands/api_cmd/api_compare_tests.rs b/src/tool/subcommands/api_cmd/api_compare_tests.rs index d5cb6bd3167b..869365dfd887 100644 --- a/src/tool/subcommands/api_cmd/api_compare_tests.rs +++ b/src/tool/subcommands/api_cmd/api_compare_tests.rs @@ -5,21 +5,20 @@ use super::{CreateTestsArgs, ReportMode, RunIgnored, TestCriteriaOverride}; use crate::blocks::{ElectionProof, Ticket, Tipset}; use crate::chain::ChainStore; use crate::db::car::ManyCar; -use crate::eth::{EthChainId as EthChainIdType, SAFE_EPOCH_DELAY}; +use crate::eth::EthChainId as EthChainIdType; use crate::lotus_json::HasLotusJson; use crate::message::{Message as _, SignedMessage}; -use crate::rpc::FilterList; use crate::rpc::auth::AuthNewParams; use crate::rpc::beacon::BeaconGetEntry; use crate::rpc::eth::{ - BlockNumberOrHash, EthInt64, ExtBlockNumberOrHash, ExtPredefined, Predefined, - new_eth_tx_from_signed_message, types::*, + BlockNumberOrHash, EthInt64, Predefined, new_eth_tx_from_signed_message, types::*, }; use crate::rpc::gas::{GasEstimateGasLimit, GasEstimateMessageGas}; use crate::rpc::miner::BlockTemplate; use crate::rpc::misc::ActorEventFilter; use crate::rpc::state::StateGetAllClaims; use crate::rpc::types::*; +use crate::rpc::{ApiPaths, FilterList}; use crate::rpc::{Permission, prelude::*}; use crate::shim::actors::MarketActorStateLoad as _; use crate::shim::actors::market; @@ -70,6 +69,7 @@ use tokio::task::JoinSet; use tracing::debug; const COLLECTION_SAMPLE_SIZE: usize = 5; +const SAFE_EPOCH_DELAY_FOR_TESTING: i64 = 20; // `SAFE_HEIGHT_DISTANCE`(200) is too large for testing /// This address has been funded by the calibnet faucet and the private keys /// has been discarded. It should always have a non-zero balance. @@ -455,7 +455,7 @@ impl RpcTest { lotus_status, test_dump: Some(TestDump { request: self.request.clone(), - path: self.request.api_path().expect("invalid api paths"), + path: self.request.api_path, forest_response, lotus_response, }), @@ -700,61 +700,50 @@ fn node_tests() -> Vec { ] } -fn event_tests_with_tipset(_store: &Arc, tipset: &Tipset) -> Vec { +fn event_tests_with_tipset( + _store: &Arc, + tipset: &Tipset, +) -> anyhow::Result> { let epoch = tipset.epoch(); - vec![ - RpcTest::identity(GetActorEventsRaw::request((None,)).unwrap()) + Ok(vec![ + RpcTest::identity(GetActorEventsRaw::request((None,))?) .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - GetActorEventsRaw::request((Some(ActorEventFilter { - addresses: vec![], - fields: Default::default(), - from_height: Some(epoch), - to_height: Some(epoch), - tipset_key: None, - }),)) - .unwrap(), - ) + RpcTest::identity(GetActorEventsRaw::request((Some(ActorEventFilter { + addresses: vec![], + fields: Default::default(), + from_height: Some(epoch), + to_height: Some(epoch), + tipset_key: None, + }),))?) .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError) .sort_policy(SortPolicy::All), - RpcTest::identity( - GetActorEventsRaw::request((Some(ActorEventFilter { - addresses: vec![], - fields: Default::default(), - from_height: Some(epoch - 100), - to_height: Some(epoch), - tipset_key: None, - }),)) - .unwrap(), - ) + RpcTest::identity(GetActorEventsRaw::request((Some(ActorEventFilter { + addresses: vec![], + fields: Default::default(), + from_height: Some(epoch - 100), + to_height: Some(epoch), + tipset_key: None, + }),))?) .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError) .sort_policy(SortPolicy::All), - RpcTest::identity( - GetActorEventsRaw::request((Some(ActorEventFilter { - addresses: vec![], - fields: Default::default(), - from_height: None, - to_height: None, - tipset_key: Some(tipset.key().clone().into()), - }),)) - .unwrap(), - ) + RpcTest::identity(GetActorEventsRaw::request((Some(ActorEventFilter { + addresses: vec![], + fields: Default::default(), + from_height: None, + to_height: None, + tipset_key: Some(tipset.key().clone().into()), + }),))?) .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError) .sort_policy(SortPolicy::All), - RpcTest::identity( - GetActorEventsRaw::request((Some(ActorEventFilter { - addresses: vec![ - Address::from_str("t410fvtakbtytk4otbnfymn4zn5ow252nj7lcpbtersq") - .unwrap() - .into(), - ], - fields: Default::default(), - from_height: Some(epoch - 100), - to_height: Some(epoch), - tipset_key: None, - }),)) - .unwrap(), - ) + RpcTest::identity(GetActorEventsRaw::request((Some(ActorEventFilter { + addresses: vec![ + Address::from_str("t410fvtakbtytk4otbnfymn4zn5ow252nj7lcpbtersq")?.into(), + ], + fields: Default::default(), + from_height: Some(epoch - 100), + to_height: Some(epoch), + tipset_key: None, + }),))?) .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError) .sort_policy(SortPolicy::All), { @@ -765,9 +754,7 @@ fn event_tests_with_tipset(_store: &Arc, tipset: &Tipset) -> use crate::lotus_json::LotusJson; use crate::rpc::misc::ActorEventBlock; - let topic = BASE64_STANDARD - .decode("0Gprf0kYSUs3GSF9GAJ4bB9REqbB2I/iz+wAtFhPauw=") - .unwrap(); + let topic = BASE64_STANDARD.decode("0Gprf0kYSUs3GSF9GAJ4bB9REqbB2I/iz+wAtFhPauw=")?; let mut fields: BTreeMap> = Default::default(); fields.insert( "t1".into(), @@ -776,20 +763,17 @@ fn event_tests_with_tipset(_store: &Arc, tipset: &Tipset) -> value: LotusJson(topic), }], ); - RpcTest::identity( - GetActorEventsRaw::request((Some(ActorEventFilter { - addresses: vec![], - fields, - from_height: Some(epoch - 100), - to_height: Some(epoch), - tipset_key: None, - }),)) - .unwrap(), - ) + RpcTest::identity(GetActorEventsRaw::request((Some(ActorEventFilter { + addresses: vec![], + fields, + from_height: Some(epoch - 100), + to_height: Some(epoch), + tipset_key: None, + }),))?) .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError) .sort_policy(SortPolicy::All) }, - ] + ]) } fn miner_tests_with_tipset( @@ -1381,76 +1365,75 @@ fn wallet_tests(worker_address: Option
) -> Vec { tests } -fn eth_tests() -> Vec { +fn eth_tests() -> anyhow::Result> { let mut tests = vec![]; for use_alias in [false, true] { - tests.push(RpcTest::identity( - EthAccounts::request_with_alias((), use_alias).unwrap(), - )); - tests.push(RpcTest::basic( - EthBlockNumber::request_with_alias((), use_alias).unwrap(), - )); - tests.push(RpcTest::identity( - EthChainId::request_with_alias((), use_alias).unwrap(), - )); + tests.push(RpcTest::identity(EthAccounts::request_with_alias( + (), + use_alias, + )?)); + tests.push(RpcTest::basic(EthBlockNumber::request_with_alias( + (), + use_alias, + )?)); + tests.push(RpcTest::identity(EthChainId::request_with_alias( + (), + use_alias, + )?)); // There is randomness in the result of this API, but at least check that the results are non-zero. tests.push(RpcTest::validate( - EthGasPrice::request_with_alias((), use_alias).unwrap(), + EthGasPrice::request_with_alias((), use_alias)?, |forest, lotus| forest.0.is_positive() && lotus.0.is_positive(), )); - tests.push(RpcTest::basic( - EthSyncing::request_with_alias((), use_alias).unwrap(), - )); - tests.push(RpcTest::identity( - EthGetBalance::request_with_alias( - ( - EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Latest), - ), - use_alias, - ) - .unwrap(), - )); - tests.push(RpcTest::identity( - EthGetBalance::request_with_alias( - ( - EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Pending), - ), - use_alias, - ) - .unwrap(), - )); - tests.push(RpcTest::basic( - Web3ClientVersion::request_with_alias((), use_alias).unwrap(), - )); - tests.push(RpcTest::basic( - EthMaxPriorityFeePerGas::request_with_alias((), use_alias).unwrap(), - )); - tests.push(RpcTest::identity( - EthProtocolVersion::request_with_alias((), use_alias).unwrap(), - )); + tests.push(RpcTest::basic(EthSyncing::request_with_alias( + (), + use_alias, + )?)); + tests.push(RpcTest::identity(EthGetBalance::request_with_alias( + ( + EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa")?, + Predefined::Latest.into(), + ), + use_alias, + )?)); + tests.push(RpcTest::identity(EthGetBalance::request_with_alias( + ( + EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa")?, + Predefined::Pending.into(), + ), + use_alias, + )?)); + tests.push(RpcTest::basic(Web3ClientVersion::request_with_alias( + (), + use_alias, + )?)); + tests.push(RpcTest::basic(EthMaxPriorityFeePerGas::request_with_alias( + (), + use_alias, + )?)); + tests.push(RpcTest::identity(EthProtocolVersion::request_with_alias( + (), + use_alias, + )?)); let cases = [ ( - Some(EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb").unwrap()), + Some(EthAddress::from_str( + "0x0c1d86d34e469770339b53613f3a2343accd62cb", + )?), Some( "0xf8b2cb4f000000000000000000000000CbfF24DED1CE6B53712078759233Ac8f91ea71B6" - .parse() - .unwrap(), + .parse()?, ), ), - (Some(EthAddress::from_str(ZERO_ADDRESS).unwrap()), None), + (Some(EthAddress::from_str(ZERO_ADDRESS)?), None), // Assert contract creation, which is invoked via setting the `to` field to `None` and // providing the contract bytecode in the `data` field. ( None, - Some( - EthBytes::from_str( - concat!("0x", include_str!("contracts/cthulhu/invoke.hex")).trim(), - ) - .unwrap(), - ), + Some(EthBytes::from_str( + concat!("0x", include_str!("contracts/cthulhu/invoke.hex")).trim(), + )?), ), ]; @@ -1461,88 +1444,81 @@ fn eth_tests() -> Vec { ..EthCallMessage::default() }; - tests.push(RpcTest::identity( - EthCall::request_with_alias( - ( - msg.clone(), - BlockNumberOrHash::from_predefined(Predefined::Latest), - ), - use_alias, - ) - .unwrap(), - )); + tests.push(RpcTest::identity(EthCall::request_with_alias( + (msg.clone(), Predefined::Latest.into()), + use_alias, + )?)); - for tag in [ - ExtPredefined::Latest, - ExtPredefined::Safe, - ExtPredefined::Finalized, - ] { - tests.push(RpcTest::identity( - EthCallV2::request_with_alias( - (msg.clone(), ExtBlockNumberOrHash::PredefinedBlock(tag)), - use_alias, - ) - .unwrap(), - )); + for tag in [Predefined::Latest, Predefined::Safe, Predefined::Finalized] { + for api_path in [ApiPaths::V1, ApiPaths::V2] { + tests.push(RpcTest::identity( + EthCall::request_with_alias( + (msg.clone(), BlockNumberOrHash::PredefinedBlock(tag)), + use_alias, + )? + .with_api_path(api_path), + )); + } } } let cases = [ Some(EthAddressList::List(vec![])), Some(EthAddressList::List(vec![ - EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb").unwrap(), - EthAddress::from_str("0x89beb26addec4bc7e9f475aacfd084300d6de719").unwrap(), + EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb")?, + EthAddress::from_str("0x89beb26addec4bc7e9f475aacfd084300d6de719")?, ])), - Some(EthAddressList::Single( - EthAddress::from_str("0x0c1d86d34e469770339b53613f3a2343accd62cb").unwrap(), - )), + Some(EthAddressList::Single(EthAddress::from_str( + "0x0c1d86d34e469770339b53613f3a2343accd62cb", + )?)), None, ]; for address in cases { - tests.push(RpcTest::basic( - EthNewFilter::request_with_alias( - (EthFilterSpec { - address, - ..Default::default() - },), - use_alias, - ) - .unwrap(), - )); + tests.push(RpcTest::basic(EthNewFilter::request_with_alias( + (EthFilterSpec { + address, + ..Default::default() + },), + use_alias, + )?)); } tests.push(RpcTest::basic( - EthNewPendingTransactionFilter::request_with_alias((), use_alias).unwrap(), - )); - tests.push(RpcTest::basic( - EthNewBlockFilter::request_with_alias((), use_alias).unwrap(), - )); - tests.push(RpcTest::identity( - EthUninstallFilter::request_with_alias((FilterID::new().unwrap(),), use_alias).unwrap(), - )); - tests.push(RpcTest::identity( - EthAddressToFilecoinAddress::request(("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa" - .parse() - .unwrap(),)) - .unwrap(), - )); - tests.push(RpcTest::identity( - FilecoinAddressToEthAddress::request((*KNOWN_CALIBNET_F0_ADDRESS, None)).unwrap(), - )); - tests.push(RpcTest::identity( - FilecoinAddressToEthAddress::request((*KNOWN_CALIBNET_F1_ADDRESS, None)).unwrap(), - )); - tests.push(RpcTest::identity( - FilecoinAddressToEthAddress::request((*KNOWN_CALIBNET_F2_ADDRESS, None)).unwrap(), - )); - tests.push(RpcTest::identity( - FilecoinAddressToEthAddress::request((*KNOWN_CALIBNET_F3_ADDRESS, None)).unwrap(), - )); - tests.push(RpcTest::identity( - FilecoinAddressToEthAddress::request((*KNOWN_CALIBNET_F4_ADDRESS, None)).unwrap(), + EthNewPendingTransactionFilter::request_with_alias((), use_alias)?, )); + tests.push(RpcTest::basic(EthNewBlockFilter::request_with_alias( + (), + use_alias, + )?)); + tests.push(RpcTest::identity(EthUninstallFilter::request_with_alias( + (FilterID::new()?,), + use_alias, + )?)); + tests.push(RpcTest::identity(EthAddressToFilecoinAddress::request(( + "0xff38c072f286e3b20b3954ca9f99c05fbecc64aa".parse()?, + ))?)); + tests.push(RpcTest::identity(FilecoinAddressToEthAddress::request(( + *KNOWN_CALIBNET_F0_ADDRESS, + None, + ))?)); + tests.push(RpcTest::identity(FilecoinAddressToEthAddress::request(( + *KNOWN_CALIBNET_F1_ADDRESS, + None, + ))?)); + tests.push(RpcTest::identity(FilecoinAddressToEthAddress::request(( + *KNOWN_CALIBNET_F2_ADDRESS, + None, + ))?)); + tests.push(RpcTest::identity(FilecoinAddressToEthAddress::request(( + *KNOWN_CALIBNET_F3_ADDRESS, + None, + ))?)); + tests.push(RpcTest::identity(FilecoinAddressToEthAddress::request(( + *KNOWN_CALIBNET_F4_ADDRESS, + None, + ))?)); } - tests + Ok(tests) } fn eth_call_api_err_tests(epoch: i64) -> Vec { @@ -1575,1005 +1551,639 @@ fn eth_call_api_err_tests(epoch: i64) -> Vec { let eth_call_request = EthCall::request((msg.clone(), BlockNumberOrHash::from_block_number(epoch))).unwrap(); - - tests.push( - RpcTest::identity(eth_call_request) + tests.extend([ + RpcTest::identity(eth_call_request.clone().with_api_path(ApiPaths::V1)) .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), - ); - - let eth_call_v2_request = - EthCallV2::request((msg, ExtBlockNumberOrHash::from_block_number(epoch))).unwrap(); - - tests.push( - RpcTest::identity(eth_call_v2_request) + RpcTest::identity(eth_call_request.with_api_path(ApiPaths::V2)) .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), - ); + ]); } tests } -fn eth_tests_with_tipset(store: &Arc, shared_tipset: &Tipset) -> Vec { - let block_cid = shared_tipset.key().cid().unwrap(); +fn eth_tests_with_tipset( + store: &Arc, + shared_tipset: &Tipset, +) -> anyhow::Result> { + let block_cid = shared_tipset.key().cid()?; let block_hash: EthHash = block_cid.into(); let mut tests = vec![ - RpcTest::identity( - EthGetBalance::request(( - EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(), - BlockNumberOrHash::from_block_number(shared_tipset.epoch()), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalance::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_block_number(shared_tipset.epoch()), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalance::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_block_number_object(shared_tipset.epoch()), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalance::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_block_hash_object(block_hash, false), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalance::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_block_hash_object(block_hash, true), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalance::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Earliest), - )) - .unwrap(), - ) + RpcTest::identity(EthGetBlockReceipts::request(( + BlockNumberOrHash::from_block_hash_object(block_hash, true), + ))?), + RpcTest::identity(EthGetTransactionByBlockHashAndIndex::request(( + block_hash, + 0.into(), + ))?) + .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), + RpcTest::identity(EthGetBlockByHash::request((block_hash, false))?), + RpcTest::identity(EthGetBlockByHash::request((block_hash, true))?), + RpcTest::identity(EthGetLogs::request((EthFilterSpec { + from_block: Some(format!("0x{:x}", shared_tipset.epoch())), + to_block: Some(format!("0x{:x}", shared_tipset.epoch())), + ..Default::default() + },))?) + .sort_policy(SortPolicy::All) .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthGetBalance::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Pending), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetBalance::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalance::request(( - generate_eth_random_address().unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalanceV2::request(( - EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa").unwrap(), - ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalanceV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalanceV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_block_number_object(shared_tipset.epoch()), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalanceV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_block_hash_object(block_hash, false), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalanceV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_block_hash_object(block_hash, true), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalanceV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Earliest), - )) - .unwrap(), - ) + RpcTest::identity(EthGetLogs::request((EthFilterSpec { + from_block: Some(format!("0x{:x}", shared_tipset.epoch())), + to_block: Some(format!("0x{:x}", shared_tipset.epoch())), + address: Some(EthAddressList::List(Vec::new())), + ..Default::default() + },))?) + .sort_policy(SortPolicy::All) .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthGetBalanceV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Pending), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetBalanceV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetBalanceV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetBalanceV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBalanceV2::request(( - generate_eth_random_address().unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockByNumber::request(( - BlockNumberOrPredefined::BlockNumber(EthInt64(shared_tipset.epoch())), - false, - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockByNumber::request(( - BlockNumberOrPredefined::BlockNumber(EthInt64(shared_tipset.epoch())), - true, - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockByNumber::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Earliest), - true, - )) - .unwrap(), - ) + RpcTest::identity(EthGetLogs::request((EthFilterSpec { + from_block: Some(format!("0x{:x}", shared_tipset.epoch() - 100)), + to_block: Some(format!("0x{:x}", shared_tipset.epoch())), + ..Default::default() + },))?) + .sort_policy(SortPolicy::All) .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthGetBlockByNumber::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Pending), - true, - )) - .unwrap(), - ), - RpcTest::basic( - EthGetBlockByNumber::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Latest), - true, - )) - .unwrap(), - ), - RpcTest::basic( - EthGetBlockByNumber::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe), - true, - )) - .unwrap(), - ), - RpcTest::basic( - EthGetBlockByNumber::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized), - true, - )) - .unwrap(), - ), + RpcTest::identity(EthGetLogs::request((EthFilterSpec { + address: Some(EthAddressList::Single(EthAddress::from_str( + "0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44", + )?)), + ..Default::default() + },))?) + .sort_policy(SortPolicy::All) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::identity(EthGetFilterLogs::request((FilterID::new()?,))?) + .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), + RpcTest::identity(EthGetFilterChanges::request((FilterID::new()?,))?) + .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), + RpcTest::identity(EthGetTransactionHashByCid::request((block_cid,))?), RpcTest::identity( - EthGetBlockByNumberV2::request(( - BlockNumberOrPredefined::BlockNumber(EthInt64(shared_tipset.epoch())), - false, + EthGetTransactionReceipt::request(( + // A transaction that should not exist, to test the `null` response in case + // of missing transaction. + EthHash::from_str( + "0xf234567890123456789d6a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f70809", + ) + .unwrap(), )) .unwrap(), ), - RpcTest::identity( - EthGetBlockByNumberV2::request(( - BlockNumberOrPredefined::BlockNumber(EthInt64(shared_tipset.epoch())), - true, - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockByNumberV2::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Earliest), - true, - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthGetBlockByNumberV2::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Pending), - true, - )) - .unwrap(), - ), - RpcTest::basic( - EthGetBlockByNumberV2::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Latest), - true, - )) - .unwrap(), - ), - RpcTest::basic( - EthGetBlockByNumberV2::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe), - true, - )) - .unwrap(), - ), - RpcTest::basic( - EthGetBlockByNumberV2::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized), - true, - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockReceipts::request((BlockNumberOrHash::from_block_hash_object( - block_hash, true, - ),)) - .unwrap(), - ), - // Nodes might be synced to different epochs, so we can't assert the exact result here. - // Regardless, we want to check if the node returns a valid response and accepts predefined - // values. - RpcTest::basic( - EthGetBlockReceipts::request((BlockNumberOrHash::from_predefined(Predefined::Latest),)) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockReceiptsV2::request((ExtBlockNumberOrHash::from_block_hash_object( - block_hash, true, - ),)) - .unwrap(), - ), - RpcTest::basic( - EthGetBlockReceiptsV2::request((ExtBlockNumberOrHash::from_predefined( - ExtPredefined::Safe, - ),)) - .unwrap(), - ), - RpcTest::basic( - EthGetBlockReceiptsV2::request((ExtBlockNumberOrHash::from_predefined( - ExtPredefined::Finalized, - ),)) - .unwrap(), - ), - RpcTest::identity(EthGetBlockTransactionCountByHash::request((block_hash,)).unwrap()), - RpcTest::identity( - EthGetBlockReceiptsLimited::request(( - BlockNumberOrHash::from_block_hash_object(block_hash, true), - 4, - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), - RpcTest::identity( - EthGetBlockReceiptsLimited::request(( - BlockNumberOrHash::from_block_hash_object(block_hash, true), - -1, - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockReceiptsLimitedV2::request(( - ExtBlockNumberOrHash::from_block_hash_object(block_hash, true), - 4, - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), - RpcTest::identity( - EthGetBlockReceiptsLimitedV2::request(( - ExtBlockNumberOrHash::from_block_hash_object(block_hash, true), - -1, - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockTransactionCountByNumber::request((EthInt64(shared_tipset.epoch()),)) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockTransactionCountByNumberV2::request((BlockNumberOrPredefined::BlockNumber( - EthInt64(shared_tipset.epoch()), - ),)) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockTransactionCountByNumberV2::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetBlockTransactionCountByNumberV2::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetTransactionCount::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_block_hash_object(block_hash, true), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetTransactionCount::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Earliest), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthGetTransactionCount::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Pending), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetTransactionCount::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetTransactionCount::request(( - generate_eth_random_address().unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetTransactionCountV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_block_hash_object(block_hash, true), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetTransactionCountV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Earliest), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthGetTransactionCountV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Pending), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetTransactionCountV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetTransactionCountV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetTransactionCountV2::request(( - EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetTransactionCountV2::request(( - generate_eth_random_address().unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetStorageAt::request(( - // https://filfox.info/en/address/f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - EthBytes(vec![0xa]), - BlockNumberOrHash::BlockNumber(EthInt64(shared_tipset.epoch())), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetStorageAt::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - EthBytes(vec![0xa]), - BlockNumberOrHash::from_predefined(Predefined::Earliest), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthGetStorageAt::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - EthBytes(vec![0xa]), - BlockNumberOrHash::from_predefined(Predefined::Pending), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetStorageAt::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - EthBytes(vec![0xa]), - BlockNumberOrHash::from_predefined(Predefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetStorageAt::request(( - generate_eth_random_address().unwrap(), - EthBytes(vec![0x0]), - BlockNumberOrHash::from_predefined(Predefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetStorageAtV2::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - EthBytes(vec![0xa]), - ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetStorageAtV2::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - EthBytes(vec![0xa]), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetStorageAtV2::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - EthBytes(vec![0xa]), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetStorageAtV2::request(( - generate_eth_random_address().unwrap(), - EthBytes(vec![0x0]), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthFeeHistory::request(( - 10.into(), - BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()), - None, - )) - .unwrap(), - ), - RpcTest::identity( - EthFeeHistory::request(( - 10.into(), - BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()), - Some(vec![10., 50., 90.]), - )) - .unwrap(), - ), - RpcTest::identity( - EthFeeHistory::request(( - 10.into(), - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Earliest), - None, - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthFeeHistory::request(( - 10.into(), - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Pending), - Some(vec![10., 50., 90.]), - )) - .unwrap(), - ), - RpcTest::basic( - EthFeeHistory::request(( - 10.into(), - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Latest), - None, - )) - .unwrap(), - ), - RpcTest::basic( - EthFeeHistory::request(( - 10.into(), - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe), - None, - )) - .unwrap(), - ), - RpcTest::basic( - EthFeeHistory::request(( - 10.into(), - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized), - Some(vec![10., 50., 90.]), - )) - .unwrap(), - ), - RpcTest::identity( - EthFeeHistoryV2::request(( - 10.into(), - ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()), - None, - )) - .unwrap(), - ), - RpcTest::identity( - EthFeeHistoryV2::request(( - 10.into(), - ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()), - Some(vec![10., 50., 90.]), - )) - .unwrap(), - ), - RpcTest::identity( - EthFeeHistoryV2::request(( - 10.into(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Earliest), - None, - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthFeeHistoryV2::request(( - 10.into(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Pending), - Some(vec![10., 50., 90.]), - )) - .unwrap(), - ), - RpcTest::basic( - EthFeeHistoryV2::request(( - 10.into(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest), - None, - )) - .unwrap(), - ), - RpcTest::basic( - EthFeeHistoryV2::request(( - 10.into(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe), - None, - )) - .unwrap(), - ), - RpcTest::basic( - EthFeeHistoryV2::request(( - 10.into(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized), - Some(vec![10., 50., 90.]), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetCode::request(( - // https://filfox.info/en/address/f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - BlockNumberOrHash::from_block_number(shared_tipset.epoch()), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetCode::request(( - // https://filfox.info/en/address/f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq - Address::from_str("f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq") - .unwrap() - .try_into() - .unwrap(), - BlockNumberOrHash::from_block_number(shared_tipset.epoch()), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetCode::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Earliest), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthGetCode::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Pending), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetCode::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetCode::request(( - generate_eth_random_address().unwrap(), - BlockNumberOrHash::from_predefined(Predefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetCodeV2::request(( - // https://filfox.info/en/address/f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetCodeV2::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe), - )) - .unwrap(), - ), - RpcTest::basic( - EthGetCodeV2::request(( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Finalized), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetCodeV2::request(( - generate_eth_random_address().unwrap(), - ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest), - )) - .unwrap(), - ), - RpcTest::identity( - EthGetTransactionByBlockNumberAndIndex::request(( - BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()), - 0.into(), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - EthGetTransactionByBlockNumberAndIndex::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Earliest), - 0.into(), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - EthGetTransactionByBlockNumberAndIndex::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Pending), - 0.into(), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - EthGetTransactionByBlockNumberAndIndex::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Latest), - 0.into(), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - EthGetTransactionByBlockNumberAndIndexV2::request(( - BlockNumberOrPredefined::BlockNumber(shared_tipset.epoch().into()), - 0.into(), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - EthGetTransactionByBlockNumberAndIndexV2::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Safe), - 0.into(), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - EthGetTransactionByBlockNumberAndIndexV2::request(( - BlockNumberOrPredefined::PredefinedBlock(ExtPredefined::Finalized), - 0.into(), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - EthGetTransactionByBlockHashAndIndex::request((block_hash, 0.into())).unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), - RpcTest::identity(EthGetBlockByHash::request((block_hash, false)).unwrap()), - RpcTest::identity(EthGetBlockByHash::request((block_hash, true)).unwrap()), - RpcTest::identity( - EthGetLogs::request((EthFilterSpec { - from_block: Some(format!("0x{:x}", shared_tipset.epoch())), - to_block: Some(format!("0x{:x}", shared_tipset.epoch())), - ..Default::default() - },)) - .unwrap(), - ) - .sort_policy(SortPolicy::All) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - EthGetLogs::request((EthFilterSpec { - from_block: Some(format!("0x{:x}", shared_tipset.epoch())), - to_block: Some(format!("0x{:x}", shared_tipset.epoch())), - address: Some(EthAddressList::List(Vec::new())), - ..Default::default() - },)) - .unwrap(), - ) - .sort_policy(SortPolicy::All) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - EthGetLogs::request((EthFilterSpec { - from_block: Some(format!("0x{:x}", shared_tipset.epoch() - 100)), - to_block: Some(format!("0x{:x}", shared_tipset.epoch())), - ..Default::default() - },)) - .unwrap(), - ) - .sort_policy(SortPolicy::All) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity( - EthGetLogs::request((EthFilterSpec { - address: Some(EthAddressList::Single( - EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44").unwrap(), - )), - ..Default::default() - },)) - .unwrap(), - ) - .sort_policy(SortPolicy::All) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::identity(EthGetFilterLogs::request((FilterID::new().unwrap(),)).unwrap()) + ]; + + for api_path in [ApiPaths::V1, ApiPaths::V2] { + tests.extend([ + // Nodes might be synced to different epochs, so we can't assert the exact result here. + // Regardless, we want to check if the node returns a valid response and accepts predefined + // values. + RpcTest::basic( + EthGetBlockReceipts::request((Predefined::Latest.into(),))?.with_api_path(api_path), + ), + RpcTest::basic( + EthGetBlockReceipts::request((Predefined::Latest.into(),))?.with_api_path(api_path), + ), + RpcTest::basic( + EthGetBlockReceipts::request((Predefined::Latest.into(),))?.with_api_path(api_path), + ), + RpcTest::identity( + EthGetBlockReceipts::request((BlockNumberOrHash::from_block_hash_object( + block_hash, true, + ),))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBlockTransactionCountByHash::request((block_hash,))?.with_api_path(api_path), + ), + RpcTest::identity( + EthGetBlockReceiptsLimited::request(( + BlockNumberOrHash::from_block_hash_object(block_hash, true), + 4, + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), + RpcTest::identity( + EthGetBlockReceiptsLimited::request(( + BlockNumberOrHash::from_block_hash_object(block_hash, true), + -1, + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBlockTransactionCountByNumber::request(( + EthInt64(shared_tipset.epoch()).into(), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBlockTransactionCountByNumber::request((Predefined::Latest.into(),))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBlockTransactionCountByNumber::request((Predefined::Safe.into(),))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBlockTransactionCountByNumber::request((Predefined::Finalized.into(),))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBlockByNumber::request((EthInt64(shared_tipset.epoch()).into(), false))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBlockByNumber::request((EthInt64(shared_tipset.epoch()).into(), true))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBlockByNumber::request((Predefined::Earliest.into(), true))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::basic( + EthGetBlockByNumber::request((Predefined::Pending.into(), true))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetBlockByNumber::request((Predefined::Latest.into(), true))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetBlockByNumber::request((Predefined::Safe.into(), true))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetBlockByNumber::request((Predefined::Finalized.into(), true))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBalance::request(( + generate_eth_random_address()?, + Predefined::Latest.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBalance::request(( + EthAddress::from_str("0xff38c072f286e3b20b3954ca9f99c05fbecc64aa")?, + BlockNumberOrHash::from_block_number(shared_tipset.epoch()), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBalance::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + BlockNumberOrHash::from_block_number(shared_tipset.epoch()), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBalance::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + BlockNumberOrHash::from_block_number_object(shared_tipset.epoch()), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBalance::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + BlockNumberOrHash::from_block_hash_object(block_hash, false), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBalance::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + BlockNumberOrHash::from_block_hash_object(block_hash, true), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBalance::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + Predefined::Earliest.into(), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::basic( + EthGetBalance::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + Predefined::Pending.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetBalance::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + Predefined::Latest.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetBalance::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + Predefined::Safe.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetBalance::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + Predefined::Finalized.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetBalance::request(( + generate_eth_random_address()?, + Predefined::Latest.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthFeeHistory::request((10.into(), EthInt64(shared_tipset.epoch()).into(), None))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthFeeHistory::request(( + 10.into(), + EthInt64(shared_tipset.epoch()).into(), + Some(vec![10., 50., 90.]), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthFeeHistory::request((10.into(), Predefined::Earliest.into(), None))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::basic( + EthFeeHistory::request(( + 10.into(), + Predefined::Pending.into(), + Some(vec![10., 50., 90.]), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthFeeHistory::request((10.into(), Predefined::Latest.into(), None))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthFeeHistory::request((10.into(), Predefined::Safe.into(), None))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthFeeHistory::request(( + 10.into(), + Predefined::Finalized.into(), + Some(vec![10., 50., 90.]), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetCode::request(( + // https://filfox.info/en/address/f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + BlockNumberOrHash::from_block_number(shared_tipset.epoch()), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetCode::request(( + // https://filfox.info/en/address/f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq + Address::from_str("f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq")? + .try_into()?, + BlockNumberOrHash::from_block_number(shared_tipset.epoch()), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetCode::request(( + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + Predefined::Earliest.into(), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::basic( + EthGetCode::request(( + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + Predefined::Pending.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetCode::request(( + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + Predefined::Safe.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetCode::request(( + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + Predefined::Finalized.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetCode::request(( + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + Predefined::Latest.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetCode::request((generate_eth_random_address()?, Predefined::Latest.into()))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetStorageAt::request(( + // https://filfox.info/en/address/f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + EthBytes(vec![0xa]), + BlockNumberOrHash::BlockNumber(EthInt64(shared_tipset.epoch())), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetStorageAt::request(( + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + EthBytes(vec![0xa]), + Predefined::Earliest.into(), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::basic( + EthGetStorageAt::request(( + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + EthBytes(vec![0xa]), + Predefined::Pending.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetStorageAt::request(( + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + EthBytes(vec![0xa]), + Predefined::Latest.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetStorageAt::request(( + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + EthBytes(vec![0xa]), + Predefined::Safe.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetStorageAt::request(( + EthAddress::from_str("0x7B90337f65fAA2B2B8ed583ba1Ba6EB0C9D7eA44")?, + EthBytes(vec![0xa]), + Predefined::Finalized.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetStorageAt::request(( + generate_eth_random_address()?, + EthBytes(vec![0x0]), + Predefined::Latest.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetTransactionCount::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + BlockNumberOrHash::from_block_hash_object(block_hash, true), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetTransactionCount::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + Predefined::Earliest.into(), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::basic( + EthGetTransactionCount::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + Predefined::Pending.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetTransactionCount::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + Predefined::Latest.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetTransactionCount::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + Predefined::Safe.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::basic( + EthGetTransactionCount::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec")?, + Predefined::Finalized.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetTransactionCount::request(( + generate_eth_random_address()?, + Predefined::Latest.into(), + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthGetTransactionByBlockNumberAndIndex::request(( + EthInt64(shared_tipset.epoch()).into(), + 0.into(), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::identity( + EthGetTransactionByBlockNumberAndIndex::request(( + Predefined::Earliest.into(), + 0.into(), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::identity( + EthGetTransactionByBlockNumberAndIndex::request(( + Predefined::Pending.into(), + 0.into(), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::identity( + EthGetTransactionByBlockNumberAndIndex::request(( + Predefined::Latest.into(), + 0.into(), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::identity( + EthGetTransactionByBlockNumberAndIndex::request(( + Predefined::Safe.into(), + 0.into(), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::identity( + EthGetTransactionByBlockNumberAndIndex::request(( + Predefined::Finalized.into(), + 0.into(), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::identity( + EthTraceBlock::request((BlockNumberOrHash::from_block_number( + shared_tipset.epoch(), + ),))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthTraceBlock::request((Predefined::Earliest.into(),))?.with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), + RpcTest::basic( + EthTraceBlock::request((Predefined::Pending.into(),))?.with_api_path(api_path), + ), + RpcTest::basic( + EthTraceBlock::request((Predefined::Latest.into(),))?.with_api_path(api_path), + ), + RpcTest::basic( + EthTraceBlock::request((Predefined::Safe.into(),))?.with_api_path(api_path), + ), + RpcTest::basic( + EthTraceBlock::request((Predefined::Finalized.into(),))?.with_api_path(api_path), + ), + RpcTest::identity( + EthTraceReplayBlockTransactions::request(( + BlockNumberOrHash::from_block_number(shared_tipset.epoch()), + vec!["trace".to_string()], + ))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthTraceFilter::request((EthTraceFilterCriteria { + from_block: Some(format!("0x{:x}", shared_tipset.epoch() - 100)), + to_block: Some(format!( + "0x{:x}", + shared_tipset.epoch() - SAFE_EPOCH_DELAY_FOR_TESTING + )), + ..Default::default() + },))? + .with_api_path(api_path), + ) + // both nodes could fail on, e.g., "too many results, maximum supported is 500, try paginating + // requests with After and Count" .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), - RpcTest::identity(EthGetFilterChanges::request((FilterID::new().unwrap(),)).unwrap()) + RpcTest::identity( + EthTraceFilter::request((EthTraceFilterCriteria { + from_block: Some(format!( + "0x{:x}", + shared_tipset.epoch() - (SAFE_EPOCH_DELAY_FOR_TESTING + 1) + )), + to_block: Some(format!( + "0x{:x}", + shared_tipset.epoch() - SAFE_EPOCH_DELAY_FOR_TESTING + )), + ..Default::default() + },))? + .with_api_path(api_path), + ) .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), - RpcTest::identity(EthGetTransactionHashByCid::request((block_cid,)).unwrap()), - RpcTest::identity( - EthTraceBlock::request((ExtBlockNumberOrHash::from_block_number( - shared_tipset.epoch(), - ),)) - .unwrap(), - ), - RpcTest::identity( - EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined( - ExtPredefined::Earliest, - ),)) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithQuasiIdenticalError), - RpcTest::basic( - EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined( - ExtPredefined::Pending, - ),)) - .unwrap(), - ), - RpcTest::basic( - EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(ExtPredefined::Latest),)) - .unwrap(), - ), - RpcTest::basic( - EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),)) - .unwrap(), - ), - RpcTest::basic( - EthTraceBlock::request((ExtBlockNumberOrHash::from_predefined( - ExtPredefined::Finalized, - ),)) - .unwrap(), - ), - RpcTest::identity( - EthTraceBlockV2::request((ExtBlockNumberOrHash::from_block_number( - shared_tipset.epoch(), - ),)) - .unwrap(), - ), - RpcTest::basic( - EthTraceBlockV2::request((ExtBlockNumberOrHash::from_predefined( - ExtPredefined::Pending, - ),)) - .unwrap(), - ), - RpcTest::basic( - EthTraceBlockV2::request((ExtBlockNumberOrHash::from_predefined( - ExtPredefined::Latest, - ),)) - .unwrap(), - ), - RpcTest::basic( - EthTraceBlockV2::request((ExtBlockNumberOrHash::from_predefined(ExtPredefined::Safe),)) - .unwrap(), - ), - RpcTest::basic( - EthTraceBlockV2::request((ExtBlockNumberOrHash::from_predefined( - ExtPredefined::Finalized, - ),)) - .unwrap(), - ), - RpcTest::identity( - EthTraceReplayBlockTransactions::request(( - ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()), - vec!["trace".to_string()], - )) - .unwrap(), - ), - RpcTest::identity( - EthTraceReplayBlockTransactionsV2::request(( - ExtBlockNumberOrHash::from_block_number(shared_tipset.epoch()), - vec!["trace".to_string()], - )) - .unwrap(), - ), - RpcTest::identity( - EthTraceFilter::request((EthTraceFilterCriteria { - from_block: Some(format!("0x{:x}", shared_tipset.epoch() - 100)), - to_block: Some(format!("0x{:x}", shared_tipset.epoch() - SAFE_EPOCH_DELAY)), - ..Default::default() - },)) - .unwrap(), - ) - // both nodes could fail on, e.g., "too many results, maximum supported is 500, try paginating - // requests with After and Count" - .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), - RpcTest::identity( - EthTraceFilter::request((EthTraceFilterCriteria { - from_block: Some(ExtPredefined::Safe.to_string()), - count: Some(1.into()), - ..Default::default() - },)) - .unwrap(), - ), - RpcTest::identity( - EthTraceFilterV2::request((EthTraceFilterCriteria { - from_block: Some(format!( - "0x{:x}", - shared_tipset.epoch() - (SAFE_EPOCH_DELAY + 1) - )), - to_block: Some(format!("0x{:x}", shared_tipset.epoch() - SAFE_EPOCH_DELAY)), - ..Default::default() - },)) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::PassWithIdenticalError), - RpcTest::identity( - EthTraceFilterV2::request((EthTraceFilterCriteria { - from_block: Some(ExtPredefined::Latest.to_string()), - count: Some(1.into()), - ..Default::default() - },)) - .unwrap(), - ), - RpcTest::identity( - EthTraceFilterV2::request((EthTraceFilterCriteria { + RpcTest::basic( + EthTraceFilter::request((EthTraceFilterCriteria { + from_block: Some(Predefined::Safe.to_string()), + count: Some(1.into()), + ..Default::default() + },))? + .with_api_path(api_path), + ), + RpcTest::identity( + EthTraceFilter::request((EthTraceFilterCriteria { + from_block: Some(Predefined::Finalized.to_string()), + count: Some(1.into()), + ..Default::default() + },))? + .with_api_path(api_path), + ) + .ignore("`finalized` is not supported by Lotus yet"), + RpcTest::identity(EthTraceFilter::request((EthTraceFilterCriteria { + from_block: Some(Predefined::Latest.to_string()), count: Some(1.into()), ..Default::default() - },)) - .unwrap(), - ), - RpcTest::identity( - EthGetTransactionReceipt::request(( - // A transaction that should not exist, to test the `null` response in case - // of missing transaction. - EthHash::from_str( - "0xf234567890123456789d6a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f70809", - ) + },))?), + RpcTest::identity( + EthTraceFilter::request((EthTraceFilterCriteria { + count: Some(1.into()), + ..Default::default() + },)) .unwrap(), - )) - .unwrap(), - ), - ]; + ), + ]); + } for block in shared_tipset.block_headers() { - tests.extend([RpcTest::identity( - FilecoinAddressToEthAddress::request(( + tests.extend([ + RpcTest::identity(FilecoinAddressToEthAddress::request(( block.miner_address, - Some(BlockNumberOrPredefined::PredefinedBlock( - ExtPredefined::Latest, - )), - )) - .unwrap(), - )]); - let (bls_messages, secp_messages) = - crate::chain::store::block_messages(store, block).unwrap(); + Some(Predefined::Latest.into()), + ))?), + RpcTest::identity(FilecoinAddressToEthAddress::request(( + block.miner_address, + Some(Predefined::Safe.into()), + ))?), + RpcTest::identity(FilecoinAddressToEthAddress::request(( + block.miner_address, + Some(Predefined::Finalized.into()), + ))?), + ]); + let (bls_messages, secp_messages) = crate::chain::store::block_messages(store, block)?; for msg in sample_messages(bls_messages.iter(), secp_messages.iter()) { - tests.extend([RpcTest::identity( - FilecoinAddressToEthAddress::request(( - msg.from(), - Some(BlockNumberOrPredefined::PredefinedBlock( - ExtPredefined::Latest, - )), - )) - .unwrap(), - )]); + tests.extend([RpcTest::identity(FilecoinAddressToEthAddress::request(( + msg.from(), + Some(Predefined::Latest.into()), + ))?)]); if let Ok(eth_to_addr) = EthAddress::try_from(msg.to) { - tests.extend([RpcTest::identity( - EthEstimateGas::request(( - EthCallMessage { - to: Some(eth_to_addr), - value: Some(msg.value.clone().into()), - data: Some(msg.params.clone().into()), - ..Default::default() - }, - Some(BlockNumberOrHash::BlockNumber(shared_tipset.epoch().into())), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::Pass)]); - tests.extend([RpcTest::identity( - EthEstimateGasV2::request(( - EthCallMessage { - to: Some(eth_to_addr), - value: Some(msg.value.clone().into()), - data: Some(msg.params.clone().into()), - ..Default::default() - }, - Some(ExtBlockNumberOrHash::BlockNumber( - shared_tipset.epoch().into(), - )), - )) - .unwrap(), - ) - .policy_on_rejected(PolicyOnRejected::Pass)]); + for api_path in [ApiPaths::V1, ApiPaths::V2] { + tests.extend([RpcTest::identity( + EthEstimateGas::request(( + EthCallMessage { + to: Some(eth_to_addr), + value: Some(msg.value.clone().into()), + data: Some(msg.params.clone().into()), + ..Default::default() + }, + Some(BlockNumberOrHash::BlockNumber(shared_tipset.epoch().into())), + ))? + .with_api_path(api_path), + ) + .policy_on_rejected(PolicyOnRejected::Pass)]); + } } } } - tests + Ok(tests) } fn read_state_api_tests(tipset: &Tipset) -> anyhow::Result> { @@ -2633,7 +2243,7 @@ fn read_state_api_tests(tipset: &Tipset) -> anyhow::Result> { tipset.key().into(), ))?), RpcTest::identity(StateReadState::request(( - Address::from_str(EVM_ADDRESS).unwrap(), // evm actor + Address::from_str(EVM_ADDRESS)?, // evm actor tipset.key().into(), ))?), ]; @@ -2789,11 +2399,11 @@ fn snapshot_tests( ) -> anyhow::Result> { let mut tests = vec![]; // shared_tipset in the snapshot might not be finalized for the offline RPC server - // use heaviest - SAFE_EPOCH_DELAY instead + // use heaviest - SAFE_EPOCH_DELAY_FOR_TESTING instead let shared_tipset = store .heaviest_tipset()? .chain(&store) - .take(SAFE_EPOCH_DELAY as usize) + .take(SAFE_EPOCH_DELAY_FOR_TESTING as usize) .last() .expect("Infallible"); @@ -2801,8 +2411,8 @@ fn snapshot_tests( tests.extend(chain_tests_with_tipset(&store, offline, &tipset)?); tests.extend(miner_tests_with_tipset(&store, &tipset, miner_address)?); tests.extend(state_tests_with_tipset(&store, &tipset)?); - tests.extend(eth_tests_with_tipset(&store, &tipset)); - tests.extend(event_tests_with_tipset(&store, &tipset)); + tests.extend(eth_tests_with_tipset(&store, &tipset)?); + tests.extend(event_tests_with_tipset(&store, &tipset)?); tests.extend(gas_tests_with_tipset(&tipset)); tests.extend(mpool_tests_with_tipset(&tipset)); tests.extend(eth_state_tests_with_tipset(&store, &tipset, eth_chain_id)?); @@ -2878,7 +2488,7 @@ pub(super) async fn create_tests( tests.extend(net_tests()); tests.extend(node_tests()); tests.extend(wallet_tests(worker_address)); - tests.extend(eth_tests()); + tests.extend(eth_tests()?); tests.extend(f3_tests()?); if !snapshot_files.is_empty() { let store = Arc::new(ManyCar::try_from(snapshot_files.clone())?); @@ -2941,7 +2551,7 @@ async fn revalidate_chain(db: Arc, n_ts_to_validate: usize) -> anyhow:: state_manager.validate_tipsets( head_ts .chain(&db) - .take(SAFE_EPOCH_DELAY as usize + n_ts_to_validate), + .take(SAFE_EPOCH_DELAY_FOR_TESTING as usize + n_ts_to_validate), )?; Ok(()) @@ -2985,7 +2595,7 @@ pub(super) async fn run_tests( rpc::Request { method_name, params, - api_paths, + api_path, .. }, ignore, @@ -2994,7 +2604,7 @@ pub(super) async fn run_tests( ( method_name.clone(), params.clone(), - *api_paths, + *api_path, ignore.is_some(), ) }, @@ -3013,7 +2623,7 @@ pub(super) async fn run_tests( } if let Some(filter_version) = filter_version - && !test.request.api_paths.contains(filter_version) + && test.request.api_path != filter_version { continue; } @@ -3157,17 +2767,13 @@ fn validate_message_lookup(req: rpc::Request) -> RpcTest { }) } -fn validate_tagged_tipset_v2(req: rpc::Request>, offline: bool) -> RpcTest { - RpcTest::validate(req, move |forest, lotus| match (forest, lotus) { - (None, None) => true, - (Some(forest), Some(lotus)) => { - if offline { - true - } else { - (forest.epoch() - lotus.epoch()).abs() <= 2 - } +fn validate_tagged_tipset_v2(req: rpc::Request, offline: bool) -> RpcTest { + RpcTest::validate(req, move |forest, lotus| { + if offline { + true + } else { + (forest.epoch() - lotus.epoch()).abs() <= 2 } - _ => false, }) } diff --git a/src/tool/subcommands/api_cmd/test_snapshot.rs b/src/tool/subcommands/api_cmd/test_snapshot.rs index e1678c35be18..86feb82e3ec8 100644 --- a/src/tool/subcommands/api_cmd/test_snapshot.rs +++ b/src/tool/subcommands/api_cmd/test_snapshot.rs @@ -1,11 +1,10 @@ // Copyright 2019-2026 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use crate::chain_sync::SyncStatusReport; use crate::{ KeyStore, KeyStoreConfig, chain::ChainStore, - chain_sync::network_context::SyncNetworkContext, + chain_sync::{SyncStatusReport, network_context::SyncNetworkContext}, db::{ MemoryDB, car::{AnyCar, ManyCar}, @@ -117,7 +116,7 @@ pub async fn run_test_from_snapshot(path: &Path) -> anyhow::Result<()> { Ok(v) => serde_json::from_value(v).map_err(|e| e.to_string()), Err(e) => Err(e), }; - assert_eq!(result, expected); + pretty_assertions::assert_eq!(result, expected); run = true; } }; diff --git a/src/tool/subcommands/api_cmd/test_snapshots.txt b/src/tool/subcommands/api_cmd/test_snapshots.txt index a68becb3cd60..e9b9478b5f2f 100644 --- a/src/tool/subcommands/api_cmd/test_snapshots.txt +++ b/src/tool/subcommands/api_cmd/test_snapshots.txt @@ -127,7 +127,7 @@ filecoin_ethtraceblock_v2_safe_1769092401374979.rpcsnap.json.zst filecoin_ethtracefilter_1742371405673188.rpcsnap.json.zst filecoin_ethtracefilter_1742983898701553.rpcsnap.json.zst filecoin_ethtracefilter_1746449543820062.rpcsnap.json.zst -filecoin_ethtracefilter_safe_1771492815155978.rpcsnap.json.zst +filecoin_ethtracefilter_1772466405810428.rpcsnap.json.zst # safe tag filecoin_ethtracefilter_v2_1770740801450325.rpcsnap.json.zst filecoin_ethtracefilter_v2_default_1771492815053207.rpcsnap.json.zst filecoin_ethtracefilter_v2_latest_1771492815053002.rpcsnap.json.zst @@ -144,9 +144,12 @@ filecoin_evm_invokecontractdelegate_statedecodeparams_1755004924714578.rpcsnap.j filecoin_evm_resurrect_statedecodeparams_1755004924714636.rpcsnap.json.zst filecoin_filecoinaddresstoethaddress_1765291874419799.rpcsnap.json.zst # F4 address filecoin_filecoinaddresstoethaddress_1765357908887069.rpcsnap.json.zst # F0 address -filecoin_filecoinaddresstoethaddress_1765363872743134.rpcsnap.json.zst # F1 address -filecoin_filecoinaddresstoethaddress_1765363872743268.rpcsnap.json.zst # F3 address -filecoin_filecoinaddresstoethaddress_1765363872743313.rpcsnap.json.zst # F2 address +filecoin_filecoinaddresstoethaddress_1771943760273649.rpcsnap.json.zst # F3 address +filecoin_filecoinaddresstoethaddress_1771943760275947.rpcsnap.json.zst # F2 address +filecoin_filecoinaddresstoethaddress_1771943760315677.rpcsnap.json.zst # F1 address +filecoin_filecoinaddresstoethaddress_1771946498262342.rpcsnap.json.zst # finalized tag +filecoin_filecoinaddresstoethaddress_1771946498262483.rpcsnap.json.zst # latest tag +filecoin_filecoinaddresstoethaddress_1771946498262885.rpcsnap.json.zst # safe tag filecoin_gasestimategaslimit_1741782110512299.rpcsnap.json.zst filecoin_gasestimatemessagegas_1758735195199472.rpcsnap.json.zst filecoin_getactoreventsraw_1741782590255476.rpcsnap.json.zst