diff --git a/CHANGELOG.md b/CHANGELOG.md index dd1d8adcdfd1..841bb1f890fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ - [#6380](https://github.com/ChainSafe/forest/pull/6380) Implemented `Filecoin.EthFeeHistory` for API v2. +- [#6387](https://github.com/ChainSafe/forest/pull/6387) Implemented `Filecoin.EthGetTransactionCount` for API v2. + ### Changed ### Removed diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs index e3779f12d8a2..680308f4baaf 100644 --- a/src/rpc/methods/eth.rs +++ b/src/rpc/methods/eth.rs @@ -2393,33 +2393,78 @@ impl RpcMethod<2> for EthGetTransactionCount { (sender, block_param): Self::Params, ) -> Result { let addr = sender.to_filecoin_address()?; - if let BlockNumberOrHash::PredefinedBlock(ref predefined) = block_param - && *predefined == Predefined::Pending - { - return Ok(EthUint64(ctx.mpool.get_sequence(&addr)?)); + match block_param { + BlockNumberOrHash::PredefinedBlock(Predefined::Pending) => { + 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 + } } - let ts = tipset_by_block_number_or_hash( - ctx.chain_store(), - block_param.clone(), - ResolveNullTipset::TakeOlder, - )?; + } +} - let (state_cid, _) = ctx.state_manager.tipset_state(&ts).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; - let state = StateTree::new_from_root(ctx.store_owned(), &state_cid)?; - let actor = state.get_required_actor(&addr)?; - if is_evm_actor(&actor.code) { - let evm_state = evm::State::load(ctx.store(), actor.code, actor.state)?; - if !evm_state.is_alive() { - return Ok(EthUint64(0)); + async fn handle( + ctx: Ctx, + (sender, block_param): Self::Params, + ) -> 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?; + eth_get_transaction_count(&ctx, &ts, addr).await } - Ok(EthUint64(evm_state.nonce())) - } else { - Ok(EthUint64(actor.sequence)) } } } +async fn eth_get_transaction_count( + ctx: &Ctx, + ts: &Tipset, + addr: FilecoinAddress, +) -> Result +where + B: Blockstore + Send + Sync + 'static, +{ + let (state_cid, _) = ctx.state_manager.tipset_state(ts).await?; + + let state = StateTree::new_from_root(ctx.store_owned(), &state_cid)?; + let actor = state.get_required_actor(&addr)?; + if is_evm_actor(&actor.code) { + let evm_state = evm::State::load(ctx.store(), actor.code, actor.state)?; + if !evm_state.is_alive() { + return Ok(EthUint64(0)); + } + Ok(EthUint64(evm_state.nonce())) + } else { + Ok(EthUint64(actor.sequence)) + } +} + pub enum EthMaxPriorityFeePerGas {} impl RpcMethod<0> for EthMaxPriorityFeePerGas { const NAME: &'static str = "Filecoin.EthMaxPriorityFeePerGas"; diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index ba494aaf102a..dcc5bfdb8fa9 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -125,6 +125,7 @@ macro_rules! for_each_rpc_method { $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::EthGetTransactionByBlockHashAndIndex); diff --git a/src/tool/subcommands/api_cmd/api_compare_tests.rs b/src/tool/subcommands/api_cmd/api_compare_tests.rs index adf169fe0b54..6a2e42b12c5f 100644 --- a/src/tool/subcommands/api_cmd/api_compare_tests.rs +++ b/src/tool/subcommands/api_cmd/api_compare_tests.rs @@ -1757,6 +1757,49 @@ fn eth_tests_with_tipset(store: &Arc, shared_tipset: &Tipset )) .unwrap(), ), + RpcTest::identity( + EthGetTransactionCountV2::request(( + EthAddress::from_str("0xff000000000000000000000000000000000003ec").unwrap(), + ExtBlockNumberOrHash::from_block_hash_object(block_hash.clone(), 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( EthGetStorageAt::request(( // https://filfox.info/en/address/f410fpoidg73f7krlfohnla52dotowde5p2sejxnd4mq diff --git a/src/tool/subcommands/api_cmd/test_snapshots.txt b/src/tool/subcommands/api_cmd/test_snapshots.txt index dd34ab5e46b8..2e645609c48f 100644 --- a/src/tool/subcommands/api_cmd/test_snapshots.txt +++ b/src/tool/subcommands/api_cmd/test_snapshots.txt @@ -78,6 +78,7 @@ filecoin_ethgettransactionbyblocknumberandindex_1740132538304408.rpcsnap.json.zs filecoin_ethgettransactionbyhash_1741272955520821.rpcsnap.json.zst filecoin_ethgettransactionbyhashlimited_1741272955509708.rpcsnap.json.zst filecoin_ethgettransactioncount_1740132538183426.rpcsnap.json.zst +filecoin_ethgettransactioncount_v2_1767847407595348.rpcsnap.json.zst filecoin_ethgettransactionhashbycid_1737446676698540.rpcsnap.json.zst filecoin_ethgettransactionreceipt_1741272955712904.rpcsnap.json.zst filecoin_ethgettransactionreceipt_1765811578590165.rpcsnap.json.zst # transaction not found