Skip to content

Commit

Permalink
Merge pull request #99 from bobanetwork/add-fjord-hardfork
Browse files Browse the repository at this point in the history
Add fjord hardfork
  • Loading branch information
boyuan-chen authored May 23, 2024
2 parents 9b33ae2 + 6477c3f commit 33a19fa
Show file tree
Hide file tree
Showing 15 changed files with 330 additions and 36 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- run: git submodule update --init --recursive --force
- uses: actions/setup-go@v4
with:
go-version: '1.20'
go-version: '1.21'
- name: Install dependencies on Linux
if: runner.os == 'Linux'
run: sudo apt update && sudo apt install build-essential
Expand All @@ -56,7 +56,7 @@ jobs:
- run: git submodule update --init --recursive --force
- uses: actions/setup-go@v4
with:
go-version: '1.20'
go-version: '1.21'

- uses: actions/cache@v3
with:
Expand Down
7 changes: 7 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ var (
Name: "override.ecotone",
Usage: "Manually specify the Optimism Ecotone fork time, overriding the bundled setting",
}
OverrideOptimismFjordFlag = flags.BigFlag{
Name: "override.fjord",
Usage: "Manually specify the Optimism Fjord fork timestamp, overriding the bundled setting",
}
// Ethash settings
EthashCachesInMemoryFlag = cli.IntFlag{
Name: "ethash.cachesinmem",
Expand Down Expand Up @@ -1837,6 +1841,9 @@ func SetEthConfig(ctx *cli.Context, nodeConfig *nodecfg.Config, cfg *ethconfig.C
"cancun", overrideCancunTime.String(), "ecotone", overrideOptimismEcotoneTime.String())
}
}
if ctx.IsSet(OverrideOptimismFjordFlag.Name) {
cfg.OverrideOptimismFjordTime = flags.GlobalBig(ctx, OverrideOptimismFjordFlag.Name)
}
if ctx.IsSet(InternalConsensusFlag.Name) && clparams.EmbeddedSupported(cfg.NetworkID) {
cfg.InternalCL = ctx.Bool(InternalConsensusFlag.Name)
}
Expand Down
6 changes: 3 additions & 3 deletions core/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestGenesisBlockHashes(t *testing.T) {
t.Fatal(err)
}
defer tx.Rollback()
_, block, err := core.WriteGenesisBlock(tx, genesis, nil, nil, nil, nil, "", logger)
_, block, err := core.WriteGenesisBlock(tx, genesis, nil, "", logger)
require.NoError(t, err)
expect := params.GenesisHashByChainName(network)
require.NotNil(t, expect, network)
Expand Down Expand Up @@ -86,13 +86,13 @@ func TestCommitGenesisIdempotency(t *testing.T) {
defer tx.Rollback()

genesis := core.GenesisBlockByChainName(networkname.MainnetChainName)
_, _, err = core.WriteGenesisBlock(tx, genesis, nil, nil, nil, nil, "", logger)
_, _, err = core.WriteGenesisBlock(tx, genesis, nil, "", logger)
require.NoError(t, err)
seq, err := tx.ReadSequence(kv.EthTx)
require.NoError(t, err)
require.Equal(t, uint64(2), seq)

_, _, err = core.WriteGenesisBlock(tx, genesis, nil, nil, nil, nil, "", logger)
_, _, err = core.WriteGenesisBlock(tx, genesis, nil, "", logger)
require.NoError(t, err)
seq, err = tx.ReadSequence(kv.EthTx)
require.NoError(t, err)
Expand Down
57 changes: 36 additions & 21 deletions core/genesis_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ import (
"github.com/ledgerwatch/erigon/turbo/trie"
)

// ChainOverrides contains the changes to chain config.
type ChainOverrides struct {
OverrideShanghaiTime *big.Int
OverrideCancunTime *big.Int
// optimism
OverrideOptimismCanyonTime *big.Int
OverrideOptimismEcotoneTime *big.Int
OverrideOptimismFjordTime *big.Int
}

// CommitGenesisBlock writes or updates the genesis block in db.
// The block that will be used is:
//
Expand All @@ -68,16 +78,16 @@ import (
//
// The returned chain configuration is never nil.
func CommitGenesisBlock(db kv.RwDB, genesis *types.Genesis, tmpDir string, logger log.Logger) (*chain.Config, *types.Block, error) {
return CommitGenesisBlockWithOverride(db, genesis, nil, nil, nil, nil, tmpDir, logger)
return CommitGenesisBlockWithOverride(db, genesis, nil, tmpDir, logger)
}

func CommitGenesisBlockWithOverride(db kv.RwDB, genesis *types.Genesis, overrideCancunTime, overrideShanghaiTime, overrideOptimismCanyonTime, overrideOptimismEcotoneTime *big.Int, tmpDir string, logger log.Logger) (*chain.Config, *types.Block, error) {
func CommitGenesisBlockWithOverride(db kv.RwDB, genesis *types.Genesis, overrides *ChainOverrides, tmpDir string, logger log.Logger) (*chain.Config, *types.Block, error) {
tx, err := db.BeginRw(context.Background())
if err != nil {
return nil, nil, err
}
defer tx.Rollback()
c, b, err := WriteGenesisBlock(tx, genesis, overrideCancunTime, overrideShanghaiTime, overrideOptimismCanyonTime, overrideOptimismEcotoneTime, tmpDir, logger)
c, b, err := WriteGenesisBlock(tx, genesis, overrides, tmpDir, logger)
if err != nil {
return c, b, err
}
Expand All @@ -88,8 +98,7 @@ func CommitGenesisBlockWithOverride(db kv.RwDB, genesis *types.Genesis, override
return c, b, nil
}

func WriteGenesisBlock(tx kv.RwTx, genesis *types.Genesis, overrideCancunTime, overrideShanghaiTime, overrideOptimismCanyonTime, overrideOptimismEcotoneTime *big.Int,
tmpDir string, logger log.Logger) (*chain.Config, *types.Block, error) {
func WriteGenesisBlock(tx kv.RwTx, genesis *types.Genesis, overrides *ChainOverrides, tmpDir string, logger log.Logger) (*chain.Config, *types.Block, error) {
var storedBlock *types.Block
if genesis != nil && genesis.Config == nil {
return params.AllProtocolChanges, nil, types.ErrGenesisNoConfig
Expand All @@ -101,38 +110,44 @@ func WriteGenesisBlock(tx kv.RwTx, genesis *types.Genesis, overrideCancunTime, o
}

applyOverrides := func(config *chain.Config) {
if overrideShanghaiTime != nil {
config.ShanghaiTime = overrideShanghaiTime
if overrides == nil {
return
}
if overrideCancunTime != nil {
config.CancunTime = overrideCancunTime
if overrides.OverrideShanghaiTime != nil {
config.ShanghaiTime = overrides.OverrideShanghaiTime
}
if config.IsOptimism() && overrideOptimismCanyonTime != nil {
config.CanyonTime = overrideOptimismCanyonTime
if overrides.OverrideCancunTime != nil {
config.CancunTime = overrides.OverrideCancunTime
}
if config.IsOptimism() && overrides.OverrideOptimismCanyonTime != nil {
config.CanyonTime = overrides.OverrideOptimismCanyonTime
// Shanghai hardfork is included in canyon hardfork
config.ShanghaiTime = overrideOptimismCanyonTime
config.ShanghaiTime = overrides.OverrideOptimismCanyonTime
if config.Optimism.EIP1559DenominatorCanyon == 0 {
logger.Warn("EIP1559DenominatorCanyon set to 0. Overriding to 250 to avoid divide by zero.")
config.Optimism.EIP1559DenominatorCanyon = 250
}
}
if overrideShanghaiTime != nil && config.IsOptimism() && overrideOptimismCanyonTime != nil {
if overrideShanghaiTime.Cmp(overrideOptimismCanyonTime) != 0 {
if overrides.OverrideShanghaiTime != nil && config.IsOptimism() && overrides.OverrideOptimismCanyonTime != nil {
if overrides.OverrideShanghaiTime.Cmp(overrides.OverrideOptimismCanyonTime) != 0 {
logger.Warn("Shanghai hardfork time is overridden by optimism canyon time",
"shanghai", overrideShanghaiTime.String(), "canyon", overrideOptimismCanyonTime.String())
"shanghai", overrides.OverrideShanghaiTime.String(), "canyon", overrides.OverrideOptimismCanyonTime.String())
}
}
if config.IsOptimism() && overrideOptimismEcotoneTime != nil {
config.EcotoneTime = overrideOptimismEcotoneTime
if config.IsOptimism() && overrides.OverrideOptimismEcotoneTime != nil {
config.EcotoneTime = overrides.OverrideOptimismEcotoneTime
// Cancun hardfork is included in Ecotone hardfork
config.CancunTime = overrideOptimismEcotoneTime
config.CancunTime = overrides.OverrideOptimismEcotoneTime
}
if overrideCancunTime != nil && config.IsOptimism() && overrideOptimismEcotoneTime != nil {
if overrideCancunTime.Cmp(overrideOptimismEcotoneTime) != 0 {
if overrides.OverrideCancunTime != nil && config.IsOptimism() && overrides.OverrideOptimismEcotoneTime != nil {
if overrides.OverrideCancunTime.Cmp(overrides.OverrideOptimismEcotoneTime) != 0 {
logger.Warn("Cancun hardfork time is overridden by optimism Ecotone time",
"cancun", overrideCancunTime.String(), "ecotone", overrideOptimismEcotoneTime.String())
"cancun", overrides.OverrideCancunTime.String(), "ecotone", overrides.OverrideOptimismEcotoneTime.String())
}
}
if overrides.OverrideOptimismFjordTime != nil {
config.FjordTime = overrides.OverrideOptimismFjordTime
}
}

if (storedHash == libcommon.Hash{}) {
Expand Down
6 changes: 3 additions & 3 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ type Receipt struct {

// OVM legacy: extend receipts with their L1 price (if a rollup tx)
L1GasPrice *big.Int `json:"l1GasPrice,omitempty"`
L1GasUsed *big.Int `json:"l1GasUsed,omitempty"`
L1Fee *big.Int `json:"l1Fee,omitempty"`
FeeScalar *big.Float `json:"l1FeeScalar,omitempty"` // always nil after Ecotone hardfork
L1GasUsed *big.Int `json:"l1GasUsed,omitempty"` // Present from pre-bedrock, deprecated as of Fjord
L1Fee *big.Int `json:"l1Fee,omitempty"` // Present from pre-bedrock
FeeScalar *big.Float `json:"l1FeeScalar,omitempty"` // Present from pre-bedrock to Ecotone. Nil after Ecotone

// DepositReceiptVersion was introduced in Canyon to indicate an update to how receipt hashes
// should be computed when set. The state transition process ensures this is only set for
Expand Down
22 changes: 22 additions & 0 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,22 @@ var PrecompiledContractsCancun = map[libcommon.Address]PrecompiledContract{
libcommon.BytesToAddress([]byte{0x0a}): &pointEvaluation{},
}

// PrecompiledContractsFjord contains the default set of pre-compiled Ethereum
// contracts used in the Fjord release.
var PrecompiledContractsFjord = map[libcommon.Address]PrecompiledContract{
libcommon.BytesToAddress([]byte{1}): &ecrecover{},
libcommon.BytesToAddress([]byte{2}): &sha256hash{},
libcommon.BytesToAddress([]byte{3}): &ripemd160hash{},
libcommon.BytesToAddress([]byte{4}): &dataCopy{},
libcommon.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
libcommon.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
libcommon.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
libcommon.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
libcommon.BytesToAddress([]byte{9}): &blake2F{},
libcommon.BytesToAddress([]byte{0x0a}): &pointEvaluation{},
libcommon.BytesToAddress([]byte{0x01, 0x00}): &p256Verify{},
}

var PrecompiledContractsNapoli = map[libcommon.Address]PrecompiledContract{
libcommon.BytesToAddress([]byte{0x01}): &ecrecover{},
libcommon.BytesToAddress([]byte{0x02}): &sha256hash{},
Expand Down Expand Up @@ -140,6 +156,7 @@ var PrecompiledContractsBLS = map[libcommon.Address]PrecompiledContract{
}

var (
PrecompiledAddressesFjord []libcommon.Address
PrecompiledAddressesNapoli []libcommon.Address
PrecompiledAddressesCancun []libcommon.Address
PrecompiledAddressesBerlin []libcommon.Address
Expand All @@ -164,6 +181,9 @@ func init() {
for k := range PrecompiledContractsCancun {
PrecompiledAddressesCancun = append(PrecompiledAddressesCancun, k)
}
for k := range PrecompiledContractsFjord {
PrecompiledAddressesFjord = append(PrecompiledAddressesFjord, k)
}
for k := range PrecompiledContractsNapoli {
PrecompiledAddressesNapoli = append(PrecompiledAddressesNapoli, k)
}
Expand All @@ -172,6 +192,8 @@ func init() {
// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules *chain.Rules) []libcommon.Address {
switch {
case rules.IsOptimismFjord:
return PrecompiledAddressesFjord
case rules.IsNapoli:
return PrecompiledAddressesNapoli
case rules.IsCancun:
Expand Down
2 changes: 2 additions & 0 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ var emptyCodeHash = crypto.Keccak256Hash(nil)
func (evm *EVM) precompile(addr libcommon.Address) (PrecompiledContract, bool) {
var precompiles map[libcommon.Address]PrecompiledContract
switch {
case evm.chainRules.IsOptimismFjord:
precompiles = PrecompiledContractsFjord
case evm.chainRules.IsNapoli:
precompiles = PrecompiledContractsNapoli
case evm.chainRules.IsCancun:
Expand Down
12 changes: 12 additions & 0 deletions erigon-lib/chain/chain_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type Config struct {
CanyonTime *big.Int `json:"canyonTime,omitempty"` // Canyon switch time (nil = no fork, 0 = already on optimism canyon)
// Delta: the Delta upgrade does not affect the execution-layer, and is thus not configurable in the chain config.
EcotoneTime *big.Int `json:"ecotoneTime,omitempty"` // Ecotone switch time (nil = no fork, 0 = already on optimism ecotone)
FjordTime *big.Int `json:"fjordTime,omitempty"` // Fjord switch time (nil = no fork, 0 = already on optimism fjord)

// Optional EIP-4844 parameters
MinBlobGasPrice *uint64 `json:"minBlobGasPrice,omitempty"`
Expand Down Expand Up @@ -274,6 +275,10 @@ func (c *Config) IsEcotone(time uint64) bool {
return isForked(c.EcotoneTime, time)
}

func (c *Config) IsFjord(time uint64) bool {
return isForked(c.FjordTime, time)
}

// IsOptimism returns whether the node is an optimism node or not.
func (c *Config) IsOptimism() bool {
return c.Optimism != nil
Expand All @@ -296,6 +301,10 @@ func (c *Config) IsOptimismEcotone(time uint64) bool {
return c.IsOptimism() && c.IsEcotone(time)
}

func (c *Config) IsOptimismFjord(time uint64) bool {
return c.IsOptimism() && c.IsFjord(time)
}

// IsOptimismPreBedrock returns true iff this is an optimism node & bedrock is not yet active
func (c *Config) IsOptimismPreBedrock(num uint64) bool {
return c.IsOptimism() && !c.IsBedrock(num)
Expand Down Expand Up @@ -588,6 +597,7 @@ type Rules struct {
IsPrague bool
IsAura bool
IsOptimismBedrock, IsOptimismRegolith bool
IsOptimismCanyon, IsOptimismFjord bool
}

// Rules ensures c's ChainID is not nil and returns a new Rules instance
Expand Down Expand Up @@ -615,6 +625,8 @@ func (c *Config) Rules(num uint64, time uint64) *Rules {
IsAura: c.Aura != nil,
IsOptimismBedrock: c.IsOptimismBedrock(num),
IsOptimismRegolith: c.IsOptimismRegolith(time),
IsOptimismCanyon: c.IsOptimismCanyon(time),
IsOptimismFjord: c.IsOptimismFjord(time),
}
}

Expand Down
48 changes: 47 additions & 1 deletion erigon-lib/opstack/rollup_cost.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,15 @@ var (

oneMillion = uint256.NewInt(1_000_000)
ecotoneDivisor = uint256.NewInt(1_000_000 * 16)
fjordDivisor = big.NewInt(1_000_000_000_000)
sixteen = uint256.NewInt(16)

L1CostIntercept = big.NewInt(-42_585_600)
L1CostFastlzCoef = big.NewInt(836_500)

MinTransactionSize = big.NewInt(100)
MinTransactionSizeScaled = new(big.Int).Mul(MinTransactionSize, big.NewInt(1e6))

emptyScalars = make([]byte, 8)
)

Expand Down Expand Up @@ -141,7 +148,12 @@ func NewL1CostFunc(config *chain.Config, statedb StateGetter) L1CostFunc {
offset := scalarSectionStart
l1BaseFeeScalar := new(uint256.Int).SetBytes(l1FeeScalars[offset : offset+4])
l1BlobBaseFeeScalar := new(uint256.Int).SetBytes(l1FeeScalars[offset+4 : offset+8])
cachedFunc = newL1CostFuncEcotone(blockTime, &l1BaseFee, &l1BlobBaseFee, l1BaseFeeScalar, l1BlobBaseFeeScalar)

if config.IsFjord(blockTime) {
cachedFunc = newL1CostFuncFjord(blockTime, &l1BaseFee, &l1BlobBaseFee, l1BaseFeeScalar, l1BlobBaseFeeScalar)
} else {
cachedFunc = newL1CostFuncEcotone(blockTime, &l1BaseFee, &l1BlobBaseFee, l1BaseFeeScalar, l1BlobBaseFeeScalar)
}
}
}
}
Expand Down Expand Up @@ -216,6 +228,40 @@ func newL1CostFuncEcotone(blockTime uint64, l1BaseFee, l1BlobBaseFee, l1BaseFeeS
}
}

func newL1CostFuncFjord(blockTime uint64, l1BaseFee, l1BlobBaseFee, l1BaseFeeScalar, l1BlobBaseFeeScalar *uint256.Int) l1CostFunc {
log.Info("Caching l1cost parameters for fjord", "blockTime", blockTime, "l1BaseFee", l1BaseFee, "l1BlobBaseFee", l1BlobBaseFee, "l1BaseFeeScalar", l1BaseFeeScalar, "l1BlobBaseFeeScalar", l1BlobBaseFeeScalar)
return func(costData types.RollupCostData) (fee, calldataGasUsed *uint256.Int) {
// Fjord L1 cost function:
//l1FeeScaled = baseFeeScalar*l1BaseFee*16 + blobFeeScalar*l1BlobBaseFee
//estimatedSize = max(minTransactionSize, intercept + fastlzCoef*fastlzSize)
//l1Cost = estimatedSize * l1FeeScaled / 1e12

calldataCostPerByte := new(uint256.Int).Set(l1BaseFee)
calldataCostPerByte = calldataCostPerByte.Mul(calldataCostPerByte, l1BaseFeeScalar)
calldataCostPerByte = calldataCostPerByte.Mul(calldataCostPerByte, sixteen)

blobCostPerByte := new(uint256.Int).Set(l1BlobBaseFee)
blobCostPerByte = blobCostPerByte.Mul(blobCostPerByte, l1BlobBaseFeeScalar)

l1FeeScaled := new(uint256.Int).Add(calldataCostPerByte, blobCostPerByte)

fastLzSize := new(big.Int).SetUint64(costData.FastLzSize)
estimatedSize := new(big.Int).Add(L1CostIntercept, new(big.Int).Mul(L1CostFastlzCoef, fastLzSize))

if estimatedSize.Cmp(MinTransactionSizeScaled) < 0 {
estimatedSize.Set(MinTransactionSizeScaled)
}

l1CostScaled := l1FeeScaled.Mul(l1FeeScaled, uint256.MustFromBig(estimatedSize))
l1Cost := new(uint256.Int).Div(l1CostScaled, uint256.MustFromBig(fjordDivisor))

calldataGasUsed = new(uint256.Int).Mul(uint256.MustFromBig(estimatedSize), new(uint256.Int).SetUint64(fixedgas.TxDataNonZeroGasEIP2028))
calldataGasUsed = calldataGasUsed.Div(calldataGasUsed, uint256.NewInt(1e6))

return l1Cost, calldataGasUsed
}
}

// ExtractL1GasParams extracts the gas parameters necessary to compute gas costs from L1 block info
// calldata.
func ExtractL1GasParams(config *chain.Config, time uint64, data []byte) (l1BaseFee *uint256.Int, costFunc l1CostFunc, feeScalar *big.Float, err error) {
Expand Down
Loading

0 comments on commit 33a19fa

Please sign in to comment.