mirror of
https://github.com/tendermint/tendermint.git
synced 2026-08-20 06:06:15 +00:00
Merge branch 'anca/fix_pbts_tests' into anca/prevote_nil_untimely
This commit is contained in:
@@ -214,7 +214,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
|
||||
|
||||
// Make proposal
|
||||
propBlockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()}
|
||||
proposal := types.NewProposal(height, round, lazyNodeState.ValidRound, propBlockID)
|
||||
proposal := types.NewProposal(height, round, lazyNodeState.ValidRound, propBlockID, block.Header.Time)
|
||||
p := proposal.ToProto()
|
||||
if err := lazyNodeState.privValidator.SignProposal(ctx, lazyNodeState.state.ChainID, p); err == nil {
|
||||
proposal.Signature = p.Signature
|
||||
|
||||
@@ -248,7 +248,7 @@ func decideProposal(
|
||||
|
||||
// Make proposal
|
||||
polRound, propBlockID := validRound, types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()}
|
||||
proposal = types.NewProposal(height, round, polRound, propBlockID)
|
||||
proposal = types.NewProposal(height, round, polRound, propBlockID, block.Header.Time)
|
||||
p := proposal.ToProto()
|
||||
if err := vs.SignProposal(ctx, chainID, p); err != nil {
|
||||
t.Fatalf("error signing proposal: %s", err)
|
||||
@@ -682,11 +682,11 @@ func ensureRelock(t *testing.T, relockCh <-chan tmpubsub.Message, height int64,
|
||||
}
|
||||
|
||||
func ensureProposal(t *testing.T, proposalCh <-chan tmpubsub.Message, height int64, round int32, propID types.BlockID) {
|
||||
ensureProposalWithTimeout(t, proposalCh, height, round, propID, ensureTimeout)
|
||||
ensureProposalWithTimeout(t, proposalCh, height, round, &propID, ensureTimeout)
|
||||
}
|
||||
|
||||
// nolint: lll
|
||||
func ensureProposalWithTimeout(t *testing.T, proposalCh <-chan tmpubsub.Message, height int64, round int32, propID types.BlockID, timeout time.Duration) {
|
||||
func ensureProposalWithTimeout(t *testing.T, proposalCh <-chan tmpubsub.Message, height int64, round int32, propID *types.BlockID, timeout time.Duration) {
|
||||
t.Helper()
|
||||
msg := ensureMessageBeforeTimeout(t, proposalCh, timeout)
|
||||
proposalEvent, ok := msg.Data().(types.EventDataCompleteProposal)
|
||||
@@ -700,10 +700,11 @@ func ensureProposalWithTimeout(t *testing.T, proposalCh <-chan tmpubsub.Message,
|
||||
if proposalEvent.Round != round {
|
||||
t.Fatalf("expected round %v, got %v", round, proposalEvent.Round)
|
||||
}
|
||||
if !proposalEvent.BlockID.Equals(propID) {
|
||||
t.Fatalf("Proposed block does not match expected block (%v != %v)", proposalEvent.BlockID, propID)
|
||||
if propID != nil && !proposalEvent.BlockID.Equals(*propID) {
|
||||
t.Fatalf("Proposed block does not match expected block (%v != %v)", proposalEvent.BlockID, *propID)
|
||||
}
|
||||
}
|
||||
|
||||
func ensurePrecommit(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, round int32) {
|
||||
t.Helper()
|
||||
ensureVote(t, voteCh, height, round, tmproto.PrecommitType)
|
||||
|
||||
@@ -147,11 +147,12 @@ func (p *pbtsTestHarness) observedValidatorProposerHeight(previousBlockTime time
|
||||
p.validatorClock.On("Now").Return(p.height2ProposedBlockTime).Times(6)
|
||||
|
||||
ensureNewRound(p.t, p.roundCh, p.currentHeight, p.currentRound)
|
||||
propBlock, partSet := p.observedState.createProposalBlock()
|
||||
bid := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: partSet.Header()}
|
||||
|
||||
timeout := time.Until(previousBlockTime.Add(ensureTimeout))
|
||||
ensureProposalWithTimeout(p.t, p.ensureProposalCh, p.currentHeight, p.currentRound, bid, timeout)
|
||||
ensureProposalWithTimeout(p.t, p.ensureProposalCh, p.currentHeight, p.currentRound, nil, timeout)
|
||||
|
||||
rs := p.observedState.GetRoundState()
|
||||
bid := types.BlockID{Hash: rs.ProposalBlock.Hash(), PartSetHeader: rs.ProposalBlockParts.Header()}
|
||||
ensurePrevote(p.t, p.ensureVoteCh, p.currentHeight, p.currentRound)
|
||||
signAddVotes(p.ctx, p.observedState, tmproto.PrevoteType, p.chainID, bid, p.otherValidators...)
|
||||
|
||||
@@ -204,7 +205,7 @@ func (p *pbtsTestHarness) nextHeight(proposer types.PrivValidator, deliverTime,
|
||||
b.Header.ProposerAddress = k.Address()
|
||||
ps := b.MakePartSet(types.BlockPartSizeBytes)
|
||||
bid := types.BlockID{Hash: b.Hash(), PartSetHeader: ps.Header()}
|
||||
prop := types.NewProposal(p.currentHeight, 0, -1, bid)
|
||||
prop := types.NewProposal(p.currentHeight, 0, -1, bid, proposedTime)
|
||||
tp := prop.ToProto()
|
||||
|
||||
if err := proposer.SignProposal(context.Background(), p.observedState.state.ChainID, tp); err != nil {
|
||||
@@ -299,8 +300,7 @@ func (p *pbtsTestHarness) run() resultSet {
|
||||
r2 := p.height2()
|
||||
p.intermediateHeights()
|
||||
r5 := p.height5()
|
||||
err := p.observedState.Stop()
|
||||
require.NoError(p.t, err)
|
||||
_ = p.observedState.Stop()
|
||||
return resultSet{
|
||||
genesisHeight: r1,
|
||||
height2: r2,
|
||||
@@ -324,81 +324,10 @@ func (hr heightResult) isComplete() bool {
|
||||
return !hr.proposalIssuedAt.IsZero() && !hr.prevoteIssuedAt.IsZero() && hr.prevote != nil
|
||||
}
|
||||
|
||||
// TestReceiveProposalWaitsForPreviousBlockTime tests that a validator receiving
|
||||
// a proposal waits until the previous block time passes before issuing a prevote.
|
||||
// The test delivers the block to the validator after the configured `timeout-propose`,
|
||||
// but before the proposer-based timestamp bound on block delivery and checks that
|
||||
// the consensus algorithm correctly waits for the new block to be delivered
|
||||
// and issues a prevote for it.
|
||||
func TestReceiveProposalWaitsForPreviousBlockTime(t *testing.T) {
|
||||
t.Skip()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
initialTime := time.Now().Add(50 * time.Millisecond)
|
||||
cfg := pbtsTestConfiguration{
|
||||
timingParams: types.TimingParams{
|
||||
Precision: 100 * time.Millisecond,
|
||||
MessageDelay: 500 * time.Millisecond,
|
||||
},
|
||||
timeoutPropose: 50 * time.Millisecond,
|
||||
genesisTime: initialTime,
|
||||
height2ProposalDeliverTime: initialTime.Add(450 * time.Millisecond),
|
||||
height2ProposedBlockTime: initialTime.Add(350 * time.Millisecond),
|
||||
}
|
||||
|
||||
pbtsTest := newPBTSTestHarness(ctx, t, cfg)
|
||||
results := pbtsTest.run()
|
||||
|
||||
// Check that the validator waited until after the proposer-based timestamp
|
||||
// waitingTime bound.
|
||||
assert.True(t, results.height2.prevoteIssuedAt.After(cfg.height2ProposalDeliverTime))
|
||||
maxWaitingTime := cfg.genesisTime.Add(cfg.timingParams.Precision).Add(cfg.timingParams.MessageDelay)
|
||||
assert.True(t, results.height2.prevoteIssuedAt.Before(maxWaitingTime))
|
||||
|
||||
// Check that the validator did not prevote for nil.
|
||||
assert.NotNil(t, results.height2.prevote.BlockID.Hash)
|
||||
}
|
||||
|
||||
// TestReceiveProposalTimesOutOnSlowDelivery tests that a validator receiving
|
||||
// a proposal times out and prevotes nil if the block is not delivered by the
|
||||
// within the proposer-based timestamp algorithm's waitingTime bound.
|
||||
// The test delivers the block to the validator after the previous block's time
|
||||
// and after the proposer-based timestamp bound on block delivery.
|
||||
// The test then checks that the validator correctly waited for the new block
|
||||
// and prevoted nil after timing out.
|
||||
func TestReceiveProposalTimesOutOnSlowDelivery(t *testing.T) {
|
||||
t.Skip()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
initialTime := time.Now()
|
||||
cfg := pbtsTestConfiguration{
|
||||
timingParams: types.TimingParams{
|
||||
Precision: 100 * time.Millisecond,
|
||||
MessageDelay: 500 * time.Millisecond,
|
||||
},
|
||||
timeoutPropose: 50 * time.Millisecond,
|
||||
genesisTime: initialTime,
|
||||
height2ProposalDeliverTime: initialTime.Add(660 * time.Millisecond),
|
||||
height2ProposedBlockTime: initialTime.Add(350 * time.Millisecond),
|
||||
}
|
||||
|
||||
pbtsTest := newPBTSTestHarness(ctx, t, cfg)
|
||||
results := pbtsTest.run()
|
||||
|
||||
// Check that the validator waited until after the proposer-based timestamp
|
||||
// waitinTime bound.
|
||||
maxWaitingTime := initialTime.Add(cfg.timingParams.Precision).Add(cfg.timingParams.MessageDelay)
|
||||
assert.True(t, results.height2.prevoteIssuedAt.After(maxWaitingTime))
|
||||
|
||||
// Ensure that the validator issued a prevote for nil.
|
||||
assert.Nil(t, results.height2.prevote.BlockID.Hash)
|
||||
}
|
||||
|
||||
// TestProposerWaitsForGenesisTime tests that a proposer will not propose a block
|
||||
// until after the genesis time has passed. The test sets the genesis time in the
|
||||
// future and then ensures that the observed validator waits to propose a block.
|
||||
func TestProposerWaitsForGenesisTime(t *testing.T) {
|
||||
t.Skip()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -429,7 +358,6 @@ func TestProposerWaitsForGenesisTime(t *testing.T) {
|
||||
// and then verifies that the observed validator waits until after the block time
|
||||
// of height 4 to propose a block at height 5.
|
||||
func TestProposerWaitsForPreviousBlock(t *testing.T) {
|
||||
t.Skip()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
initialTime := time.Now().Add(time.Millisecond * 50)
|
||||
@@ -495,56 +423,3 @@ func TestProposerWaitTime(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
mockSource := new(tmtimemocks.Source)
|
||||
mockSource.On("Now").Return(testCase.localTime)
|
||||
|
||||
tp := types.TimingParams{
|
||||
Precision: testCase.precision,
|
||||
MessageDelay: testCase.msgDelay,
|
||||
}
|
||||
|
||||
ti := proposalStepWaitingTime(mockSource, testCase.previousBlockTime, tp)
|
||||
assert.Equal(t, testCase.expectedDuration, ti)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +384,7 @@ func setupSimulator(ctx context.Context, t *testing.T) *simulatorTestSuite {
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
|
||||
proposal := types.NewProposal(vss[1].Height, round, -1, blockID)
|
||||
proposal := types.NewProposal(vss[1].Height, round, -1, blockID, propBlock.Header.Time)
|
||||
p := proposal.ToProto()
|
||||
if err := vss[1].SignProposal(ctx, cfg.ChainID(), p); err != nil {
|
||||
t.Fatal("failed to sign bad proposal", err)
|
||||
@@ -416,7 +416,7 @@ func setupSimulator(ctx context.Context, t *testing.T) *simulatorTestSuite {
|
||||
propBlockParts = propBlock.MakePartSet(partSize)
|
||||
blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
|
||||
proposal = types.NewProposal(vss[2].Height, round, -1, blockID)
|
||||
proposal = types.NewProposal(vss[2].Height, round, -1, blockID, propBlock.Header.Time)
|
||||
p = proposal.ToProto()
|
||||
if err := vss[2].SignProposal(ctx, cfg.ChainID(), p); err != nil {
|
||||
t.Fatal("failed to sign bad proposal", err)
|
||||
@@ -475,7 +475,7 @@ func setupSimulator(ctx context.Context, t *testing.T) *simulatorTestSuite {
|
||||
|
||||
selfIndex := valIndexFn(0)
|
||||
|
||||
proposal = types.NewProposal(vss[3].Height, round, -1, blockID)
|
||||
proposal = types.NewProposal(vss[3].Height, round, -1, blockID, propBlock.Header.Time)
|
||||
p = proposal.ToProto()
|
||||
if err := vss[3].SignProposal(ctx, cfg.ChainID(), p); err != nil {
|
||||
t.Fatal("failed to sign bad proposal", err)
|
||||
@@ -540,7 +540,7 @@ func setupSimulator(ctx context.Context, t *testing.T) *simulatorTestSuite {
|
||||
sort.Sort(ValidatorStubsByPower(newVss))
|
||||
|
||||
selfIndex = valIndexFn(0)
|
||||
proposal = types.NewProposal(vss[1].Height, round, -1, blockID)
|
||||
proposal = types.NewProposal(vss[1].Height, round, -1, blockID, propBlock.Header.Time)
|
||||
p = proposal.ToProto()
|
||||
if err := vss[1].SignProposal(ctx, cfg.ChainID(), p); err != nil {
|
||||
t.Fatal("failed to sign bad proposal", err)
|
||||
|
||||
+16
-35
@@ -1131,11 +1131,8 @@ func (cs *State) enterPropose(height int64, round int32) {
|
||||
}
|
||||
}()
|
||||
|
||||
waitingTime := proposalStepWaitingTime(tmtime.DefaultSource{}, cs.state.LastBlockTime, cs.state.ConsensusParams.Timing) // nolint: lll
|
||||
proposalTimeout := maxDuration(cs.config.Propose(round), waitingTime)
|
||||
|
||||
// If we don't get the proposal and all block parts quick enough, enterPrevote
|
||||
cs.scheduleTimeout(proposalTimeout, height, round, cstypes.RoundStepPropose)
|
||||
cs.scheduleTimeout(cs.config.Propose(round), height, round, cstypes.RoundStepPropose)
|
||||
|
||||
// Nothing more to do if we're not a validator
|
||||
if cs.privValidator == nil {
|
||||
@@ -1203,7 +1200,7 @@ func (cs *State) defaultDecideProposal(height int64, round int32) {
|
||||
|
||||
// Make proposal
|
||||
propBlockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()}
|
||||
proposal := types.NewProposal(height, round, cs.ValidRound, propBlockID)
|
||||
proposal := types.NewProposal(height, round, cs.ValidRound, propBlockID, block.Header.Time)
|
||||
p := proposal.ToProto()
|
||||
|
||||
// wait the max amount we would wait for a proposal
|
||||
@@ -1325,6 +1322,12 @@ func (cs *State) defaultDoPrevote(height int64, round int32) {
|
||||
return
|
||||
}
|
||||
|
||||
if !cs.Proposal.Timestamp.Equal(cs.ProposalBlock.Header.Time) {
|
||||
logger.Debug("proposal timestamp not equal, prevoting nil")
|
||||
cs.signAddVote(tmproto.PrevoteType, nil, types.PartSetHeader{})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate proposal block
|
||||
err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock)
|
||||
if err != nil {
|
||||
@@ -1349,6 +1352,7 @@ func (cs *State) defaultDoPrevote(height int64, round int32) {
|
||||
*/
|
||||
if cs.Proposal.POLRound == -1 {
|
||||
if cs.LockedRound == -1 {
|
||||
// TODO(@wbanfield) add check for timely here as well
|
||||
logger.Debug("prevote step: ProposalBlock is valid and there is no locked block; prevoting the proposal")
|
||||
cs.signAddVote(tmproto.PrevoteType, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header())
|
||||
return
|
||||
@@ -1484,9 +1488,15 @@ func (cs *State) enterPrecommit(height int64, round int32) {
|
||||
cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{})
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, +2/3 prevoted for a particular block.
|
||||
|
||||
// If the proposal time does not match the block time, precommit nil.
|
||||
if !cs.Proposal.Timestamp.Equal(cs.ProposalBlock.Header.Time) {
|
||||
logger.Debug("proposal timestamp not equal, precommitting nil")
|
||||
cs.signAddVote(tmproto.PrecommitType, nil, types.PartSetHeader{})
|
||||
return
|
||||
}
|
||||
|
||||
// If we're already locked on that block, precommit it, and update the LockedRound
|
||||
if cs.LockedBlock.HashesTo(blockID.Hash) {
|
||||
logger.Debug("precommit step; +2/3 prevoted locked block; relocking")
|
||||
@@ -2454,32 +2464,3 @@ func proposerWaitTime(lt tmtime.Source, bt time.Time) time.Duration {
|
||||
}
|
||||
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, bt time.Time, tp types.TimingParams) time.Duration {
|
||||
t := lt.Now()
|
||||
wt := bt.Add(tp.Precision).Add(tp.MessageDelay)
|
||||
if t.After(wt) {
|
||||
return 0
|
||||
}
|
||||
return wt.Sub(t)
|
||||
}
|
||||
|
||||
func maxDuration(d1, d2 time.Duration) time.Duration {
|
||||
if d1 >= d2 {
|
||||
return d1
|
||||
}
|
||||
return d2
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ func TestStateBadProposal(t *testing.T) {
|
||||
propBlock.AppHash = stateHash
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
proposal := types.NewProposal(vs2.Height, round, -1, blockID)
|
||||
proposal := types.NewProposal(vs2.Height, round, -1, blockID, propBlock.Header.Time)
|
||||
p := proposal.ToProto()
|
||||
if err := vs2.SignProposal(ctx, config.ChainID(), p); err != nil {
|
||||
t.Fatal("failed to sign bad proposal", err)
|
||||
@@ -306,7 +306,7 @@ func TestStateOversizedBlock(t *testing.T) {
|
||||
|
||||
propBlockParts := propBlock.MakePartSet(partSize)
|
||||
blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
proposal := types.NewProposal(height, round, -1, blockID)
|
||||
proposal := types.NewProposal(height, round, -1, blockID, propBlock.Header.Time)
|
||||
p := proposal.ToProto()
|
||||
if err := vs2.SignProposal(ctx, config.ChainID(), p); err != nil {
|
||||
t.Fatal("failed to sign bad proposal", err)
|
||||
@@ -856,7 +856,7 @@ func TestStateLock_POLRelock(t *testing.T) {
|
||||
t.Log("### Starting Round 1")
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
round++
|
||||
propR1 := types.NewProposal(height, round, cs1.ValidRound, blockID)
|
||||
propR1 := types.NewProposal(height, round, cs1.ValidRound, blockID, theBlock.Header.Time)
|
||||
p := propR1.ToProto()
|
||||
if err := vs2.SignProposal(ctx, cs1.state.ChainID, p); err != nil {
|
||||
t.Fatalf("error signing proposal: %s", err)
|
||||
@@ -1588,7 +1588,7 @@ func TestStateLock_POLSafety2(t *testing.T) {
|
||||
|
||||
round++ // moving to the next round
|
||||
// in round 2 we see the polkad block from round 0
|
||||
newProp := types.NewProposal(height, round, 0, propBlockID0)
|
||||
newProp := types.NewProposal(height, round, 0, propBlockID0, propBlock0.Header.Time)
|
||||
p := newProp.ToProto()
|
||||
if err := vs3.SignProposal(ctx, config.ChainID(), p); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1730,7 +1730,7 @@ func TestState_PrevotePOLFromPreviousRound(t *testing.T) {
|
||||
t.Log("### Starting Round 2")
|
||||
incrementRound(vs2, vs3, vs4)
|
||||
round++
|
||||
propR2 := types.NewProposal(height, round, 1, r1BlockID)
|
||||
propR2 := types.NewProposal(height, round, 1, r1BlockID, propBlockR1.Header.Time)
|
||||
p := propR2.ToProto()
|
||||
if err := vs3.SignProposal(ctx, cs1.state.ChainID, p); err != nil {
|
||||
t.Fatalf("error signing proposal: %s", err)
|
||||
@@ -2649,6 +2649,104 @@ func TestSignSameVoteTwice(t *testing.T) {
|
||||
require.Equal(t, vote, vote2)
|
||||
}
|
||||
|
||||
// TestStateTimestamp_ProposalNotMatch tests that a validator does not prevote a
|
||||
// proposed block if the timestamp in the block does not matche the timestamp in the
|
||||
// corresponding proposal message.
|
||||
func TestStateTimestamp_ProposalNotMatch(t *testing.T) {
|
||||
config := configSetup(t)
|
||||
logger := log.TestingLogger()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
cs1, vss, err := makeState(ctx, config, logger, 4)
|
||||
require.NoError(t, err)
|
||||
height, round := cs1.Height, cs1.Round
|
||||
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
|
||||
|
||||
proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
pv1, err := cs1.privValidator.GetPubKey(ctx)
|
||||
require.NoError(t, err)
|
||||
addr := pv1.Address()
|
||||
voteCh := subscribeToVoter(ctx, t, cs1, addr)
|
||||
|
||||
propBlock, _ := cs1.createProposalBlock()
|
||||
round++
|
||||
incrementRound(vss[1:]...)
|
||||
|
||||
propBlockParts := propBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
|
||||
// Create a proposal with a timestamp that does not match the timestamp of the block.
|
||||
proposal := types.NewProposal(vs2.Height, round, -1, blockID, propBlock.Header.Time.Add(time.Millisecond))
|
||||
p := proposal.ToProto()
|
||||
if err := vs2.SignProposal(ctx, config.ChainID(), p); err != nil {
|
||||
t.Fatal("failed to sign bad proposal", err)
|
||||
}
|
||||
proposal.Signature = p.Signature
|
||||
require.NoError(t, cs1.SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"))
|
||||
|
||||
startTestRound(ctx, cs1, height, round)
|
||||
ensureProposal(t, proposalCh, height, round, blockID)
|
||||
|
||||
signAddVotes(ctx, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vs2, vs3, vs4)
|
||||
|
||||
// ensure that the validator prevotes nil.
|
||||
ensurePrevote(t, voteCh, height, round)
|
||||
validatePrevote(ctx, t, cs1, round, vss[0], nil)
|
||||
|
||||
ensurePrecommit(t, voteCh, height, round)
|
||||
validatePrecommit(ctx, t, cs1, round, -1, vss[0], nil, nil)
|
||||
}
|
||||
|
||||
// TestStateTimestamp_ProposalMatch tests that a validator prevotes a
|
||||
// proposed block if the timestamp in the block matches the timestamp in the
|
||||
// corresponding proposal message.
|
||||
func TestStateTimestamp_ProposalMatch(t *testing.T) {
|
||||
config := configSetup(t)
|
||||
logger := log.TestingLogger()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
cs1, vss, err := makeState(ctx, config, logger, 4)
|
||||
require.NoError(t, err)
|
||||
height, round := cs1.Height, cs1.Round
|
||||
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
|
||||
|
||||
proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
pv1, err := cs1.privValidator.GetPubKey(ctx)
|
||||
require.NoError(t, err)
|
||||
addr := pv1.Address()
|
||||
voteCh := subscribeToVoter(ctx, t, cs1, addr)
|
||||
|
||||
propBlock, _ := cs1.createProposalBlock()
|
||||
round++
|
||||
incrementRound(vss[1:]...)
|
||||
|
||||
propBlockParts := propBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()}
|
||||
|
||||
// Create a proposal with a timestamp that matches the timestamp of the block.
|
||||
proposal := types.NewProposal(vs2.Height, round, -1, blockID, propBlock.Header.Time)
|
||||
p := proposal.ToProto()
|
||||
if err := vs2.SignProposal(ctx, config.ChainID(), p); err != nil {
|
||||
t.Fatal("failed to sign bad proposal", err)
|
||||
}
|
||||
proposal.Signature = p.Signature
|
||||
require.NoError(t, cs1.SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"))
|
||||
|
||||
startTestRound(ctx, cs1, height, round)
|
||||
ensureProposal(t, proposalCh, height, round, blockID)
|
||||
|
||||
signAddVotes(ctx, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vs2, vs3, vs4)
|
||||
|
||||
// ensure that the validator prevotes the block.
|
||||
ensurePrevote(t, voteCh, height, round)
|
||||
validatePrevote(ctx, t, cs1, round, vss[0], propBlock.Hash())
|
||||
|
||||
ensurePrecommit(t, voteCh, height, round)
|
||||
validatePrecommit(ctx, t, cs1, round, 1, vss[0], propBlock.Hash(), propBlock.Hash())
|
||||
}
|
||||
|
||||
// subscribe subscribes test client to the given query and returns a channel with cap = 1.
|
||||
func subscribe(
|
||||
ctx context.Context,
|
||||
|
||||
+2
-2
@@ -34,14 +34,14 @@ type Proposal struct {
|
||||
|
||||
// NewProposal returns a new Proposal.
|
||||
// If there is no POLRound, polRound should be -1.
|
||||
func NewProposal(height int64, round int32, polRound int32, blockID BlockID) *Proposal {
|
||||
func NewProposal(height int64, round int32, polRound int32, blockID BlockID, ts time.Time) *Proposal {
|
||||
return &Proposal{
|
||||
Type: tmproto.ProposalType,
|
||||
Height: height,
|
||||
Round: round,
|
||||
BlockID: blockID,
|
||||
POLRound: polRound,
|
||||
Timestamp: tmtime.Now(),
|
||||
Timestamp: tmtime.Canonical(ts),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"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"
|
||||
)
|
||||
@@ -63,7 +64,7 @@ func TestProposalVerifySignature(t *testing.T) {
|
||||
|
||||
prop := NewProposal(
|
||||
4, 2, 2,
|
||||
BlockID{tmrand.Bytes(tmhash.Size), PartSetHeader{777, tmrand.Bytes(tmhash.Size)}})
|
||||
BlockID{tmrand.Bytes(tmhash.Size), PartSetHeader{777, tmrand.Bytes(tmhash.Size)}}, tmtime.Now())
|
||||
p := prop.ToProto()
|
||||
signBytes := ProposalSignBytes("test_chain_id", p)
|
||||
|
||||
@@ -154,7 +155,7 @@ func TestProposalValidateBasic(t *testing.T) {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
prop := NewProposal(
|
||||
4, 2, 2,
|
||||
blockID)
|
||||
blockID, tmtime.Now())
|
||||
p := prop.ToProto()
|
||||
err := privVal.SignProposal(context.Background(), "test_chain_id", p)
|
||||
prop.Signature = p.Signature
|
||||
@@ -166,9 +167,9 @@ func TestProposalValidateBasic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProposalProtoBuf(t *testing.T) {
|
||||
proposal := NewProposal(1, 2, 3, makeBlockID([]byte("hash"), 2, []byte("part_set_hash")))
|
||||
proposal := NewProposal(1, 2, 3, makeBlockID([]byte("hash"), 2, []byte("part_set_hash")), tmtime.Now())
|
||||
proposal.Signature = []byte("sig")
|
||||
proposal2 := NewProposal(1, 2, 3, BlockID{})
|
||||
proposal2 := NewProposal(1, 2, 3, BlockID{}, tmtime.Now())
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
|
||||
Reference in New Issue
Block a user