|
|
|
@ -371,9 +371,12 @@ type ChainConfig struct { |
|
|
|
|
ArrowGlacierBlock *big.Int `json:"arrowGlacierBlock,omitempty"` // Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated)
|
|
|
|
|
GrayGlacierBlock *big.Int `json:"grayGlacierBlock,omitempty"` // Eip-5133 (bomb delay) switch block (nil = no fork, 0 = already activated)
|
|
|
|
|
MergeNetsplitBlock *big.Int `json:"mergeNetsplitBlock,omitempty"` // Virtual fork after The Merge to use as a network splitter
|
|
|
|
|
ShanghaiTime *big.Int `json:"shanghaiBlock,omitempty"` // Shanghai switch time (nil = no fork, 0 = already on shanghai)
|
|
|
|
|
CancunBlock *big.Int `json:"cancunBlock,omitempty"` // Cancun switch block (nil = no fork, 0 = already on cancun)
|
|
|
|
|
|
|
|
|
|
// Fork scheduling was switched from blocks to timestamps here
|
|
|
|
|
|
|
|
|
|
ShanghaiTime *big.Int `json:"shanghaiTime,omitempty"` // Shanghai switch time (nil = no fork, 0 = already on shanghai)
|
|
|
|
|
|
|
|
|
|
// TerminalTotalDifficulty is the amount of total difficulty reached by
|
|
|
|
|
// the network that triggers the consensus upgrade.
|
|
|
|
|
TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"` |
|
|
|
@ -465,9 +468,7 @@ func (c *ChainConfig) Description() string { |
|
|
|
|
if c.GrayGlacierBlock != nil { |
|
|
|
|
banner += fmt.Sprintf(" - Gray Glacier: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/gray-glacier.md)\n", c.GrayGlacierBlock) |
|
|
|
|
} |
|
|
|
|
if c.ShanghaiTime != nil { |
|
|
|
|
banner += fmt.Sprintf(" - Shanghai: %-8v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md)\n", c.ShanghaiTime) |
|
|
|
|
} |
|
|
|
|
if c.CancunBlock != nil { |
|
|
|
|
banner += fmt.Sprintf(" - Cancun: %-8v\n", c.CancunBlock) |
|
|
|
|
} |
|
|
|
@ -489,74 +490,74 @@ func (c *ChainConfig) Description() string { |
|
|
|
|
|
|
|
|
|
// IsHomestead returns whether num is either equal to the homestead block or greater.
|
|
|
|
|
func (c *ChainConfig) IsHomestead(num *big.Int) bool { |
|
|
|
|
return isForked(c.HomesteadBlock, num) |
|
|
|
|
return isBlockForked(c.HomesteadBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsDAOFork returns whether num is either equal to the DAO fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsDAOFork(num *big.Int) bool { |
|
|
|
|
return isForked(c.DAOForkBlock, num) |
|
|
|
|
return isBlockForked(c.DAOForkBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsEIP150 returns whether num is either equal to the EIP150 fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsEIP150(num *big.Int) bool { |
|
|
|
|
return isForked(c.EIP150Block, num) |
|
|
|
|
return isBlockForked(c.EIP150Block, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsEIP155 returns whether num is either equal to the EIP155 fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsEIP155(num *big.Int) bool { |
|
|
|
|
return isForked(c.EIP155Block, num) |
|
|
|
|
return isBlockForked(c.EIP155Block, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsEIP158 returns whether num is either equal to the EIP158 fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsEIP158(num *big.Int) bool { |
|
|
|
|
return isForked(c.EIP158Block, num) |
|
|
|
|
return isBlockForked(c.EIP158Block, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsByzantium returns whether num is either equal to the Byzantium fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsByzantium(num *big.Int) bool { |
|
|
|
|
return isForked(c.ByzantiumBlock, num) |
|
|
|
|
return isBlockForked(c.ByzantiumBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsConstantinople returns whether num is either equal to the Constantinople fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsConstantinople(num *big.Int) bool { |
|
|
|
|
return isForked(c.ConstantinopleBlock, num) |
|
|
|
|
return isBlockForked(c.ConstantinopleBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsMuirGlacier returns whether num is either equal to the Muir Glacier (EIP-2384) fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsMuirGlacier(num *big.Int) bool { |
|
|
|
|
return isForked(c.MuirGlacierBlock, num) |
|
|
|
|
return isBlockForked(c.MuirGlacierBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsPetersburg returns whether num is either
|
|
|
|
|
// - equal to or greater than the PetersburgBlock fork block,
|
|
|
|
|
// - OR is nil, and Constantinople is active
|
|
|
|
|
func (c *ChainConfig) IsPetersburg(num *big.Int) bool { |
|
|
|
|
return isForked(c.PetersburgBlock, num) || c.PetersburgBlock == nil && isForked(c.ConstantinopleBlock, num) |
|
|
|
|
return isBlockForked(c.PetersburgBlock, num) || c.PetersburgBlock == nil && isBlockForked(c.ConstantinopleBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsIstanbul returns whether num is either equal to the Istanbul fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsIstanbul(num *big.Int) bool { |
|
|
|
|
return isForked(c.IstanbulBlock, num) |
|
|
|
|
return isBlockForked(c.IstanbulBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsBerlin returns whether num is either equal to the Berlin fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsBerlin(num *big.Int) bool { |
|
|
|
|
return isForked(c.BerlinBlock, num) |
|
|
|
|
return isBlockForked(c.BerlinBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsLondon returns whether num is either equal to the London fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsLondon(num *big.Int) bool { |
|
|
|
|
return isForked(c.LondonBlock, num) |
|
|
|
|
return isBlockForked(c.LondonBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsArrowGlacier returns whether num is either equal to the Arrow Glacier (EIP-4345) fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsArrowGlacier(num *big.Int) bool { |
|
|
|
|
return isForked(c.ArrowGlacierBlock, num) |
|
|
|
|
return isBlockForked(c.ArrowGlacierBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsGrayGlacier returns whether num is either equal to the Gray Glacier (EIP-5133) fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsGrayGlacier(num *big.Int) bool { |
|
|
|
|
return isForked(c.GrayGlacierBlock, num) |
|
|
|
|
return isBlockForked(c.GrayGlacierBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsTerminalPoWBlock returns whether the given block is the last block of PoW stage.
|
|
|
|
@ -567,30 +568,37 @@ func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *bi |
|
|
|
|
return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsShanghai returns whether time is either equal to the Shanghai fork time or greater.
|
|
|
|
|
func (c *ChainConfig) IsShanghai(time *big.Int) bool { |
|
|
|
|
return isForked(c.ShanghaiTime, time) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsCancun returns whether num is either equal to the Cancun fork block or greater.
|
|
|
|
|
func (c *ChainConfig) IsCancun(num *big.Int) bool { |
|
|
|
|
return isForked(c.CancunBlock, num) |
|
|
|
|
return isBlockForked(c.CancunBlock, num) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IsShanghai returns whether time is either equal to the Shanghai fork time or greater.
|
|
|
|
|
func (c *ChainConfig) IsShanghai(time *big.Int) bool { |
|
|
|
|
return isTimestampForked(c.ShanghaiTime, time) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// CheckCompatible checks whether scheduled fork transitions have been imported
|
|
|
|
|
// with a mismatching chain configuration.
|
|
|
|
|
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError { |
|
|
|
|
bhead := new(big.Int).SetUint64(height) |
|
|
|
|
|
|
|
|
|
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError { |
|
|
|
|
var ( |
|
|
|
|
bhead = new(big.Int).SetUint64(height) |
|
|
|
|
btime = new(big.Int).SetUint64(time) |
|
|
|
|
) |
|
|
|
|
// Iterate checkCompatible to find the lowest conflict.
|
|
|
|
|
var lasterr *ConfigCompatError |
|
|
|
|
for { |
|
|
|
|
err := c.checkCompatible(newcfg, bhead) |
|
|
|
|
if err == nil || (lasterr != nil && err.RewindTo == lasterr.RewindTo) { |
|
|
|
|
err := c.checkCompatible(newcfg, bhead, btime) |
|
|
|
|
if err == nil || (lasterr != nil && err.RewindToBlock == lasterr.RewindToBlock && err.RewindToTime == lasterr.RewindToTime) { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
lasterr = err |
|
|
|
|
bhead.SetUint64(err.RewindTo) |
|
|
|
|
|
|
|
|
|
if err.RewindToTime > 0 { |
|
|
|
|
btime.SetUint64(err.RewindToTime) |
|
|
|
|
} else { |
|
|
|
|
bhead.SetUint64(err.RewindToBlock) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return lasterr |
|
|
|
|
} |
|
|
|
@ -600,7 +608,8 @@ func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *Confi |
|
|
|
|
func (c *ChainConfig) CheckConfigForkOrder() error { |
|
|
|
|
type fork struct { |
|
|
|
|
name string |
|
|
|
|
block *big.Int |
|
|
|
|
block *big.Int // forks up to - and including the merge - were defined with block numbers
|
|
|
|
|
timestamp *big.Int // forks after the merge are scheduled using timestamps
|
|
|
|
|
optional bool // if true, the fork may be nil and next fork is still allowed
|
|
|
|
|
} |
|
|
|
|
var lastFork fork |
|
|
|
@ -620,93 +629,107 @@ func (c *ChainConfig) CheckConfigForkOrder() error { |
|
|
|
|
{name: "arrowGlacierBlock", block: c.ArrowGlacierBlock, optional: true}, |
|
|
|
|
{name: "grayGlacierBlock", block: c.GrayGlacierBlock, optional: true}, |
|
|
|
|
{name: "mergeNetsplitBlock", block: c.MergeNetsplitBlock, optional: true}, |
|
|
|
|
//{name: "shanghaiBlock", block: c.ShanghaiBlock, optional: true},
|
|
|
|
|
{name: "cancunBlock", block: c.CancunBlock, optional: true}, |
|
|
|
|
{name: "shanghaiTime", timestamp: c.ShanghaiTime}, |
|
|
|
|
} { |
|
|
|
|
if lastFork.name != "" { |
|
|
|
|
// Next one must be higher number
|
|
|
|
|
if lastFork.block == nil && cur.block != nil { |
|
|
|
|
return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at %v", |
|
|
|
|
switch { |
|
|
|
|
// Non-optional forks must all be present in the chain config up to the last defined fork
|
|
|
|
|
case lastFork.block == nil && lastFork.timestamp == nil && (cur.block != nil || cur.timestamp != nil): |
|
|
|
|
if cur.block != nil { |
|
|
|
|
return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at block %v", |
|
|
|
|
lastFork.name, cur.name, cur.block) |
|
|
|
|
} else { |
|
|
|
|
return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at timestamp %v", |
|
|
|
|
lastFork.name, cur.name, cur.timestamp) |
|
|
|
|
} |
|
|
|
|
if lastFork.block != nil && cur.block != nil { |
|
|
|
|
if lastFork.block.Cmp(cur.block) > 0 { |
|
|
|
|
return fmt.Errorf("unsupported fork ordering: %v enabled at %v, but %v enabled at %v", |
|
|
|
|
|
|
|
|
|
// Fork (whether defined by block or timestamp) must follow the fork definition sequence
|
|
|
|
|
case (lastFork.block != nil && cur.block != nil) || (lastFork.timestamp != nil && cur.timestamp != nil): |
|
|
|
|
if lastFork.block != nil && lastFork.block.Cmp(cur.block) > 0 { |
|
|
|
|
return fmt.Errorf("unsupported fork ordering: %v enabled at block %v, but %v enabled at block %v", |
|
|
|
|
lastFork.name, lastFork.block, cur.name, cur.block) |
|
|
|
|
} else if lastFork.timestamp != nil && lastFork.timestamp.Cmp(cur.timestamp) > 0 { |
|
|
|
|
return fmt.Errorf("unsupported fork ordering: %v enabled at timestamp %v, but %v enabled at timestamp %v", |
|
|
|
|
lastFork.name, lastFork.timestamp, cur.name, cur.timestamp) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Timestamp based forks can follow block based ones, but not the other way around
|
|
|
|
|
if lastFork.timestamp != nil && cur.block != nil { |
|
|
|
|
return fmt.Errorf("unsupported fork ordering: %v used timestamp ordering, but %v reverted to block ordering", |
|
|
|
|
lastFork.name, cur.name) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// If it was optional and not set, then ignore it
|
|
|
|
|
if !cur.optional || cur.block != nil { |
|
|
|
|
if !cur.optional || (cur.block != nil || cur.timestamp != nil) { |
|
|
|
|
lastFork = cur |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError { |
|
|
|
|
if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, head) { |
|
|
|
|
return newCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock) |
|
|
|
|
func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int, headTimestamp *big.Int) *ConfigCompatError { |
|
|
|
|
if isForkBlockIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, head) { |
|
|
|
|
return newCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock) |
|
|
|
|
if isForkBlockIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock) |
|
|
|
|
} |
|
|
|
|
if c.IsDAOFork(head) && c.DAOForkSupport != newcfg.DAOForkSupport { |
|
|
|
|
return newCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock) |
|
|
|
|
if c.IsDAOFork(headNumber) && c.DAOForkSupport != newcfg.DAOForkSupport { |
|
|
|
|
return newBlockCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.EIP150Block, newcfg.EIP150Block, head) { |
|
|
|
|
return newCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block) |
|
|
|
|
if isForkBlockIncompatible(c.EIP150Block, newcfg.EIP150Block, headNumber) { |
|
|
|
|
return newBlockCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, head) { |
|
|
|
|
return newCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block) |
|
|
|
|
if isForkBlockIncompatible(c.EIP155Block, newcfg.EIP155Block, headNumber) { |
|
|
|
|
return newBlockCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, head) { |
|
|
|
|
return newCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block) |
|
|
|
|
if isForkBlockIncompatible(c.EIP158Block, newcfg.EIP158Block, headNumber) { |
|
|
|
|
return newBlockCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block) |
|
|
|
|
} |
|
|
|
|
if c.IsEIP158(head) && !configNumEqual(c.ChainID, newcfg.ChainID) { |
|
|
|
|
return newCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block) |
|
|
|
|
if c.IsEIP158(headNumber) && !configBlockEqual(c.ChainID, newcfg.ChainID) { |
|
|
|
|
return newBlockCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, head) { |
|
|
|
|
return newCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock) |
|
|
|
|
if isForkBlockIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) { |
|
|
|
|
return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock) |
|
|
|
|
if isForkBlockIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.PetersburgBlock, newcfg.PetersburgBlock, head) { |
|
|
|
|
if isForkBlockIncompatible(c.PetersburgBlock, newcfg.PetersburgBlock, headNumber) { |
|
|
|
|
// the only case where we allow Petersburg to be set in the past is if it is equal to Constantinople
|
|
|
|
|
// mainly to satisfy fork ordering requirements which state that Petersburg fork be set if Constantinople fork is set
|
|
|
|
|
if isForkIncompatible(c.ConstantinopleBlock, newcfg.PetersburgBlock, head) { |
|
|
|
|
return newCompatError("Petersburg fork block", c.PetersburgBlock, newcfg.PetersburgBlock) |
|
|
|
|
if isForkBlockIncompatible(c.ConstantinopleBlock, newcfg.PetersburgBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Petersburg fork block", c.PetersburgBlock, newcfg.PetersburgBlock) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, head) { |
|
|
|
|
return newCompatError("Istanbul fork block", c.IstanbulBlock, newcfg.IstanbulBlock) |
|
|
|
|
if isForkBlockIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Istanbul fork block", c.IstanbulBlock, newcfg.IstanbulBlock) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, head) { |
|
|
|
|
return newCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock) |
|
|
|
|
if isForkBlockIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.BerlinBlock, newcfg.BerlinBlock, head) { |
|
|
|
|
return newCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock) |
|
|
|
|
if isForkBlockIncompatible(c.BerlinBlock, newcfg.BerlinBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.LondonBlock, newcfg.LondonBlock, head) { |
|
|
|
|
return newCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock) |
|
|
|
|
if isForkBlockIncompatible(c.LondonBlock, newcfg.LondonBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock, head) { |
|
|
|
|
return newCompatError("Arrow Glacier fork block", c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock) |
|
|
|
|
if isForkBlockIncompatible(c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Arrow Glacier fork block", c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.GrayGlacierBlock, newcfg.GrayGlacierBlock, head) { |
|
|
|
|
return newCompatError("Gray Glacier fork block", c.GrayGlacierBlock, newcfg.GrayGlacierBlock) |
|
|
|
|
if isForkBlockIncompatible(c.GrayGlacierBlock, newcfg.GrayGlacierBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Gray Glacier fork block", c.GrayGlacierBlock, newcfg.GrayGlacierBlock) |
|
|
|
|
} |
|
|
|
|
if isForkIncompatible(c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock, head) { |
|
|
|
|
return newCompatError("Merge netsplit fork block", c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock) |
|
|
|
|
if isForkBlockIncompatible(c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Merge netsplit fork block", c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock) |
|
|
|
|
} |
|
|
|
|
/* |
|
|
|
|
if isForkIncompatible(c.ShanghaiBlock, newcfg.ShanghaiBlock, head) { |
|
|
|
|
return newCompatError("Shanghai fork block", c.ShanghaiBlock, newcfg.ShanghaiBlock) |
|
|
|
|
if isForkBlockIncompatible(c.CancunBlock, newcfg.CancunBlock, headNumber) { |
|
|
|
|
return newBlockCompatError("Cancun fork block", c.CancunBlock, newcfg.CancunBlock) |
|
|
|
|
} |
|
|
|
|
*/ |
|
|
|
|
if isForkIncompatible(c.CancunBlock, newcfg.CancunBlock, head) { |
|
|
|
|
return newCompatError("Cancun fork block", c.CancunBlock, newcfg.CancunBlock) |
|
|
|
|
if isForkTimestampIncompatible(c.ShanghaiTime, newcfg.ShanghaiTime, headTimestamp) { |
|
|
|
|
return newTimestampCompatError("Shanghai fork timestamp", c.ShanghaiTime, newcfg.ShanghaiTime) |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
@ -721,21 +744,49 @@ func (c *ChainConfig) ElasticityMultiplier() uint64 { |
|
|
|
|
return DefaultElasticityMultiplier |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// isForkIncompatible returns true if a fork scheduled at s1 cannot be rescheduled to
|
|
|
|
|
// block s2 because head is already past the fork.
|
|
|
|
|
func isForkIncompatible(s1, s2, head *big.Int) bool { |
|
|
|
|
return (isForked(s1, head) || isForked(s2, head)) && !configNumEqual(s1, s2) |
|
|
|
|
// isForkBlockIncompatible returns true if a fork scheduled at block s1 cannot be
|
|
|
|
|
// rescheduled to block s2 because head is already past the fork.
|
|
|
|
|
func isForkBlockIncompatible(s1, s2, head *big.Int) bool { |
|
|
|
|
return (isBlockForked(s1, head) || isBlockForked(s2, head)) && !configBlockEqual(s1, s2) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// isForked returns whether a fork scheduled at block s is active at the given head block.
|
|
|
|
|
func isForked(s, head *big.Int) bool { |
|
|
|
|
// isBlockForked returns whether a fork scheduled at block s is active at the
|
|
|
|
|
// given head block. Whilst this method is the same as isTimestampForked, they
|
|
|
|
|
// are explicitly separate for clearer reading.
|
|
|
|
|
func isBlockForked(s, head *big.Int) bool { |
|
|
|
|
if s == nil || head == nil { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
return s.Cmp(head) <= 0 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func configNumEqual(x, y *big.Int) bool { |
|
|
|
|
func configBlockEqual(x, y *big.Int) bool { |
|
|
|
|
if x == nil { |
|
|
|
|
return y == nil |
|
|
|
|
} |
|
|
|
|
if y == nil { |
|
|
|
|
return x == nil |
|
|
|
|
} |
|
|
|
|
return x.Cmp(y) == 0 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// isForkTimestampIncompatible returns true if a fork scheduled at timestamp s1
|
|
|
|
|
// cannot be rescheduled to timestamp s2 because head is already past the fork.
|
|
|
|
|
func isForkTimestampIncompatible(s1, s2, head *big.Int) bool { |
|
|
|
|
return (isTimestampForked(s1, head) || isTimestampForked(s2, head)) && !configTimestampEqual(s1, s2) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// isTimestampForked returns whether a fork scheduled at timestamp s is active
|
|
|
|
|
// at the given head timestamp. Whilst this method is the same as isBlockForked,
|
|
|
|
|
// they are explicitly separate for clearer reading.
|
|
|
|
|
func isTimestampForked(s, head *big.Int) bool { |
|
|
|
|
if s == nil || head == nil { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
return s.Cmp(head) <= 0 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func configTimestampEqual(x, y *big.Int) bool { |
|
|
|
|
if x == nil { |
|
|
|
|
return y == nil |
|
|
|
|
} |
|
|
|
@ -749,13 +800,21 @@ func configNumEqual(x, y *big.Int) bool { |
|
|
|
|
// ChainConfig that would alter the past.
|
|
|
|
|
type ConfigCompatError struct { |
|
|
|
|
What string |
|
|
|
|
// block numbers of the stored and new configurations
|
|
|
|
|
StoredConfig, NewConfig *big.Int |
|
|
|
|
|
|
|
|
|
// block numbers of the stored and new configurations if block based forking
|
|
|
|
|
StoredBlock, NewBlock *big.Int |
|
|
|
|
|
|
|
|
|
// timestamps of the stored and new configurations if time based forking
|
|
|
|
|
StoredTime, NewTime *big.Int |
|
|
|
|
|
|
|
|
|
// the block number to which the local chain must be rewound to correct the error
|
|
|
|
|
RewindTo uint64 |
|
|
|
|
RewindToBlock uint64 |
|
|
|
|
|
|
|
|
|
// the timestamp to which the local chain must be rewound to correct the error
|
|
|
|
|
RewindToTime uint64 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func newCompatError(what string, storedblock, newblock *big.Int) *ConfigCompatError { |
|
|
|
|
func newBlockCompatError(what string, storedblock, newblock *big.Int) *ConfigCompatError { |
|
|
|
|
var rew *big.Int |
|
|
|
|
switch { |
|
|
|
|
case storedblock == nil: |
|
|
|
@ -765,15 +824,45 @@ func newCompatError(what string, storedblock, newblock *big.Int) *ConfigCompatEr |
|
|
|
|
default: |
|
|
|
|
rew = newblock |
|
|
|
|
} |
|
|
|
|
err := &ConfigCompatError{what, storedblock, newblock, 0} |
|
|
|
|
err := &ConfigCompatError{ |
|
|
|
|
What: what, |
|
|
|
|
StoredBlock: storedblock, |
|
|
|
|
NewBlock: newblock, |
|
|
|
|
RewindToBlock: 0, |
|
|
|
|
} |
|
|
|
|
if rew != nil && rew.Sign() > 0 { |
|
|
|
|
err.RewindTo = rew.Uint64() - 1 |
|
|
|
|
err.RewindToBlock = rew.Uint64() - 1 |
|
|
|
|
} |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func newTimestampCompatError(what string, storedtime, newtime *big.Int) *ConfigCompatError { |
|
|
|
|
var rew *big.Int |
|
|
|
|
switch { |
|
|
|
|
case storedtime == nil: |
|
|
|
|
rew = newtime |
|
|
|
|
case newtime == nil || storedtime.Cmp(newtime) < 0: |
|
|
|
|
rew = storedtime |
|
|
|
|
default: |
|
|
|
|
rew = newtime |
|
|
|
|
} |
|
|
|
|
err := &ConfigCompatError{ |
|
|
|
|
What: what, |
|
|
|
|
StoredTime: storedtime, |
|
|
|
|
NewTime: newtime, |
|
|
|
|
RewindToTime: 0, |
|
|
|
|
} |
|
|
|
|
if rew != nil && rew.Sign() > 0 { |
|
|
|
|
err.RewindToTime = rew.Uint64() - 1 |
|
|
|
|
} |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (err *ConfigCompatError) Error() string { |
|
|
|
|
return fmt.Sprintf("mismatching %s in database (have %d, want %d, rewindto %d)", err.What, err.StoredConfig, err.NewConfig, err.RewindTo) |
|
|
|
|
if err.StoredBlock != nil { |
|
|
|
|
return fmt.Sprintf("mismatching %s in database (have block %d, want block %d, rewindto block %d)", err.What, err.StoredBlock, err.NewBlock, err.RewindToBlock) |
|
|
|
|
} |
|
|
|
|
return fmt.Sprintf("mismatching %s in database (have timestamp %d, want timestamp %d, rewindto timestamp %d)", err.What, err.StoredTime, err.NewTime, err.RewindToTime) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Rules wraps ChainConfig and is merely syntactic sugar or can be used for functions
|
|
|
|
|