From b778f3ce457a1544a5a957e19f6d0e72ab36e4b1 Mon Sep 17 00:00:00 2001 From: Hubert Bugaj Date: Tue, 23 Jun 2026 10:29:25 +0200 Subject: [PATCH 1/5] fix: invalid `Filecoin.GasEstimateGasPremium` response type --- CHANGELOG.md | 2 ++ src/rpc/methods/gas.rs | 6 ++---- src/tool/subcommands/api_cmd/api_compare_tests.rs | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dda289f3c2..a00805bcc38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ ### Fixed +- [#7227](https://github.com/ChainSafe/forest/issues/7227): Fixed invalid `Filecoin.GasEstimateGasPremium` response that was returning a fraction instead of an integer. + ## Forest v0.33.7 "Shimmergloom" ### Added diff --git a/src/rpc/methods/gas.rs b/src/rpc/methods/gas.rs index af7cdec3c02..558e3f624bd 100644 --- a/src/rpc/methods/gas.rs +++ b/src/rpc/methods/gas.rs @@ -85,16 +85,14 @@ impl RpcMethod<4> for GasEstimateGasPremium { Some("Returns the estimated gas premium for the given parameters."); type Params = (u64, Address, i64, ApiTipsetKey); - type Ok = String; + type Ok = TokenAmount; async fn handle( ctx: Ctx, (nblocksincl, _sender, _gas_limit, tsk): Self::Params, _: &http::Extensions, ) -> Result { - estimate_gas_premium(&ctx, nblocksincl, &tsk) - .await - .map(|n| TokenAmount::to_string(&n)) + estimate_gas_premium(&ctx, nblocksincl, &tsk).await } } diff --git a/src/tool/subcommands/api_cmd/api_compare_tests.rs b/src/tool/subcommands/api_cmd/api_compare_tests.rs index c4adc623224..fae1cedb45b 100644 --- a/src/tool/subcommands/api_cmd/api_compare_tests.rs +++ b/src/tool/subcommands/api_cmd/api_compare_tests.rs @@ -2417,6 +2417,20 @@ fn gas_tests_with_tipset(shared_tipset: &Tipset) -> Vec { && forest_premium.is_within_percent(lotus_premium, 5) }, ), + RpcTest::validate( + GasEstimateGasPremium::request((3, addr, 9, ApiTipsetKey(None))).unwrap(), + // Gas estimation is inherently non-deterministic due to randomness in gas premium + // calculation and network state changes. We validate that both implementations + // return reasonable values within expected bounds rather than exact equality. + |forest_premium, lotus_premium| { + // Gas premium should not be negative + if forest_premium.is_negative() || lotus_premium.is_negative() { + return false; + } + + forest_premium.is_within_percent(&lotus_premium, 5) + }, + ), ] } From 9768e50728d2d47c8ba89c777a625eb98290ac74 Mon Sep 17 00:00:00 2001 From: Hubert Bugaj Date: Tue, 23 Jun 2026 10:40:44 +0200 Subject: [PATCH 2/5] fix: `Filecoin.GasEstimateFeeCap` incorrect output type --- CHANGELOG.md | 2 +- src/rpc/methods/gas.rs | 4 ++-- .../subcommands/api_cmd/api_compare_tests.rs | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a00805bcc38..1236b67b3f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,7 @@ ### Fixed -- [#7227](https://github.com/ChainSafe/forest/issues/7227): Fixed invalid `Filecoin.GasEstimateGasPremium` response that was returning a fraction instead of an integer. +- [#7227](https://github.com/ChainSafe/forest/issues/7227): Fixed invalid `Filecoin.GasEstimateGasPremium` and `Filecoin.GasEstimateFeeCap` responses that were returning a fraction instead of an integer. ## Forest v0.33.7 "Shimmergloom" diff --git a/src/rpc/methods/gas.rs b/src/rpc/methods/gas.rs index 558e3f624bd..7d82dae054a 100644 --- a/src/rpc/methods/gas.rs +++ b/src/rpc/methods/gas.rs @@ -37,14 +37,14 @@ impl RpcMethod<3> for GasEstimateFeeCap { Some("Returns the estimated fee cap for the given parameters."); type Params = (Message, i64, ApiTipsetKey); - type Ok = String; + type Ok = TokenAmount; async fn handle( ctx: Ctx, (msg, max_queue_blks, tsk): Self::Params, _: &http::Extensions, ) -> Result { - estimate_fee_cap(&ctx, &msg, max_queue_blks, &tsk).map(|n| TokenAmount::to_string(&n)) + estimate_fee_cap(&ctx, &msg, max_queue_blks, &tsk) } } diff --git a/src/tool/subcommands/api_cmd/api_compare_tests.rs b/src/tool/subcommands/api_cmd/api_compare_tests.rs index fae1cedb45b..194565be57c 100644 --- a/src/tool/subcommands/api_cmd/api_compare_tests.rs +++ b/src/tool/subcommands/api_cmd/api_compare_tests.rs @@ -2388,7 +2388,7 @@ fn gas_tests_with_tipset(shared_tipset: &Tipset) -> Vec { // return reasonable values within expected bounds rather than exact equality. RpcTest::validate( GasEstimateMessageGas::request(( - message, + message.clone(), None, // No MessageSendSpec shared_tipset.key().into(), )) @@ -2431,6 +2431,20 @@ fn gas_tests_with_tipset(shared_tipset: &Tipset) -> Vec { forest_premium.is_within_percent(&lotus_premium, 5) }, ), + // The fee cap depends on the (non-deterministic) gas premium, so validate + // that both implementations return non-negative values within expected bounds + // rather than exact equality. This also guards against the response being + // serialized as a FIL decimal string instead of an attoFIL integer. + RpcTest::validate( + GasEstimateFeeCap::request((message, 20, ApiTipsetKey(None))).unwrap(), + |forest_fee_cap, lotus_fee_cap| { + if forest_fee_cap.is_negative() || lotus_fee_cap.is_negative() { + return false; + } + + forest_fee_cap.is_within_percent(&lotus_fee_cap, 5) + }, + ), ] } From 0f786b1f402165aaec48246584760d0a4cd62cdb Mon Sep 17 00:00:00 2001 From: Hubert Bugaj Date: Tue, 23 Jun 2026 11:18:27 +0200 Subject: [PATCH 3/5] update insta snaps --- src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap | 4 ++-- src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap index 616900104cd..522afa58b03 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v0.snap @@ -1869,7 +1869,7 @@ methods: name: Filecoin.GasEstimateFeeCap.Result required: true schema: - type: string + $ref: "#/components/schemas/TokenAmount" paramStructure: by-position - name: Filecoin.GasEstimateGasLimit description: Returns the estimated gas limit for the given parameters. @@ -1923,7 +1923,7 @@ methods: name: Filecoin.GasEstimateGasPremium.Result required: true schema: - type: string + $ref: "#/components/schemas/TokenAmount" paramStructure: by-position - name: Filecoin.GasEstimateMessageGas description: Returns the estimated gas for the given parameters. diff --git a/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap b/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap index 103f87e5e3b..0b86416f0f0 100644 --- a/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap +++ b/src/rpc/snapshots/forest__rpc__tests__rpc__v1.snap @@ -1949,7 +1949,7 @@ methods: name: Filecoin.GasEstimateFeeCap.Result required: true schema: - type: string + $ref: "#/components/schemas/TokenAmount" paramStructure: by-position - name: Filecoin.GasEstimateGasLimit description: Returns the estimated gas limit for the given parameters. @@ -2003,7 +2003,7 @@ methods: name: Filecoin.GasEstimateGasPremium.Result required: true schema: - type: string + $ref: "#/components/schemas/TokenAmount" paramStructure: by-position - name: Filecoin.GasEstimateMessageGas description: Returns the estimated gas for the given parameters. From 916351567becf089000a875cc39079d63500af37 Mon Sep 17 00:00:00 2001 From: Hubert Bugaj Date: Tue, 23 Jun 2026 11:26:44 +0200 Subject: [PATCH 4/5] add feecap to ignored via lotus gateway --- scripts/tests/api_compare/filter-list-gateway | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/tests/api_compare/filter-list-gateway b/scripts/tests/api_compare/filter-list-gateway index 870225d6f8c..ada82a50479 100644 --- a/scripts/tests/api_compare/filter-list-gateway +++ b/scripts/tests/api_compare/filter-list-gateway @@ -12,6 +12,7 @@ !Filecoin.EthGetTransactionByHashLimited !Filecoin.F3 !Filecoin.GasEstimateGasLimit +!Filecoin.GasEstimateFeeCap !Filecoin.MinerCreateBlock !Filecoin.MpoolGetConfig !Filecoin.MpoolSelect From 7a122818f33e5a6d5eb881ae64cf7aba5f66bc1f Mon Sep 17 00:00:00 2001 From: Hubert Bugaj Date: Tue, 23 Jun 2026 12:13:50 +0200 Subject: [PATCH 5/5] fix test tipset --- .../subcommands/api_cmd/api_compare_tests.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/tool/subcommands/api_cmd/api_compare_tests.rs b/src/tool/subcommands/api_cmd/api_compare_tests.rs index 194565be57c..b880adaa971 100644 --- a/src/tool/subcommands/api_cmd/api_compare_tests.rs +++ b/src/tool/subcommands/api_cmd/api_compare_tests.rs @@ -2417,11 +2417,12 @@ fn gas_tests_with_tipset(shared_tipset: &Tipset) -> Vec { && forest_premium.is_within_percent(lotus_premium, 5) }, ), + // Pin to `shared_tipset` so both nodes compute over the same chain history; + // the only remaining difference is each node's ±1% gaussian noise, which the + // 5% tolerance absorbs. The heaviest tipset would compare two independently- + // synced heads and diff arbitrarily. RpcTest::validate( - GasEstimateGasPremium::request((3, addr, 9, ApiTipsetKey(None))).unwrap(), - // Gas estimation is inherently non-deterministic due to randomness in gas premium - // calculation and network state changes. We validate that both implementations - // return reasonable values within expected bounds rather than exact equality. + GasEstimateGasPremium::request((3, addr, 9, shared_tipset.key().into())).unwrap(), |forest_premium, lotus_premium| { // Gas premium should not be negative if forest_premium.is_negative() || lotus_premium.is_negative() { @@ -2431,12 +2432,12 @@ fn gas_tests_with_tipset(shared_tipset: &Tipset) -> Vec { forest_premium.is_within_percent(&lotus_premium, 5) }, ), - // The fee cap depends on the (non-deterministic) gas premium, so validate - // that both implementations return non-negative values within expected bounds - // rather than exact equality. This also guards against the response being - // serialized as a FIL decimal string instead of an attoFIL integer. + // The fee cap derives from the noise-perturbed premium, so use the same 5% + // tolerance; pinning to `shared_tipset` fixes the parent base fee both nodes + // read. Deserializing the result as `TokenAmount` is what rejects a FIL-decimal + // serialization regression (such a string fails to parse as an integer). RpcTest::validate( - GasEstimateFeeCap::request((message, 20, ApiTipsetKey(None))).unwrap(), + GasEstimateFeeCap::request((message, 20, shared_tipset.key().into())).unwrap(), |forest_fee_cap, lotus_fee_cap| { if forest_fee_cap.is_negative() || lotus_fee_cap.is_negative() { return false;