diff --git a/context.go b/context.go index 1ad088da..84139c20 100644 --- a/context.go +++ b/context.go @@ -91,6 +91,9 @@ type Context[H Hash] struct { lastBlockTime time.Time // Wall clock time of when the last block was first seen (used for timer adjustments). lastBlockIndex uint32 lastBlockView byte + + prepareSentTime time.Time + rttEstimates rtt } // N returns total number of validators. @@ -236,6 +239,7 @@ func (c *Context[H]) PreBlock() PreBlock[H] { func (c *Context[H]) reset(view byte, ts uint64) { c.MyIndex = -1 + c.prepareSentTime = time.Time{} c.lastBlockTimestamp = ts if view == 0 { diff --git a/dbft.go b/dbft.go index 9a1b0748..69ca15f3 100644 --- a/dbft.go +++ b/dbft.go @@ -153,6 +153,7 @@ func (d *DBFT[H]) initializeConsensus(view byte, ts uint64) { var ts = d.Timer.Now() var diff = ts.Sub(d.lastBlockTime) timeout -= diff + timeout -= d.rttEstimates.avg timeout = max(0, timeout) } d.changeTimer(timeout) @@ -482,6 +483,10 @@ func (d *DBFT[H]) onPrepareResponse(msg ConsensusPayload[H]) { } } + if d.IsPrimary() && !d.prepareSentTime.IsZero() && !d.recovering { + d.rttEstimates.addTime(time.Since(d.prepareSentTime)) + } + d.extendTimer(2) if !d.Context.WatchOnly() && !d.CommitSent() && (!d.isAntiMEVExtensionEnabled() || !d.PreCommitSent()) && d.RequestSentOrReceived() { diff --git a/rtt.go b/rtt.go new file mode 100644 index 00000000..ae5a06e1 --- /dev/null +++ b/rtt.go @@ -0,0 +1,26 @@ +package dbft + +import ( + "time" +) + +const rttLength = 7 * 10 + +type rtt struct { + times [rttLength]time.Duration + idx int + avg time.Duration +} + +func (r *rtt) addTime(new time.Duration) { + var old = r.times[r.idx] + + if old != 0 { + new = min(new, 2*old) // Too long delays should be normalized, we don't want to overshoot. + } + + r.avg = r.avg + (new-old)/time.Duration(len(r.times)) + r.avg = max(0, r.avg) // Can't be less than zero. + r.times[r.idx] = new + r.idx = (r.idx + 1) % len(r.times) +} diff --git a/send.go b/send.go index cc9c5082..70c12e4c 100644 --- a/send.go +++ b/send.go @@ -27,6 +27,8 @@ func (d *DBFT[H]) sendPrepareRequest() { d.PreparationPayloads[d.MyIndex] = msg d.broadcast(msg) + d.prepareSentTime = d.Timer.Now() + delay := d.SecondsPerBlock << (d.ViewNumber + 1) if d.ViewNumber == 0 { delay -= d.SecondsPerBlock