Skip to content

feat: impl basefee change from FIP-0115#6702

Open
LesnyRumcajs wants to merge 12 commits intomainfrom
fip-0115-basefee
Open

feat: impl basefee change from FIP-0115#6702
LesnyRumcajs wants to merge 12 commits intomainfrom
fip-0115-basefee

Conversation

@LesnyRumcajs
Copy link
Member

@LesnyRumcajs LesnyRumcajs commented Mar 6, 2026

Summary of changes

Changes introduced in this pull request:

  • simplified some existing code,
  • ported basefee change from FIP-0115 from Lotus to Forest

Reference issue to close (if applicable)

Closes #6686

Other information and links

Don't merge before the reference is merged to Lotus master. There might be some additional changes there.

Change checklist

  • I have performed a self-review of my own code,
  • I have made corresponding changes to the documentation. All new code adheres to the team's documentation standards,
  • I have added tests that prove my fix is effective or that my feature works (if possible),
  • I have made sure the CHANGELOG is up-to-date. All user-facing changes should be reflected in this document.

Outside contributions

  • I have read and agree to the CONTRIBUTING document.
  • I have read and agree to the AI Policy document. I understand that failure to comply with the guidelines will lead to rejection of the pull request.

Summary by CodeRabbit

  • New Features

    • Implements FIP-0115: after the upgrade height, base fee can be driven by transaction premium percentiles in addition to utilization.
  • Behavioral Change

    • Base fee computation now deduplicates transactions by sender+sequence and aggregates gas/premiums for fairer, more responsive fee adjustments.
    • Node components now consult the upgrade height to determine which base-fee path to use.
  • Tests / Changelog

    • Added unit and property tests for premium-based logic; changelog updated for the upcoming network upgrade.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c866d2b8-ebc7-4f21-a6d5-0d1c6ad11d6f

📥 Commits

Reviewing files that changed from the base of the PR and between fa2c52a and 346167f.

📒 Files selected for processing (1)
  • CHANGELOG.md

Walkthrough

Implements FIP-0115: adds premium-weighted base-fee computation with a weighted quick-select module, new helper paths/constants, message deduplication by (from, sequence), tests, and updates call sites to pass a next-upgrade activation height.

Changes

Cohort / File(s) Summary
Core base-fee logic
src/chain/store/base_fee.rs
Added BLOCK_GAS_TARGET_INDEX; extended compute_base_fee(..., next_upgrade_height); added premium-based flows: compute_next_base_fee_from_premiums, compute_next_base_fee_from_premium, compute_next_base_fee_from_utlilization; deduplicate messages by (from, sequence); added/updated tests.
Weighted quick-select
src/chain/store/weighted_quick_select.rs
New module with pub fn weighted_quick_select(premiums, limits, target_index) -> TokenAmount; implements weighted percentile selection and includes unit/property tests.
Module registration
src/chain/store/mod.rs
Registered new private module mod weighted_quick_select;.
Call-site updates
src/chain_sync/tipset_syncer.rs, src/message_pool/msgpool/provider.rs, src/rpc/methods/miner.rs
Updated callers to pass an additional activation height (Xxx / next_upgrade_height) into compute_base_fee.
Message tests
src/message/mod.rs
Added unit tests for effective_gas_premium reflecting FIP-0115 cases.
Changelog
CHANGELOG.md
Added unreleased entry noting FIP-0115 implementation.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant RPC as Miner/RPC
participant Chain as Chain Store
participant DB as BlockStore/DB
participant WQS as weighted_quick_select
RPC->>Chain: compute_base_fee(ts, smoke_height, next_upgrade_height)
alt ts.epoch >= next_upgrade_height
Chain->>DB: fetch tipset messages; dedupe by (from,seq); collect gas limits & premiums
Chain->>WQS: weighted_quick_select(premiums, limits, BLOCK_GAS_TARGET_INDEX)
WQS-->>Chain: percentile_premium
Chain->>Chain: compute_next_base_fee_from_premium(base_fee, percentile_premium)
else
Chain->>DB: compute utilization totals from unique messages
Chain->>Chain: compute_next_base_fee_from_utlilization(...)
end
Chain-->>RPC: next_base_fee

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR 6706 — nv28 skeleton introducing the Xxx activation epoch used as the next_upgrade_height parameter.

Suggested reviewers

  • akaladarshi
  • hanabi1224
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: implementing the basefee modification from FIP-0115, which aligns with the primary objective of the pull request.
Linked Issues check ✅ Passed The PR addresses both objectives from issue #6686: implements FIP-0115 basefee changes following the Lotus reference and ensures logic alignment with FIP specification.
Out of Scope Changes check ✅ Passed All changes directly support FIP-0115 implementation: basefee computation refactoring, weighted quick-select algorithm, integration updates, and tests are in-scope with no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fip-0115-basefee
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@LesnyRumcajs
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/chain/store/base_fee.rs`:
- Around line 13-17: The placeholder constant PLACEHOLDER_NEXT_UPGRADE_HEIGHT
(in src/chain/store/base_fee.rs) is disabling the FIP-0115 branch; replace this
compile-time MAX sentinel with a real activation epoch sourced from the network
configuration (e.g., add a field to your network/chain config struct or
ChainConfig and use that value instead of PLACEHOLDER_NEXT_UPGRADE_HEIGHT),
update all callers that currently import PLACEHOLDER_NEXT_UPGRADE_HEIGHT to read
the configured activation epoch (or accept it via function parameters) so the
premium-based branch can activate at runtime, and adjust any tests/fixtures to
inject the desired activation epoch for deterministic behavior.

In `@src/chain/store/weighted_quick_select.rs`:
- Around line 347-363: The property test prop_result_respects_weight_ordering is
vacuous because it sets target = pair.weight_high so the conditional `if target
< pair.weight_high` never runs; change how target is chosen (in the prop or
before calling weighted_quick_select) to produce a value strictly less than
pair.weight_high (for example pick target = pair.weight_low or generate target =
pair.weight_high - 1 ensuring non-negative), then call weighted_quick_select
with that target and assert the result equals
TokenAmount::from_atto(pair.high_premium) when target < pair.weight_high; adjust
logic around OrderedPremiumPair and TokenAmount conversions to match the chosen
target generation.
- Around line 30-31: Replace the panic-prone unwrap() call on
premiums.into_iter().next() with expect(...) that provides context; the
surrounding check uses limits.first().is_some_and(|&limit| limit > target_index)
so change premiums.into_iter().next().unwrap() to
premiums.into_iter().next().expect("expected at least one premium when
limits.first() indicates a valid target index") to document why next() is safe
and give a helpful error if it ever fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c5ed8d65-73fa-4ce4-aebd-af197db579f3

📥 Commits

Reviewing files that changed from the base of the PR and between bbdcde9 and 5ce6520.

📒 Files selected for processing (7)
  • src/chain/store/base_fee.rs
  • src/chain/store/mod.rs
  • src/chain/store/weighted_quick_select.rs
  • src/chain_sync/tipset_syncer.rs
  • src/message/mod.rs
  • src/message_pool/msgpool/provider.rs
  • src/rpc/methods/miner.rs


/// Placeholder for the FIP-0115 activation height.
/// Replace with the actual network upgrade height once finalized.
pub const PLACEHOLDER_NEXT_UPGRADE_HEIGHT: ChainEpoch = ChainEpoch::MAX;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking in #6704

@LesnyRumcajs LesnyRumcajs marked this pull request as ready for review March 9, 2026 09:33
@LesnyRumcajs LesnyRumcajs requested a review from a team as a code owner March 9, 2026 09:33
@LesnyRumcajs LesnyRumcajs requested review from akaladarshi and hanabi1224 and removed request for a team March 9, 2026 09:33
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/chain/store/weighted_quick_select.rs (1)

21-25: Assert the premiums/limits 1:1 contract.

zip will silently drop trailing entries if those vectors ever diverge, which would skew the selected premium without failing fast. A small guard here would make the invariant explicit in this consensus path.

♻️ Suggested guard
 pub fn weighted_quick_select(
     mut premiums: Vec<TokenAmount>,
     mut limits: Vec<u64>,
     mut target_index: u64,
 ) -> TokenAmount {
+    debug_assert_eq!(
+        premiums.len(),
+        limits.len(),
+        "premiums and limits must stay 1:1",
+    );
     loop {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/chain/store/weighted_quick_select.rs` around lines 21 - 25, Ensure the
1:1 invariant between premiums and limits in weighted_quick_select by adding an
explicit guard that checks premiums.len() == limits.len() (and fails with a
clear message if not) at the start of the function so zip cannot silently drop
entries; refer to the function weighted_quick_select and the parameters premiums
and limits when adding this assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/chain/store/base_fee.rs`:
- Around line 58-72: The activation check in compute_base_fee is off-by-one:
since compute_base_fee receives the parent tipset (ts), change the condition
that selects compute_next_base_fee_from_premiums so it compares the child epoch
to next_upgrade_height by using ts.epoch() + 1 instead of ts.epoch(); update the
if in compute_base_fee to use ts.epoch() + 1 >= next_upgrade_height so the
premium rule calls compute_next_base_fee_from_premiums at the correct epoch,
otherwise continue to compute_next_base_fee_from_utlilization.

---

Nitpick comments:
In `@src/chain/store/weighted_quick_select.rs`:
- Around line 21-25: Ensure the 1:1 invariant between premiums and limits in
weighted_quick_select by adding an explicit guard that checks premiums.len() ==
limits.len() (and fails with a clear message if not) at the start of the
function so zip cannot silently drop entries; refer to the function
weighted_quick_select and the parameters premiums and limits when adding this
assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0675fdf2-09e1-46da-ad53-c63fe9e97969

📥 Commits

Reviewing files that changed from the base of the PR and between 5ce6520 and 34fc466.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • src/chain/store/base_fee.rs
  • src/chain/store/mod.rs
  • src/chain/store/weighted_quick_select.rs
  • src/chain_sync/tipset_syncer.rs
  • src/message/mod.rs
  • src/message_pool/msgpool/provider.rs
  • src/rpc/methods/miner.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/rpc/methods/miner.rs
  • src/message/mod.rs
  • src/message_pool/msgpool/provider.rs

@codecov
Copy link

codecov bot commented Mar 9, 2026

Codecov Report

❌ Patch coverage is 91.18457% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.57%. Comparing base (8149f08) to head (346167f).

Files with missing lines Patch % Lines
src/chain/store/base_fee.rs 79.16% 25 Missing ⚠️
src/rpc/methods/miner.rs 0.00% 4 Missing ⚠️
src/chain_sync/tipset_syncer.rs 66.66% 2 Missing and 1 partial ⚠️
Additional details and impacted files
Files with missing lines Coverage Δ
src/chain/store/weighted_quick_select.rs 100.00% <100.00%> (ø)
src/message/mod.rs 90.56% <100.00%> (+3.06%) ⬆️
src/message_pool/msgpool/provider.rs 36.36% <100.00%> (+1.47%) ⬆️
src/chain_sync/tipset_syncer.rs 63.45% <66.66%> (+0.61%) ⬆️
src/rpc/methods/miner.rs 7.69% <0.00%> (-0.23%) ⬇️
src/chain/store/base_fee.rs 86.66% <79.16%> (-12.46%) ⬇️

... and 2 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8149f08...346167f. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.


#[test]
fn test_weighted_quick_select_deterministic() {
// Run multiple times to verify correctness despite randomness

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want deterministic testing of the randomized algorithm, you can mock the randomness (forest_rng). and iterate through all possible responses like a depth-first-search, verifying correctness for all of them. As-is, if it failed in exactly one rare case, it might be difficult to reproduce and also flaky test failures tend to be ignored.
I implemented such a mock in this commit

Copy link

@wjmelements wjmelements left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation, testing, and activation look correct to me

hanabi1224
hanabi1224 previously approved these changes Mar 9, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/chain/store/base_fee.rs (1)

118-118: Typo in function name: "utlilization" → "utilization".

Consider fixing this typo for consistency and clarity. Since this is a private function, it won't cause breaking changes.

Suggested fix
-fn compute_next_base_fee_from_utlilization<DB>(
+fn compute_next_base_fee_from_utilization<DB>(

Also update the call site at line 72:

-    compute_next_base_fee_from_utlilization(db, ts, smoke_height)
+    compute_next_base_fee_from_utilization(db, ts, smoke_height)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/chain/store/base_fee.rs` at line 118, Rename the private function
compute_next_base_fee_from_utlilization to
compute_next_base_fee_from_utilization and update every call site that
references compute_next_base_fee_from_utlilization (e.g., the place that
currently invokes it from the base fee calculation code) to use the corrected
name; ensure any unit tests or local references are updated as well to keep
consistency since this is an internal/private function.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/chain/store/base_fee.rs`:
- Line 118: Rename the private function compute_next_base_fee_from_utlilization
to compute_next_base_fee_from_utilization and update every call site that
references compute_next_base_fee_from_utlilization (e.g., the place that
currently invokes it from the base fee calculation code) to use the corrected
name; ensure any unit tests or local references are updated as well to keep
consistency since this is an internal/private function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 988112e5-5000-40df-859e-b27d947e45ea

📥 Commits

Reviewing files that changed from the base of the PR and between 34fc466 and a1b8946.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • src/chain/store/base_fee.rs
  • src/chain/store/mod.rs
  • src/chain/store/weighted_quick_select.rs
  • src/chain_sync/tipset_syncer.rs
  • src/message/mod.rs
  • src/message_pool/msgpool/provider.rs
  • src/rpc/methods/miner.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/message/mod.rs
  • src/message_pool/msgpool/provider.rs
  • CHANGELOG.md

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/chain/store/base_fee.rs (1)

83-94: Pin replacement-message tie-breaking with a regression test.

This loop keeps the first (from, sequence) seen in the tipset. If two blocks carry different replacements for the same sender/nonce, the derived base fee changes with iteration order, so a small fixture for that case would help protect this consensus path from future ordering refactors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/chain/store/base_fee.rs` around lines 83 - 94, Add a regression test that
pins the tie-breaking behavior of the replacement-message loop in base fee
derivation: create a tipset with two blocks that both include different messages
from the same sender and same sequence, then verify that computing the base fee
(via the code path that calls crate::chain::block_messages and iterates over
bls_msgs/secp_msgs, using seen.insert((m.from(), m.sequence()))) yields the same
base fee regardless of block iteration order; ensure the test exercises
effective_gas_premium and gas_limit accumulation and fails if a later-seen
replacement overrides the first-seen message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/chain/store/base_fee.rs`:
- Around line 281-298: The property test prop_next_base_fee_change_bounded
allows negative premiums which the real code never uses; constrain or clamp the
test premium to non-negative before calling compute_next_base_fee_from_premium.
Specifically, in prop_next_base_fee_change_bounded (which calls
compute_next_base_fee_from_premium) either skip cases where premium is negative
or replace premium with its non-negative equivalent (e.g., premium =
premium.max(&TokenAmount::from_atto(0)) or check premium.is_negative() and
return true) so the test mirrors production's effective_gas_premium clamping.

---

Nitpick comments:
In `@src/chain/store/base_fee.rs`:
- Around line 83-94: Add a regression test that pins the tie-breaking behavior
of the replacement-message loop in base fee derivation: create a tipset with two
blocks that both include different messages from the same sender and same
sequence, then verify that computing the base fee (via the code path that calls
crate::chain::block_messages and iterates over bls_msgs/secp_msgs, using
seen.insert((m.from(), m.sequence()))) yields the same base fee regardless of
block iteration order; ensure the test exercises effective_gas_premium and
gas_limit accumulation and fails if a later-seen replacement overrides the
first-seen message.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fa13c311-d55c-4552-b652-6f18f38d8f7c

📥 Commits

Reviewing files that changed from the base of the PR and between a1b8946 and fa2c52a.

📒 Files selected for processing (5)
  • src/chain/store/base_fee.rs
  • src/chain_sync/tipset_syncer.rs
  • src/message/mod.rs
  • src/message_pool/msgpool/provider.rs
  • src/rpc/methods/miner.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/chain_sync/tipset_syncer.rs
  • src/rpc/methods/miner.rs

@LesnyRumcajs
Copy link
Member Author

Upstream merged

@LesnyRumcajs
Copy link
Member Author

Implementation, testing, and activation look correct to me

@wjmelements Thanks for reviewing, much appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

implement FIP-0115

3 participants