diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 2c304f10e..8d9c5189e 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -1298,7 +1298,7 @@ func encodeSigHeader(w io.Writer, header *types.Header, chainId *big.Int) { } } -func backOffTime(snap *Snapshot, val common.Address) uint64 { +func (p *Parlia) backOffTime(snap *Snapshot, header *types.Header, val common.Address) uint64 { if snap.inturn(val) { return 0 } else { @@ -1311,13 +1311,50 @@ func backOffTime(snap *Snapshot, val common.Address) uint64 { 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) + + if !p.chainConfig.IsBohr(header.Number) { + for i := uint64(0); i < uint64(n); i++ { + backOffSteps = append(backOffSteps, i) + } + r.Shuffle(n, func(i, j int) { + backOffSteps[i], backOffSteps[j] = backOffSteps[j], backOffSteps[i] + }) + delay := initialBackOffTime + backOffSteps[idx]*wiggleTime + return delay } - r.Shuffle(n, func(i, j int) { + + // Exclude the recently signed validators first, and then compute the backOffTime. + recentVals := make(map[common.Address]bool, len(snap.Recents)) + //for seen, recent := range snap.Recents { + for _, recent := range snap.Recents { + if val == recent { + // The backOffTime does not matter when a validator has signed recently. + return 0 + } + recentVals[recent] = true + } + + backOffIndex := idx + validators := snap.validators() + for i := 0; i < n; i++ { + if isRecent, ok := recentVals[validators[i]]; ok && isRecent { + if i < idx { + backOffIndex-- + } + continue + } + backOffSteps = append(backOffSteps, uint64(len(backOffSteps))) + } + r.Shuffle(len(backOffSteps), func(i, j int) { backOffSteps[i], backOffSteps[j] = backOffSteps[j], backOffSteps[i] }) - delay := initialBackOffTime + backOffSteps[idx]*wiggleTime + delay := initialBackOffTime + backOffSteps[backOffIndex]*wiggleTime + + // If the in turn validator has recently signed, no initial delay. + inTurnVal := validators[(snap.Number+1)%uint64(len(validators))] + if isRecent, ok := recentVals[inTurnVal]; ok && isRecent { + delay -= initialBackOffTime + } return delay } } diff --git a/consensus/parlia/ramanujanfork.go b/consensus/parlia/ramanujanfork.go index 9b702ca6c..ce9089deb 100644 --- a/consensus/parlia/ramanujanfork.go +++ b/consensus/parlia/ramanujanfork.go @@ -29,14 +29,14 @@ func (p *Parlia) delayForRamanujanFork(snap *Snapshot, header *types.Header) tim 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) + blockTime = blockTime + p.backOffTime(snap, header, 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) { + if header.Time < parent.Time+p.config.Period+p.backOffTime(snap, header, header.Coinbase) { return consensus.ErrFutureBlock } }