Skip to content

Commit

Permalink
feat: wait miner finish the later multi-proposals when restarting the…
Browse files Browse the repository at this point in the history
… node; (#2845)
  • Loading branch information
galaio authored Jan 7, 2025
1 parent 990226a commit 4ef505f
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 8 deletions.
2 changes: 2 additions & 0 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,6 @@ type PoSA interface {
GetFinalizedHeader(chain ChainHeaderReader, header *types.Header) *types.Header
VerifyVote(chain ChainHeaderReader, vote *types.VoteEnvelope) error
IsActiveValidatorAt(chain ChainHeaderReader, header *types.Header, checkVoteKeyFn func(bLSPublicKey *types.BLSPublicKey) bool) bool
BlockInterval() uint64
NextProposalBlock(chain ChainHeaderReader, header *types.Header, proposer common.Address) (uint64, uint64, error)
}
13 changes: 13 additions & 0 deletions consensus/parlia/parlia.go
Original file line number Diff line number Diff line change
Expand Up @@ -2123,6 +2123,19 @@ func (p *Parlia) backOffTime(snap *Snapshot, header *types.Header, val common.Ad
}
}

func (p *Parlia) BlockInterval() uint64 {
return p.config.Period
}

func (p *Parlia) NextProposalBlock(chain consensus.ChainHeaderReader, header *types.Header, proposer common.Address) (uint64, uint64, error) {
snap, err := p.snapshot(chain, header.Number.Uint64(), header.Hash(), nil)
if err != nil {
return 0, 0, err
}

return snap.nextProposalBlock(proposer)
}

// chain context
type chainContext struct {
Chain consensus.ChainHeaderReader
Expand Down
31 changes: 31 additions & 0 deletions consensus/parlia/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,37 @@ func (s *Snapshot) inturnValidator() common.Address {
return validators[offset]
}

func (s *Snapshot) nexValidatorsChangeBlock() uint64 {
currentEpoch := s.Number - s.Number%s.config.Epoch
checkLen := s.minerHistoryCheckLen()
if s.Number%s.config.Epoch < checkLen {
return currentEpoch + checkLen
}
return currentEpoch + s.config.Epoch + checkLen
}

// nextProposalBlock returns the validator next proposal block.
func (s *Snapshot) nextProposalBlock(proposer common.Address) (uint64, uint64, error) {
validators := s.validators()
currentIndex := int(s.Number / uint64(s.TurnLength) % uint64(len(validators)))
expectIndex := s.indexOfVal(proposer)
if expectIndex < 0 {
return 0, 0, errors.New("proposer not in validator set")
}
startBlock := s.Number + uint64(((expectIndex+len(validators)-currentIndex)%len(validators))*int(s.TurnLength))
startBlock = startBlock - startBlock%uint64(s.TurnLength)
endBlock := startBlock + uint64(s.TurnLength) - 1

changeValidatorsBlock := s.nexValidatorsChangeBlock()
if startBlock >= changeValidatorsBlock {
return 0, 0, errors.New("next proposal block is out of current epoch")
}
if endBlock >= changeValidatorsBlock {
endBlock = changeValidatorsBlock
}
return startBlock, endBlock, nil
}

func (s *Snapshot) enoughDistance(validator common.Address, header *types.Header) bool {
idx := s.indexOfVal(validator)
if idx < 0 {
Expand Down
3 changes: 3 additions & 0 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,9 @@ func (s *Ethereum) setupDiscovery() error {
// Stop implements node.Lifecycle, terminating all internal goroutines used by the
// Ethereum protocol.
func (s *Ethereum) Stop() error {
if s.miner.Mining() {
s.miner.TryWaitProposalDoneWhenStopping()
}
// Stop all the peer-related stuff first.
s.discmix.Close()
s.handler.Stop()
Expand Down
4 changes: 4 additions & 0 deletions miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ func (miner *Miner) InTurn() bool {
return miner.worker.inTurn()
}

func (miner *Miner) TryWaitProposalDoneWhenStopping() {
miner.worker.tryWaitProposalDoneWhenStopping()
}

// Pending returns the currently pending block and associated receipts, logs
// and statedb. The returned values can be nil in case the pending block is
// not initialized.
Expand Down
21 changes: 13 additions & 8 deletions miner/minerconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ import (

// Config is the configuration parameters of mining.
type Config struct {
Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards
ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner
DelayLeftOver time.Duration // Time reserved to finalize a block(calculate root, distribute income...)
GasFloor uint64 // Target gas floor for mined blocks.
GasCeil uint64 // Target gas ceiling for mined blocks.
GasPrice *big.Int // Minimum gas price for mining a transaction
Recommit time.Duration // The time interval for miner to re-create mining work.
VoteEnable bool // Whether to vote when mining
Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards
ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner
DelayLeftOver time.Duration // Time reserved to finalize a block(calculate root, distribute income...)
GasFloor uint64 // Target gas floor for mined blocks.
GasCeil uint64 // Target gas ceiling for mined blocks.
GasPrice *big.Int // Minimum gas price for mining a transaction
Recommit time.Duration // The time interval for miner to re-create mining work.
VoteEnable bool // Whether to vote when mining
MaxWaitProposalInSecs uint64 // The maximum time to wait for the proposal to be done, it's aimed to prevent validator being slashed when restarting

DisableVoteAttestation bool // Whether to skip assembling vote attestation

Expand All @@ -54,6 +55,10 @@ var DefaultConfig = Config{
Recommit: 3 * time.Second,
DelayLeftOver: 50 * time.Millisecond,

// The default value is set to 30 seconds.
// Because the avg restart time in mainnet is around 30s, so the node try to wait for the next multi-proposals to be done.
MaxWaitProposalInSecs: 30,

Mev: DefaultMevConfig,
}

Expand Down
33 changes: 33 additions & 0 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,39 @@ func (w *worker) getSealingBlock(params *generateParams) *newPayloadResult {
}
}

func (w *worker) tryWaitProposalDoneWhenStopping() {
posa, ok := w.engine.(consensus.PoSA)
// if the consensus is not PoSA, just skip waiting
if !ok {
return
}

currentHeader := w.chain.CurrentBlock()
currentBlock := currentHeader.Number.Uint64()
startBlock, endBlock, err := posa.NextProposalBlock(w.chain, currentHeader, w.coinbase)
if err != nil {
log.Warn("Failed to get next proposal block, skip waiting", "err", err)
return
}

log.Info("Checking miner's next proposal block", "current", currentBlock,
"proposalStart", startBlock, "proposalEnd", endBlock, "maxWait", w.config.MaxWaitProposalInSecs)
if endBlock <= currentBlock {
log.Warn("next proposal end block has passed, ignore")
return
}
if startBlock > currentBlock && (startBlock-currentBlock)*posa.BlockInterval() > w.config.MaxWaitProposalInSecs {
log.Warn("the next proposal start block is too far, just skip waiting")
return
}

// wait one more block for safety
waitSecs := (endBlock - currentBlock + 1) * posa.BlockInterval()
log.Info("The miner will propose in later, waiting for the proposal to be done",
"currentBlock", currentBlock, "nextProposalStart", startBlock, "nextProposalEnd", endBlock, "waitTime", waitSecs)
time.Sleep(time.Duration(waitSecs) * time.Second)
}

// copyReceipts makes a deep copy of the given receipts.
func copyReceipts(receipts []*types.Receipt) []*types.Receipt {
result := make([]*types.Receipt, len(receipts))
Expand Down

0 comments on commit 4ef505f

Please sign in to comment.