enforce backoff time for out-turn validator (#23)
This commit is contained in:
parent
83d72b8e91
commit
a8c9e53eb0
@ -133,6 +133,7 @@ type CParamsParams struct {
|
||||
ConstantinopleForkBlock *math.HexOrDecimal64 `json:"constantinopleForkBlock"`
|
||||
ConstantinopleFixForkBlock *math.HexOrDecimal64 `json:"constantinopleFixForkBlock"`
|
||||
IstanbulBlock *math.HexOrDecimal64 `json:"istanbulForkBlock"`
|
||||
RamanujanForkBlock *math.HexOrDecimal64 `json:"ramanujanForkBlock"`
|
||||
ChainID *math.HexOrDecimal256 `json:"chainID"`
|
||||
MaximumExtraDataSize math.HexOrDecimal64 `json:"maximumExtraDataSize"`
|
||||
TieBreakingGas bool `json:"tieBreakingGas"`
|
||||
@ -322,6 +323,7 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
|
||||
constantinopleBlock *big.Int
|
||||
petersburgBlock *big.Int
|
||||
istanbulBlock *big.Int
|
||||
ramanujanBlock *big.Int
|
||||
)
|
||||
if chainParams.Params.HomesteadForkBlock != nil {
|
||||
homesteadBlock = big.NewInt(int64(*chainParams.Params.HomesteadForkBlock))
|
||||
@ -351,6 +353,9 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
|
||||
if chainParams.Params.IstanbulBlock != nil {
|
||||
istanbulBlock = big.NewInt(int64(*chainParams.Params.IstanbulBlock))
|
||||
}
|
||||
if chainParams.Params.RamanujanForkBlock != nil {
|
||||
ramanujanBlock = big.NewInt(int64(*chainParams.Params.RamanujanForkBlock))
|
||||
}
|
||||
|
||||
genesis := &core.Genesis{
|
||||
Config: ¶ms.ChainConfig{
|
||||
@ -365,6 +370,7 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
|
||||
ConstantinopleBlock: constantinopleBlock,
|
||||
PetersburgBlock: petersburgBlock,
|
||||
IstanbulBlock: istanbulBlock,
|
||||
RamanujanBlock: ramanujanBlock,
|
||||
},
|
||||
Nonce: uint64(chainParams.Genesis.Nonce),
|
||||
Timestamp: uint64(chainParams.Genesis.Timestamp),
|
||||
|
@ -48,8 +48,8 @@ const (
|
||||
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
||||
|
||||
validatorBytesLength = common.AddressLength
|
||||
wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers
|
||||
fixedBackOffTime = 200 * time.Millisecond
|
||||
wiggleTime = uint64(1) // second, Random delay (per signer) to allow concurrent signers
|
||||
initialBackOffTime = uint64(1) // second
|
||||
|
||||
systemRewardPercent = 4 // it means 1/2^4 = 1/16 percentage of gas fee incoming will be distributed to system
|
||||
|
||||
@ -395,6 +395,16 @@ func (p *Parlia) verifyCascadingFields(chain consensus.ChainReader, header *type
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
|
||||
snap, err := p.snapshot(chain, number-1, header.ParentHash, parents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.blockTimeVerifyForRamanujanFork(snap, header, parent)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify that the gas limit is <= 2^63-1
|
||||
capacity := uint64(0x7fffffffffffffff)
|
||||
if header.GasLimit > capacity {
|
||||
@ -570,7 +580,7 @@ func (p *Parlia) verifySeal(chain consensus.ChainReader, header *types.Header, p
|
||||
|
||||
// Ensure that the difficulty corresponds to the turn-ness of the signer
|
||||
if !p.fakeDiff {
|
||||
inturn := snap.inturn(header.Number.Uint64(), signer)
|
||||
inturn := snap.inturn(signer)
|
||||
if inturn && header.Difficulty.Cmp(diffInTurn) != 0 {
|
||||
return errWrongDifficulty
|
||||
}
|
||||
@ -626,8 +636,7 @@ func (p *Parlia) Prepare(chain consensus.ChainReader, header *types.Header) erro
|
||||
if parent == nil {
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
|
||||
header.Time = parent.Time + p.config.Period
|
||||
header.Time = p.blockTimeForRamanujanFork(snap, header, parent)
|
||||
if header.Time < uint64(time.Now().Unix()) {
|
||||
header.Time = uint64(time.Now().Unix())
|
||||
}
|
||||
@ -809,14 +818,7 @@ func (p *Parlia) Seal(chain consensus.ChainReader, block *types.Block, results c
|
||||
}
|
||||
|
||||
// Sweet, the protocol permits us to sign the block, wait for our time
|
||||
delay := time.Until(time.Unix(int64(header.Time), 0)) // nolint: gosimple
|
||||
if header.Difficulty.Cmp(diffNoTurn) == 0 {
|
||||
// It's not our turn explicitly to sign, delay it a bit
|
||||
wiggle := time.Duration(len(snap.Validators)/2+1) * wiggleTime
|
||||
delay += time.Duration(fixedBackOffTime) + time.Duration(rand.Int63n(int64(wiggle)))
|
||||
|
||||
log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
|
||||
}
|
||||
delay := p.delayForRamanujanFork(snap, header)
|
||||
|
||||
log.Info("Sealing block with", "number", number, "delay", delay, "headerDifficulty", header.Difficulty, "val", val.Hex())
|
||||
|
||||
@ -861,7 +863,7 @@ func (p *Parlia) CalcDifficulty(chain consensus.ChainReader, time uint64, parent
|
||||
// that a new block should have based on the previous blocks in the chain and the
|
||||
// current signer.
|
||||
func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int {
|
||||
if snap.inturn(snap.Number+1, signer) {
|
||||
if snap.inturn(signer) {
|
||||
return new(big.Int).Set(diffInTurn)
|
||||
}
|
||||
return new(big.Int).Set(diffNoTurn)
|
||||
@ -1140,6 +1142,26 @@ func encodeSigHeader(w io.Writer, header *types.Header, chainId *big.Int) {
|
||||
}
|
||||
}
|
||||
|
||||
func backOffTime(snap *Snapshot, val common.Address) uint64 {
|
||||
if snap.inturn(val) {
|
||||
return 0
|
||||
} else {
|
||||
dis := snap.distanceToInTurn(val)
|
||||
s := rand.NewSource(int64(snap.Number))
|
||||
r := rand.New(s)
|
||||
n := len(snap.Validators)
|
||||
backOffSteps := make([]uint64, 0, n)
|
||||
for idx := uint64(0); idx < uint64(n); idx++ {
|
||||
backOffSteps = append(backOffSteps, idx)
|
||||
}
|
||||
r.Shuffle(n, func(i, j int) {
|
||||
backOffSteps[i], backOffSteps[j] = backOffSteps[j], backOffSteps[i]
|
||||
})
|
||||
delay := initialBackOffTime + backOffSteps[dis]*wiggleTime
|
||||
return delay
|
||||
}
|
||||
}
|
||||
|
||||
// chain context
|
||||
type chainContext struct {
|
||||
Chain consensus.ChainReader
|
||||
@ -1194,4 +1216,3 @@ func applyMessage(
|
||||
}
|
||||
return msg.Gas() - returnGas, err
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
@ -57,7 +56,7 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
|
||||
return validators[idx]
|
||||
}
|
||||
|
||||
downDelay := time.Duration(0)
|
||||
downDelay := uint64(0)
|
||||
for h := 1; h <= downBlocks; h++ {
|
||||
if limit := uint64(totalValidators/2 + 1); uint64(h) >= limit {
|
||||
delete(recents, uint64(h)-limit)
|
||||
@ -73,7 +72,7 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
|
||||
if len(candidates) == 0 {
|
||||
panic("can not test such case")
|
||||
}
|
||||
idx, delay := producerBlockDelay(candidates, totalValidators)
|
||||
idx, delay := producerBlockDelay(candidates, h, totalValidators)
|
||||
downDelay = downDelay + delay
|
||||
recents[uint64(h)] = idx
|
||||
} else {
|
||||
@ -81,13 +80,13 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
|
||||
}
|
||||
}
|
||||
fmt.Printf("average delay is %v when there is %d validators and %d is down \n",
|
||||
downDelay/time.Duration(downBlocks), totalValidators, downValidators)
|
||||
downDelay/uint64(downBlocks), totalValidators, downValidators)
|
||||
|
||||
for i := 0; i < downValidators; i++ {
|
||||
validators[down[i]] = true
|
||||
}
|
||||
|
||||
recoverDelay := time.Duration(0)
|
||||
recoverDelay := uint64(0)
|
||||
lastseen := downBlocks
|
||||
for h := downBlocks + 1; h <= downBlocks+recoverBlocks; h++ {
|
||||
if limit := uint64(totalValidators/2 + 1); uint64(h) >= limit {
|
||||
@ -105,7 +104,7 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
|
||||
if len(candidates) == 0 {
|
||||
panic("can not test such case")
|
||||
}
|
||||
idx, delay := producerBlockDelay(candidates, totalValidators)
|
||||
idx, delay := producerBlockDelay(candidates, h, totalValidators)
|
||||
recoverDelay = recoverDelay + delay
|
||||
recents[uint64(h)] = idx
|
||||
} else {
|
||||
@ -116,18 +115,28 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
|
||||
recoverDelay, downValidators, lastseen)
|
||||
}
|
||||
|
||||
func producerBlockDelay(candidates map[int]bool, numOfValidators int) (int, time.Duration) {
|
||||
minDur := time.Duration(0)
|
||||
minIdx := 0
|
||||
wiggle := time.Duration(numOfValidators/2+1) * wiggleTime
|
||||
for idx := range candidates {
|
||||
sleepTime := rand.Int63n(int64(wiggle))
|
||||
if int64(minDur) < sleepTime {
|
||||
minDur = time.Duration(rand.Int63n(int64(wiggle)))
|
||||
minIdx = idx
|
||||
func producerBlockDelay(candidates map[int]bool, height, numOfValidators int) (int, uint64) {
|
||||
|
||||
s := rand.NewSource(int64(height))
|
||||
r := rand.New(s)
|
||||
n := numOfValidators
|
||||
backOffSteps := make([]int, 0, n)
|
||||
for idx := 0; idx < n; idx++ {
|
||||
backOffSteps = append(backOffSteps, idx)
|
||||
}
|
||||
r.Shuffle(n, func(i, j int) {
|
||||
backOffSteps[i], backOffSteps[j] = backOffSteps[j], backOffSteps[i]
|
||||
})
|
||||
minDelay := numOfValidators
|
||||
minCandidate := 0
|
||||
for c := range candidates {
|
||||
if minDelay > backOffSteps[c] {
|
||||
minDelay = backOffSteps[c]
|
||||
minCandidate = c
|
||||
}
|
||||
}
|
||||
return minIdx, minDur
|
||||
delay := initialBackOffTime + uint64(minDelay)*wiggleTime
|
||||
return minCandidate, delay
|
||||
}
|
||||
|
||||
func randomAddress() common.Address {
|
||||
|
40
consensus/parlia/ramanujanfork.go
Normal file
40
consensus/parlia/ramanujanfork.go
Normal file
@ -0,0 +1,40 @@
|
||||
package parlia
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
const (
|
||||
wiggleTimeBeforeFork = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers
|
||||
fixedBackOffTimeBeforeFork = 200 * time.Millisecond
|
||||
)
|
||||
|
||||
func (p *Parlia) delayForRamanujanFork(snap *Snapshot, header *types.Header) time.Duration {
|
||||
delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple
|
||||
if p.chainConfig.IsRamanujan(header.Number) {
|
||||
return delay
|
||||
}
|
||||
wiggle := time.Duration(len(snap.Validators)/2+1) * wiggleTimeBeforeFork
|
||||
return delay + time.Duration(fixedBackOffTimeBeforeFork) + time.Duration(rand.Int63n(int64(wiggle)))
|
||||
}
|
||||
|
||||
func (p *Parlia) blockTimeForRamanujanFork(snap *Snapshot, header, parent *types.Header) uint64 {
|
||||
blockTime := parent.Time + p.config.Period
|
||||
if p.chainConfig.IsRamanujan(header.Number) {
|
||||
blockTime = blockTime + backOffTime(snap, p.val)
|
||||
}
|
||||
return blockTime
|
||||
}
|
||||
|
||||
func (p *Parlia) blockTimeVerifyForRamanujanFork(snap *Snapshot, header, parent *types.Header) error {
|
||||
if p.chainConfig.IsRamanujan(header.Number) {
|
||||
if header.Time < parent.Time+p.config.Period+backOffTime(snap, header.Coinbase) {
|
||||
return consensus.ErrFutureBlock
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -210,12 +210,26 @@ func (s *Snapshot) validators() []common.Address {
|
||||
}
|
||||
|
||||
// inturn returns if a validator at a given block height is in-turn or not.
|
||||
func (s *Snapshot) inturn(number uint64, validator common.Address) bool {
|
||||
func (s *Snapshot) inturn(validator common.Address) bool {
|
||||
validators := s.validators()
|
||||
offset := number % uint64(len(validators))
|
||||
offset := (s.Number + 1) % uint64(len(validators))
|
||||
return validators[offset] == validator
|
||||
}
|
||||
|
||||
func (s *Snapshot) distanceToInTurn(validator common.Address) uint64 {
|
||||
validators := s.validators()
|
||||
offset := (s.Number + 1) % uint64(len(validators))
|
||||
idx := uint64(0)
|
||||
for idx < uint64(len(validator)) && validators[idx] != validator {
|
||||
idx++
|
||||
}
|
||||
if offset > idx {
|
||||
return uint64(len(validators)) + idx - offset
|
||||
} else {
|
||||
return idx - offset
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Snapshot) supposeValidator() common.Address {
|
||||
validators := s.validators()
|
||||
index := (s.Number + 1) % uint64(len(validators))
|
||||
|
@ -222,7 +222,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override
|
||||
// Special case: don't change the existing config of a non-mainnet chain if no new
|
||||
// config is supplied. These chains would get AllProtocolChanges (and a compat error)
|
||||
// if we just continued here.
|
||||
if genesis == nil && stored != params.MainnetGenesisHash {
|
||||
// The full node of two BSC testnets may run without genesis file after been inited.
|
||||
if genesis == nil && stored != params.MainnetGenesisHash &&
|
||||
stored != params.ChapelGenesisHash && stored != params.RialtoGenesisHash {
|
||||
return storedcfg, stored, nil
|
||||
}
|
||||
|
||||
@ -252,6 +254,10 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||
return params.RinkebyChainConfig
|
||||
case ghash == params.GoerliGenesisHash:
|
||||
return params.GoerliChainConfig
|
||||
case ghash == params.ChapelGenesisHash:
|
||||
return params.ChapelChainConfig
|
||||
case ghash == params.RialtoGenesisHash:
|
||||
return params.RialtoChainConfig
|
||||
default:
|
||||
return params.AllEthashProtocolChanges
|
||||
}
|
||||
|
@ -681,11 +681,12 @@ func (f *BlockFetcher) insert(peer string, block *types.Block) {
|
||||
go f.broadcastBlock(block, true)
|
||||
|
||||
case consensus.ErrFutureBlock:
|
||||
// Weird future block, don't fail, but neither propagate
|
||||
log.Error("Received future block", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
|
||||
f.dropPeer(peer)
|
||||
|
||||
default:
|
||||
// Something went very wrong, drop the peer
|
||||
log.Debug("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
|
||||
log.Error("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
|
||||
f.dropPeer(peer)
|
||||
return
|
||||
}
|
||||
|
@ -31,6 +31,9 @@ var (
|
||||
RopstenGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
||||
RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||
GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a")
|
||||
|
||||
ChapelGenesisHash = common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34")
|
||||
RialtoGenesisHash = common.HexToHash("0xaa1c1e0af675e846942719466ab72822eff51ebf8462ead0897ae1240e3c0da1")
|
||||
)
|
||||
|
||||
// TrustedCheckpoints associates each known checkpoint with the genesis hash of
|
||||
@ -210,21 +213,57 @@ var (
|
||||
Threshold: 2,
|
||||
}
|
||||
|
||||
ChapelChainConfig = &ChainConfig{
|
||||
ChainID: big.NewInt(97),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
MuirGlacierBlock: big.NewInt(0),
|
||||
RamanujanBlock: big.NewInt(1066095),
|
||||
Parlia: &ParliaConfig{
|
||||
Period: 3,
|
||||
Epoch: 200,
|
||||
},
|
||||
}
|
||||
|
||||
RialtoChainConfig = &ChainConfig{
|
||||
ChainID: big.NewInt(1417),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
MuirGlacierBlock: big.NewInt(0),
|
||||
RamanujanBlock: big.NewInt(200987),
|
||||
Parlia: &ParliaConfig{
|
||||
Period: 3,
|
||||
Epoch: 200,
|
||||
},
|
||||
}
|
||||
|
||||
// AllEthashProtocolChanges contains every protocol change (EIPs) introduced
|
||||
// and accepted by the Ethereum core developers into the Ethash consensus.
|
||||
//
|
||||
// This configuration is intentionally not using keyed fields to force anyone
|
||||
// adding flags to the config to also have to set these fields.
|
||||
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, nil}
|
||||
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil, nil}
|
||||
|
||||
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
|
||||
// and accepted by the Ethereum core developers into the Clique consensus.
|
||||
//
|
||||
// This configuration is intentionally not using keyed fields to force anyone
|
||||
// adding flags to the config to also have to set these fields.
|
||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil}
|
||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil}
|
||||
|
||||
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, nil}
|
||||
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil, nil}
|
||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||
)
|
||||
|
||||
@ -296,6 +335,7 @@ type ChainConfig struct {
|
||||
IstanbulBlock *big.Int `json:"istanbulBlock,omitempty" toml:",omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul)
|
||||
MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty" toml:",omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated)
|
||||
EWASMBlock *big.Int `json:"ewasmBlock,omitempty" toml:",omitempty"` // EWASM switch block (nil = no fork, 0 = already activated)
|
||||
RamanujanBlock *big.Int `json:"ramanujanBlock,omitempty" toml:",omitempty"` // ramanujanBlock switch block (nil = no fork, 0 = already activated)
|
||||
|
||||
// Various consensus engines
|
||||
Ethash *EthashConfig `json:"ethash,omitempty" toml:",omitempty"`
|
||||
@ -346,7 +386,7 @@ func (c *ChainConfig) String() string {
|
||||
default:
|
||||
engine = "unknown"
|
||||
}
|
||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Engine: %v}",
|
||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Ramanujan: %v, Engine: %v}",
|
||||
c.ChainID,
|
||||
c.HomesteadBlock,
|
||||
c.DAOForkBlock,
|
||||
@ -359,6 +399,7 @@ func (c *ChainConfig) String() string {
|
||||
c.PetersburgBlock,
|
||||
c.IstanbulBlock,
|
||||
c.MuirGlacierBlock,
|
||||
c.RamanujanBlock,
|
||||
engine,
|
||||
)
|
||||
}
|
||||
@ -398,6 +439,11 @@ func (c *ChainConfig) IsConstantinople(num *big.Int) bool {
|
||||
return isForked(c.ConstantinopleBlock, num)
|
||||
}
|
||||
|
||||
// IsRamanujan returns whether num is either equal to the IsRamanujan fork block or greater.
|
||||
func (c *ChainConfig) IsRamanujan(num *big.Int) bool {
|
||||
return isForked(c.RamanujanBlock, 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)
|
||||
@ -456,6 +502,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
|
||||
{"petersburgBlock", c.PetersburgBlock},
|
||||
{"istanbulBlock", c.IstanbulBlock},
|
||||
{"muirGlacierBlock", c.MuirGlacierBlock},
|
||||
{"ramanujanBlock", c.RamanujanBlock},
|
||||
} {
|
||||
if lastFork.name != "" {
|
||||
// Next one must be higher number
|
||||
@ -515,6 +562,9 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi
|
||||
if isForkIncompatible(c.EWASMBlock, newcfg.EWASMBlock, head) {
|
||||
return newCompatError("ewasm fork block", c.EWASMBlock, newcfg.EWASMBlock)
|
||||
}
|
||||
if isForkIncompatible(c.RamanujanBlock, newcfg.RamanujanBlock, head) {
|
||||
return newCompatError("ramanujan fork block", c.RamanujanBlock, newcfg.RamanujanBlock)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user