diff --git a/CHANGELOG.md b/CHANGELOG.md index 97b76783bed..b4468d0481f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,8 @@ - [#6975](https://github.com/ChainSafe/forest/issues/6975): Fixed `Filecoin.MpoolSelect` to not remove the messages from the live pool, only simulate the head change. +- [#7205](https://github.com/ChainSafe/forest/issues/7205): Fixed eth methods serializing empty lists as `null` instead of `[]`. + ## Forest v0.33.6 "Ebb" Non-mandatory release for all node operators. It fixes a critical memory leak in `v0.33.5`. (Earlier releases are not affected) diff --git a/src/lotus_json/vec.rs b/src/lotus_json/vec.rs index 342139fa946..afae9ed49c2 100644 --- a/src/lotus_json/vec.rs +++ b/src/lotus_json/vec.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use super::*; +use get_size2::GetSize; impl HasLotusJson for Vec // TODO(forest): https://github.com/ChainSafe/forest/issues/4032 @@ -37,7 +38,7 @@ where // while an empty `NotNullVec` serializes into `[]` // this is a temporary workaround and will likely be deprecated once // other issues on serde of `Vec` are resolved. -#[derive(Debug, Clone, PartialEq, JsonSchema)] +#[derive(Debug, Clone, Default, PartialEq, JsonSchema, GetSize)] pub struct NotNullVec(pub Vec); impl HasLotusJson for NotNullVec diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs index 33216942ecb..6d5e68eb669 100644 --- a/src/rpc/methods/eth.rs +++ b/src/rpc/methods/eth.rs @@ -26,7 +26,7 @@ use crate::eth::{ EAMMethod, EVMMethod, EthChainId as EthChainIdType, EthEip1559TxArgs, EthLegacyEip155TxArgs, EthLegacyHomesteadTxArgs, parse_eth_transaction, }; -use crate::lotus_json::{HasLotusJson, lotus_json_with_self}; +use crate::lotus_json::{HasLotusJson, NotNullVec, lotus_json_with_self}; use crate::message::{ChainMessage, MessageRead as _, MessageReadWrite as _, SignedMessage}; use crate::networks::Height; use crate::prelude::*; @@ -644,7 +644,7 @@ pub struct ApiEthTx { pub gas_price: Option, #[schemars(with = "Option>")] #[serde(with = "crate::lotus_json")] - pub access_list: Vec, + pub access_list: Option>, pub v: EthBigInt, pub r: EthBigInt, pub s: EthBigInt, @@ -846,7 +846,7 @@ impl RpcMethod<0> for EthAccounts { ); type Params = (); - type Ok = Vec; + type Ok = NotNullVec; async fn handle( _: Ctx, @@ -854,7 +854,7 @@ impl RpcMethod<0> for EthAccounts { _: &http::Extensions, ) -> Result { // EthAccounts will always return [] since we don't expect Forest to manage private keys - Ok(vec![]) + Ok(NotNullVec(vec![])) } } @@ -1263,7 +1263,7 @@ fn eth_tx_from_native_message( gas: EthUint64(msg.gas_limit), max_fee_per_gas: Some(msg.gas_fee_cap.clone().into()), max_priority_fee_per_gas: Some(msg.gas_premium.clone().into()), - access_list: vec![], + access_list: Some(NotNullVec(vec![])), ..ApiEthTx::default() }) } @@ -1602,7 +1602,7 @@ impl RpcMethod<1> for EthGetBlockReceipts { ); type Params = (BlockNumberOrHash,); - type Ok = Vec; + type Ok = NotNullVec; async fn handle( ctx: Ctx, @@ -1615,6 +1615,7 @@ impl RpcMethod<1> for EthGetBlockReceipts { .await?; get_block_receipts(&ctx, ts, None) .await + .map(NotNullVec) .map_err(ServerError::from) } } @@ -1631,7 +1632,7 @@ impl RpcMethod<2> for EthGetBlockReceiptsLimited { ); type Params = (BlockNumberOrHash, ChainEpoch); - type Ok = Vec; + type Ok = NotNullVec; async fn handle( ctx: Ctx, @@ -1644,6 +1645,7 @@ impl RpcMethod<2> for EthGetBlockReceiptsLimited { .await?; get_block_receipts(&ctx, ts, Some(limit)) .await + .map(NotNullVec) .map_err(ServerError::from) } } @@ -3572,7 +3574,7 @@ impl RpcMethod<1> for EthTraceBlock { const DESCRIPTION: Option<&'static str> = Some("Returns traces created at given block."); type Params = (BlockNumberOrHash,); - type Ok = Vec; + type Ok = NotNullVec; async fn handle( ctx: Ctx, (block_param,): Self::Params, @@ -3582,7 +3584,9 @@ impl RpcMethod<1> for EthTraceBlock { let ts = resolver .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) .await?; - eth_trace_block(&ctx.state_manager, &ts).await + eth_trace_block(&ctx.state_manager, &ts) + .await + .map(NotNullVec) } } @@ -3971,7 +3975,7 @@ impl RpcMethod<1> for EthTraceTransaction { Some("Returns the traces for a specific transaction."); type Params = (String,); - type Ok = Vec; + type Ok = NotNullVec; async fn handle( ctx: Ctx, (tx_hash,): Self::Params, @@ -3992,7 +3996,7 @@ impl RpcMethod<1> for EthTraceTransaction { .into_iter() .filter(|trace| trace.transaction_hash == eth_hash) .collect(); - Ok(traces) + Ok(NotNullVec(traces)) } } @@ -4009,7 +4013,7 @@ impl RpcMethod<2> for EthTraceReplayBlockTransactions { ); type Params = (BlockNumberOrHash, Vec); - type Ok = Vec; + type Ok = NotNullVec; async fn handle( ctx: Ctx, @@ -4028,7 +4032,9 @@ impl RpcMethod<2> for EthTraceReplayBlockTransactions { .tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder) .await?; - eth_trace_replay_block_transactions(&ctx, &ts).await + eth_trace_replay_block_transactions(&ctx, &ts) + .await + .map(NotNullVec) } } @@ -4081,7 +4087,7 @@ impl RpcMethod<1> for EthTraceFilter { const DESCRIPTION: Option<&'static str> = Some("Returns the traces for transactions matching the filter criteria."); type Params = (EthTraceFilterCriteria,); - type Ok = Vec; + type Ok = NotNullVec; async fn handle( ctx: Ctx, @@ -4115,7 +4121,9 @@ impl RpcMethod<1> for EthTraceFilter { return Err(EthErrors::limit_exceeded(max_block_range, range).into()); } } - Ok(trace_filter(ctx, filter, from_block, to_block, ext).await?) + Ok(NotNullVec( + trace_filter(ctx, filter, from_block, to_block, ext).await?, + )) } } @@ -4147,7 +4155,7 @@ async fn trace_filter( ext, ) .await?; - for block_trace in block_traces { + for block_trace in block_traces.0 { if block_trace .trace .match_filter_criteria(filter.from_address.as_ref(), filter.to_address.as_ref())? @@ -4201,6 +4209,27 @@ mod test { } } + #[test] + fn empty_access_list_serializes_as_empty_array() { + let tx = ApiEthTx { + access_list: Some(NotNullVec(vec![])), + ..Default::default() + }; + let json = serde_json::to_value(tx.into_lotus_json()).unwrap(); + assert_eq!(json["accessList"], serde_json::json!([])); + } + + #[test] + fn populated_access_list_serializes_as_array() { + let tx = ApiEthTx { + access_list: Some(NotNullVec(vec![EthHash::default()])), + ..Default::default() + }; + let json = serde_json::to_value(tx.into_lotus_json()).unwrap(); + assert!(json["accessList"].is_array()); + assert_eq!(json["accessList"].as_array().unwrap().len(), 1); + } + #[quickcheck] fn gas_price_result_serde_roundtrip(i: u128) { let r = EthBigInt(ethereum_types::U256::from(i)); diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap index f396d7eb6dd..7ced0d94154 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap @@ -474,11 +474,9 @@ methods: params: [] result: name: Filecoin.EthAccounts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: type: string paramStructure: by-position @@ -487,11 +485,9 @@ methods: params: [] result: name: eth_accounts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: type: string paramStructure: by-position @@ -848,11 +844,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthGetBlockReceipts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -865,11 +859,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: eth_getBlockReceipts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -887,11 +879,9 @@ methods: format: int64 result: name: Filecoin.EthGetBlockReceiptsLimited.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -909,11 +899,9 @@ methods: format: int64 result: name: eth_getBlockReceiptsLimited.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -1620,11 +1608,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthTraceBlock.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1637,11 +1623,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: trace_block.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1654,11 +1638,9 @@ methods: $ref: "#/components/schemas/EthTraceFilterCriteria" result: name: Filecoin.EthTraceFilter.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1671,11 +1653,9 @@ methods: $ref: "#/components/schemas/EthTraceFilterCriteria" result: name: trace_filter.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1688,11 +1668,9 @@ methods: type: string result: name: Filecoin.EthTraceTransaction.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1705,11 +1683,9 @@ methods: type: string result: name: trace_transaction.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1730,11 +1706,9 @@ methods: type: string result: name: Filecoin.EthTraceReplayBlockTransactions.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthReplayBlockTransactionTrace" paramStructure: by-position @@ -1755,11 +1729,9 @@ methods: type: string result: name: trace_replayBlockTransactions.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthReplayBlockTransactionTrace" paramStructure: by-position diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap index 33e0ba1e08b..def2294edf0 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap @@ -470,11 +470,9 @@ methods: params: [] result: name: Filecoin.EthAccounts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: type: string paramStructure: by-position @@ -483,11 +481,9 @@ methods: params: [] result: name: eth_accounts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: type: string paramStructure: by-position @@ -844,11 +840,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthGetBlockReceipts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -861,11 +855,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: eth_getBlockReceipts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -883,11 +875,9 @@ methods: format: int64 result: name: Filecoin.EthGetBlockReceiptsLimited.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -905,11 +895,9 @@ methods: format: int64 result: name: eth_getBlockReceiptsLimited.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -1616,11 +1604,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthTraceBlock.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1633,11 +1619,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: trace_block.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1696,11 +1680,9 @@ methods: $ref: "#/components/schemas/EthTraceFilterCriteria" result: name: Filecoin.EthTraceFilter.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1713,11 +1695,9 @@ methods: $ref: "#/components/schemas/EthTraceFilterCriteria" result: name: trace_filter.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1730,11 +1710,9 @@ methods: type: string result: name: Filecoin.EthTraceTransaction.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1747,11 +1725,9 @@ methods: type: string result: name: trace_transaction.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1810,11 +1786,9 @@ methods: type: string result: name: Filecoin.EthTraceReplayBlockTransactions.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthReplayBlockTransactionTrace" paramStructure: by-position @@ -1835,11 +1809,9 @@ methods: type: string result: name: trace_replayBlockTransactions.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthReplayBlockTransactionTrace" paramStructure: by-position diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap index 719d85d9ad2..b1b96964be8 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v2.snap @@ -74,11 +74,9 @@ methods: params: [] result: name: Filecoin.EthAccounts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: type: string paramStructure: by-position @@ -87,11 +85,9 @@ methods: params: [] result: name: eth_accounts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: type: string paramStructure: by-position @@ -448,11 +444,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthGetBlockReceipts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -465,11 +459,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: eth_getBlockReceipts.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -487,11 +479,9 @@ methods: format: int64 result: name: Filecoin.EthGetBlockReceiptsLimited.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -509,11 +499,9 @@ methods: format: int64 result: name: eth_getBlockReceiptsLimited.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthTxReceipt" paramStructure: by-position @@ -1220,11 +1208,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: Filecoin.EthTraceBlock.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1237,11 +1223,9 @@ methods: $ref: "#/components/schemas/BlockNumberOrHash" result: name: trace_block.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1300,11 +1284,9 @@ methods: $ref: "#/components/schemas/EthTraceFilterCriteria" result: name: Filecoin.EthTraceFilter.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1317,11 +1299,9 @@ methods: $ref: "#/components/schemas/EthTraceFilterCriteria" result: name: trace_filter.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1334,11 +1314,9 @@ methods: type: string result: name: Filecoin.EthTraceTransaction.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1351,11 +1329,9 @@ methods: type: string result: name: trace_transaction.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthBlockTrace" paramStructure: by-position @@ -1414,11 +1390,9 @@ methods: type: string result: name: Filecoin.EthTraceReplayBlockTransactions.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthReplayBlockTransactionTrace" paramStructure: by-position @@ -1439,11 +1413,9 @@ methods: type: string result: name: trace_replayBlockTransactions.Result - required: false + required: true schema: - type: - - array - - "null" + type: array items: $ref: "#/components/schemas/EthReplayBlockTransactionTrace" paramStructure: by-position