Skip to content

Commit

Permalink
Merge pull request #113 from ethereum-optimism/superchain-config
Browse files Browse the repository at this point in the history
params: source chainConfig and genesis from superchain-registry
  • Loading branch information
protolambda authored Aug 18, 2023
2 parents cd4a1e4 + daade41 commit f7376a2
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 8 deletions.
31 changes: 29 additions & 2 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ var (
Usage: "Sepolia network: pre-configured proof-of-work test network",
Category: flags.EthCategory,
}
BetaOPNetworkFlag = &cli.StringFlag{
Name: "beta.op-network",
Usage: "Beta feature: pick an OP Stack network configuration",
Category: flags.EthCategory,
}

// Dev mode
DeveloperFlag = &cli.BoolFlag{
Expand Down Expand Up @@ -988,7 +993,7 @@ var (
SepoliaFlag,
}
// NetworkFlags is the flag group of all built-in supported networks.
NetworkFlags = append([]cli.Flag{MainnetFlag}, TestnetFlags...)
NetworkFlags = append([]cli.Flag{MainnetFlag, BetaOPNetworkFlag}, TestnetFlags...)

// DatabasePathFlags is the flag group of all database path flags.
DatabasePathFlags = []cli.Flag{
Expand Down Expand Up @@ -1019,6 +1024,9 @@ func MakeDataDir(ctx *cli.Context) string {
if ctx.Bool(SepoliaFlag.Name) {
return filepath.Join(path, "sepolia")
}
if ctx.IsSet(BetaOPNetworkFlag.Name) {
return filepath.Join(path, ctx.String(BetaOPNetworkFlag.Name))
}
return path
}
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
Expand Down Expand Up @@ -1522,6 +1530,8 @@ func SetDataDir(ctx *cli.Context, cfg *node.Config) {
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
case ctx.Bool(SepoliaFlag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia")
case ctx.IsSet(BetaOPNetworkFlag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), ctx.String(BetaOPNetworkFlag.Name))
}
}

Expand Down Expand Up @@ -1684,7 +1694,7 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
// SetEthConfig applies eth-related command line flags to the config.
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// Avoid conflicting network flags
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, RinkebyFlag, GoerliFlag, SepoliaFlag)
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, RinkebyFlag, GoerliFlag, SepoliaFlag, BetaOPNetworkFlag)
CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light")
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
if ctx.String(GCModeFlag.Name) == "archive" && ctx.Uint64(TxLookupLimitFlag.Name) != 0 {
Expand Down Expand Up @@ -1927,6 +1937,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if !ctx.IsSet(MinerGasPriceFlag.Name) {
cfg.Miner.GasPrice = big.NewInt(1)
}
case ctx.IsSet(BetaOPNetworkFlag.Name):
genesis := MakeGenesis(ctx)
if !ctx.IsSet(NetworkIdFlag.Name) {
cfg.NetworkId = genesis.Config.ChainID.Uint64()
}
cfg.Genesis = genesis
default:
if cfg.NetworkId == 1 {
SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash)
Expand Down Expand Up @@ -2187,6 +2203,17 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
genesis = core.DefaultRinkebyGenesisBlock()
case ctx.Bool(GoerliFlag.Name):
genesis = core.DefaultGoerliGenesisBlock()
case ctx.IsSet(BetaOPNetworkFlag.Name):
name := ctx.String(BetaOPNetworkFlag.Name)
ch, err := params.OPStackChainIDByName(name)
if err != nil {
Fatalf("failed to load OP-Stack chain %q: %v", name, err)
}
genesis, err := core.LoadOPStackGenesis(ch)
if err != nil {
Fatalf("failed to load genesis for OP-Stack chain %q (%d): %v", name, ch, err)
}
return genesis
case ctx.Bool(DeveloperFlag.Name):
Fatalf("Developer chains are ephemeral")
}
Expand Down
20 changes: 16 additions & 4 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ type Genesis struct {
GasUsed uint64 `json:"gasUsed"`
ParentHash common.Hash `json:"parentHash"`
BaseFee *big.Int `json:"baseFeePerGas"`

// StateHash represents the genesis state, to allow instantiation of a chain with missing initial state.
// Chains with history pruning, or extraordinarily large genesis allocation (e.g. after a regenesis event)
// may utilize this to get started, and then state-sync the latest state, while still verifying the header chain.
StateHash *common.Hash `json:"stateHash,omitempty"`
}

func ReadGenesis(db ethdb.Database) (*Genesis, error) {
Expand Down Expand Up @@ -297,11 +302,11 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
}
applyOverrides := func(config *params.ChainConfig) {
if config != nil {
if config.IsOptimism() && config.ChainID != nil && config.ChainID.Cmp(params.OptimismGoerliChainId) == 0 {
if config.IsOptimism() && config.ChainID != nil && config.ChainID.Cmp(big.NewInt(params.OPGoerliChainID)) == 0 {
// Apply Optimism Goerli regolith time
config.RegolithTime = &params.OptimismGoerliRegolithTime
}
if config.IsOptimism() && config.ChainID != nil && config.ChainID.Cmp(params.BaseGoerliChainId) == 0 {
if config.IsOptimism() && config.ChainID != nil && config.ChainID.Cmp(big.NewInt(params.BaseGoerliChainID)) == 0 {
// Apply Base Goerli regolith time
config.RegolithTime = &params.BaseGoerliRegolithTime
}
Expand Down Expand Up @@ -457,8 +462,15 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {

// ToBlock returns the genesis block according to genesis specification.
func (g *Genesis) ToBlock() *types.Block {
root, err := g.Alloc.deriveHash()
if err != nil {
var root common.Hash
var err error
if g.StateHash != nil {
if len(g.Alloc) > 0 {
panic(fmt.Errorf("cannot both have genesis hash %s "+
"and non-empty state-allocation", *g.StateHash))
}
root = *g.StateHash
} else if root, err = g.Alloc.deriveHash(); err != nil {
panic(err)
}
head := &types.Header{
Expand Down
99 changes: 99 additions & 0 deletions core/superchain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package core

import (
"fmt"
"math/big"

"github.com/ethereum-optimism/superchain-registry/superchain"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)

func LoadOPStackGenesis(chainID uint64) (*Genesis, error) {
chConfig, ok := superchain.OPChains[chainID]
if !ok {
return nil, fmt.Errorf("unknown chain ID: %d", chainID)
}

cfg, err := params.LoadOPStackChainConfig(chainID)
if err != nil {
return nil, fmt.Errorf("failed to load params.ChainConfig for chain %d: %w", chainID, err)
}

gen, err := superchain.LoadGenesis(chainID)
if err != nil {
return nil, fmt.Errorf("failed to load genesis definition for chain %d: %w", chainID, err)
}

genesis := &Genesis{
Config: cfg,
Nonce: gen.Nonce,
Timestamp: gen.Timestamp,
ExtraData: gen.ExtraData,
GasLimit: gen.GasLimit,
Difficulty: (*big.Int)(gen.Difficulty),
Mixhash: common.Hash(gen.Mixhash),
Coinbase: common.Address(gen.Coinbase),
Alloc: make(GenesisAlloc),
Number: gen.Number,
GasUsed: gen.GasUsed,
ParentHash: common.Hash(gen.ParentHash),
BaseFee: (*big.Int)(gen.BaseFee),
}

for addr, acc := range gen.Alloc {
var code []byte
if acc.CodeHash != ([32]byte{}) {
dat, err := superchain.LoadContractBytecode(acc.CodeHash)
if err != nil {
return nil, fmt.Errorf("failed to load bytecode %s of address %s in chain %d: %w", acc.CodeHash, addr, chainID, err)
}
code = dat
}
var storage map[common.Hash]common.Hash
if len(acc.Storage) > 0 {
storage = make(map[common.Hash]common.Hash)
for k, v := range acc.Storage {
storage[common.Hash(k)] = common.Hash(v)
}
}
bal := common.Big0
if acc.Balance != nil {
bal = (*big.Int)(acc.Balance)
}
genesis.Alloc[common.Address(addr)] = GenesisAccount{
Code: code,
Storage: storage,
Balance: bal,
Nonce: acc.Nonce,
}
}
if gen.StateHash != nil {
if len(gen.Alloc) > 0 {
return nil, fmt.Errorf("chain definition unexpectedly contains both allocation (%d) and state-hash %s", len(gen.Alloc), *gen.StateHash)
}
genesis.StateHash = (*common.Hash)(gen.StateHash)
}

genesisBlock := genesis.ToBlock()
genesisBlockHash := genesisBlock.Hash()
expectedHash := common.Hash([32]byte(chConfig.Genesis.L2.Hash))

// Verify we correctly produced the genesis config by recomputing the genesis-block-hash,
// and check the genesis matches the chain genesis definition.
if chConfig.Genesis.L2.Number != genesisBlock.NumberU64() {
switch chainID {
case params.OPMainnetChainID:
expectedHash = common.HexToHash("0x7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b")
case params.OPGoerliChainID:
expectedHash = common.HexToHash("0xc1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1")
default:
return nil, fmt.Errorf("unknown stateless genesis definition for chain %d", chainID)
}
}
if expectedHash != genesisBlockHash {
return nil, fmt.Errorf("produced genesis with hash %s but expected %s", genesisBlockHash, expectedHash)
}
return genesis, nil
}
17 changes: 17 additions & 0 deletions core/superchain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package core

import (
"testing"

"github.com/ethereum-optimism/superchain-registry/superchain"
)

func TestOPStackGenesis(t *testing.T) {
for id := range superchain.OPChains {
gen, err := LoadOPStackGenesis(id)
if err != nil {
t.Fatal(err)
}
t.Logf("chain: %d, genesis block hash: %s", id, gen.ToBlock().Hash())
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require (
github.com/deckarep/golang-set/v2 v2.1.0
github.com/docker/docker v1.6.2
github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20230817174831-5d3ca1966435
github.com/ethereum/c-kzg-4844 v0.2.0
github.com/fatih/color v1.7.0
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20230817174831-5d3ca1966435 h1:2CzkJkkTLuVyoVFkoW5w6vDB2Q7eJzxXw/ybA17xjqM=
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20230817174831-5d3ca1966435/go.mod h1:v2YpePbdGBF0Gr6VWq49MFFmcTW0kRYZ2ingBJYWEwg=
github.com/ethereum/c-kzg-4844 v0.2.0 h1:+cUvymlnoDDQgMInp25Bo3OmLajmmY8mLJ/tLjqd77Q=
github.com/ethereum/c-kzg-4844 v0.2.0/go.mod h1:WI2Nd82DMZAAZI1wV2neKGost9EKjvbpQR9OqE5Qqa8=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
Expand Down
8 changes: 6 additions & 2 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ var (
GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a")
)

const (
OPMainnetChainID = 10
OPGoerliChainID = 420
BaseGoerliChainID = 84531
)

// OP Stack chain config
var (
OptimismGoerliChainId = big.NewInt(420)
// March 17, 2023 @ 7:00:00 pm UTC
OptimismGoerliRegolithTime = uint64(1679079600)
BaseGoerliChainId = big.NewInt(84531)
// May 4, 2023 @ 5:00:00 pm UTC
BaseGoerliRegolithTime = uint64(1683219600)
)
Expand Down
96 changes: 96 additions & 0 deletions params/superchain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package params

import (
"fmt"
"math/big"

"github.com/ethereum-optimism/superchain-registry/superchain"
"github.com/ethereum/go-ethereum/common"
)

func init() {
for id, ch := range superchain.OPChains {
NetworkNames[fmt.Sprintf("%d", id)] = ch.Name
}
}

func OPStackChainIDByName(name string) (uint64, error) {
for id, ch := range superchain.OPChains {
if ch.Chain+"-"+ch.Superchain == name {
return id, nil
}
}
return 0, fmt.Errorf("unknown chain %q", name)
}

func LoadOPStackChainConfig(chainID uint64) (*ChainConfig, error) {
chConfig, ok := superchain.OPChains[chainID]
if !ok {
return nil, fmt.Errorf("unknown chain ID: %d", chainID)
}
superchainConfig, ok := superchain.Superchains[chConfig.Superchain]
if !ok {
return nil, fmt.Errorf("unknown superchain %q", chConfig.Superchain)
}

genesisActivation := uint64(0)
out := &ChainConfig{
ChainID: new(big.Int).SetUint64(chainID),
HomesteadBlock: common.Big0,
DAOForkBlock: nil,
DAOForkSupport: false,
EIP150Block: common.Big0,
EIP155Block: common.Big0,
EIP158Block: common.Big0,
ByzantiumBlock: common.Big0,
ConstantinopleBlock: common.Big0,
PetersburgBlock: common.Big0,
IstanbulBlock: common.Big0,
MuirGlacierBlock: common.Big0,
BerlinBlock: common.Big0,
LondonBlock: common.Big0,
ArrowGlacierBlock: common.Big0,
GrayGlacierBlock: common.Big0,
MergeNetsplitBlock: common.Big0,
ShanghaiTime: nil,
CancunTime: nil,
PragueTime: nil,
BedrockBlock: common.Big0,
RegolithTime: &genesisActivation,
TerminalTotalDifficulty: common.Big0,
TerminalTotalDifficultyPassed: true,
Ethash: nil,
Clique: nil,
Optimism: &OptimismConfig{
EIP1559Elasticity: 6,
EIP1559Denominator: 50,
},
}

// note: no actual parameters are being loaded, yet.
// Future superchain upgrades are loaded from the superchain chConfig and applied to the geth ChainConfig here.
_ = superchainConfig.Config

// special overrides for OP-Stack chains with pre-Regolith upgrade history
switch chainID {
case OPGoerliChainID:
out.LondonBlock = big.NewInt(4061224)
out.ArrowGlacierBlock = big.NewInt(4061224)
out.GrayGlacierBlock = big.NewInt(4061224)
out.MergeNetsplitBlock = big.NewInt(4061224)
out.BedrockBlock = big.NewInt(4061224)
out.RegolithTime = &OptimismGoerliRegolithTime
out.Optimism.EIP1559Elasticity = 10
case OPMainnetChainID:
out.BerlinBlock = big.NewInt(3950000)
out.LondonBlock = big.NewInt(105235063)
out.ArrowGlacierBlock = big.NewInt(105235063)
out.GrayGlacierBlock = big.NewInt(105235063)
out.MergeNetsplitBlock = big.NewInt(105235063)
out.BedrockBlock = big.NewInt(105235063)
case BaseGoerliChainID:
out.RegolithTime = &BaseGoerliRegolithTime
}

return out, nil
}

0 comments on commit f7376a2

Please sign in to comment.