Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

params: source chainConfig and genesis from superchain-registry #113

Merged
merged 10 commits into from
Aug 18, 2023
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
}