types: make timely predicate adaptive per round

This commit is contained in:
William Banfield
2022-01-28 18:32:46 -05:00
parent e87d40719d
commit 5ccd8ed804
3 changed files with 50 additions and 34 deletions

View File

@@ -3,6 +3,7 @@ package types
import (
"errors"
"fmt"
"math"
"time"
"github.com/tendermint/tendermint/internal/libs/protoio"
@@ -89,11 +90,19 @@ func (p *Proposal) ValidateBasic() error {
//
// For more information on the meaning of 'timely', see the proposer-based timestamp specification:
// https://github.com/tendermint/spec/tree/master/spec/consensus/proposer-based-timestamp
func (p *Proposal) IsTimely(recvTime time.Time, sp SynchronyParams) bool {
func (p *Proposal) IsTimely(recvTime time.Time, sp SynchronyParams, round int32) bool {
// The message delay values are scaled as rounds progress.
// Every 10 rounds, the message delay is doubled to allow consensus to
// proceed in the case that the choosen value was too small for the given network conditions.
// For more information and discussion on this mechanism, see the relevant github issue:
// https://github.com/tendermint/spec/issues/371
roundModifier := time.Duration(math.Exp2(float64(round / 10)))
msgDelay := sp.MessageDelay * roundModifier
// lhs is `proposedBlockTime - Precision` in the first inequality
lhs := p.Timestamp.Add(-sp.Precision)
// rhs is `proposedBlockTime + MsgDelay + Precision` in the second inequality
rhs := p.Timestamp.Add(sp.MessageDelay).Add(sp.Precision)
rhs := p.Timestamp.Add(msgDelay).Add(sp.Precision)
if recvTime.Before(lhs) || recvTime.After(rhs) {
return false

View File

@@ -216,54 +216,61 @@ func TestIsTimely(t *testing.T) {
genesisTime, err := time.Parse(time.RFC3339, "2019-03-13T23:00:00Z")
require.NoError(t, err)
testCases := []struct {
name string
proposalHeight int64
proposalTime time.Time
recvTime time.Time
precision time.Duration
msgDelay time.Duration
expectTimely bool
name string
proposalTime time.Time
recvTime time.Time
precision time.Duration
msgDelay time.Duration
expectTimely bool
round int32
}{
// proposalTime - precision <= localTime <= proposalTime + msgDelay + precision
{
// Checking that the following inequality evaluates to true:
// 0 - 2 <= 1 <= 0 + 1 + 2
name: "basic timely",
proposalHeight: 2,
proposalTime: genesisTime,
recvTime: genesisTime.Add(1 * time.Nanosecond),
precision: time.Nanosecond * 2,
msgDelay: time.Nanosecond,
expectTimely: true,
name: "basic timely",
proposalTime: genesisTime,
recvTime: genesisTime.Add(1 * time.Nanosecond),
precision: time.Nanosecond * 2,
msgDelay: time.Nanosecond,
expectTimely: true,
},
{
// Checking that the following inequality evaluates to false:
// 0 - 2 <= 4 <= 0 + 1 + 2
name: "local time too large",
proposalHeight: 2,
proposalTime: genesisTime,
recvTime: genesisTime.Add(4 * time.Nanosecond),
precision: time.Nanosecond * 2,
msgDelay: time.Nanosecond,
expectTimely: false,
name: "local time too large",
proposalTime: genesisTime,
recvTime: genesisTime.Add(4 * time.Nanosecond),
precision: time.Nanosecond * 2,
msgDelay: time.Nanosecond,
expectTimely: false,
},
{
// Checking that the following inequality evaluates to false:
// 4 - 2 <= 0 <= 4 + 2 + 1
name: "proposal time too large",
proposalHeight: 2,
proposalTime: genesisTime.Add(4 * time.Nanosecond),
recvTime: genesisTime,
precision: time.Nanosecond * 2,
msgDelay: time.Nanosecond,
expectTimely: false,
name: "proposal time too large",
proposalTime: genesisTime.Add(4 * time.Nanosecond),
recvTime: genesisTime,
precision: time.Nanosecond * 2,
msgDelay: time.Nanosecond,
expectTimely: false,
},
{
// Checking that the following inequality evaluates to true:
// 0 - (2 * 2) <= 4 <= 0 + (1 * 2) + 2
name: "message delay adapts after 10 rounds",
proposalTime: genesisTime,
recvTime: genesisTime.Add(4 * time.Nanosecond),
precision: time.Nanosecond * 2,
msgDelay: time.Nanosecond,
expectTimely: true,
round: 10,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
p := Proposal{
Height: testCase.proposalHeight,
Timestamp: testCase.proposalTime,
}
@@ -272,7 +279,7 @@ func TestIsTimely(t *testing.T) {
MessageDelay: testCase.msgDelay,
}
ti := p.IsTimely(testCase.recvTime, sp)
ti := p.IsTimely(testCase.recvTime, sp, testCase.round)
assert.Equal(t, testCase.expectTimely, ti)
})
}