feat(forks): prague2 for new minimum base fee

This commit is contained in:
Cal Bera 2025-09-10 10:06:36 -07:00
parent b8ea5f66d0
commit f42560029e
8 changed files with 86 additions and 20 deletions

View file

@ -57,11 +57,9 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
calculatedBaseFee := calcBaseFee(config, parent) calculatedBaseFee := calcBaseFee(config, parent)
// Starting at the Prague1 fork, the base fee must be at least the minimum base fee. // Starting at the Prague1 fork, the base fee must be at least the minimum base fee.
if config.IsPrague1(parent.Number, parent.Time) { minBaseFee := new(big.Int).SetUint64(config.MinBaseFee(parent.Number, parent.Time))
minBaseFee := new(big.Int).SetUint64(config.Berachain.Prague1.MinimumBaseFeeWei) if calculatedBaseFee.Cmp(minBaseFee) < 0 {
if calculatedBaseFee.Cmp(minBaseFee) < 0 { calculatedBaseFee = minBaseFee
calculatedBaseFee = minBaseFee
}
} }
return calculatedBaseFee return calculatedBaseFee

View file

@ -34,7 +34,7 @@ import (
) )
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Berachain:Prague-1 PoL validation tests // Berachain: Prague-1 PoL validation tests
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// newPrague1Config returns a ChainConfig where Prague (and Prague-1) are active // newPrague1Config returns a ChainConfig where Prague (and Prague-1) are active

View file

@ -268,11 +268,14 @@ func gatherForks(config *params.ChainConfig, genesis uint64) ([]uint64, []uint64
} }
} }
} }
// Berachain-specific: include nested Prague1 fork time in the fork id set // Berachain-specific: include nested Prague1 and Prague2 fork times in the fork id set
// so that clients using it (e.g. reth) compute the same fork checksum. // so that clients using it (e.g. reth) compute the same fork checksum.
if config != nil && config.Berachain.Prague1.Time != nil { if config != nil && config.Berachain.Prague1.Time != nil {
forksByTime = append(forksByTime, *config.Berachain.Prague1.Time) forksByTime = append(forksByTime, *config.Berachain.Prague1.Time)
} }
if config != nil && config.Berachain.Prague2.Time != nil {
forksByTime = append(forksByTime, *config.Berachain.Prague2.Time)
}
slices.Sort(forksByBlock) slices.Sort(forksByBlock)
slices.Sort(forksByTime) slices.Sort(forksByTime)

View file

@ -241,7 +241,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3P11(update engine.ForkchoiceStateV1,
return engine.STATUS_INVALID, attributesErr("missing beacon root") return engine.STATUS_INVALID, attributesErr("missing beacon root")
case params.ProposerPubkey == nil: case params.ProposerPubkey == nil:
return engine.STATUS_INVALID, attributesErr("missing proposer pubkey") return engine.STATUS_INVALID, attributesErr("missing proposer pubkey")
case !api.checkFork(params.Timestamp, forks.Prague1, forks.Osaka): case !api.checkFork(params.Timestamp, forks.Prague1, forks.Prague2, forks.Osaka):
return engine.STATUS_INVALID, unsupportedForkErr("fcuV3P11 must only be called for prague1 payloads") return engine.STATUS_INVALID, unsupportedForkErr("fcuV3P11 must only be called for prague1 payloads")
} }
} }
@ -700,7 +700,7 @@ func (api *ConsensusAPI) NewPayloadV4P11(params engine.ExecutableData, versioned
return invalidStatus, paramsErr("nil executionRequests post-prague") return invalidStatus, paramsErr("nil executionRequests post-prague")
case proposerPubkey == nil: case proposerPubkey == nil:
return invalidStatus, paramsErr("nil proposerPubkey post-prague1") return invalidStatus, paramsErr("nil proposerPubkey post-prague1")
case !api.checkFork(params.Timestamp, forks.Prague1, forks.Osaka): case !api.checkFork(params.Timestamp, forks.Prague1, forks.Prague2, forks.Osaka):
return invalidStatus, unsupportedForkErr("newPayloadV4P11 must only be called for prague1 payloads") return invalidStatus, unsupportedForkErr("newPayloadV4P11 must only be called for prague1 payloads")
} }
requests := convertRequests(executionRequests) requests := convertRequests(executionRequests)

View file

@ -100,7 +100,7 @@ type SimulatedBeacon struct {
func payloadVersion(config *params.ChainConfig, time uint64) engine.PayloadVersion { func payloadVersion(config *params.ChainConfig, time uint64) engine.PayloadVersion {
switch config.LatestFork(time) { switch config.LatestFork(time) {
case forks.Prague1: case forks.Prague1, forks.Prague2:
return engine.PayloadV3P11 return engine.PayloadV3P11
case forks.Prague, forks.Cancun: case forks.Prague, forks.Cancun:
return engine.PayloadV3 return engine.PayloadV3

View file

@ -101,7 +101,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3P11(update engine.Forkcho
return engine.STATUS_INVALID, attributesErr("missing beacon root") return engine.STATUS_INVALID, attributesErr("missing beacon root")
case params.ProposerPubkey == nil: case params.ProposerPubkey == nil:
return engine.STATUS_INVALID, attributesErr("missing proposer pubkey") return engine.STATUS_INVALID, attributesErr("missing proposer pubkey")
case !api.checkFork(params.Timestamp, forks.Prague1, forks.Osaka): case !api.checkFork(params.Timestamp, forks.Prague1, forks.Prague2, forks.Osaka):
return engine.STATUS_INVALID, unsupportedForkErr("fcuV3P11 must only be called for prague1 payloads") return engine.STATUS_INVALID, unsupportedForkErr("fcuV3P11 must only be called for prague1 payloads")
} }
} }
@ -207,7 +207,7 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV4P11(params engine.ExecutableData
return invalidStatus, paramsErr("nil executionRequests post-prague") return invalidStatus, paramsErr("nil executionRequests post-prague")
case proposerPubkey == nil: case proposerPubkey == nil:
return invalidStatus, paramsErr("nil proposerPubkey post-prague1") return invalidStatus, paramsErr("nil proposerPubkey post-prague1")
case !api.checkFork(params.Timestamp, forks.Prague1, forks.Osaka): case !api.checkFork(params.Timestamp, forks.Prague1, forks.Prague2, forks.Osaka):
return invalidStatus, unsupportedForkErr("newPayloadV4P11 must only be called for prague1 payloads") return invalidStatus, unsupportedForkErr("newPayloadV4P11 must only be called for prague1 payloads")
} }
requests := convertRequests(executionRequests) requests := convertRequests(executionRequests)
@ -312,7 +312,7 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4P11(params engine.ExecutableDa
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil executionRequests post-prague") return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil executionRequests post-prague")
case proposerPubkey == nil: case proposerPubkey == nil:
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil proposerPubkey post-prague1") return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, paramsErr("nil proposerPubkey post-prague1")
case !api.checkFork(params.Timestamp, forks.Prague1, forks.Osaka): case !api.checkFork(params.Timestamp, forks.Prague1, forks.Prague2, forks.Osaka):
return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("executeStatelessPayloadV4P11 must only be called for prague1 payloads") return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, unsupportedForkErr("executeStatelessPayloadV4P11 must only be called for prague1 payloads")
} }
requests := convertRequests(executionRequests) requests := convertRequests(executionRequests)

View file

@ -198,6 +198,10 @@ var (
BaseFeeChangeDenominator: BerachainBaseFeeChangeDenominator, BaseFeeChangeDenominator: BerachainBaseFeeChangeDenominator,
PoLDistributorAddress: PoLDistributorAddress, PoLDistributorAddress: PoLDistributorAddress,
}, },
Prague2: Prague2Config{
Time: newUint64(9999999999), // TODO(Prague2): Replace with actual time.
MinimumBaseFeeWei: 0,
},
}, },
} }
// BepoliaChainConfig contains the chain parameters to run a node on the Bepolia test network. // BepoliaChainConfig contains the chain parameters to run a node on the Bepolia test network.
@ -236,6 +240,10 @@ var (
BaseFeeChangeDenominator: BerachainBaseFeeChangeDenominator, BaseFeeChangeDenominator: BerachainBaseFeeChangeDenominator,
PoLDistributorAddress: PoLDistributorAddress, PoLDistributorAddress: PoLDistributorAddress,
}, },
Prague2: Prague2Config{
Time: newUint64(9999999999), // TODO(Prague2): Replace with actual time.
MinimumBaseFeeWei: 0,
},
}, },
} }
// AllEthashProtocolChanges contains every protocol change (EIPs) introduced // AllEthashProtocolChanges contains every protocol change (EIPs) introduced
@ -540,6 +548,9 @@ type ChainConfig struct {
type BerachainConfig struct { type BerachainConfig struct {
// Prague1 fork values. // Prague1 fork values.
Prague1 Prague1Config `json:"prague1,omitempty"` Prague1 Prague1Config `json:"prague1,omitempty"`
// Prague2 fork values.
Prague2 Prague2Config `json:"prague2,omitempty"`
} }
// String implements the stringer interface. // String implements the stringer interface.
@ -548,6 +559,9 @@ func (o *BerachainConfig) String() string {
if o.Prague1.Time != nil { if o.Prague1.Time != nil {
banner += fmt.Sprintf("(%s)", o.Prague1) banner += fmt.Sprintf("(%s)", o.Prague1)
} }
if o.Prague2.Time != nil {
banner += fmt.Sprintf("(%s)", o.Prague2)
}
return banner return banner
} }
@ -575,6 +589,23 @@ func (c Prague1Config) String() string {
return banner return banner
} }
// Prague2Config is the config values for the Prague2 fork on Berachain.
type Prague2Config struct {
// Time is the time of the Prague2 fork.
Time *uint64 `json:"time,omitempty"` // Prague2 switch time (0 = already on prague2, nil = no fork)
// MinimumBaseFeeWei is the minimum base fee in wei.
MinimumBaseFeeWei uint64 `json:"minimumBaseFeeWei,omitempty"`
}
// String implements the stringer interface.
func (c Prague2Config) String() string {
banner := "prague2"
if c.Time != nil {
banner += fmt.Sprintf("(time: %v, minimumBaseFeeWei: %v)", *c.Time, c.MinimumBaseFeeWei)
}
return banner
}
// EthashConfig is the consensus engine configs for proof-of-work based sealing. // EthashConfig is the consensus engine configs for proof-of-work based sealing.
type EthashConfig struct{} type EthashConfig struct{}
@ -666,6 +697,9 @@ func (c *ChainConfig) Description() string {
if c.Berachain.Prague1.Time != nil { if c.Berachain.Prague1.Time != nil {
banner += fmt.Sprintf(" - Prague1: %-10v (https://github.com/berachain/BRIPs/blob/main/meta/BRIP-0004.md)\n", c.Berachain.Prague1) banner += fmt.Sprintf(" - Prague1: %-10v (https://github.com/berachain/BRIPs/blob/main/meta/BRIP-0004.md)\n", c.Berachain.Prague1)
} }
if c.Berachain.Prague2.Time != nil {
banner += fmt.Sprintf(" - Prague2: %-10v\n", c.Berachain.Prague2)
}
if c.OsakaTime != nil { if c.OsakaTime != nil {
banner += fmt.Sprintf(" - Osaka: @%-10v\n", *c.OsakaTime) banner += fmt.Sprintf(" - Osaka: @%-10v\n", *c.OsakaTime)
} }
@ -811,6 +845,12 @@ func (c *ChainConfig) IsPrague1(num *big.Int, time uint64) bool {
return c.IsPrague(num, time) && isTimestampForked(c.Berachain.Prague1.Time, time) return c.IsPrague(num, time) && isTimestampForked(c.Berachain.Prague1.Time, time)
} }
// IsPrague2 returns whether time is either equal to the Prague2 fork time or greater.
// NOTE: Prague2 is a Berachain fork and must be on Ethereum's Prague fork.
func (c *ChainConfig) IsPrague2(num *big.Int, time uint64) bool {
return c.IsPrague(num, time) && isTimestampForked(c.Berachain.Prague2.Time, time)
}
// IsOsaka returns whether time is either equal to the Osaka fork time or greater. // IsOsaka returns whether time is either equal to the Osaka fork time or greater.
func (c *ChainConfig) IsOsaka(num *big.Int, time uint64) bool { func (c *ChainConfig) IsOsaka(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.OsakaTime, time) return c.IsLondon(num) && isTimestampForked(c.OsakaTime, time)
@ -920,6 +960,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
{name: "cancunTime", timestamp: c.CancunTime, optional: true}, {name: "cancunTime", timestamp: c.CancunTime, optional: true},
{name: "pragueTime", timestamp: c.PragueTime, optional: true}, {name: "pragueTime", timestamp: c.PragueTime, optional: true},
{name: "prague1Time", timestamp: c.Berachain.Prague1.Time, optional: true}, {name: "prague1Time", timestamp: c.Berachain.Prague1.Time, optional: true},
{name: "prague2Time", timestamp: c.Berachain.Prague2.Time, optional: true},
{name: "osakaTime", timestamp: c.OsakaTime, optional: true}, {name: "osakaTime", timestamp: c.OsakaTime, optional: true},
{name: "verkleTime", timestamp: c.VerkleTime, optional: true}, {name: "verkleTime", timestamp: c.VerkleTime, optional: true},
{name: "bpo1", timestamp: c.BPO1Time, optional: true}, {name: "bpo1", timestamp: c.BPO1Time, optional: true},
@ -1088,6 +1129,9 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int,
if isForkTimestampIncompatible(c.Berachain.Prague1.Time, newcfg.Berachain.Prague1.Time, headTimestamp) { if isForkTimestampIncompatible(c.Berachain.Prague1.Time, newcfg.Berachain.Prague1.Time, headTimestamp) {
return newTimestampCompatError("Prague1 fork timestamp", c.Berachain.Prague1.Time, newcfg.Berachain.Prague1.Time) return newTimestampCompatError("Prague1 fork timestamp", c.Berachain.Prague1.Time, newcfg.Berachain.Prague1.Time)
} }
if isForkTimestampIncompatible(c.Berachain.Prague2.Time, newcfg.Berachain.Prague2.Time, headTimestamp) {
return newTimestampCompatError("Prague2 fork timestamp", c.Berachain.Prague2.Time, newcfg.Berachain.Prague2.Time)
}
if isForkTimestampIncompatible(c.OsakaTime, newcfg.OsakaTime, headTimestamp) { if isForkTimestampIncompatible(c.OsakaTime, newcfg.OsakaTime, headTimestamp) {
return newTimestampCompatError("Osaka fork timestamp", c.OsakaTime, newcfg.OsakaTime) return newTimestampCompatError("Osaka fork timestamp", c.OsakaTime, newcfg.OsakaTime)
} }
@ -1122,6 +1166,17 @@ func (c *ChainConfig) BaseFeeChangeDenominator(num *big.Int, time uint64) uint64
return DefaultBaseFeeChangeDenominator return DefaultBaseFeeChangeDenominator
} }
// MinBaseFee returns the minimum base fee (in wei) based on the active fork.
func (c *ChainConfig) MinBaseFee(num *big.Int, time uint64) uint64 {
if c.IsPrague2(num, time) {
return c.Berachain.Prague2.MinimumBaseFeeWei
}
if c.IsPrague1(num, time) {
return c.Berachain.Prague1.MinimumBaseFeeWei
}
return 0
}
// ElasticityMultiplier bounds the maximum gas limit an EIP-1559 block may have. // ElasticityMultiplier bounds the maximum gas limit an EIP-1559 block may have.
func (c *ChainConfig) ElasticityMultiplier() uint64 { func (c *ChainConfig) ElasticityMultiplier() uint64 {
return DefaultElasticityMultiplier return DefaultElasticityMultiplier
@ -1135,6 +1190,8 @@ func (c *ChainConfig) LatestFork(time uint64) forks.Fork {
switch { switch {
case c.IsOsaka(london, time): case c.IsOsaka(london, time):
return forks.Osaka return forks.Osaka
case c.IsPrague2(london, time):
return forks.Prague2
case c.IsPrague1(london, time): case c.IsPrague1(london, time):
return forks.Prague1 return forks.Prague1
case c.IsPrague(london, time): case c.IsPrague(london, time):
@ -1153,6 +1210,8 @@ func (c *ChainConfig) BlobConfig(fork forks.Fork) *BlobConfig {
switch fork { switch fork {
case forks.Osaka: case forks.Osaka:
return DefaultOsakaBlobConfig return DefaultOsakaBlobConfig
case forks.Prague1, forks.Prague2:
return DefaultBerachainPragueBlobConfig
case forks.Prague: case forks.Prague:
return DefaultPragueBlobConfig return DefaultPragueBlobConfig
case forks.Cancun: case forks.Cancun:
@ -1170,6 +1229,7 @@ func (c *ChainConfig) ActiveSystemContracts(time uint64) map[string]common.Addre
if fork >= forks.Osaka { if fork >= forks.Osaka {
// no new system contracts // no new system contracts
} }
// TODO(BRIP-4): Add PoL distributor address to the system contracts?
if fork >= forks.Prague { if fork >= forks.Prague {
active["CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS"] = ConsolidationQueueAddress active["CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS"] = ConsolidationQueueAddress
active["DEPOSIT_CONTRACT_ADDRESS"] = c.DepositContractAddress active["DEPOSIT_CONTRACT_ADDRESS"] = c.DepositContractAddress
@ -1188,6 +1248,8 @@ func (c *ChainConfig) Timestamp(fork forks.Fork) *uint64 {
switch { switch {
case fork == forks.Osaka: case fork == forks.Osaka:
return c.OsakaTime return c.OsakaTime
case fork == forks.Prague2:
return c.Berachain.Prague2.Time
case fork == forks.Prague1: case fork == forks.Prague1:
return c.Berachain.Prague1.Time return c.Berachain.Prague1.Time
case fork == forks.Prague: case fork == forks.Prague:
@ -1336,13 +1398,13 @@ func (err *ConfigCompatError) Error() string {
// Rules is a one time interface meaning that it shouldn't be used in between transition // Rules is a one time interface meaning that it shouldn't be used in between transition
// phases. // phases.
type Rules struct { type Rules struct {
ChainID *big.Int ChainID *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsEIP2929, IsEIP4762 bool IsEIP2929, IsEIP4762 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon bool IsBerlin, IsLondon bool
IsMerge, IsShanghai, IsCancun, IsPrague, IsPrague1, IsOsaka bool IsMerge, IsShanghai, IsCancun, IsPrague, IsPrague1, IsPrague2, IsOsaka bool
IsVerkle bool IsVerkle bool
} }
// Rules ensures c's ChainID is not nil. // Rules ensures c's ChainID is not nil.
@ -1372,6 +1434,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
IsCancun: isMerge && c.IsCancun(num, timestamp), IsCancun: isMerge && c.IsCancun(num, timestamp),
IsPrague: isMerge && c.IsPrague(num, timestamp), IsPrague: isMerge && c.IsPrague(num, timestamp),
IsPrague1: isMerge && c.IsPrague1(num, timestamp), IsPrague1: isMerge && c.IsPrague1(num, timestamp),
IsPrague2: isMerge && c.IsPrague2(num, timestamp),
IsOsaka: isMerge && c.IsOsaka(num, timestamp), IsOsaka: isMerge && c.IsOsaka(num, timestamp),
IsVerkle: isVerkle, IsVerkle: isVerkle,
IsEIP4762: isVerkle, IsEIP4762: isVerkle,

View file

@ -40,6 +40,7 @@ const (
Cancun Cancun
Prague Prague
Prague1 Prague1
Prague2
Osaka Osaka
) )
@ -73,5 +74,6 @@ var forkToString = map[Fork]string{
Cancun: "Cancun", Cancun: "Cancun",
Prague: "Prague", Prague: "Prague",
Prague1: "Prague1", Prague1: "Prague1",
Prague2: "Prague2",
Osaka: "Osaka", Osaka: "Osaka",
} }