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.
|
// Rewind the chain in case of an incompatible config upgrade.
|
||||||
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
||||||
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
|
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)
|
rawdb.WriteChainConfig(db, genesisHash, chainConfig)
|
||||||
}
|
}
|
||||||
// Start tx indexer/unindexer if required.
|
// Start tx indexer/unindexer if required.
|
||||||
|
@ -4275,7 +4275,7 @@ func TestEIP3651(t *testing.T) {
|
|||||||
|
|
||||||
gspec.Config.BerlinBlock = common.Big0
|
gspec.Config.BerlinBlock = common.Big0
|
||||||
gspec.Config.LondonBlock = common.Big0
|
gspec.Config.LondonBlock = common.Big0
|
||||||
gspec.Config.ShanghaiBlock = common.Big0
|
gspec.Config.ShanghaiTime = common.Big0
|
||||||
signer := types.LatestSigner(gspec.Config)
|
signer := types.LatestSigner(gspec.Config)
|
||||||
|
|
||||||
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
_, 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
|
// Check config compatibility and write the config. Compatibility errors
|
||||||
// are returned to the caller unless we're already at block zero.
|
// are returned to the caller unless we're already at block zero.
|
||||||
height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db))
|
head := rawdb.ReadHeadHeader(db)
|
||||||
if height == nil {
|
if head == nil {
|
||||||
return newcfg, stored, fmt.Errorf("missing block number for head header hash")
|
return newcfg, stored, fmt.Errorf("missing head header")
|
||||||
}
|
}
|
||||||
compatErr := storedcfg.CheckCompatible(newcfg, *height)
|
compatErr := storedcfg.CheckCompatible(newcfg, head.Number.Uint64(), head.Time)
|
||||||
if compatErr != nil && *height != 0 && compatErr.RewindTo != 0 {
|
if compatErr != nil && ((head.Number.Uint64() != 0 && compatErr.RewindToBlock != 0) || (head.Time != 0 && compatErr.RewindToTime != 0)) {
|
||||||
return newcfg, stored, compatErr
|
return newcfg, stored, compatErr
|
||||||
}
|
}
|
||||||
// Don't overwrite if the old is identical to the new
|
// Don't overwrite if the old is identical to the new
|
||||||
|
@ -132,10 +132,10 @@ func TestSetupGenesis(t *testing.T) {
|
|||||||
wantHash: customghash,
|
wantHash: customghash,
|
||||||
wantConfig: customg.Config,
|
wantConfig: customg.Config,
|
||||||
wantErr: ¶ms.ConfigCompatError{
|
wantErr: ¶ms.ConfigCompatError{
|
||||||
What: "Homestead fork block",
|
What: "Homestead fork block",
|
||||||
StoredConfig: big.NewInt(2),
|
StoredBlock: big.NewInt(2),
|
||||||
NewConfig: big.NewInt(3),
|
NewBlock: big.NewInt(3),
|
||||||
RewindTo: 1,
|
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.
|
// Rewind the chain in case of an incompatible config upgrade.
|
||||||
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
||||||
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
|
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)
|
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)
|
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)
|
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
|
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)
|
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
|
// TerminalTotalDifficulty is the amount of total difficulty reached by
|
||||||
// the network that triggers the consensus upgrade.
|
// the network that triggers the consensus upgrade.
|
||||||
TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"`
|
TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"`
|
||||||
@ -465,9 +468,7 @@ func (c *ChainConfig) Description() string {
|
|||||||
if c.GrayGlacierBlock != nil {
|
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)
|
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 {
|
if c.CancunBlock != nil {
|
||||||
banner += fmt.Sprintf(" - Cancun: %-8v\n", c.CancunBlock)
|
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.
|
// IsHomestead returns whether num is either equal to the homestead block or greater.
|
||||||
func (c *ChainConfig) IsHomestead(num *big.Int) bool {
|
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.
|
// IsDAOFork returns whether num is either equal to the DAO fork block or greater.
|
||||||
func (c *ChainConfig) IsDAOFork(num *big.Int) bool {
|
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.
|
// IsEIP150 returns whether num is either equal to the EIP150 fork block or greater.
|
||||||
func (c *ChainConfig) IsEIP150(num *big.Int) bool {
|
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.
|
// IsEIP155 returns whether num is either equal to the EIP155 fork block or greater.
|
||||||
func (c *ChainConfig) IsEIP155(num *big.Int) bool {
|
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.
|
// IsEIP158 returns whether num is either equal to the EIP158 fork block or greater.
|
||||||
func (c *ChainConfig) IsEIP158(num *big.Int) bool {
|
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.
|
// IsByzantium returns whether num is either equal to the Byzantium fork block or greater.
|
||||||
func (c *ChainConfig) IsByzantium(num *big.Int) bool {
|
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.
|
// IsConstantinople returns whether num is either equal to the Constantinople fork block or greater.
|
||||||
func (c *ChainConfig) IsConstantinople(num *big.Int) bool {
|
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.
|
// 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 {
|
func (c *ChainConfig) IsMuirGlacier(num *big.Int) bool {
|
||||||
return isForked(c.MuirGlacierBlock, num)
|
return isBlockForked(c.MuirGlacierBlock, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPetersburg returns whether num is either
|
// IsPetersburg returns whether num is either
|
||||||
// - equal to or greater than the PetersburgBlock fork block,
|
// - equal to or greater than the PetersburgBlock fork block,
|
||||||
// - OR is nil, and Constantinople is active
|
// - OR is nil, and Constantinople is active
|
||||||
func (c *ChainConfig) IsPetersburg(num *big.Int) bool {
|
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.
|
// IsIstanbul returns whether num is either equal to the Istanbul fork block or greater.
|
||||||
func (c *ChainConfig) IsIstanbul(num *big.Int) bool {
|
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.
|
// IsBerlin returns whether num is either equal to the Berlin fork block or greater.
|
||||||
func (c *ChainConfig) IsBerlin(num *big.Int) bool {
|
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.
|
// IsLondon returns whether num is either equal to the London fork block or greater.
|
||||||
func (c *ChainConfig) IsLondon(num *big.Int) bool {
|
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.
|
// 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 {
|
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.
|
// 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 {
|
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.
|
// 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
|
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.
|
// IsCancun returns whether num is either equal to the Cancun fork block or greater.
|
||||||
func (c *ChainConfig) IsCancun(num *big.Int) bool {
|
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
|
// CheckCompatible checks whether scheduled fork transitions have been imported
|
||||||
// with a mismatching chain configuration.
|
// with a mismatching chain configuration.
|
||||||
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError {
|
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError {
|
||||||
bhead := new(big.Int).SetUint64(height)
|
var (
|
||||||
|
bhead = new(big.Int).SetUint64(height)
|
||||||
|
btime = new(big.Int).SetUint64(time)
|
||||||
|
)
|
||||||
// Iterate checkCompatible to find the lowest conflict.
|
// Iterate checkCompatible to find the lowest conflict.
|
||||||
var lasterr *ConfigCompatError
|
var lasterr *ConfigCompatError
|
||||||
for {
|
for {
|
||||||
err := c.checkCompatible(newcfg, bhead)
|
err := c.checkCompatible(newcfg, bhead, btime)
|
||||||
if err == nil || (lasterr != nil && err.RewindTo == lasterr.RewindTo) {
|
if err == nil || (lasterr != nil && err.RewindToBlock == lasterr.RewindToBlock && err.RewindToTime == lasterr.RewindToTime) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
lasterr = err
|
lasterr = err
|
||||||
bhead.SetUint64(err.RewindTo)
|
|
||||||
|
if err.RewindToTime > 0 {
|
||||||
|
btime.SetUint64(err.RewindToTime)
|
||||||
|
} else {
|
||||||
|
bhead.SetUint64(err.RewindToBlock)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return lasterr
|
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
|
// to guarantee that forks can be implemented in a different order than on official networks
|
||||||
func (c *ChainConfig) CheckConfigForkOrder() error {
|
func (c *ChainConfig) CheckConfigForkOrder() error {
|
||||||
type fork struct {
|
type fork struct {
|
||||||
name string
|
name string
|
||||||
block *big.Int
|
block *big.Int // forks up to - and including the merge - were defined with block numbers
|
||||||
optional bool // if true, the fork may be nil and next fork is still allowed
|
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
|
var lastFork fork
|
||||||
for _, cur := range []fork{
|
for _, cur := range []fork{
|
||||||
@ -620,93 +629,107 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
|
|||||||
{name: "arrowGlacierBlock", block: c.ArrowGlacierBlock, optional: true},
|
{name: "arrowGlacierBlock", block: c.ArrowGlacierBlock, optional: true},
|
||||||
{name: "grayGlacierBlock", block: c.GrayGlacierBlock, optional: true},
|
{name: "grayGlacierBlock", block: c.GrayGlacierBlock, optional: true},
|
||||||
{name: "mergeNetsplitBlock", block: c.MergeNetsplitBlock, optional: true},
|
{name: "mergeNetsplitBlock", block: c.MergeNetsplitBlock, optional: true},
|
||||||
//{name: "shanghaiBlock", block: c.ShanghaiBlock, optional: true},
|
|
||||||
{name: "cancunBlock", block: c.CancunBlock, optional: true},
|
{name: "cancunBlock", block: c.CancunBlock, optional: true},
|
||||||
|
{name: "shanghaiTime", timestamp: c.ShanghaiTime},
|
||||||
} {
|
} {
|
||||||
if lastFork.name != "" {
|
if lastFork.name != "" {
|
||||||
// Next one must be higher number
|
switch {
|
||||||
if lastFork.block == nil && cur.block != nil {
|
// Non-optional forks must all be present in the chain config up to the last defined fork
|
||||||
return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at %v",
|
case lastFork.block == nil && lastFork.timestamp == nil && (cur.block != nil || cur.timestamp != nil):
|
||||||
lastFork.name, cur.name, cur.block)
|
if cur.block != nil {
|
||||||
}
|
return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at block %v",
|
||||||
if lastFork.block != nil && cur.block != nil {
|
lastFork.name, cur.name, cur.block)
|
||||||
if lastFork.block.Cmp(cur.block) > 0 {
|
} else {
|
||||||
return fmt.Errorf("unsupported fork ordering: %v enabled at %v, but %v enabled at %v",
|
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)
|
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 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
|
lastFork = cur
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError {
|
func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int, headTimestamp *big.Int) *ConfigCompatError {
|
||||||
if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, head) {
|
if isForkBlockIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, headNumber) {
|
||||||
return newCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock)
|
return newBlockCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, head) {
|
if isForkBlockIncompatible(c.DAOForkBlock, newcfg.DAOForkBlock, headNumber) {
|
||||||
return newCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock)
|
return newBlockCompatError("DAO fork block", c.DAOForkBlock, newcfg.DAOForkBlock)
|
||||||
}
|
}
|
||||||
if c.IsDAOFork(head) && c.DAOForkSupport != newcfg.DAOForkSupport {
|
if c.IsDAOFork(headNumber) && c.DAOForkSupport != newcfg.DAOForkSupport {
|
||||||
return newCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock)
|
return newBlockCompatError("DAO fork support flag", c.DAOForkBlock, newcfg.DAOForkBlock)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.EIP150Block, newcfg.EIP150Block, head) {
|
if isForkBlockIncompatible(c.EIP150Block, newcfg.EIP150Block, headNumber) {
|
||||||
return newCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block)
|
return newBlockCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, head) {
|
if isForkBlockIncompatible(c.EIP155Block, newcfg.EIP155Block, headNumber) {
|
||||||
return newCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block)
|
return newBlockCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, head) {
|
if isForkBlockIncompatible(c.EIP158Block, newcfg.EIP158Block, headNumber) {
|
||||||
return newCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block)
|
return newBlockCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block)
|
||||||
}
|
}
|
||||||
if c.IsEIP158(head) && !configNumEqual(c.ChainID, newcfg.ChainID) {
|
if c.IsEIP158(headNumber) && !configBlockEqual(c.ChainID, newcfg.ChainID) {
|
||||||
return newCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block)
|
return newBlockCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, head) {
|
if isForkBlockIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, headNumber) {
|
||||||
return newCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock)
|
return newBlockCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) {
|
if isForkBlockIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, headNumber) {
|
||||||
return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock)
|
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
|
// 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
|
// 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) {
|
if isForkBlockIncompatible(c.ConstantinopleBlock, newcfg.PetersburgBlock, headNumber) {
|
||||||
return newCompatError("Petersburg fork block", c.PetersburgBlock, newcfg.PetersburgBlock)
|
return newBlockCompatError("Petersburg fork block", c.PetersburgBlock, newcfg.PetersburgBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, head) {
|
if isForkBlockIncompatible(c.IstanbulBlock, newcfg.IstanbulBlock, headNumber) {
|
||||||
return newCompatError("Istanbul fork block", c.IstanbulBlock, newcfg.IstanbulBlock)
|
return newBlockCompatError("Istanbul fork block", c.IstanbulBlock, newcfg.IstanbulBlock)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, head) {
|
if isForkBlockIncompatible(c.MuirGlacierBlock, newcfg.MuirGlacierBlock, headNumber) {
|
||||||
return newCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock)
|
return newBlockCompatError("Muir Glacier fork block", c.MuirGlacierBlock, newcfg.MuirGlacierBlock)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.BerlinBlock, newcfg.BerlinBlock, head) {
|
if isForkBlockIncompatible(c.BerlinBlock, newcfg.BerlinBlock, headNumber) {
|
||||||
return newCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock)
|
return newBlockCompatError("Berlin fork block", c.BerlinBlock, newcfg.BerlinBlock)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.LondonBlock, newcfg.LondonBlock, head) {
|
if isForkBlockIncompatible(c.LondonBlock, newcfg.LondonBlock, headNumber) {
|
||||||
return newCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock)
|
return newBlockCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock, head) {
|
if isForkBlockIncompatible(c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock, headNumber) {
|
||||||
return newCompatError("Arrow Glacier fork block", c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock)
|
return newBlockCompatError("Arrow Glacier fork block", c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.GrayGlacierBlock, newcfg.GrayGlacierBlock, head) {
|
if isForkBlockIncompatible(c.GrayGlacierBlock, newcfg.GrayGlacierBlock, headNumber) {
|
||||||
return newCompatError("Gray Glacier fork block", c.GrayGlacierBlock, newcfg.GrayGlacierBlock)
|
return newBlockCompatError("Gray Glacier fork block", c.GrayGlacierBlock, newcfg.GrayGlacierBlock)
|
||||||
}
|
}
|
||||||
if isForkIncompatible(c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock, head) {
|
if isForkBlockIncompatible(c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock, headNumber) {
|
||||||
return newCompatError("Merge netsplit fork block", c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock)
|
return newBlockCompatError("Merge netsplit fork block", c.MergeNetsplitBlock, newcfg.MergeNetsplitBlock)
|
||||||
}
|
}
|
||||||
/*
|
if isForkBlockIncompatible(c.CancunBlock, newcfg.CancunBlock, headNumber) {
|
||||||
if isForkIncompatible(c.ShanghaiBlock, newcfg.ShanghaiBlock, head) {
|
return newBlockCompatError("Cancun fork block", c.CancunBlock, newcfg.CancunBlock)
|
||||||
return newCompatError("Shanghai fork block", c.ShanghaiBlock, newcfg.ShanghaiBlock)
|
}
|
||||||
}
|
if isForkTimestampIncompatible(c.ShanghaiTime, newcfg.ShanghaiTime, headTimestamp) {
|
||||||
*/
|
return newTimestampCompatError("Shanghai fork timestamp", c.ShanghaiTime, newcfg.ShanghaiTime)
|
||||||
if isForkIncompatible(c.CancunBlock, newcfg.CancunBlock, head) {
|
|
||||||
return newCompatError("Cancun fork block", c.CancunBlock, newcfg.CancunBlock)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -721,21 +744,49 @@ func (c *ChainConfig) ElasticityMultiplier() uint64 {
|
|||||||
return DefaultElasticityMultiplier
|
return DefaultElasticityMultiplier
|
||||||
}
|
}
|
||||||
|
|
||||||
// isForkIncompatible returns true if a fork scheduled at s1 cannot be rescheduled to
|
// isForkBlockIncompatible returns true if a fork scheduled at block s1 cannot be
|
||||||
// block s2 because head is already past the fork.
|
// rescheduled to block s2 because head is already past the fork.
|
||||||
func isForkIncompatible(s1, s2, head *big.Int) bool {
|
func isForkBlockIncompatible(s1, s2, head *big.Int) bool {
|
||||||
return (isForked(s1, head) || isForked(s2, head)) && !configNumEqual(s1, s2)
|
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.
|
// isBlockForked returns whether a fork scheduled at block s is active at the
|
||||||
func isForked(s, head *big.Int) bool {
|
// 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 {
|
if s == nil || head == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return s.Cmp(head) <= 0
|
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 {
|
if x == nil {
|
||||||
return y == nil
|
return y == nil
|
||||||
}
|
}
|
||||||
@ -749,13 +800,21 @@ func configNumEqual(x, y *big.Int) bool {
|
|||||||
// ChainConfig that would alter the past.
|
// ChainConfig that would alter the past.
|
||||||
type ConfigCompatError struct {
|
type ConfigCompatError struct {
|
||||||
What string
|
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
|
// 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
|
var rew *big.Int
|
||||||
switch {
|
switch {
|
||||||
case storedblock == nil:
|
case storedblock == nil:
|
||||||
@ -765,15 +824,45 @@ func newCompatError(what string, storedblock, newblock *big.Int) *ConfigCompatEr
|
|||||||
default:
|
default:
|
||||||
rew = newblock
|
rew = newblock
|
||||||
}
|
}
|
||||||
err := &ConfigCompatError{what, storedblock, newblock, 0}
|
err := &ConfigCompatError{
|
||||||
|
What: what,
|
||||||
|
StoredBlock: storedblock,
|
||||||
|
NewBlock: newblock,
|
||||||
|
RewindToBlock: 0,
|
||||||
|
}
|
||||||
if rew != nil && rew.Sign() > 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *ConfigCompatError) Error() string {
|
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
|
// Rules wraps ChainConfig and is merely syntactic sugar or can be used for functions
|
||||||
|
@ -20,79 +20,99 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCheckCompatible(t *testing.T) {
|
func TestCheckCompatible(t *testing.T) {
|
||||||
type test struct {
|
type test struct {
|
||||||
stored, new *ChainConfig
|
stored, new *ChainConfig
|
||||||
head uint64
|
headBlock uint64
|
||||||
wantErr *ConfigCompatError
|
headTimestamp uint64
|
||||||
|
wantErr *ConfigCompatError
|
||||||
}
|
}
|
||||||
tests := []test{
|
tests := []test{
|
||||||
{stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, head: 0, wantErr: nil},
|
{stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, headBlock: 0, headTimestamp: 0, wantErr: nil},
|
||||||
{stored: AllEthashProtocolChanges, new: AllEthashProtocolChanges, head: 100, 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)},
|
stored: &ChainConfig{EIP150Block: big.NewInt(10)},
|
||||||
new: &ChainConfig{EIP150Block: big.NewInt(20)},
|
new: &ChainConfig{EIP150Block: big.NewInt(20)},
|
||||||
head: 9,
|
headBlock: 9,
|
||||||
wantErr: nil,
|
wantErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stored: AllEthashProtocolChanges,
|
stored: AllEthashProtocolChanges,
|
||||||
new: &ChainConfig{HomesteadBlock: nil},
|
new: &ChainConfig{HomesteadBlock: nil},
|
||||||
head: 3,
|
headBlock: 3,
|
||||||
wantErr: &ConfigCompatError{
|
wantErr: &ConfigCompatError{
|
||||||
What: "Homestead fork block",
|
What: "Homestead fork block",
|
||||||
StoredConfig: big.NewInt(0),
|
StoredBlock: big.NewInt(0),
|
||||||
NewConfig: nil,
|
NewBlock: nil,
|
||||||
RewindTo: 0,
|
RewindToBlock: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stored: AllEthashProtocolChanges,
|
stored: AllEthashProtocolChanges,
|
||||||
new: &ChainConfig{HomesteadBlock: big.NewInt(1)},
|
new: &ChainConfig{HomesteadBlock: big.NewInt(1)},
|
||||||
head: 3,
|
headBlock: 3,
|
||||||
wantErr: &ConfigCompatError{
|
wantErr: &ConfigCompatError{
|
||||||
What: "Homestead fork block",
|
What: "Homestead fork block",
|
||||||
StoredConfig: big.NewInt(0),
|
StoredBlock: big.NewInt(0),
|
||||||
NewConfig: big.NewInt(1),
|
NewBlock: big.NewInt(1),
|
||||||
RewindTo: 0,
|
RewindToBlock: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stored: &ChainConfig{HomesteadBlock: big.NewInt(30), EIP150Block: big.NewInt(10)},
|
stored: &ChainConfig{HomesteadBlock: big.NewInt(30), EIP150Block: big.NewInt(10)},
|
||||||
new: &ChainConfig{HomesteadBlock: big.NewInt(25), EIP150Block: big.NewInt(20)},
|
new: &ChainConfig{HomesteadBlock: big.NewInt(25), EIP150Block: big.NewInt(20)},
|
||||||
head: 25,
|
headBlock: 25,
|
||||||
wantErr: &ConfigCompatError{
|
wantErr: &ConfigCompatError{
|
||||||
What: "EIP150 fork block",
|
What: "EIP150 fork block",
|
||||||
StoredConfig: big.NewInt(10),
|
StoredBlock: big.NewInt(10),
|
||||||
NewConfig: big.NewInt(20),
|
NewBlock: big.NewInt(20),
|
||||||
RewindTo: 9,
|
RewindToBlock: 9,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
|
stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
|
||||||
new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(30)},
|
new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(30)},
|
||||||
head: 40,
|
headBlock: 40,
|
||||||
wantErr: nil,
|
wantErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
|
stored: &ChainConfig{ConstantinopleBlock: big.NewInt(30)},
|
||||||
new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(31)},
|
new: &ChainConfig{ConstantinopleBlock: big.NewInt(30), PetersburgBlock: big.NewInt(31)},
|
||||||
head: 40,
|
headBlock: 40,
|
||||||
wantErr: &ConfigCompatError{
|
wantErr: &ConfigCompatError{
|
||||||
What: "Petersburg fork block",
|
What: "Petersburg fork block",
|
||||||
StoredConfig: nil,
|
StoredBlock: nil,
|
||||||
NewConfig: big.NewInt(31),
|
NewBlock: big.NewInt(31),
|
||||||
RewindTo: 30,
|
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 {
|
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) {
|
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