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,12 +57,10 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
calculatedBaseFee := calcBaseFee(config, parent)
// 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.Berachain.Prague1.MinimumBaseFeeWei)
minBaseFee := new(big.Int).SetUint64(config.MinBaseFee(parent.Number, parent.Time))
if calculatedBaseFee.Cmp(minBaseFee) < 0 {
calculatedBaseFee = minBaseFee
}
}
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

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.
if config != nil && config.Berachain.Prague1.Time != nil {
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(forksByTime)

View file

@ -241,7 +241,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3P11(update engine.ForkchoiceStateV1,
return engine.STATUS_INVALID, attributesErr("missing beacon root")
case params.ProposerPubkey == nil:
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")
}
}
@ -700,7 +700,7 @@ func (api *ConsensusAPI) NewPayloadV4P11(params engine.ExecutableData, versioned
return invalidStatus, paramsErr("nil executionRequests post-prague")
case proposerPubkey == nil:
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")
}
requests := convertRequests(executionRequests)

View file

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

View file

@ -101,7 +101,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedWithWitnessV3P11(update engine.Forkcho
return engine.STATUS_INVALID, attributesErr("missing beacon root")
case params.ProposerPubkey == nil:
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")
}
}
@ -207,7 +207,7 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV4P11(params engine.ExecutableData
return invalidStatus, paramsErr("nil executionRequests post-prague")
case proposerPubkey == nil:
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")
}
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")
case proposerPubkey == nil:
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")
}
requests := convertRequests(executionRequests)

View file

@ -198,6 +198,10 @@ var (
BaseFeeChangeDenominator: BerachainBaseFeeChangeDenominator,
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.
@ -236,6 +240,10 @@ var (
BaseFeeChangeDenominator: BerachainBaseFeeChangeDenominator,
PoLDistributorAddress: PoLDistributorAddress,
},
Prague2: Prague2Config{
Time: newUint64(9999999999), // TODO(Prague2): Replace with actual time.
MinimumBaseFeeWei: 0,
},
},
}
// AllEthashProtocolChanges contains every protocol change (EIPs) introduced
@ -540,6 +548,9 @@ type ChainConfig struct {
type BerachainConfig struct {
// Prague1 fork values.
Prague1 Prague1Config `json:"prague1,omitempty"`
// Prague2 fork values.
Prague2 Prague2Config `json:"prague2,omitempty"`
}
// String implements the stringer interface.
@ -548,6 +559,9 @@ func (o *BerachainConfig) String() string {
if o.Prague1.Time != nil {
banner += fmt.Sprintf("(%s)", o.Prague1)
}
if o.Prague2.Time != nil {
banner += fmt.Sprintf("(%s)", o.Prague2)
}
return banner
}
@ -575,6 +589,23 @@ func (c Prague1Config) String() string {
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.
type EthashConfig struct{}
@ -666,6 +697,9 @@ func (c *ChainConfig) Description() string {
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)
}
if c.Berachain.Prague2.Time != nil {
banner += fmt.Sprintf(" - Prague2: %-10v\n", c.Berachain.Prague2)
}
if c.OsakaTime != nil {
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)
}
// 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.
func (c *ChainConfig) IsOsaka(num *big.Int, time uint64) bool {
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: "pragueTime", timestamp: c.PragueTime, 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: "verkleTime", timestamp: c.VerkleTime, 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) {
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) {
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
}
// 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.
func (c *ChainConfig) ElasticityMultiplier() uint64 {
return DefaultElasticityMultiplier
@ -1135,6 +1190,8 @@ func (c *ChainConfig) LatestFork(time uint64) forks.Fork {
switch {
case c.IsOsaka(london, time):
return forks.Osaka
case c.IsPrague2(london, time):
return forks.Prague2
case c.IsPrague1(london, time):
return forks.Prague1
case c.IsPrague(london, time):
@ -1153,6 +1210,8 @@ func (c *ChainConfig) BlobConfig(fork forks.Fork) *BlobConfig {
switch fork {
case forks.Osaka:
return DefaultOsakaBlobConfig
case forks.Prague1, forks.Prague2:
return DefaultBerachainPragueBlobConfig
case forks.Prague:
return DefaultPragueBlobConfig
case forks.Cancun:
@ -1170,6 +1229,7 @@ func (c *ChainConfig) ActiveSystemContracts(time uint64) map[string]common.Addre
if fork >= forks.Osaka {
// no new system contracts
}
// TODO(BRIP-4): Add PoL distributor address to the system contracts?
if fork >= forks.Prague {
active["CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS"] = ConsolidationQueueAddress
active["DEPOSIT_CONTRACT_ADDRESS"] = c.DepositContractAddress
@ -1188,6 +1248,8 @@ func (c *ChainConfig) Timestamp(fork forks.Fork) *uint64 {
switch {
case fork == forks.Osaka:
return c.OsakaTime
case fork == forks.Prague2:
return c.Berachain.Prague2.Time
case fork == forks.Prague1:
return c.Berachain.Prague1.Time
case fork == forks.Prague:
@ -1341,7 +1403,7 @@ type Rules struct {
IsEIP2929, IsEIP4762 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon bool
IsMerge, IsShanghai, IsCancun, IsPrague, IsPrague1, IsOsaka bool
IsMerge, IsShanghai, IsCancun, IsPrague, IsPrague1, IsPrague2, IsOsaka bool
IsVerkle bool
}
@ -1372,6 +1434,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
IsCancun: isMerge && c.IsCancun(num, timestamp),
IsPrague: isMerge && c.IsPrague(num, timestamp),
IsPrague1: isMerge && c.IsPrague1(num, timestamp),
IsPrague2: isMerge && c.IsPrague2(num, timestamp),
IsOsaka: isMerge && c.IsOsaka(num, timestamp),
IsVerkle: isVerkle,
IsEIP4762: isVerkle,

View file

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