From cda3a1bbd0e937ee815a38f74288832e75412830 Mon Sep 17 00:00:00 2001 From: William Banfield Date: Wed, 22 Dec 2021 11:01:03 -0500 Subject: [PATCH] save proposal time on roundstate --- internal/consensus/state.go | 13 ++++++------- internal/consensus/types/round_state.go | 17 +++++++++-------- types/proposal.go | 10 ++++------ types/proposal_test.go | 14 +++++--------- 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/internal/consensus/state.go b/internal/consensus/state.go index 7b0ab3b19..e4fb2cf00 100644 --- a/internal/consensus/state.go +++ b/internal/consensus/state.go @@ -1311,13 +1311,13 @@ func (cs *State) enterPrevote(height int64, round int32) { // (so we have more time to try and collect +2/3 prevotes for a single block) } -func (cs *State) proposalIsTimely(proposal *types.Proposal) bool { +func (cs *State) proposalIsTimely() bool { tp := types.TimingParams{ Precision: cs.state.ConsensusParams.Timing.Precision, MessageDelay: cs.state.ConsensusParams.Timing.MessageDelay, } - return proposal.IsTimely(tmtime.DefaultSource{}, tp, cs.state.InitialHeight) + return cs.Proposal.IsTimely(cs.ProposalReceiveTime, tp, cs.state.InitialHeight) } func (cs *State) defaultDoPrevote(height int64, round int32) { @@ -1366,7 +1366,7 @@ func (cs *State) defaultDoPrevote(height int64, round int32) { */ if cs.Proposal.POLRound == -1 { if cs.LockedRound == -1 { - if !cs.Proposal.Valid { + if !cs.proposalIsTimely() { logger.Debug("prevote step: ProposalBlock is not timely; prevoting nil") cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{}) return @@ -1905,6 +1905,8 @@ func (cs *State) RecordMetrics(height int64, block *types.Block) { //----------------------------------------------------------------------------- func (cs *State) defaultSetProposal(proposal *types.Proposal) error { + recvTime := tmtime.Now() + // Already have one // TODO: possibly catch double proposals if cs.Proposal != nil || proposal == nil { @@ -1932,6 +1934,7 @@ func (cs *State) defaultSetProposal(proposal *types.Proposal) error { proposal.Signature = p.Signature cs.Proposal = proposal + cs.ProposalReceiveTime = recvTime // We don't update cs.ProposalBlockParts if it is already set. // This happens if we're already in cstypes.RoundStepCommit or if there is a valid block in the current round. // TODO: We can check if Proposal is for a different block as this is a sign of misbehavior! @@ -1939,10 +1942,6 @@ func (cs *State) defaultSetProposal(proposal *types.Proposal) error { cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockID.PartSetHeader) } - // Mark the proposal as invalid if too far in the past or future - if proposal.POLRound > -1 || cs.proposalIsTimely(proposal) { - cs.Proposal.Valid = true - } cs.Logger.Info("received proposal", "proposal", proposal) return nil } diff --git a/internal/consensus/types/round_state.go b/internal/consensus/types/round_state.go index 9e67b76c0..10cbec8cf 100644 --- a/internal/consensus/types/round_state.go +++ b/internal/consensus/types/round_state.go @@ -71,14 +71,15 @@ type RoundState struct { StartTime time.Time `json:"start_time"` // Subjective time when +2/3 precommits for Block at Round were found - CommitTime time.Time `json:"commit_time"` - Validators *types.ValidatorSet `json:"validators"` - Proposal *types.Proposal `json:"proposal"` - ProposalBlock *types.Block `json:"proposal_block"` - ProposalBlockParts *types.PartSet `json:"proposal_block_parts"` - LockedRound int32 `json:"locked_round"` - LockedBlock *types.Block `json:"locked_block"` - LockedBlockParts *types.PartSet `json:"locked_block_parts"` + CommitTime time.Time `json:"commit_time"` + Validators *types.ValidatorSet `json:"validators"` + Proposal *types.Proposal `json:"proposal"` + ProposalReceiveTime time.Time `json:"proposal_received_time"` + ProposalBlock *types.Block `json:"proposal_block"` + ProposalBlockParts *types.PartSet `json:"proposal_block_parts"` + LockedRound int32 `json:"locked_round"` + LockedBlock *types.Block `json:"locked_block"` + LockedBlockParts *types.PartSet `json:"locked_block_parts"` // Last known round with POL for non-nil valid block. ValidRound int32 `json:"valid_round"` diff --git a/types/proposal.go b/types/proposal.go index 323bfdf37..d32ab65dd 100644 --- a/types/proposal.go +++ b/types/proposal.go @@ -30,7 +30,6 @@ type Proposal struct { BlockID BlockID `json:"block_id"` Timestamp time.Time `json:"timestamp"` Signature []byte `json:"signature"` - Valid bool } // NewProposal returns a new Proposal. @@ -90,16 +89,15 @@ 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(clock tmtime.Source, tp TimingParams, initialHeight int64) bool { - localTime := clock.Now() +func (p *Proposal) IsTimely(recvTime time.Time, tp TimingParams, initialHeight int64) bool { // lhs is `proposedBlockTime - Precision` in the first inequality lhs := p.Timestamp.Add(-tp.Precision) // rhs is `proposedBlockTime + MsgDelay + Precision` in the second inequality rhs := p.Timestamp.Add(tp.MessageDelay).Add(tp.Precision) - localTimeAfterOrEqLHS := localTime.After(lhs) || localTime.Equal(lhs) - localTimeBeforeOrEqRHS := localTime.Before(rhs) || localTime.Equal(rhs) - if localTimeAfterOrEqLHS && (p.Height == initialHeight || localTimeBeforeOrEqRHS) { + recvTimeAfterOrEqLHS := recvTime.After(lhs) || recvTime.Equal(lhs) + recvTimeBeforeOrEqRHS := recvTime.Before(rhs) || recvTime.Equal(rhs) + if recvTimeAfterOrEqLHS && (p.Height == initialHeight || recvTimeBeforeOrEqRHS) { return true } return false diff --git a/types/proposal_test.go b/types/proposal_test.go index 2d6dcd6bf..dfd3eb712 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -14,7 +14,6 @@ import ( "github.com/tendermint/tendermint/internal/libs/protoio" 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" ) @@ -200,7 +199,7 @@ func TestIsTimely(t *testing.T) { testCases := []struct { name string proposalTime time.Time - localTime time.Time + recvTime time.Time precision time.Duration msgDelay time.Duration expectTimely bool @@ -211,7 +210,7 @@ func TestIsTimely(t *testing.T) { // 0 - 2 <= 1 <= 0 + 1 + 2 name: "basic timely", proposalTime: genesisTime, - localTime: genesisTime.Add(1 * time.Nanosecond), + recvTime: genesisTime.Add(1 * time.Nanosecond), precision: time.Nanosecond * 2, msgDelay: time.Nanosecond, expectTimely: true, @@ -221,7 +220,7 @@ func TestIsTimely(t *testing.T) { // 0 - 2 <= 4 <= 0 + 1 + 2 name: "local time too large", proposalTime: genesisTime, - localTime: genesisTime.Add(4 * time.Nanosecond), + recvTime: genesisTime.Add(4 * time.Nanosecond), precision: time.Nanosecond * 2, msgDelay: time.Nanosecond, expectTimely: false, @@ -231,7 +230,7 @@ func TestIsTimely(t *testing.T) { // 4 - 2 <= 0 <= 4 + 2 + 1 name: "proposal time too large", proposalTime: genesisTime.Add(4 * time.Nanosecond), - localTime: genesisTime, + recvTime: genesisTime, precision: time.Nanosecond * 2, msgDelay: time.Nanosecond, expectTimely: false, @@ -248,10 +247,7 @@ func TestIsTimely(t *testing.T) { MessageDelay: testCase.msgDelay, } - mockSource := new(tmtimemocks.Source) - mockSource.On("Now").Return(testCase.localTime) - - ti := p.IsTimely(mockSource, tp, 2) + ti := p.IsTimely(testCase.recvTime, tp, 2) assert.Equal(t, testCase.expectTimely, ti) }) }