move time checks into block.go and add time source mechanism

This commit is contained in:
William Banfield
2021-10-28 11:29:30 +02:00
parent c76c0781a0
commit f03da27f19
6 changed files with 133 additions and 51 deletions
+20
View File
@@ -17,6 +17,7 @@ import (
"github.com/tendermint/tendermint/libs/bits"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
tmmath "github.com/tendermint/tendermint/libs/math"
tmtime "github.com/tendermint/tendermint/libs/time"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/version"
)
@@ -94,6 +95,25 @@ func (b *Block) ValidateBasic() error {
return nil
}
// IsTimely validates that the block timestamp is 'timely' according to the proposer-based timestamp algorithm.
// To evaluate if a block is timely, its timestamp is compared to the local time of the validator along with the configured
// Precision and MsgDelay parameters.
// Specifically, a proposed block timestamp is considered timely if it is satisfies the following inequalities:
//
// proposedBlockTime < validatorLocalTime + Precision + MsgDelay && proposedBlockTime > validatorLocaltime - Precision.
//
// 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 (b *Block) IsTimely(clock tmtime.Source, p TimestampParams) bool {
lt := clock.Now()
lhs := lt.Add(-p.Precision).UnixMilli()
rhs := lt.Add(p.Precision).Add(p.MsgDelay).UnixMilli()
if lhs < b.Header.Time.UnixMilli() && b.Header.Time.UnixMilli() < rhs {
return true
}
return false
}
// fillHeader fills in any remaining header fields that are a function of the block data
func (b *Block) fillHeader() {
if b.LastCommitHash == nil {
+65
View File
@@ -24,6 +24,7 @@ import (
"github.com/tendermint/tendermint/libs/bytes"
tmrand "github.com/tendermint/tendermint/libs/rand"
tmtime "github.com/tendermint/tendermint/libs/time"
tmtimemocks "github.com/tendermint/tendermint/libs/time/mocks"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmversion "github.com/tendermint/tendermint/proto/tendermint/version"
"github.com/tendermint/tendermint/version"
@@ -1347,3 +1348,67 @@ func TestHeaderHashVector(t *testing.T) {
require.Equal(t, tc.expBytes, hex.EncodeToString(hash))
}
}
func TestIsTimely(t *testing.T) {
genesisTime, err := time.Parse(time.RFC3339, "2019-03-13T23:00:00Z")
require.NoError(t, err)
testCases := []struct {
name string
blockTime time.Time
localTime time.Time
precision time.Duration
msgDelay time.Duration
expectTimely bool
}{
{
// Checking that the following inequality evaluates to true:
// 1 - 2 < 0 < 1 + 2 + 1
name: "basic timely",
blockTime: genesisTime,
localTime: genesisTime.Add(1 * time.Millisecond),
precision: time.Millisecond * 2,
msgDelay: time.Millisecond,
expectTimely: true,
},
{
// Checking that the following inequality evaluates to false:
// 3 - 2 < 0 < 3 + 2 + 1
name: "local time too large",
blockTime: genesisTime,
localTime: genesisTime.Add(3 * time.Millisecond),
precision: time.Millisecond * 2,
msgDelay: time.Millisecond,
expectTimely: false,
},
{
// Checking that the following inequality evaluates to false:
// 0 - 2 < 2 < 2 + 1
name: "block time too large",
blockTime: genesisTime.Add(4 * time.Millisecond),
localTime: genesisTime,
precision: time.Millisecond * 2,
msgDelay: time.Millisecond,
expectTimely: false,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
b := Block{
Header: Header{
Time: testCase.blockTime,
},
}
p := TimestampParams{
Precision: testCase.precision,
MsgDelay: testCase.msgDelay,
}
mockSource := new(tmtimemocks.Source)
mockSource.On("Now").Return(testCase.localTime)
ti := b.IsTimely(mockSource, p)
assert.Equal(t, testCase.expectTimely, ti)
})
}
}
+6
View File
@@ -75,6 +75,12 @@ type VersionParams struct {
AppVersion uint64 `json:"app_version"`
}
type TimestampParams struct {
Precision time.Duration `json:"precision"`
Accuracy time.Duration `json:"accuracy"`
MsgDelay time.Duration `json:"msg_delay"`
}
// DefaultConsensusParams returns a default ConsensusParams.
func DefaultConsensusParams() *ConsensusParams {
return &ConsensusParams{