diff --git a/context.go b/context.go index 1ad088da..d4842f4a 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. diff --git a/dbft.go b/dbft.go index 9a1b0748..cae1d110 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) @@ -446,6 +447,10 @@ func (d *DBFT[H]) onPrepareResponse(msg ConsensusPayload[H]) { return } + if d.IsPrimary() { + d.rttEstimates.addTime(time.Since(d.prepareSentTime)) + } + // ignore PrepareResponse if in process of changing view m := d.PreparationPayloads[msg.ValidatorIndex()] if m != nil || d.ViewChanging() && !d.MoreThanFNodesCommittedOrLost() { diff --git a/rtt.go b/rtt.go new file mode 100644 index 00000000..93ff11bc --- /dev/null +++ b/rtt.go @@ -0,0 +1,22 @@ +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] + + 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