core, les, params: add timestamp based fork compatibility checks
This commit is contained in:
parent
a4e19c5ca3
commit
08481028fe
@ -427,7 +427,11 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
// Rewind the chain in case of an incompatible config upgrade.
|
||||
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
||||
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
|
||||
bc.SetHead(compat.RewindTo)
|
||||
if compat.RewindToTime > 0 {
|
||||
log.Crit("Timestamp based rewinds not implemented yet /sad")
|
||||
} else {
|
||||
bc.SetHead(compat.RewindToBlock)
|
||||
}
|
||||
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
|
||||
}
|
||||
// Start tx indexer/unindexer if required.
|
||||
|
@ -4275,7 +4275,7 @@ func TestEIP3651(t *testing.T) {
|
||||
|
||||
gspec.Config.BerlinBlock = common.Big0
|
||||
gspec.Config.LondonBlock = common.Big0
|
||||
gspec.Config.ShanghaiBlock = common.Big0
|
||||
gspec.Config.ShanghaiTime = common.Big0
|
||||
signer := types.LatestSigner(gspec.Config)
|
||||
|
||||
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
||||
|
@ -371,12 +371,12 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
|
||||
}
|
||||
// Check config compatibility and write the config. Compatibility errors
|
||||
// are returned to the caller unless we're already at block zero.
|
||||
height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db))
|
||||
if height == nil {
|
||||
return newcfg, stored, fmt.Errorf("missing block number for head header hash")
|
||||
head := rawdb.ReadHeadHeader(db)
|
||||
if head == nil {
|
||||
return newcfg, stored, fmt.Errorf("missing head header")
|
||||
}
|
||||
compatErr := storedcfg.CheckCompatible(newcfg, *height)
|
||||
if compatErr != nil && *height != 0 && compatErr.RewindTo != 0 {
|
||||
compatErr := storedcfg.CheckCompatible(newcfg, head.Number.Uint64(), head.Time)
|
||||
if compatErr != nil && ((head.Number.Uint64() != 0 && compatErr.RewindToBlock != 0) || (head.Time != 0 && compatErr.RewindToTime != 0)) {
|
||||
return newcfg, stored, compatErr
|
||||
}
|
||||
// Don't overwrite if the old is identical to the new
|
||||
|
@ -132,10 +132,10 @@ func TestSetupGenesis(t *testing.T) {
|
||||
wantHash: customghash,
|
||||
wantConfig: customg.Config,
|
||||
wantErr: ¶ms.ConfigCompatError{
|
||||
What: "Homestead fork block",
|
||||
StoredConfig: big.NewInt(2),
|
||||
NewConfig: big.NewInt(3),
|
||||
RewindTo: 1,
|
||||
What: "Homestead fork block",
|
||||
StoredBlock: big.NewInt(2),
|
||||
NewBlock: big.NewInt(3),
|
||||
RewindToBlock: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -179,7 +179,11 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
|
||||
// Rewind the chain in case of an incompatible config upgrade.
|
||||
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
||||
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
|
||||
leth.blockchain.SetHead(compat.RewindTo)
|
||||
if compat.RewindToTime > 0 {
|
||||
log.Crit("Timestamp based rewinds not implemented yet /sad")
|
||||
} else {
|
||||
leth.blockchain.SetHead(compat.RewindToBlock)
|
||||
}
|
||||
rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
|
||||
}
|
||||
|
||||
|
289
params/config.go
289
params/config.go
@ -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)
|
||||
}
|
||||
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
|
||||
}
|
||||
@ -599,9 +607,10 @@ func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *Confi
|
||||
// to guarantee that forks can be implemented in a different order than on official networks
|
||||
func (c *ChainConfig) CheckConfigForkOrder() error {
|
||||
type fork struct {
|
||||
name string
|
||||
block *big.Int
|
||||
optional bool // if true, the fork may be nil and next fork is still allowed
|
||||
name string
|
||||
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
|
||||
for _, cur := range []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",
|
||||
lastFork.name, cur.name, cur.block)
|
||||
}
|
||||
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",
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 isForkIncompatible(c.CancunBlock, newcfg.CancunBlock, head) {
|
||||
return newCompatError("Cancun fork block", c.CancunBlock, newcfg.CancunBlock)
|
||||
if isForkBlockIncompatible(c.CancunBlock, newcfg.CancunBlock, headNumber) {
|
||||
return newBlockCompatError("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
|
||||
|
@ -20,79 +20,99 @@ import (
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCheckCompatible(t *testing.T) {
|
||||
type test struct {
|
||||
stored, new *ChainConfig
|
||||
head uint64
|
||||
wantErr *ConfigCompatError
|
||||
stored, new *ChainConfig
|
||||
headBlock uint64
|
||||
headTimestamp uint64
|
||||
wantErr *ConfigCompatError
|
||||
}
|
||||
tests := []test{
|
||||
{stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, head: 0, wantErr: nil},
|
||||
{stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, head: 100, wantErr: nil},
|
||||
{stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, headBlock: 0, headTimestamp: 0, wantErr: nil},
|
||||
{stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, headBlock: 0, headTimestamp: uint64(time.Now().Unix()), wantErr: nil},
|
||||
{stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, headBlock: 100, wantErr: nil},
|
||||
{
|
||||
stored: &ChainConfig{EIP150Block: big.NewInt(10)},
|
||||
new: &ChainConfig{EIP150Block: big.NewInt(20)},
|
||||
head: 9,
|
||||
wantErr: nil,
|
||||
stored: &ChainConfig{EIP150Block: big.NewInt(10)},
|
||||
new: &ChainConfig{EIP150Block: big.NewInt(20)},
|
||||
headBlock: 9,
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
stored: AllEthashProtocolChanges,
|
||||
new: &ChainConfig{HomesteadBlock: nil},
|
||||
head: 3,
|
||||
stored: AllEthashProtocolChanges,
|
||||
new: &ChainConfig{HomesteadBlock: nil},
|
||||
headBlock: 3,
|
||||
wantErr: &ConfigCompatError{
|
||||
What: "Homestead fork block",
|
||||
StoredConfig: big.NewInt(0),
|
||||
NewConfig: nil,
|
||||
RewindTo: 0,
|
||||
What: "Homestead fork block",
|
||||
StoredBlock: big.NewInt(0),
|
||||
NewBlock: nil,
|
||||
RewindToBlock: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
stored: AllEthashProtocolChanges,
|
||||
new: &ChainConfig{HomesteadBlock: big.NewInt(1)},
|
||||
head: 3,
|
||||
stored: AllEthashProtocolChanges,
|
||||
new: &ChainConfig{HomesteadBlock: big.NewInt(1)},
|
||||
headBlock: 3,
|
||||
wantErr: &ConfigCompatError{
|
||||
What: "Homestead fork block",
|
||||
StoredConfig: big.NewInt(0),
|
||||
NewConfig: big.NewInt(1),
|
||||
RewindTo: 0,
|
||||
What: "Homestead fork block",
|
||||
StoredBlock: big.NewInt(0),
|
||||
NewBlock: big.NewInt(1),
|
||||
RewindToBlock: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
stored: &ChainConfig{HomesteadBlock: big.NewInt(30), EIP150Block: big.NewInt(10)},
|
||||
new: &ChainConfig{HomesteadBlock: big.NewInt(25), EIP150Block: big.NewInt(20)},
|
||||
head: 25,
|
||||
stored: &ChainConfig{HomesteadBlock: big.NewInt(30), EIP150Block: big.NewInt(10)},
|
||||
new: &ChainConfig{HomesteadBlock: big.NewInt(25), EIP150Block: big.NewInt(20)},
|
||||
headBlock: 25,
|
||||
wantErr: &ConfigCompatError{
|
||||
What: "EIP150 fork block",
|
||||
StoredConfig: big.NewInt(10),
|
||||
NewConfig: big.NewInt(20),
|
||||
RewindTo: 9,
|
||||
What: "EIP150 fork block",
|
||||
StoredBlock: big.NewInt(10),
|
||||
NewBlock: big.NewInt(20),
|
||||
RewindToBlock: 9,
|
||||
},
|
||||
},
|
||||
{
|
||||
stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
|
||||
new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(30)},
|
||||
head: 40,
|
||||
wantErr: nil,
|
||||
stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
|
||||
new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(30)},
|
||||
headBlock: 40,
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
|
||||
new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(31)},
|
||||
head: 40,
|
||||
stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
|
||||
new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(31)},
|
||||
headBlock: 40,
|
||||
wantErr: &ConfigCompatError{
|
||||
What: "Petersburg fork block",
|
||||
StoredConfig: nil,
|
||||
NewConfig: big.NewInt(31),
|
||||
RewindTo: 30,
|
||||
What: "Petersburg fork block",
|
||||
StoredBlock: nil,
|
||||
NewBlock: big.NewInt(31),
|
||||
RewindToBlock: 30,
|
||||
},
|
||||
},
|
||||
{
|
||||
stored: &ChainConfig{ShanghaiTime: big.NewInt(10)},
|
||||
new: &ChainConfig{ShanghaiTime: big.NewInt(20)},
|
||||
headTimestamp: 9,
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
stored: &ChainConfig{ShanghaiTime: big.NewInt(10)},
|
||||
new: &ChainConfig{ShanghaiTime: big.NewInt(20)},
|
||||
headTimestamp: 25,
|
||||
wantErr: &ConfigCompatError{
|
||||
What: "Shanghai fork timestamp",
|
||||
StoredTime: big.NewInt(10),
|
||||
NewTime: big.NewInt(20),
|
||||
RewindToTime: 9,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := test.stored.CheckCompatible(test.new, test.head)
|
||||
err := test.stored.CheckCompatible(test.new, test.headBlock, test.headTimestamp)
|
||||
if !reflect.DeepEqual(err, test.wantErr) {
|
||||
t.Errorf("error mismatch:\nstored: %v\nnew: %v\nhead: %v\nerr: %v\nwant: %v", test.stored, test.new, test.head, err, test.wantErr)
|
||||
t.Errorf("error mismatch:\nstored: %v\nnew: %v\nheadBlock: %v\nheadTimestamp: %v\nerr: %v\nwant: %v", test.stored, test.new, test.headBlock, test.headTimestamp, err, test.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user