From 59f1116f3abd46c3a75fc193be3a15e2d4c6918d Mon Sep 17 00:00:00 2001 From: Pangsu Date: Tue, 13 Feb 2024 14:18:54 +0900 Subject: [PATCH] feat(core): load and override chain-config if the chain id is known --- core/genesis.go | 17 ++- params/kroma.go | 25 ---- params/superchain.go | 274 ++++++++++++++++++++++++++++++++++++++ params/superchain_test.go | 172 ++++++++++++++++++++++++ 4 files changed, 457 insertions(+), 31 deletions(-) delete mode 100644 params/kroma.go create mode 100644 params/superchain.go create mode 100644 params/superchain_test.go diff --git a/core/genesis.go b/core/genesis.go index 74d9ced24..678a3133f 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -313,16 +313,21 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen // } // [Kroma: START] - zero := uint64(0) if config.IsKroma() { + // Load the chain-config for the given chain id, and overrides it if it exists. + if config.ChainID != nil && config.ChainID.IsUint64() { + conf, err := params.LoadKromaChainConfig(config.ChainID.Uint64()) + if err != nil { + log.Warn("failed to load chain config from registry, skipping override", "err", err, "chain_id", config.ChainID) + } else { + *config = *conf + } + } + // NOTE: kroma always post-regolith + zero := uint64(0) config.BedrockBlock = new(big.Int).SetUint64(zero) config.RegolithTime = &zero - // Canyon upgrade - canyonTime := ¶ms.UpgradeConfigs[config.ChainID.Uint64()].CanyonTime - config.CanyonTime = canyonTime - config.ShanghaiTime = canyonTime - config.Kroma.EIP1559DenominatorCanyon = 250 } // [Kroma: END] if overrides != nil && overrides.OverrideCancun != nil { diff --git a/params/kroma.go b/params/kroma.go deleted file mode 100644 index 6c68d6cec..000000000 --- a/params/kroma.go +++ /dev/null @@ -1,25 +0,0 @@ -package params - -import ( - "fmt" -) - -func init() { - NetworkNames[fmt.Sprintf("%d", KromaMainnetChainID)] = "KromaMainnet" - NetworkNames[fmt.Sprintf("%d", KromaSepoliaChainID)] = "KromaSepolia" - NetworkNames[fmt.Sprintf("%d", KromaDevnetChainID)] = "KromaDevnet" -} - -type UpgradeConfig struct { - CanyonTime uint64 -} - -var UpgradeConfigs = map[uint64]*UpgradeConfig{ - KromaMainnetChainID: {}, - KromaSepoliaChainID: { - CanyonTime: 1707897600, - }, - KromaDevnetChainID: { - CanyonTime: 1707292800, - }, -} diff --git a/params/superchain.go b/params/superchain.go new file mode 100644 index 000000000..c07aa101f --- /dev/null +++ b/params/superchain.go @@ -0,0 +1,274 @@ +package params + +import ( + "encoding/binary" + "fmt" + "math/big" + "sort" + "strings" + + "github.com/ethereum-optimism/superchain-registry/superchain" + + "github.com/ethereum/go-ethereum/common" +) + +type KromaChainConfig struct { + CanyonTime *uint64 +} + +var KromaChainConfigs = map[uint64]*KromaChainConfig{ + KromaMainnetChainID: { + CanyonTime: nil, + }, + KromaSepoliaChainID: { + CanyonTime: uint64ptr(1707897600), + }, + KromaDevnetChainID: { + CanyonTime: uint64ptr(1707292800), + }, +} + +var OPStackSupport = ProtocolVersionV0{Build: [8]byte{}, Major: 4, Minor: 0, Patch: 0, PreRelease: 1}.Encode() + +func init() { + NetworkNames[fmt.Sprintf("%d", KromaMainnetChainID)] = "KromaMainnet" + NetworkNames[fmt.Sprintf("%d", KromaSepoliaChainID)] = "KromaSepolia" + NetworkNames[fmt.Sprintf("%d", KromaDevnetChainID)] = "KromaDevnet" +} + +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 OPStackChainNames() (out []string) { + for _, ch := range superchain.OPChains { + out = append(out, ch.Chain+"-"+ch.Superchain) + } + sort.Strings(out) + return +} + +func LoadKromaChainConfig(chainID uint64) (*ChainConfig, error) { + kromaChainConfig, ok := KromaChainConfigs[chainID] + if !ok { + return nil, fmt.Errorf("unknown chain id %q", chainID) + } + + 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: kromaChainConfig.CanyonTime, // Shanghai activates with Canyon + CancunTime: nil, + PragueTime: nil, + BedrockBlock: common.Big0, + RegolithTime: &genesisActivation, + CanyonTime: kromaChainConfig.CanyonTime, + TerminalTotalDifficulty: common.Big0, + TerminalTotalDifficultyPassed: true, + Ethash: nil, + Clique: nil, + Kroma: &KromaConfig{ + EIP1559Elasticity: 6, + EIP1559Denominator: 50, + EIP1559DenominatorCanyon: 250, + }, + Zktrie: true, + } + + // [Kroma: START] + // special overrides for Kroma chains + switch chainID { + case KromaSepoliaChainID: + out.Kroma.EIP1559Elasticity = 10 + case KromaMainnetChainID: + case KromaDevnetChainID: + out.Kroma.EIP1559Elasticity = 10 + } + // [Kroma: END] + + return out, nil +} + +// ProtocolVersion encodes the OP-Stack protocol version. See OP-Stack superchain-upgrade specification. +type ProtocolVersion [32]byte + +func (p ProtocolVersion) MarshalText() ([]byte, error) { + return common.Hash(p).MarshalText() +} + +func (p *ProtocolVersion) UnmarshalText(input []byte) error { + return (*common.Hash)(p).UnmarshalText(input) +} + +func (p ProtocolVersion) Parse() (versionType uint8, build [8]byte, major, minor, patch, preRelease uint32) { + versionType = p[0] + if versionType != 0 { + return + } + // bytes 1:8 reserved for future use + copy(build[:], p[8:16]) // differentiates forks and custom-builds of standard protocol + major = binary.BigEndian.Uint32(p[16:20]) // incompatible API changes + minor = binary.BigEndian.Uint32(p[20:24]) // identifies additional functionality in backwards compatible manner + patch = binary.BigEndian.Uint32(p[24:28]) // identifies backward-compatible bug-fixes + preRelease = binary.BigEndian.Uint32(p[28:32]) // identifies unstable versions that may not satisfy the above + return +} + +func (p ProtocolVersion) String() string { + versionType, build, major, minor, patch, preRelease := p.Parse() + if versionType != 0 { + return "v0.0.0-unknown." + common.Hash(p).String() + } + ver := fmt.Sprintf("v%d.%d.%d", major, minor, patch) + if preRelease != 0 { + ver += fmt.Sprintf("-%d", preRelease) + } + if build != ([8]byte{}) { + if humanBuildTag(build) { + ver += fmt.Sprintf("+%s", strings.TrimRight(string(build[:]), "\x00")) + } else { + ver += fmt.Sprintf("+0x%x", build) + } + } + return ver +} + +// humanBuildTag identifies which build tag we can stringify for human-readable versions +func humanBuildTag(v [8]byte) bool { + for i, c := range v { // following semver.org advertised regex, alphanumeric with '-' and '.', except leading '.'. + if c == 0 { // trailing zeroed are allowed + for _, d := range v[i:] { + if d != 0 { + return false + } + } + return true + } + if !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || (c == '.' && i > 0)) { + return false + } + } + return true +} + +// ProtocolVersionComparison is used to identify how far ahead/outdated a protocol version is relative to another. +// This value is used in metrics and switch comparisons, to easily identify each type of version difference. +// Negative values mean the version is outdated. +// Positive values mean the version is up-to-date. +// Matching versions have a 0. +type ProtocolVersionComparison int + +const ( + AheadMajor ProtocolVersionComparison = 4 + OutdatedMajor ProtocolVersionComparison = -4 + AheadMinor ProtocolVersionComparison = 3 + OutdatedMinor ProtocolVersionComparison = -3 + AheadPatch ProtocolVersionComparison = 2 + OutdatedPatch ProtocolVersionComparison = -2 + AheadPrerelease ProtocolVersionComparison = 1 + OutdatedPrerelease ProtocolVersionComparison = -1 + Matching ProtocolVersionComparison = 0 + DiffVersionType ProtocolVersionComparison = 100 + DiffBuild ProtocolVersionComparison = 101 + EmptyVersion ProtocolVersionComparison = 102 + InvalidVersion ProtocolVersionComparison = 103 +) + +func (p ProtocolVersion) Compare(other ProtocolVersion) (cmp ProtocolVersionComparison) { + if p == (ProtocolVersion{}) || (other == (ProtocolVersion{})) { + return EmptyVersion + } + aVersionType, aBuild, aMajor, aMinor, aPatch, aPreRelease := p.Parse() + bVersionType, bBuild, bMajor, bMinor, bPatch, bPreRelease := other.Parse() + if aVersionType != bVersionType { + return DiffVersionType + } + if aBuild != bBuild { + return DiffBuild + } + // max values are reserved, consider versions with these values invalid + if aMajor == ^uint32(0) || aMinor == ^uint32(0) || aPatch == ^uint32(0) || aPreRelease == ^uint32(0) || + bMajor == ^uint32(0) || bMinor == ^uint32(0) || bPatch == ^uint32(0) || bPreRelease == ^uint32(0) { + return InvalidVersion + } + fn := func(a, b uint32, ahead, outdated ProtocolVersionComparison) ProtocolVersionComparison { + if a == b { + return Matching + } + if a > b { + return ahead + } + return outdated + } + if aPreRelease != 0 { // if A is a pre-release, then decrement the version before comparison + if aPatch != 0 { + aPatch -= 1 + } else if aMinor != 0 { + aMinor -= 1 + aPatch = ^uint32(0) // max value + } else if aMajor != 0 { + aMajor -= 1 + aMinor = ^uint32(0) // max value + } + } + if bPreRelease != 0 { // if B is a pre-release, then decrement the version before comparison + if bPatch != 0 { + bPatch -= 1 + } else if bMinor != 0 { + bMinor -= 1 + bPatch = ^uint32(0) // max value + } else if bMajor != 0 { + bMajor -= 1 + bMinor = ^uint32(0) // max value + } + } + if c := fn(aMajor, bMajor, AheadMajor, OutdatedMajor); c != Matching { + return c + } + if c := fn(aMinor, bMinor, AheadMinor, OutdatedMinor); c != Matching { + return c + } + if c := fn(aPatch, bPatch, AheadPatch, OutdatedPatch); c != Matching { + return c + } + return fn(aPreRelease, bPreRelease, AheadPrerelease, OutdatedPrerelease) +} + +type ProtocolVersionV0 struct { + Build [8]byte + Major, Minor, Patch, PreRelease uint32 +} + +func (v ProtocolVersionV0) Encode() (out ProtocolVersion) { + copy(out[8:16], v.Build[:]) + binary.BigEndian.PutUint32(out[16:20], v.Major) + binary.BigEndian.PutUint32(out[20:24], v.Minor) + binary.BigEndian.PutUint32(out[24:28], v.Patch) + binary.BigEndian.PutUint32(out[28:32], v.PreRelease) + return +} + +func uint64ptr(n uint64) *uint64 { + return &n +} diff --git a/params/superchain_test.go b/params/superchain_test.go new file mode 100644 index 000000000..abb72bbd3 --- /dev/null +++ b/params/superchain_test.go @@ -0,0 +1,172 @@ +package params + +import ( + "fmt" + "testing" +) + +type HumanProtocolVersion struct { + VersionType uint8 + Major, Minor, Patch uint32 + Prerelease uint32 + Build [8]byte +} + +type ComparisonCase struct { + A, B HumanProtocolVersion + Cmp ProtocolVersionComparison +} + +func TestProtocolVersion_Compare(t *testing.T) { + testCases := []ComparisonCase{ + { + A: HumanProtocolVersion{0, 2, 1, 1, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 1, 2, 2, 2, [8]byte{}}, + Cmp: AheadMajor, + }, + { + A: HumanProtocolVersion{0, 1, 2, 1, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 1, 1, 2, 2, [8]byte{}}, + Cmp: AheadMinor, + }, + { + A: HumanProtocolVersion{0, 1, 1, 2, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 1, 1, 1, 2, [8]byte{}}, + Cmp: AheadPatch, + }, + { + A: HumanProtocolVersion{0, 1, 1, 1, 2, [8]byte{}}, + B: HumanProtocolVersion{0, 1, 1, 1, 1, [8]byte{}}, + Cmp: AheadPrerelease, + }, + { + A: HumanProtocolVersion{0, 1, 2, 3, 4, [8]byte{}}, + B: HumanProtocolVersion{0, 1, 2, 3, 4, [8]byte{}}, + Cmp: Matching, + }, + { + A: HumanProtocolVersion{0, 3, 2, 1, 5, [8]byte{3}}, + B: HumanProtocolVersion{1, 1, 2, 3, 3, [8]byte{6}}, + Cmp: DiffVersionType, + }, + { + A: HumanProtocolVersion{0, 3, 2, 1, 5, [8]byte{3}}, + B: HumanProtocolVersion{0, 1, 2, 3, 3, [8]byte{6}}, + Cmp: DiffBuild, + }, + { + A: HumanProtocolVersion{0, 0, 0, 0, 0, [8]byte{}}, + B: HumanProtocolVersion{0, 1, 3, 3, 3, [8]byte{3}}, + Cmp: EmptyVersion, + }, + { + A: HumanProtocolVersion{0, 4, 0, 0, 0, [8]byte{}}, + B: HumanProtocolVersion{0, 4, 0, 0, 1, [8]byte{}}, + Cmp: AheadMajor, + }, + { + A: HumanProtocolVersion{0, 4, 1, 0, 0, [8]byte{}}, + B: HumanProtocolVersion{0, 4, 1, 0, 1, [8]byte{}}, + Cmp: AheadMinor, + }, + { + A: HumanProtocolVersion{0, 4, 0, 1, 0, [8]byte{}}, + B: HumanProtocolVersion{0, 4, 0, 1, 1, [8]byte{}}, + Cmp: AheadPatch, + }, + { + A: HumanProtocolVersion{0, 4, 0, 0, 2, [8]byte{}}, + B: HumanProtocolVersion{0, 4, 0, 0, 1, [8]byte{}}, + Cmp: AheadPrerelease, + }, + { + A: HumanProtocolVersion{0, 4, 1, 0, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 4, 0, 0, 0, [8]byte{}}, + Cmp: AheadPatch, + }, + { + A: HumanProtocolVersion{0, 4, 0, 1, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 4, 0, 0, 0, [8]byte{}}, + Cmp: AheadPrerelease, + }, + { + A: HumanProtocolVersion{0, 4, 1, 1, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 4, 0, 0, 0, [8]byte{}}, + Cmp: AheadMinor, + }, + { + A: HumanProtocolVersion{0, 4, 0, 2, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 4, 0, 0, 0, [8]byte{}}, + Cmp: AheadPatch, + }, + { + A: HumanProtocolVersion{0, 5, 0, 1, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 4, 0, 0, 0, [8]byte{}}, + Cmp: AheadMajor, + }, + { + A: HumanProtocolVersion{0, 1, 0, 0, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 0, 9, 0, 0, [8]byte{}}, + Cmp: AheadMinor, + }, + { + A: HumanProtocolVersion{0, 0, 1, 0, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 0, 0, 9, 0, [8]byte{}}, + Cmp: AheadPatch, + }, + { + A: HumanProtocolVersion{0, 1, ^uint32(0), 0, 1, [8]byte{}}, + B: HumanProtocolVersion{0, 0, 1, 0, 0, [8]byte{}}, + Cmp: InvalidVersion, + }, + } + for i, tc := range testCases { + tc := tc // not a parallel sub-test, but better than a flake + t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) { + a := ProtocolVersionV0{tc.A.Build, tc.A.Major, tc.A.Minor, tc.A.Patch, tc.A.Prerelease}.Encode() + a[0] = tc.A.VersionType + b := ProtocolVersionV0{tc.B.Build, tc.B.Major, tc.B.Minor, tc.B.Patch, tc.B.Prerelease}.Encode() + b[0] = tc.B.VersionType + cmp := a.Compare(b) + if cmp != tc.Cmp { + t.Fatalf("expected %d but got %d", tc.Cmp, cmp) + } + switch tc.Cmp { + case AheadMajor, AheadMinor, AheadPatch, AheadPrerelease: + inv := b.Compare(a) + if inv != -tc.Cmp { + t.Fatalf("expected inverse when reversing the comparison, %d but got %d", -tc.Cmp, inv) + } + case DiffVersionType, DiffBuild, EmptyVersion, Matching: + inv := b.Compare(a) + if inv != tc.Cmp { + t.Fatalf("expected comparison reversed to hold the same, expected %d but got %d", tc.Cmp, inv) + } + } + }) + } +} +func TestProtocolVersion_String(t *testing.T) { + testCases := []struct { + version ProtocolVersion + expected string + }{ + {ProtocolVersionV0{[8]byte{}, 0, 0, 0, 0}.Encode(), "v0.0.0"}, + {ProtocolVersionV0{[8]byte{}, 0, 0, 0, 1}.Encode(), "v0.0.0-1"}, + {ProtocolVersionV0{[8]byte{}, 0, 0, 1, 0}.Encode(), "v0.0.1"}, + {ProtocolVersionV0{[8]byte{}, 4, 3, 2, 1}.Encode(), "v4.3.2-1"}, + {ProtocolVersionV0{[8]byte{}, 0, 100, 2, 0}.Encode(), "v0.100.2"}, + {ProtocolVersionV0{[8]byte{'O', 'P', '-', 'm', 'o', 'd'}, 42, 0, 2, 1}.Encode(), "v42.0.2-1+OP-mod"}, + {ProtocolVersionV0{[8]byte{'b', 'e', 't', 'a', '.', '1', '2', '3'}, 1, 0, 0, 0}.Encode(), "v1.0.0+beta.123"}, + {ProtocolVersionV0{[8]byte{'a', 'b', 1}, 42, 0, 2, 0}.Encode(), "v42.0.2+0x6162010000000000"}, // do not render invalid alpha numeric + {ProtocolVersionV0{[8]byte{1, 2, 3, 4, 5, 6, 7, 8}, 42, 0, 2, 0}.Encode(), "v42.0.2+0x0102030405060708"}, + } + for _, tc := range testCases { + t.Run(tc.expected, func(t *testing.T) { + got := tc.version.String() + if got != tc.expected { + t.Fatalf("got %q but expected %q", got, tc.expected) + } + }) + } +}