Skip to content

core,params: add fork readiness indicator in logs #31340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"errors"
"fmt"
"io"
"math"
"math/big"
"runtime"
"slices"
Expand Down Expand Up @@ -98,6 +99,10 @@ var (
errInvalidNewChain = errors.New("invalid new chain")
)

var (
forkReadyInterval = 3 * time.Minute
)

const (
bodyCacheLimit = 256
blockCacheLimit = 256
Expand Down Expand Up @@ -268,6 +273,8 @@ type BlockChain struct {
processor Processor // Block transaction processor interface
vmConfig vm.Config
logger *tracing.Hooks

lastForkReadyAlert time.Time // Last time there was a fork readiness print out
}

// NewBlockChain returns a fully initialised block chain using information
Expand Down Expand Up @@ -1825,6 +1832,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
trieDiffNodes, trieBufNodes, _ := bc.triedb.Size()
stats.report(chain, it.index, snapDiffItems, snapBufItems, trieDiffNodes, trieBufNodes, setHead)

// Print confirmation that a future fork is scheduled, but not yet active.
bc.logForkReadiness(block)

if !setHead {
// After merge we expect few side chains. Simply count
// all blocks the CL gives us for GC processing time
Expand Down Expand Up @@ -1858,6 +1868,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
"root", block.Root())
}
}

stats.ignored += it.remaining()
return witness, it.index, err
}
Expand Down Expand Up @@ -2455,6 +2466,23 @@ func (bc *BlockChain) reportBlock(block *types.Block, res *ProcessResult, err er
log.Error(summarizeBadBlock(block, receipts, bc.Config(), err))
}

// logForkReadiness will write a log when a future fork is scheduled, but not
// active. This is useful so operators know their client is ready for the fork.
func (bc *BlockChain) logForkReadiness(block *types.Block) {
c := bc.Config()
current, last := c.LatestFork(block.Time()), c.LatestFork(math.MaxUint64)
t := c.Timestamp(last)
if t == nil {
return
}
at := time.Unix(int64(*t), 0)
if current < last && time.Now().After(bc.lastForkReadyAlert.Add(forkReadyInterval)) {
log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822),
"remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix())
bc.lastForkReadyAlert = time.Now()
}
}

Comment on lines +2469 to +2485
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not check if we should log out before we compute the ready activation?

Suggested change
// logForkReadiness will write a log when a future fork is scheduled, but not
// active. This is useful so operators know their client is ready for the fork.
func (bc *BlockChain) logForkReadiness(block *types.Block) {
c := bc.Config()
current, last := c.LatestFork(block.Time()), c.LatestFork(math.MaxUint64)
t := c.Timestamp(last)
if t == nil {
return
}
at := time.Unix(int64(*t), 0)
if current < last && time.Now().After(bc.lastForkReadyAlert.Add(forkReadyInterval)) {
log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822),
"remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix())
bc.lastForkReadyAlert = time.Now()
}
}
// logForkReadiness will write a log when a future fork is scheduled, but not
// active. This is useful so operators know their client is ready for the fork.
func (bc *BlockChain) logForkReadiness(block *types.Block) {
if time.Since(bc.lastForkReadyAlert) > forkReadyInterval {
c := bc.Config()
current, last := c.LatestFork(block.Time()), c.LatestFork(math.MaxUint64)
t := c.Timestamp(last)
if t == nil {
return
}
at := time.Unix(int64(*t), 0)
if current < last {
log.Info("Ready for fork activation", "fork", last, "date", at.Format(time.RFC822),
"remaining", time.Until(at).Round(time.Second), "timestamp", at.Unix())
bc.lastForkReadyAlert = time.Now()
}
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nvm, we're never updating bc.lastForkReadyAlert this way

// summarizeBadBlock returns a string summarizing the bad block and other
// relevant information.
func summarizeBadBlock(block *types.Block, receipts []*types.Receipt, config *params.ChainConfig, err error) string {
Expand Down
17 changes: 17 additions & 0 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,23 @@ func (c *ChainConfig) LatestFork(time uint64) forks.Fork {
}
}

// Timestamp returns the timestamp associated with the fork or returns nil if
// the fork isn't defined or isn't a time-based fork.
func (c *ChainConfig) Timestamp(fork forks.Fork) *uint64 {
switch {
case fork == forks.Osaka:
return c.OsakaTime
case fork == forks.Prague:
return c.PragueTime
case fork == forks.Cancun:
return c.CancunTime
case fork == forks.Shanghai:
return c.ShanghaiTime
default:
return nil
}
}

// 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 {
Expand Down
34 changes: 33 additions & 1 deletion params/forks/forks.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ package forks
type Fork int

const (
Frontier = iota
Frontier Fork = iota
FrontierThawing
Homestead
DAO
Expand All @@ -41,3 +41,35 @@ const (
Prague
Osaka
)

// String implements fmt.Stringer.
func (f Fork) String() string {
s, ok := forkToString[f]
if !ok {
return "Unknown fork"
}
return s
}

var forkToString = map[Fork]string{
Frontier: "Frontier",
FrontierThawing: "Frontier Thawing",
Homestead: "Homestead",
DAO: "DAO",
TangerineWhistle: "Tangerine Whistle",
SpuriousDragon: "Spurious Dragon",
Byzantium: "Byzantium",
Constantinople: "Constantinople",
Petersburg: "Petersburg",
Istanbul: "Istanbul",
MuirGlacier: "Muir Glacier",
Berlin: "Berlin",
London: "London",
ArrowGlacier: "Arrow Glacier",
GrayGlacier: "Gray Glacier",
Paris: "Paris",
Shanghai: "Shanghai",
Cancun: "Cancun",
Prague: "Prague",
Osaka: "Osaka",
}