From 3998ddcdc8c30a98c3c125b55c85c2db8940a677 Mon Sep 17 00:00:00 2001 From: Lucca Martins Date: Wed, 4 Feb 2026 19:31:25 -0300 Subject: [PATCH 1/3] covering super low fee scenario --- consensus/misc/eip1559/eip1559.go | 13 + consensus/misc/eip1559/eip1559_test.go | 454 +++++++++++++++++++++++++ 2 files changed, 467 insertions(+) diff --git a/consensus/misc/eip1559/eip1559.go b/consensus/misc/eip1559/eip1559.go index 7071916b6c..4874c68f2f 100644 --- a/consensus/misc/eip1559/eip1559.go +++ b/consensus/misc/eip1559/eip1559.go @@ -77,6 +77,12 @@ func verifyBaseFeeWithinBoundaries(parent, header *types.Header) error { maxAllowedChange := new(big.Int).Mul(parent.BaseFee, big.NewInt(MaxBaseFeeChangePercent)) maxAllowedChange.Div(maxAllowedChange, big.NewInt(100)) + // Ensure minimum 1 wei cap to prevent unlimited growth at very low base fees. + // This matches the logic in CalcBaseFee. + if maxAllowedChange.Cmp(common.Big1) < 0 { + maxAllowedChange = new(big.Int).Set(common.Big1) + } + // Calculate the actual change in base fee actualChange := new(big.Int) if header.BaseFee.Cmp(parent.BaseFee) >= 0 { @@ -122,6 +128,13 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { if applyBoundaryCap { maxAllowedChange = new(big.Int).Mul(parent.BaseFee, big.NewInt(MaxBaseFeeChangePercent)) maxAllowedChange.Div(maxAllowedChange, big.NewInt(100)) + + // Ensure minimum 1 wei cap to prevent unlimited growth at very low base fees. + // When percentage calculation rounds to 0 (baseFee < 20 wei), this ensures + // there's still an absolute cap of 1 wei per block. + if maxAllowedChange.Cmp(common.Big1) < 0 { + maxAllowedChange = new(big.Int).Set(common.Big1) + } } if parent.GasUsed > parentGasTarget { diff --git a/consensus/misc/eip1559/eip1559_test.go b/consensus/misc/eip1559/eip1559_test.go index b06597f127..1d1acd0a38 100644 --- a/consensus/misc/eip1559/eip1559_test.go +++ b/consensus/misc/eip1559/eip1559_test.go @@ -910,3 +910,457 @@ func TestBaseFeeValidationPreDandeli(t *testing.T) { require.NoError(t, err, "should accept base fee within boundary post-Dandeli: calculated=%s, header=%s", calculatedBaseFee, baseFeeWithinBoundary) }) } + +// TestCalcBaseFeeVeryLowFeesCapping tests that base fee changes are capped +// even at very low base fees (1-20 wei) post-Dandeli +func TestCalcBaseFeeVeryLowFeesCapping(t *testing.T) { + t.Parallel() + + testConfig := copyConfig(config()) + testConfig.Bor.DandeliBlock = big.NewInt(10) + testConfig.Bor.BhilaiBlock = big.NewInt(5) + + // Use aggressive parameters (low denominator) to force large increases + // that would exceed the 5% boundary at very low base fees + aggressiveDenom := uint64(4) + testConfig.Bor.BaseFeeChangeDenominator = &aggressiveDenom + + tests := []struct { + name string + parentBaseFee int64 + parentGasUsed uint64 + maxAllowedChange int64 // Expected max change in wei + }{ + { + name: "1 wei base fee - aggressive params", + parentBaseFee: 1, + parentGasUsed: 30_000_000, // 100% usage + maxAllowedChange: 1, // 5% rounds to 0, should cap at 1 wei + }, + { + name: "10 wei base fee - aggressive params", + parentBaseFee: 10, + parentGasUsed: 30_000_000, + maxAllowedChange: 1, // 5% = 0.5, rounds to 0, should cap at 1 wei + }, + { + name: "20 wei base fee - aggressive params", + parentBaseFee: 20, + parentGasUsed: 30_000_000, + maxAllowedChange: 1, // 5% = 1 wei exactly + }, + { + name: "100 wei base fee - aggressive params", + parentBaseFee: 100, + parentGasUsed: 30_000_000, + maxAllowedChange: 5, // 5% = 5 wei + }, + { + name: "1 gwei base fee - aggressive params", + parentBaseFee: 1_000_000_000, + parentGasUsed: 30_000_000, + maxAllowedChange: 50_000_000, // 5% of 1 gwei + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parent := &types.Header{ + Number: big.NewInt(20), // Post-Dandeli + GasLimit: 30_000_000, + GasUsed: tt.parentGasUsed, + BaseFee: big.NewInt(tt.parentBaseFee), + } + + calculatedBaseFee := CalcBaseFee(testConfig, parent) + actualChange := new(big.Int).Sub(calculatedBaseFee, parent.BaseFee) + + // Verify change is capped + require.LessOrEqual(t, actualChange.Int64(), tt.maxAllowedChange, + "Base fee increase should be capped at %d wei, got %d wei (%.2f%%)", + tt.maxAllowedChange, actualChange.Int64(), + float64(actualChange.Int64())*100.0/float64(parent.BaseFee.Int64())) + }) + } +} + +// TestVerifyBaseFeeWithinBoundariesLowFees tests header validation at very low base fees +func TestVerifyBaseFeeWithinBoundariesLowFees(t *testing.T) { + t.Parallel() + + testConfig := copyConfig(config()) + testConfig.Bor.DandeliBlock = big.NewInt(10) + testConfig.Bor.BhilaiBlock = big.NewInt(5) + + tests := []struct { + name string + parentBaseFee int64 + headerBaseFee int64 + expectError bool + description string + }{ + { + name: "1 wei → 2 wei (within 1 wei cap)", + parentBaseFee: 1, + headerBaseFee: 2, + expectError: false, + description: "1 wei increase should be accepted", + }, + { + name: "1 wei → 3 wei (exceeds 1 wei cap)", + parentBaseFee: 1, + headerBaseFee: 3, + expectError: true, + description: "2 wei increase should be rejected", + }, + { + name: "10 wei → 11 wei (within 1 wei cap)", + parentBaseFee: 10, + headerBaseFee: 11, + expectError: false, + description: "1 wei increase should be accepted", + }, + { + name: "10 wei → 12 wei (exceeds 1 wei cap)", + parentBaseFee: 10, + headerBaseFee: 12, + expectError: true, + description: "2 wei increase should be rejected", + }, + { + name: "20 wei → 21 wei (5% = 1 wei)", + parentBaseFee: 20, + headerBaseFee: 21, + expectError: false, + description: "1 wei increase (5%) should be accepted", + }, + { + name: "20 wei → 22 wei (exceeds 5%)", + parentBaseFee: 20, + headerBaseFee: 22, + expectError: true, + description: "2 wei increase (10%) should be rejected", + }, + { + name: "100 wei → 105 wei (5%)", + parentBaseFee: 100, + headerBaseFee: 105, + expectError: false, + description: "5% increase should be accepted", + }, + { + name: "100 wei → 107 wei (exceeds 5%)", + parentBaseFee: 100, + headerBaseFee: 107, + expectError: true, + description: "7% increase should be rejected", + }, + { + name: "1 gwei → 1.05 gwei (5%)", + parentBaseFee: 1_000_000_000, + headerBaseFee: 1_050_000_000, + expectError: false, + description: "5% increase should be accepted", + }, + { + name: "1 gwei → 1.1 gwei (10%)", + parentBaseFee: 1_000_000_000, + headerBaseFee: 1_100_000_000, + expectError: true, + description: "10% increase should be rejected", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parent := &types.Header{ + Number: big.NewInt(20), // Post-Dandeli + GasLimit: 30_000_000, + GasUsed: 15_000_000, + BaseFee: big.NewInt(tt.parentBaseFee), + } + + header := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 20_000_000, + BaseFee: big.NewInt(tt.headerBaseFee), + } + + err := VerifyEIP1559Header(testConfig, parent, header) + + if tt.expectError { + require.Error(t, err, tt.description) + require.Contains(t, err.Error(), "baseFee change exceeds", "should mention boundary exceeded") + } else { + require.NoError(t, err, tt.description) + } + }) + } +} + +// TestLowBaseFeeEdgeCases tests edge cases at very low base fees +func TestLowBaseFeeEdgeCases(t *testing.T) { + t.Parallel() + + testConfig := copyConfig(config()) + testConfig.Bor.DandeliBlock = big.NewInt(10) + testConfig.Bor.BhilaiBlock = big.NewInt(5) + + t.Run("exact 5% boundary at 20 wei", func(t *testing.T) { + parent := &types.Header{ + Number: big.NewInt(20), + GasLimit: 30_000_000, + GasUsed: 15_000_000, + BaseFee: big.NewInt(20), // 5% = 1 wei + } + + header := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 20_000_000, + BaseFee: big.NewInt(21), // +1 wei = exactly 5% + } + + err := VerifyEIP1559Header(testConfig, parent, header) + require.NoError(t, err, "exactly 5% increase should be accepted") + }) + + t.Run("just over 5% at 20 wei", func(t *testing.T) { + parent := &types.Header{ + Number: big.NewInt(20), + GasLimit: 30_000_000, + GasUsed: 15_000_000, + BaseFee: big.NewInt(20), + } + + header := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 20_000_000, + BaseFee: big.NewInt(22), // +2 wei = 10% + } + + err := VerifyEIP1559Header(testConfig, parent, header) + require.Error(t, err, "10% increase should be rejected") + }) + + t.Run("decrease capping at very low fees", func(t *testing.T) { + parent := &types.Header{ + Number: big.NewInt(20), + GasLimit: 30_000_000, + GasUsed: 25_000_000, // High usage to get increase first + BaseFee: big.NewInt(2), + } + + calculatedBaseFee := CalcBaseFee(testConfig, parent) + require.GreaterOrEqual(t, calculatedBaseFee.Int64(), parent.BaseFee.Int64(), + "high usage should not decrease base fee") + + // Now test decrease + parent2 := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 10_000_000, // Low usage for decrease + BaseFee: big.NewInt(10), + } + + header := &types.Header{ + Number: big.NewInt(22), + GasLimit: 30_000_000, + GasUsed: 10_000_000, + BaseFee: big.NewInt(9), // -1 wei + } + + err := VerifyEIP1559Header(testConfig, parent2, header) + require.NoError(t, err, "1 wei decrease should be accepted") + + // Test exceeding decrease + header.BaseFee = big.NewInt(8) // -2 wei + err = VerifyEIP1559Header(testConfig, parent2, header) + require.Error(t, err, "2 wei decrease should be rejected") + }) + + t.Run("multiple blocks of growth from 1 wei", func(t *testing.T) { + baseFee := big.NewInt(1) + + // Simulate 5 blocks of full usage + for i := 0; i < 5; i++ { + parent := &types.Header{ + Number: big.NewInt(int64(20 + i)), + GasLimit: 30_000_000, + GasUsed: 30_000_000, // Full usage + BaseFee: baseFee, + } + + newBaseFee := CalcBaseFee(testConfig, parent) + change := new(big.Int).Sub(newBaseFee, baseFee) + + // Each block should be capped at 1 wei increase + require.LessOrEqual(t, change.Int64(), int64(1), + "block %d: increase should be capped at 1 wei", i) + + baseFee = newBaseFee + } + + // After 5 blocks of 1 wei increases, should be at most 6 wei + require.LessOrEqual(t, baseFee.Int64(), int64(6), + "after 5 blocks starting from 1 wei, should be at most 6 wei") + }) + + t.Run("zero base fee edge case", func(t *testing.T) { + // Test starting from 0 wei (theoretical edge case) + parentZero := &types.Header{ + Number: big.NewInt(20), + GasLimit: 30_000_000, + GasUsed: 30_000_000, // Full usage + BaseFee: big.NewInt(0), + } + + // Calculate from 0 wei - should be able to increase + calculatedBaseFee := CalcBaseFee(testConfig, parentZero) + require.Greater(t, calculatedBaseFee.Int64(), int64(0), + "base fee should be able to increase from 0") + require.LessOrEqual(t, calculatedBaseFee.Int64(), int64(1), + "increase from 0 wei should be capped at 1 wei") + + // Test validation: 0 → 0 should pass (no change) + headerZero := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 30_000_000, + BaseFee: big.NewInt(0), + } + err := VerifyEIP1559Header(testConfig, parentZero, headerZero) + require.NoError(t, err, "0 → 0 wei should be accepted") + + // Test validation: 0 → 1 should pass (within 1 wei cap) + headerOne := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 30_000_000, + BaseFee: big.NewInt(1), + } + err = VerifyEIP1559Header(testConfig, parentZero, headerOne) + require.NoError(t, err, "0 → 1 wei should be accepted (within 1 wei cap)") + + // Test validation: 0 → 2 should fail (exceeds 1 wei cap) + headerTwo := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 30_000_000, + BaseFee: big.NewInt(2), + } + err = VerifyEIP1559Header(testConfig, parentZero, headerTwo) + require.Error(t, err, "0 → 2 wei should be rejected (exceeds 1 wei cap)") + require.Contains(t, err.Error(), "baseFee change exceeds", + "error should mention boundary exceeded") + }) +} + +// TestLowBaseFeesPreDandeli tests that pre-Dandeli strict validation still works at low fees +func TestLowBaseFeesPreDandeli(t *testing.T) { + t.Parallel() + + testConfig := copyConfig(config()) + testConfig.Bor.DandeliBlock = big.NewInt(100) // Far in future + testConfig.Bor.BhilaiBlock = big.NewInt(5) + + t.Run("pre-Dandeli: low fees use strict validation", func(t *testing.T) { + parent := &types.Header{ + Number: big.NewInt(20), // Pre-Dandeli + GasLimit: 30_000_000, + GasUsed: 15_000_000, + BaseFee: big.NewInt(10), // Very low, but pre-Dandeli + } + + calculatedBaseFee := CalcBaseFee(testConfig, parent) + + // Even 1 wei off should fail pre-Dandeli + wrongBaseFee := new(big.Int).Add(calculatedBaseFee, big.NewInt(1)) + + header := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 20_000_000, + BaseFee: wrongBaseFee, + } + + err := VerifyEIP1559Header(testConfig, parent, header) + require.Error(t, err, "should reject even 1 wei difference pre-Dandeli") + require.Contains(t, err.Error(), "invalid baseFee", "should use strict validation") + }) + + t.Run("pre-Dandeli: exact match accepted", func(t *testing.T) { + parent := &types.Header{ + Number: big.NewInt(20), + GasLimit: 30_000_000, + GasUsed: 15_000_000, + BaseFee: big.NewInt(10), + } + + calculatedBaseFee := CalcBaseFee(testConfig, parent) + + header := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 20_000_000, + BaseFee: calculatedBaseFee, + } + + err := VerifyEIP1559Header(testConfig, parent, header) + require.NoError(t, err, "exact match should be accepted pre-Dandeli") + }) +} + +// TestLowBaseFeeIntegrationWithExistingBoundary verifies new behavior doesn't break existing tests +func TestLowBaseFeeIntegrationWithExistingBoundary(t *testing.T) { + t.Parallel() + + testConfig := copyConfig(config()) + testConfig.Bor.DandeliBlock = big.NewInt(10) + testConfig.Bor.BhilaiBlock = big.NewInt(5) + + // Normal production base fees - should behave identically to before + parent := &types.Header{ + Number: big.NewInt(20), + GasLimit: 30_000_000, + GasUsed: 15_000_000, + BaseFee: big.NewInt(25_000_000_000), // 25 gwei (typical production) + } + + t.Run("normal fees: 3% increase accepted", func(t *testing.T) { + header := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 20_000_000, + BaseFee: big.NewInt(25_750_000_000), // 3% increase + } + err := VerifyEIP1559Header(testConfig, parent, header) + require.NoError(t, err, "3% increase should be accepted") + }) + + t.Run("normal fees: 7% increase rejected", func(t *testing.T) { + header := &types.Header{ + Number: big.NewInt(21), + GasLimit: 30_000_000, + GasUsed: 20_000_000, + BaseFee: big.NewInt(26_750_000_000), // 7% increase + } + err := VerifyEIP1559Header(testConfig, parent, header) + require.Error(t, err, "7% increase should be rejected") + require.Contains(t, err.Error(), "baseFee change exceeds", "should mention boundary exceeded") + }) + + t.Run("normal fees: CalcBaseFee produces value within boundary", func(t *testing.T) { + calculatedBaseFee := CalcBaseFee(testConfig, parent) + + // Calculate percentage change + change := new(big.Int).Sub(calculatedBaseFee, parent.BaseFee) + changePercent := new(big.Int).Mul(change, big.NewInt(100)) + changePercent.Div(changePercent, parent.BaseFee) + + require.LessOrEqual(t, changePercent.Int64(), int64(5), + "CalcBaseFee should produce value within 5%% boundary") + }) +} From ec50f6a46faab8102f087de43cd271fe75f4bfe2 Mon Sep 17 00:00:00 2001 From: Lucca Martins Date: Wed, 4 Feb 2026 21:14:32 -0300 Subject: [PATCH 2/3] moving configurable rules to lisovo --- consensus/misc/eip1559/eip1559.go | 22 +++++++------- .../eip1559/eip1559_basefee_boundary_test.go | 14 +++++---- consensus/misc/eip1559/eip1559_test.go | 30 ++++++++++++++----- docs/cli/server.md | 4 +-- internal/cli/server/config.go | 4 +-- internal/cli/server/flags.go | 4 +-- params/config.go | 8 ++--- params/protocol_params.go | 6 ++-- 8 files changed, 54 insertions(+), 38 deletions(-) diff --git a/consensus/misc/eip1559/eip1559.go b/consensus/misc/eip1559/eip1559.go index 4874c68f2f..c5b88d3329 100644 --- a/consensus/misc/eip1559/eip1559.go +++ b/consensus/misc/eip1559/eip1559.go @@ -37,9 +37,9 @@ const ( // VerifyEIP1559Header verifies some header attributes which were changed in EIP-1559, // - gas limit check -// - basefee check with different rules pre/post Dandeli: -// - Pre-Dandeli: Strict validation (baseFee must exactly match calculated value) -// - Post-Dandeli: Boundary validation (baseFee change must be within MaxBaseFeeChangePercent) +// - basefee check with different rules pre/post Lisovo: +// - Pre-Lisovo: Strict validation (baseFee must exactly match calculated value) +// - Post-Lisovo: Boundary validation (baseFee change must be within MaxBaseFeeChangePercent) func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Header) error { // Verify that the gas limit remains within allowed bounds parentGasLimit := parent.GasLimit @@ -54,12 +54,12 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade return errors.New("header is missing baseFee") } - // Post-Dandeli: Validate that base fee changes are within allowed boundaries - if config.Bor != nil && config.Bor.IsDandeli(header.Number) { + // Post-Lisovo: Validate that base fee changes are within allowed boundaries + if config.Bor != nil && config.Bor.IsLisovo(header.Number) { return verifyBaseFeeWithinBoundaries(parent, header) } - // Pre-Dandeli: Verify the baseFee is correct based on the parent header + // Pre-Lisovo: Verify the baseFee is correct based on the parent header expectedBaseFee := CalcBaseFee(config, parent) if header.BaseFee.Cmp(expectedBaseFee) != 0 { return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d", @@ -70,7 +70,7 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade } // verifyBaseFeeWithinBoundaries checks that the base fee change is within the allowed boundary. -// This prevents excessive fee volatility while allowing dynamic fee adjustment post-Dandeli. +// This prevents excessive fee volatility while allowing dynamic fee adjustment post-Lisovo. // The boundary limit is defined by MaxBaseFeeChangePercent constant. func verifyBaseFeeWithinBoundaries(parent, header *types.Header) error { // Calculate the maximum allowed change (MaxBaseFeeChangePercent of parent base fee) @@ -122,9 +122,9 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { baseFeeChangeDenominatorUint64 = params.BaseFeeChangeDenominator(config.Bor, parent.Number) ) - // Calculate maximum allowed change (only applies post-Dandeli) + // Calculate maximum allowed change (only applies post-Lisovo) var maxAllowedChange *big.Int - applyBoundaryCap := config.Bor != nil && config.Bor.IsDandeli(parent.Number) + applyBoundaryCap := config.Bor != nil && config.Bor.IsLisovo(parent.Number) if applyBoundaryCap { maxAllowedChange = new(big.Int).Mul(parent.BaseFee, big.NewInt(MaxBaseFeeChangePercent)) maxAllowedChange.Div(maxAllowedChange, big.NewInt(100)) @@ -145,7 +145,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { num.Div(num, denom.SetUint64(parentGasTarget)) num.Div(num, denom.SetUint64(baseFeeChangeDenominatorUint64)) - // Cap the increase to MaxBaseFeeChangePercent post-Dandeli + // Cap the increase to MaxBaseFeeChangePercent post-Lisovo if applyBoundaryCap && num.Cmp(maxAllowedChange) > 0 { num.Set(maxAllowedChange) } @@ -162,7 +162,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { num.Div(num, denom.SetUint64(parentGasTarget)) num.Div(num, denom.SetUint64(baseFeeChangeDenominatorUint64)) - // Cap the decrease to MaxBaseFeeChangePercent post-Dandeli + // Cap the decrease to MaxBaseFeeChangePercent post-Lisovo if applyBoundaryCap && num.Cmp(maxAllowedChange) > 0 { num.Set(maxAllowedChange) } diff --git a/consensus/misc/eip1559/eip1559_basefee_boundary_test.go b/consensus/misc/eip1559/eip1559_basefee_boundary_test.go index 6c513db875..d80e205a1f 100644 --- a/consensus/misc/eip1559/eip1559_basefee_boundary_test.go +++ b/consensus/misc/eip1559/eip1559_basefee_boundary_test.go @@ -27,12 +27,13 @@ import ( "github.com/stretchr/testify/require" ) -// TestBaseFeeBoundary tests the MaxBaseFeeChangePercent base fee change limit post-Dandeli. +// TestBaseFeeBoundary tests the MaxBaseFeeChangePercent base fee change limit post-Lisovo. // It verifies both the CalcBaseFee capping and VerifyEIP1559Header validation. func TestBaseFeeBoundary(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) + testConfig.Bor.LisovoBlock = big.NewInt(15) testConfig.Bor.DandeliBlock = big.NewInt(10) testConfig.Bor.BhilaiBlock = big.NewInt(5) @@ -141,8 +142,8 @@ func TestBaseFeeBoundary(t *testing.T) { } } -// TestBaseFeeBoundaryPreDandeli tests that pre-Dandeli blocks still use strict validation -func TestBaseFeeBoundaryPreDandeli(t *testing.T) { +// TestBaseFeeBoundaryPreLisovo tests that pre-Lisovo blocks still use strict validation +func TestBaseFeeBoundaryPreLisovo(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) @@ -204,11 +205,12 @@ func TestBaseFeeBoundaryPreDandeli(t *testing.T) { } // TestAggressiveParametersExceedBoundary tests that aggressive parameter configurations -// (low denominators) are properly capped at MaxBaseFeeChangePercent by CalcBaseFee post-Dandeli +// (low denominators) are properly capped at MaxBaseFeeChangePercent by CalcBaseFee post-Lisovo func TestAggressiveParametersExceedBoundary(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) + testConfig.Bor.LisovoBlock = big.NewInt(15) testConfig.Bor.DandeliBlock = big.NewInt(10) testConfig.Bor.BhilaiBlock = big.NewInt(5) @@ -320,8 +322,8 @@ func TestAggressiveParametersExceedBoundary(t *testing.T) { } } -// TestDefaultParametersWithinBoundary documents and verifies that default post-Dandeli -// parameters stay well within the MaxBaseFeeChangePercent boundary at all gas usage levels +// TestDefaultParametersWithinBoundary documents and verifies that default post-Dandeli parameters +// (65% gas target) stay well within the MaxBaseFeeChangePercent boundary post-Lisovo at all gas usage levels func TestDefaultParametersWithinBoundary(t *testing.T) { t.Parallel() diff --git a/consensus/misc/eip1559/eip1559_test.go b/consensus/misc/eip1559/eip1559_test.go index 1d1acd0a38..e6785d06aa 100644 --- a/consensus/misc/eip1559/eip1559_test.go +++ b/consensus/misc/eip1559/eip1559_test.go @@ -72,6 +72,7 @@ func copyConfig(original *params.ChainConfig) *params.ChainConfig { MadhugiriBlock: original.Bor.MadhugiriBlock, MadhugiriProBlock: original.Bor.MadhugiriProBlock, DandeliBlock: original.Bor.DandeliBlock, + LisovoBlock: original.Bor.LisovoBlock, } } return config @@ -238,6 +239,7 @@ func TestCalcParentGasTarget(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) + testConfig.Bor.LisovoBlock = big.NewInt(20) testConfig.Bor.DandeliBlock = big.NewInt(20) defaultGasLimit := uint64(60_000_000) @@ -332,6 +334,7 @@ func TestCalcBaseFeeDandeli(t *testing.T) { testConfig := copyConfig(config()) testConfig.Bor.BhilaiBlock = big.NewInt(8) + testConfig.Bor.LisovoBlock = big.NewInt(20) testConfig.Bor.DandeliBlock = big.NewInt(20) // Case 1: Create pre-dandeli cases where HF is defined in future. Validate @@ -445,12 +448,13 @@ func TestCalcBaseFeeDandeli(t *testing.T) { } // TestDynamicTargetGasPercentage verifies that the TargetGasPercentage parameter -// can be dynamically set after Dandeli HF and affects base fee calculations correctly +// can be dynamically set after Lisovo HF and affects base fee calculations correctly func TestDynamicTargetGasPercentage(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) testConfig.Bor.BhilaiBlock = big.NewInt(8) + testConfig.Bor.LisovoBlock = big.NewInt(20) testConfig.Bor.DandeliBlock = big.NewInt(20) // Test with 70% target gas percentage @@ -530,12 +534,13 @@ func TestDynamicTargetGasPercentage(t *testing.T) { } // TestDynamicBaseFeeChangeDenominator verifies that the BaseFeeChangeDenominator parameter -// can be dynamically set after Dandeli HF and affects the rate of base fee change correctly +// can be dynamically set after Lisovo HF and affects the rate of base fee change correctly func TestDynamicBaseFeeChangeDenominator(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) testConfig.Bor.BhilaiBlock = big.NewInt(8) + testConfig.Bor.LisovoBlock = big.NewInt(20) testConfig.Bor.DandeliBlock = big.NewInt(20) gasLimit := uint64(60_000_000) @@ -615,12 +620,13 @@ func TestDynamicBaseFeeChangeDenominator(t *testing.T) { }) } -// TestVerifyEIP1559HeaderNoBaseFeeValidation tests post-Dandeli boundary validation +// TestVerifyEIP1559HeaderNoBaseFeeValidation tests post-Lisovo boundary validation // instead of strict validation. Base fees must be within MaxBaseFeeChangePercent boundary. func TestVerifyEIP1559HeaderNoBaseFeeValidation(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) + testConfig.Bor.LisovoBlock = big.NewInt(20) testConfig.Bor.DandeliBlock = big.NewInt(20) testConfig.Bor.BhilaiBlock = big.NewInt(5) @@ -716,6 +722,7 @@ func TestInvalidTargetGasPercentage(t *testing.T) { testConfig := copyConfig(config()) testConfig.Bor.BhilaiBlock = big.NewInt(8) + testConfig.Bor.LisovoBlock = big.NewInt(20) testConfig.Bor.DandeliBlock = big.NewInt(20) gasLimit := uint64(60_000_000) @@ -793,6 +800,7 @@ func TestInvalidBaseFeeChangeDenominator(t *testing.T) { testConfig := copyConfig(config()) testConfig.Bor.BhilaiBlock = big.NewInt(8) + testConfig.Bor.LisovoBlock = big.NewInt(20) testConfig.Bor.DandeliBlock = big.NewInt(20) gasLimit := uint64(60_000_000) @@ -847,6 +855,8 @@ func TestBaseFeeValidationPreDandeli(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) + testConfig.Bor.LisovoBlock = big.NewInt(20) + testConfig.Bor.LisovoBlock = big.NewInt(20) testConfig.Bor.DandeliBlock = big.NewInt(20) parent := &types.Header{ @@ -888,7 +898,7 @@ func TestBaseFeeValidationPreDandeli(t *testing.T) { t.Run("post-Dandeli: accepts base fee within boundary", func(t *testing.T) { parent := &types.Header{ - Number: big.NewInt(20), // Post-Dandeli + Number: big.NewInt(25), // Post-Lisovo (boundary validation active) GasLimit: 30_000_000, GasUsed: 15_000_000, BaseFee: big.NewInt(1_000_000_000), @@ -900,7 +910,7 @@ func TestBaseFeeValidationPreDandeli(t *testing.T) { baseFeeWithinBoundary.Div(baseFeeWithinBoundary, big.NewInt(100)) header := &types.Header{ - Number: big.NewInt(21), + Number: big.NewInt(26), GasLimit: 30_000_000, GasUsed: 20_000_000, BaseFee: baseFeeWithinBoundary, // Within boundary @@ -912,11 +922,12 @@ func TestBaseFeeValidationPreDandeli(t *testing.T) { } // TestCalcBaseFeeVeryLowFeesCapping tests that base fee changes are capped -// even at very low base fees (1-20 wei) post-Dandeli +// even at very low base fees (1-20 wei) post-Lisovo func TestCalcBaseFeeVeryLowFeesCapping(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) + testConfig.Bor.LisovoBlock = big.NewInt(15) testConfig.Bor.DandeliBlock = big.NewInt(10) testConfig.Bor.BhilaiBlock = big.NewInt(5) @@ -989,6 +1000,7 @@ func TestVerifyBaseFeeWithinBoundariesLowFees(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) + testConfig.Bor.LisovoBlock = big.NewInt(15) testConfig.Bor.DandeliBlock = big.NewInt(10) testConfig.Bor.BhilaiBlock = big.NewInt(5) @@ -1104,6 +1116,7 @@ func TestLowBaseFeeEdgeCases(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) + testConfig.Bor.LisovoBlock = big.NewInt(15) testConfig.Bor.DandeliBlock = big.NewInt(10) testConfig.Bor.BhilaiBlock = big.NewInt(5) @@ -1258,8 +1271,8 @@ func TestLowBaseFeeEdgeCases(t *testing.T) { }) } -// TestLowBaseFeesPreDandeli tests that pre-Dandeli strict validation still works at low fees -func TestLowBaseFeesPreDandeli(t *testing.T) { +// TestLowBaseFeesPreLisovo tests that pre-Lisovo strict validation still works at low fees +func TestLowBaseFeesPreLisovo(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) @@ -1318,6 +1331,7 @@ func TestLowBaseFeeIntegrationWithExistingBoundary(t *testing.T) { t.Parallel() testConfig := copyConfig(config()) + testConfig.Bor.LisovoBlock = big.NewInt(15) testConfig.Bor.DandeliBlock = big.NewInt(10) testConfig.Bor.BhilaiBlock = big.NewInt(5) diff --git a/docs/cli/server.md b/docs/cli/server.md index 5da0aeb50d..b10194153c 100644 --- a/docs/cli/server.md +++ b/docs/cli/server.md @@ -324,9 +324,9 @@ The ```bor server``` command runs the Bor client. - ```miner.baseFeeBuffer```: Buffer around target base fee in wei (no adjustment when within buffer) (default: 0) -- ```miner.targetGasPercentage```: Target gas as percentage of gas limit (1-100, default 65) for post-Dandeli blocks (default: 0) +- ```miner.targetGasPercentage```: Target gas as percentage of gas limit (1-100, default 65) for post-Lisovo blocks (default: 0) -- ```miner.baseFeeChangeDenominator```: Base fee change rate denominator (must be >0, default 64) for post-Dandeli blocks (default: 0) +- ```miner.baseFeeChangeDenominator```: Base fee change rate denominator (must be >0, default 64) for post-Lisovo blocks (default: 0) ### Telemetry Options diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index 082edcb2c8..cee9219214 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -421,10 +421,10 @@ type SealerConfig struct { BlockTime time.Duration `hcl:"-,optional" toml:"-"` BlockTimeRaw string `hcl:"blocktime,optional" toml:"blocktime,optional"` - // TargetGasPercentage is the target gas as percentage of gas limit (1-100, default 65) for post-Dandeli blocks + // TargetGasPercentage is the target gas as percentage of gas limit (1-100, default 65) for post-Lisovo blocks TargetGasPercentage uint64 `hcl:"target-gas-percentage,optional" toml:"target-gas-percentage,optional"` - // BaseFeeChangeDenominator is the base fee change rate (must be >0, default 64) for post-Dandeli blocks + // BaseFeeChangeDenominator is the base fee change rate (must be >0, default 64) for post-Lisovo blocks BaseFeeChangeDenominator uint64 `hcl:"base-fee-change-denominator,optional" toml:"base-fee-change-denominator,optional"` } diff --git a/internal/cli/server/flags.go b/internal/cli/server/flags.go index 5984ff9398..a7ae19265c 100644 --- a/internal/cli/server/flags.go +++ b/internal/cli/server/flags.go @@ -443,14 +443,14 @@ func (c *Command) Flags(config *Config) *flagset.Flagset { }) f.Uint64Flag(&flagset.Uint64Flag{ Name: "miner.targetGasPercentage", - Usage: "Target gas as percentage of gas limit (1-100, default 65) for post-Dandeli blocks", + Usage: "Target gas as percentage of gas limit (1-100, default 65) for post-Lisovo blocks", Value: &c.cliConfig.Sealer.TargetGasPercentage, Default: c.cliConfig.Sealer.TargetGasPercentage, Group: "Sealer", }) f.Uint64Flag(&flagset.Uint64Flag{ Name: "miner.baseFeeChangeDenominator", - Usage: "Base fee change rate denominator (must be >0, default 64) for post-Dandeli blocks", + Usage: "Base fee change rate denominator (must be >0, default 64) for post-Lisovo blocks", Value: &c.cliConfig.Sealer.BaseFeeChangeDenominator, Default: c.cliConfig.Sealer.BaseFeeChangeDenominator, Group: "Sealer", diff --git a/params/config.go b/params/config.go index baeb6d4da2..428e8c0eb6 100644 --- a/params/config.go +++ b/params/config.go @@ -916,8 +916,8 @@ type BorConfig struct { // Runtime miner configuration (set via sealer/miner CLI flags, not from genesis JSON) // These affect consensus gas pricing but are configurable per-node for operational flexibility - TargetGasPercentage *uint64 `json:"-"` // Post-Dandeli: target gas as % of gas limit (1-100, default 65). Set via --miner.target-gas-percentage - BaseFeeChangeDenominator *uint64 `json:"-"` // Post-Dandeli: base fee change rate (must be >0, default 64). Set via --miner.base-fee-change-denominator + TargetGasPercentage *uint64 `json:"-"` // Post-Lisovo: target gas as % of gas limit (configurable post-Lisovo, default 65% post-Dandeli). Set via --miner.target-gas-percentage + BaseFeeChangeDenominator *uint64 `json:"-"` // Post-Lisovo: base fee change rate (configurable post-Lisovo, must be >0, default 64). Set via --miner.base-fee-change-denominator JaipurBlock *big.Int `json:"jaipurBlock"` // Jaipur switch block (nil = no fork, 0 = already on jaipur) DelhiBlock *big.Int `json:"delhiBlock"` // Delhi switch block (nil = no fork, 0 = already on delhi) @@ -998,7 +998,7 @@ func (c *BorConfig) IsLisovo(number *big.Int) bool { } // GetTargetGasPercentage returns the target gas percentage for gas limit calculation. -// After Dandeli hard fork, this value can be configured via CLI flags (stored in BorConfig at runtime). +// After Lisovo hard fork, this value can be configured via CLI flags (stored in BorConfig at runtime). // It validates the configured value and falls back to defaults if invalid or nil. // Valid range: 1-100 (percentage). func (c *BorConfig) GetTargetGasPercentage(number *big.Int) uint64 { @@ -1008,7 +1008,7 @@ func (c *BorConfig) GetTargetGasPercentage(number *big.Int) uint64 { } // If custom value is set, validate it - if c.TargetGasPercentage != nil { + if c.TargetGasPercentage != nil && c.IsLisovo(number) { val := *c.TargetGasPercentage // Validate: must be between 1 and 100 if val > 0 && val <= 100 { diff --git a/params/protocol_params.go b/params/protocol_params.go index 8109e26a90..17d9fafae8 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -246,15 +246,15 @@ var ( // - Pre-Delhi: 8 (default) // - Post-Delhi: 16 // - Post-Bhilai: 64 -// - Post-Dandeli: Configurable via BorConfig.BaseFeeChangeDenominator (validated, falls back to Bhilai default if invalid) +// - Post-Lisovo: Configurable via BorConfig.BaseFeeChangeDenominator (validated, falls back to Bhilai default if invalid) // If borConfig is nil, returns the default value of 8. func BaseFeeChangeDenominator(borConfig *BorConfig, number *big.Int) uint64 { // Handle cases where bor consensus isn't available to avoid panic if borConfig == nil { return DefaultBaseFeeChangeDenominator } - // If Dandeli is active and custom value is set, validate and use it - if borConfig.IsDandeli(number) && borConfig.BaseFeeChangeDenominator != nil { + // If Lisovo is active and custom value is set, validate and use it + if borConfig.IsLisovo(number) && borConfig.BaseFeeChangeDenominator != nil { val := *borConfig.BaseFeeChangeDenominator // Validate: must be non-zero to prevent division by zero if val > 0 { From ded88b3e78f5a4543c8fc2bd758e4979392293d8 Mon Sep 17 00:00:00 2001 From: Lucca Martins Date: Wed, 4 Feb 2026 22:39:54 -0300 Subject: [PATCH 3/3] fix missing tests --- consensus/misc/eip1559/eip1559_test.go | 1 - params/config_test.go | 31 ++++--- tests/bor/bor_config_change_test.go | 111 ++++++++++++++----------- 3 files changed, 79 insertions(+), 64 deletions(-) diff --git a/consensus/misc/eip1559/eip1559_test.go b/consensus/misc/eip1559/eip1559_test.go index e6785d06aa..1305c8df14 100644 --- a/consensus/misc/eip1559/eip1559_test.go +++ b/consensus/misc/eip1559/eip1559_test.go @@ -856,7 +856,6 @@ func TestBaseFeeValidationPreDandeli(t *testing.T) { testConfig := copyConfig(config()) testConfig.Bor.LisovoBlock = big.NewInt(20) - testConfig.Bor.LisovoBlock = big.NewInt(20) testConfig.Bor.DandeliBlock = big.NewInt(20) parent := &types.Header{ diff --git a/params/config_test.go b/params/config_test.go index da57ced1c4..8653ec50cb 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -813,7 +813,7 @@ func TestGetTargetGasPercentage(t *testing.T) { } }) - t.Run("Post-Dandeli with valid custom values", func(t *testing.T) { + t.Run("Post-Lisovo with valid custom values", func(t *testing.T) { testCases := []struct { customValue uint64 description string @@ -829,12 +829,13 @@ func TestGetTargetGasPercentage(t *testing.T) { val := tc.customValue config := &BorConfig{ DandeliBlock: big.NewInt(1000), + LisovoBlock: big.NewInt(1000), // Configurable params require Lisovo TargetGasPercentage: &val, } result := config.GetTargetGasPercentage(big.NewInt(1000)) if result != tc.customValue { - t.Errorf("Post-Dandeli with custom value %d: expected %d, got %d", + t.Errorf("Post-Lisovo with custom value %d: expected %d, got %d", tc.customValue, tc.customValue, result) } }) @@ -948,28 +949,29 @@ func TestGetBaseFeeChangeDenominator(t *testing.T) { } }) - t.Run("Post-Dandeli with nil custom value falls back to Bhilai default", func(t *testing.T) { + t.Run("Post-Lisovo with nil custom value falls back to Bhilai default", func(t *testing.T) { config := &BorConfig{ DelhiBlock: big.NewInt(1000), BhilaiBlock: big.NewInt(2000), DandeliBlock: big.NewInt(3000), + LisovoBlock: big.NewInt(3000), // Configurable params require Lisovo BaseFeeChangeDenominator: nil, } result := BaseFeeChangeDenominator(config, big.NewInt(3000)) if result != BaseFeeChangeDenominatorPostBhilai { - t.Errorf("Post-Dandeli with nil custom value: expected %d, got %d", + t.Errorf("Post-Lisovo with nil custom value: expected %d, got %d", BaseFeeChangeDenominatorPostBhilai, result) } result = BaseFeeChangeDenominator(config, big.NewInt(4000)) if result != BaseFeeChangeDenominatorPostBhilai { - t.Errorf("Post-Dandeli with nil custom value (block 4000): expected %d, got %d", + t.Errorf("Post-Lisovo with nil custom value (block 4000): expected %d, got %d", BaseFeeChangeDenominatorPostBhilai, result) } }) - t.Run("Post-Dandeli with valid custom value", func(t *testing.T) { + t.Run("Post-Lisovo with valid custom value", func(t *testing.T) { testCases := []uint64{1, 8, 16, 32, 64, 128} for _, customVal := range testCases { @@ -979,47 +981,50 @@ func TestGetBaseFeeChangeDenominator(t *testing.T) { DelhiBlock: big.NewInt(1000), BhilaiBlock: big.NewInt(2000), DandeliBlock: big.NewInt(3000), + LisovoBlock: big.NewInt(3000), // Configurable params require Lisovo BaseFeeChangeDenominator: &val, } result := BaseFeeChangeDenominator(config, big.NewInt(3000)) if result != customVal { - t.Errorf("Post-Dandeli with custom value %d: expected %d, got %d", + t.Errorf("Post-Lisovo with custom value %d: expected %d, got %d", customVal, customVal, result) } }) } }) - t.Run("Post-Dandeli with invalid value 0 falls back to Bhilai default", func(t *testing.T) { + t.Run("Post-Lisovo with invalid value 0 falls back to Bhilai default", func(t *testing.T) { invalidVal := uint64(0) config := &BorConfig{ DelhiBlock: big.NewInt(1000), BhilaiBlock: big.NewInt(2000), DandeliBlock: big.NewInt(3000), + LisovoBlock: big.NewInt(3000), // Configurable params require Lisovo BaseFeeChangeDenominator: &invalidVal, } result := BaseFeeChangeDenominator(config, big.NewInt(3000)) if result != BaseFeeChangeDenominatorPostBhilai { - t.Errorf("Post-Dandeli with invalid value 0: expected %d, got %d", + t.Errorf("Post-Lisovo with invalid value 0: expected %d, got %d", BaseFeeChangeDenominatorPostBhilai, result) } }) - t.Run("Pre-Dandeli with custom value ignores it", func(t *testing.T) { + t.Run("Pre-Lisovo with custom value ignores it", func(t *testing.T) { customVal := uint64(999) config := &BorConfig{ DelhiBlock: big.NewInt(1000), BhilaiBlock: big.NewInt(2000), DandeliBlock: big.NewInt(3000), + LisovoBlock: big.NewInt(4000), // Lisovo after Dandeli BaseFeeChangeDenominator: &customVal, } - // Before Dandeli, custom value should be ignored - result := BaseFeeChangeDenominator(config, big.NewInt(2500)) + // Before Lisovo, custom value should be ignored + result := BaseFeeChangeDenominator(config, big.NewInt(3500)) if result != BaseFeeChangeDenominatorPostBhilai { - t.Errorf("Pre-Dandeli with custom value (block 2500): expected %d, got %d", + t.Errorf("Pre-Lisovo with custom value (block 3500): expected %d, got %d", BaseFeeChangeDenominatorPostBhilai, result) } }) diff --git a/tests/bor/bor_config_change_test.go b/tests/bor/bor_config_change_test.go index c55d897887..3628e9da10 100644 --- a/tests/bor/bor_config_change_test.go +++ b/tests/bor/bor_config_change_test.go @@ -42,11 +42,15 @@ func TestBorConfigParameterChange(t *testing.T) { genesis.Config.LondonBlock = common.Big0 genesis.Config.Bor.JaipurBlock = common.Big0 - // Enable Dandeli fork at block 20 - this is where we'll change the parameters - dandeliBlock := big.NewInt(20) + // Enable Dandeli fork at block 15 for percentage-based calculation + dandeliBlock := big.NewInt(15) genesis.Config.Bor.DandeliBlock = dandeliBlock - // Set custom BaseFeeChangeDenominator and TargetGasPercentage that will take effect at Dandeli + // Enable Lisovo fork at block 20 - this is where configurable parameters take effect + lisovoBlock := big.NewInt(20) + genesis.Config.Bor.LisovoBlock = lisovoBlock + + // Set custom BaseFeeChangeDenominator and TargetGasPercentage that will take effect at Lisovo customBaseFeeChangeDenominator := uint64(32) // Different from default (64) customTargetGasPercentage := uint64(70) // Different from default (65) genesis.Config.Bor.BaseFeeChangeDenominator = &customBaseFeeChangeDenominator @@ -68,8 +72,8 @@ func TestBorConfigParameterChange(t *testing.T) { } } - // Wait for blocks to be mined beyond the Dandeli fork block - targetBlockNum := dandeliBlock.Uint64() + 10 // Mine 10 blocks after the fork + // Wait for blocks to be mined beyond the Lisovo fork block + targetBlockNum := lisovoBlock.Uint64() + 10 // Mine 10 blocks after the fork timeout := time.After(120 * time.Second) for { @@ -97,13 +101,13 @@ checkResults: require.Equal(t, chain0.CurrentHeader().Number.Uint64(), chain1.CurrentHeader().Number.Uint64(), "Both nodes should be at the same block height") - // Verify blocks around the Dandeli fork - preDandeliBlock := dandeliBlock.Uint64() - 1 - atDandeliBlock := dandeliBlock.Uint64() - postDandeliBlock := dandeliBlock.Uint64() + 5 + // Verify blocks around the Lisovo fork (where configurable parameters activate) + preLisovoBlock := lisovoBlock.Uint64() - 1 + atLisovoBlock := lisovoBlock.Uint64() + postLisovoBlock := lisovoBlock.Uint64() + 5 // Check block hashes match between nodes (validator accepted producer's blocks) - for _, blockNum := range []uint64{preDandeliBlock, atDandeliBlock, postDandeliBlock} { + for _, blockNum := range []uint64{preLisovoBlock, atLisovoBlock, postLisovoBlock} { header0 := chain0.GetHeaderByNumber(blockNum) header1 := chain1.GetHeaderByNumber(blockNum) @@ -118,50 +122,50 @@ checkResults: } // Verify that the BaseFeeChangeDenominator and TargetGasPercentage are being used correctly - // by checking the base fee calculation before and after Dandeli fork + // by checking the base fee calculation before and after Lisovo fork (configurable parameters) - // Pre-Dandeli block: should use default parameters - preHeader := chain0.GetHeaderByNumber(preDandeliBlock) - preParentHeader := chain0.GetHeaderByNumber(preDandeliBlock - 1) + // Pre-Lisovo block: should use default parameters + preHeader := chain0.GetHeaderByNumber(preLisovoBlock) + preParentHeader := chain0.GetHeaderByNumber(preLisovoBlock - 1) if preParentHeader != nil && preHeader != nil { - // Calculate expected base fee using pre-Dandeli parameters + // Calculate expected base fee using pre-Lisovo parameters (default 65%) expectedPreBaseFee := eip1559.CalcBaseFee(genesis.Config, preParentHeader) - t.Logf("Pre-Dandeli block %d: Expected BaseFee=%s, Actual BaseFee=%s", - preDandeliBlock, expectedPreBaseFee.String(), preHeader.BaseFee.String()) + t.Logf("Pre-Lisovo block %d: Expected BaseFee=%s, Actual BaseFee=%s", + preLisovoBlock, expectedPreBaseFee.String(), preHeader.BaseFee.String()) // Note: We don't assert equality here because the block producer might have chosen different gas usage } - // Post-Dandeli block: should use custom parameters - postHeader := chain0.GetHeaderByNumber(postDandeliBlock) - postParentHeader := chain0.GetHeaderByNumber(postDandeliBlock - 1) + // Post-Lisovo block: should use custom parameters + postHeader := chain0.GetHeaderByNumber(postLisovoBlock) + postParentHeader := chain0.GetHeaderByNumber(postLisovoBlock - 1) if postParentHeader != nil && postHeader != nil { - // Calculate expected base fee using post-Dandeli parameters + // Calculate expected base fee using post-Lisovo parameters expectedPostBaseFee := eip1559.CalcBaseFee(genesis.Config, postParentHeader) - t.Logf("Post-Dandeli block %d: Expected BaseFee=%s, Actual BaseFee=%s", - postDandeliBlock, expectedPostBaseFee.String(), postHeader.BaseFee.String()) + t.Logf("Post-Lisovo block %d: Expected BaseFee=%s, Actual BaseFee=%s", + postLisovoBlock, expectedPostBaseFee.String(), postHeader.BaseFee.String()) - // After Dandeli, base fee validation is skipped to allow dynamic configuration - // But we can verify that the calculation uses the new parameters by checking the target gas + // After Lisovo, configurable parameters take effect + // Verify that the calculation uses the custom parameters by checking the target gas targetPercentage := genesis.Config.Bor.GetTargetGasPercentage(postParentHeader.Number) expectedTargetGas := postParentHeader.GasLimit * targetPercentage / 100 - t.Logf("Post-Dandeli target gas calculation: TargetPercentage=%d, GasLimit=%d, Expected TargetGas=%d", + t.Logf("Post-Lisovo target gas calculation: TargetPercentage=%d, GasLimit=%d, Expected TargetGas=%d", targetPercentage, postParentHeader.GasLimit, expectedTargetGas) // Verify the custom target percentage is being used assert.Equal(t, customTargetGasPercentage, targetPercentage, - "Custom TargetGasPercentage should be used after Dandeli fork") + "Custom TargetGasPercentage should be used after Lisovo fork") // Verify the custom base fee change denominator is being used baseFeeChangeDenom := params.BaseFeeChangeDenominator(genesis.Config.Bor, postParentHeader.Number) assert.Equal(t, customBaseFeeChangeDenominator, baseFeeChangeDenom, - "Custom BaseFeeChangeDenominator should be used after Dandeli fork") + "Custom BaseFeeChangeDenominator should be used after Lisovo fork") } // Verify both nodes successfully validated the blocks with new parameters // by checking that the validator node has the same blocks as the producer - for blockNum := atDandeliBlock; blockNum <= postDandeliBlock; blockNum++ { + for blockNum := atLisovoBlock; blockNum <= postLisovoBlock; blockNum++ { header0 := chain0.GetHeaderByNumber(blockNum) header1 := chain1.GetHeaderByNumber(blockNum) @@ -200,10 +204,14 @@ func TestBorConfigParameterChangeVerification(t *testing.T) { genesis.Config.LondonBlock = common.Big0 genesis.Config.Bor.JaipurBlock = common.Big0 - // Enable Dandeli fork at block 10 - dandeliBlock := big.NewInt(10) + // Enable Dandeli fork at block 5 (percentage-based calculation) + dandeliBlock := big.NewInt(5) genesis.Config.Bor.DandeliBlock = dandeliBlock + // Enable Lisovo fork at block 10 (configurable parameters) + lisovoBlock := big.NewInt(10) + genesis.Config.Bor.LisovoBlock = lisovoBlock + // Set custom parameters customBaseFeeChangeDenominator := uint64(128) // Very different from default customTargetGasPercentage := uint64(80) // Very different from default @@ -219,8 +227,8 @@ func TestBorConfigParameterChangeVerification(t *testing.T) { err = ethBackend.StartMining() require.NoError(t, err) - // Wait for blocks to be mined beyond the Dandeli fork - targetBlockNum := dandeliBlock.Uint64() + 5 + // Wait for blocks to be mined beyond the Lisovo fork + targetBlockNum := lisovoBlock.Uint64() + 5 timeout := time.After(60 * time.Second) for { @@ -250,8 +258,8 @@ verifyHeaders: require.NoError(t, err, "Header %d should be valid", blockNum) // Check which parameters are in effect - if blockNum >= dandeliBlock.Uint64() { - // Post-Dandeli: custom parameters should be used + if blockNum >= lisovoBlock.Uint64() { + // Post-Lisovo: custom parameters should be used targetPercentage := genesis.Config.Bor.GetTargetGasPercentage(header.Number) baseFeeChangeDenom := params.BaseFeeChangeDenominator(genesis.Config.Bor, header.Number) @@ -260,18 +268,18 @@ verifyHeaders: assert.Equal(t, customBaseFeeChangeDenominator, baseFeeChangeDenom, "Block %d should use custom BaseFeeChangeDenominator", blockNum) - t.Logf("Block %d (Post-Dandeli): TargetGasPercentage=%d, BaseFeeChangeDenominator=%d, BaseFee=%s", + t.Logf("Block %d (Post-Lisovo): TargetGasPercentage=%d, BaseFeeChangeDenominator=%d, BaseFee=%s", blockNum, targetPercentage, baseFeeChangeDenom, header.BaseFee.String()) } else { - // Pre-Dandeli: default parameters should be used + // Pre-Lisovo: default parameters should be used targetPercentage := genesis.Config.Bor.GetTargetGasPercentage(header.Number) baseFeeChangeDenom := params.BaseFeeChangeDenominator(genesis.Config.Bor, header.Number) - // Pre-Dandeli should use pre-Dandeli defaults (not our custom values) + // Pre-Lisovo should use default parameters (not our custom values) assert.NotEqual(t, customTargetGasPercentage, targetPercentage, "Block %d should NOT use custom TargetGasPercentage", blockNum) - t.Logf("Block %d (Pre-Dandeli): TargetGasPercentage=%d, BaseFeeChangeDenominator=%d, BaseFee=%s", + t.Logf("Block %d (Pre-Lisovo): TargetGasPercentage=%d, BaseFeeChangeDenominator=%d, BaseFee=%s", blockNum, targetPercentage, baseFeeChangeDenom, header.BaseFee.String()) } } @@ -298,7 +306,8 @@ func TestBorConfigParameterDivergence(t *testing.T) { genesisProducer := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 16) genesisProducer.Config.LondonBlock = common.Big0 genesisProducer.Config.Bor.JaipurBlock = common.Big0 - genesisProducer.Config.Bor.DandeliBlock = big.NewInt(10) // Enable Dandeli early + genesisProducer.Config.Bor.DandeliBlock = big.NewInt(5) // Enable Dandeli early (percentage-based calc) + genesisProducer.Config.Bor.LisovoBlock = big.NewInt(10) // Enable Lisovo (configurable params) // Producer uses first set of parameters producerBaseFeeChangeDenominator := uint64(32) @@ -310,7 +319,8 @@ func TestBorConfigParameterDivergence(t *testing.T) { genesisValidator := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 16) genesisValidator.Config.LondonBlock = common.Big0 genesisValidator.Config.Bor.JaipurBlock = common.Big0 - genesisValidator.Config.Bor.DandeliBlock = big.NewInt(10) // Same Dandeli activation + genesisValidator.Config.Bor.DandeliBlock = big.NewInt(5) // Same Dandeli activation + genesisValidator.Config.Bor.LisovoBlock = big.NewInt(10) // Same Lisovo activation // Validator uses DIFFERENT parameters (simulating a "second change") validatorBaseFeeChangeDenominator := uint64(128) // Much larger denominator @@ -379,9 +389,9 @@ checkDivergence: t.Logf("Producer current block: %d", chainProducer.CurrentHeader().Number.Uint64()) t.Logf("Validator current block: %d", chainValidator.CurrentHeader().Number.Uint64()) - // Verify blocks at key positions - dandeliBlock := uint64(10) - checkBlocks := []uint64{dandeliBlock, dandeliBlock + 5, dandeliBlock + 10} + // Verify blocks at key positions (focus on post-Lisovo where configurable params apply) + lisovoBlock := uint64(10) + checkBlocks := []uint64{lisovoBlock, lisovoBlock + 5, lisovoBlock + 10} for _, blockNum := range checkBlocks { producerHeader := chainProducer.GetHeaderByNumber(blockNum) @@ -410,8 +420,8 @@ checkDivergence: t.Logf(" Block BaseFee=%s, GasLimit=%d, GasUsed=%d", producerHeader.BaseFee.String(), producerHeader.GasLimit, producerHeader.GasUsed) - // Post-Dandeli: verify nodes are using their respective different configs - if blockNum >= dandeliBlock { + // Post-Lisovo: verify nodes are using their respective different configs + if blockNum >= lisovoBlock { assert.Equal(t, producerTargetGasPercentage, producerTargetPct, "Producer should use its own TargetGasPercentage") assert.Equal(t, producerBaseFeeChangeDenominator, producerDenom, @@ -430,7 +440,7 @@ checkDivergence: // Verify validator explicitly accepts producer's headers despite config divergence engineValidator := nodeValidator.Engine().(*bor.Bor) - for blockNum := dandeliBlock; blockNum <= dandeliBlock+10; blockNum++ { + for blockNum := lisovoBlock; blockNum <= lisovoBlock+10; blockNum++ { producerHeader := chainProducer.GetHeaderByNumber(blockNum) if producerHeader == nil { continue @@ -445,7 +455,7 @@ checkDivergence: } t.Log("Test completed successfully: Validator accepted blocks from producer despite divergent BorConfig parameters") - t.Log("This demonstrates that post-Dandeli base fee validation skip allows for flexible parameter updates") + t.Log("This demonstrates that post-Lisovo boundary validation allows for flexible parameter updates") } // TestBorConfigMultipleParameterChanges tests a scenario where parameters are conceptually @@ -463,11 +473,12 @@ func TestBorConfigMultipleParameterChanges(t *testing.T) { faucets[i], _ = crypto.GenerateKey() } - // Initialize genesis with Dandeli fork at block 10 + // Initialize genesis with Dandeli fork at block 5 and Lisovo fork at block 10 genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 16) genesis.Config.LondonBlock = common.Big0 genesis.Config.Bor.JaipurBlock = common.Big0 - genesis.Config.Bor.DandeliBlock = big.NewInt(10) + genesis.Config.Bor.DandeliBlock = big.NewInt(5) // Percentage-based calculation + genesis.Config.Bor.LisovoBlock = big.NewInt(10) // Configurable parameters // Start with first set of parameters (will be used from block 10 onward) firstBaseFeeChangeDenominator := uint64(32)