Compare commits

...

9 Commits

Author SHA1 Message Date
William Banfield
57722da6ec move tests to separate pbts test file 2021-11-23 15:02:12 -05:00
William Banfield
6ee6f349a1 change accuracy to precision 2021-11-23 10:12:31 -05:00
William Banfield
00cbab12c9 reword comment 2021-11-18 10:17:19 -05:00
William Banfield
c7c6f40b84 Update internal/consensus/state.go
Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
2021-11-18 09:52:28 -05:00
William Banfield
792f250a6d minor language fix 2021-11-16 10:00:59 -05:00
William Banfield
f36f94e5e9 minor aesthetic change to IsTimely 2021-11-16 09:58:21 -05:00
William Banfield
4a5e6da299 add proposal step waiting time with tests 2021-11-15 16:45:52 -05:00
William Banfield
12eb73d51d switch to duration for easier use with timeout scheduling 2021-11-14 17:09:35 -05:00
William Banfield
6f173b8bba initial proposerWaitsUntil implementation 2021-11-14 16:42:30 -05:00
3 changed files with 152 additions and 1 deletions

View File

@@ -0,0 +1,115 @@
package consensus
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
tmtimemocks "github.com/tendermint/tendermint/libs/time/mocks"
"github.com/tendermint/tendermint/types"
)
func TestProposerWaitTime(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
expectedWait time.Duration
}{
{
name: "block time greater than local time",
blockTime: genesisTime.Add(5 * time.Nanosecond),
localTime: genesisTime.Add(1 * time.Nanosecond),
expectedWait: 4 * time.Nanosecond,
},
{
name: "local time greater than block time",
blockTime: genesisTime.Add(1 * time.Nanosecond),
localTime: genesisTime.Add(5 * time.Nanosecond),
expectedWait: 0,
},
{
name: "both times equal",
blockTime: genesisTime.Add(5 * time.Nanosecond),
localTime: genesisTime.Add(5 * time.Nanosecond),
expectedWait: 0,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
b := types.Block{
Header: types.Header{
Time: testCase.blockTime,
},
}
mockSource := new(tmtimemocks.Source)
mockSource.On("Now").Return(testCase.localTime)
ti := proposerWaitTime(mockSource, b.Header)
assert.Equal(t, testCase.expectedWait, ti)
})
}
}
func TestProposalTimeout(t *testing.T) {
genesisTime, err := time.Parse(time.RFC3339, "2019-03-13T23:00:00Z")
require.NoError(t, err)
testCases := []struct {
name string
localTime time.Time
previousBlockTime time.Time
precision time.Duration
msgDelay time.Duration
expectedDuration time.Duration
}{
{
name: "MsgDelay + Precision has not quite elapsed",
localTime: genesisTime.Add(525 * time.Millisecond),
previousBlockTime: genesisTime.Add(6 * time.Millisecond),
precision: time.Millisecond * 20,
msgDelay: time.Millisecond * 500,
expectedDuration: 1 * time.Millisecond,
},
{
name: "MsgDelay + Precision equals current time",
localTime: genesisTime.Add(525 * time.Millisecond),
previousBlockTime: genesisTime.Add(5 * time.Millisecond),
precision: time.Millisecond * 20,
msgDelay: time.Millisecond * 500,
expectedDuration: 0,
},
{
name: "MsgDelay + Precision has elapsed",
localTime: genesisTime.Add(725 * time.Millisecond),
previousBlockTime: genesisTime.Add(5 * time.Millisecond),
precision: time.Millisecond * 20,
msgDelay: time.Millisecond * 500,
expectedDuration: 0,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
b := types.Block{
Header: types.Header{
Time: testCase.previousBlockTime,
},
}
mockSource := new(tmtimemocks.Source)
mockSource.On("Now").Return(testCase.localTime)
tp := types.TimestampParams{
Precision: testCase.precision,
MsgDelay: testCase.msgDelay,
}
ti := proposalStepWaitingTime(mockSource, b.Header, tp)
assert.Equal(t, testCase.expectedDuration, ti)
})
}
}

View File

@@ -2412,3 +2412,39 @@ func repairWalFile(src, dst string) error {
return nil
}
// proposerWaitTime determines how long the proposer should wait to propose its next block.
// If the result is zero, a block can be proposed immediately.
//
// Block times must be monotonically increasing, so if the block time of the previous
// block is larger than the proposer's current time, then the proposer will sleep
// until its local clock exceeds the previous block time.
func proposerWaitTime(lt tmtime.Source, h types.Header) time.Duration {
t := lt.Now()
if h.Time.After(t) {
return h.Time.Sub(t)
}
return 0
}
// proposalStepWaitingTime is used along with the `timeout-propose` configuration
// parameter to determines how long a validator will wait for a block to be sent from a proposer.
// proposalStepWaitingTime ensures that the validator waits long enough for the proposer to
// deliver a block with a monotically increasing timestamp.
//
// To ensure that the validator waits long enough, it must wait until the previous
// block's timestamp. It also must account for the difference between its own clock and
// the proposer's clock, i.e. the 'Precision', and the amount of time for the message to be transmitted,
// i.e. the MsgDelay.
//
// The result of proposalStepWaitingTime is compared with the configured `timeout-propose` duration,
// and the validator waits for whichever duration is larger before advancing to the next step
// and prevoting nil.
func proposalStepWaitingTime(lt tmtime.Source, h types.Header, tp types.TimestampParams) time.Duration {
t := lt.Now()
wt := h.Time.Add(tp.Precision).Add(tp.MsgDelay)
if t.After(wt) {
return 0
}
return wt.Sub(t)
}

View File

@@ -92,7 +92,7 @@ func (p *Proposal) IsTimely(clock tmtime.Source, tp TimestampParams) bool {
lt := clock.Now()
lhs := lt.Add(-tp.Precision)
rhs := lt.Add(tp.Precision).Add(tp.MsgDelay)
if lhs.Before(p.Timestamp) && p.Timestamp.Before(rhs) {
if lhs.Before(p.Timestamp) && rhs.After(p.Timestamp) {
return true
}
return false