package consensus
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gogo/protobuf/proto"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/tendermint/tendermint/crypto/merkle"
|
|
"github.com/tendermint/tendermint/crypto/tmhash"
|
|
cstypes "github.com/tendermint/tendermint/internal/consensus/types"
|
|
"github.com/tendermint/tendermint/internal/test/factory"
|
|
"github.com/tendermint/tendermint/libs/bits"
|
|
"github.com/tendermint/tendermint/libs/bytes"
|
|
tmrand "github.com/tendermint/tendermint/libs/rand"
|
|
tmcons "github.com/tendermint/tendermint/proto/tendermint/consensus"
|
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
"github.com/tendermint/tendermint/types"
|
|
)
|
|
|
|
func TestMsgToProto(t *testing.T) {
|
|
psh := types.PartSetHeader{
|
|
Total: 1,
|
|
Hash: tmrand.Bytes(32),
|
|
}
|
|
pbPsh := psh.ToProto()
|
|
bi := types.BlockID{
|
|
Hash: tmrand.Bytes(32),
|
|
PartSetHeader: psh,
|
|
}
|
|
pbBi := bi.ToProto()
|
|
bits := bits.NewBitArray(1)
|
|
pbBits := bits.ToProto()
|
|
|
|
parts := types.Part{
|
|
Index: 1,
|
|
Bytes: []byte("test"),
|
|
Proof: merkle.Proof{
|
|
Total: 1,
|
|
Index: 1,
|
|
LeafHash: tmrand.Bytes(32),
|
|
Aunts: [][]byte{},
|
|
},
|
|
}
|
|
pbParts, err := parts.ToProto()
|
|
require.NoError(t, err)
|
|
|
|
proposal := types.Proposal{
|
|
Type: tmproto.ProposalType,
|
|
Height: 1,
|
|
Round: 1,
|
|
POLRound: 1,
|
|
BlockID: bi,
|
|
Timestamp: time.Now(),
|
|
Signature: tmrand.Bytes(20),
|
|
}
|
|
pbProposal := proposal.ToProto()
|
|
|
|
pv := types.NewMockPV()
|
|
vote, err := factory.MakeVote(pv, factory.DefaultTestChainID,
|
|
0, 1, 0, 2, types.BlockID{}, time.Now())
|
|
require.NoError(t, err)
|
|
pbVote := vote.ToProto()
|
|
|
|
testsCases := []struct {
|
|
testName string
|
|
msg Message
|
|
want *tmcons.Message
|
|
wantErr bool
|
|
}{
|
|
{"successful NewRoundStepMessage", &NewRoundStepMessage{
|
|
Height: 2,
|
|
Round: 1,
|
|
Step: 1,
|
|
SecondsSinceStartTime: 1,
|
|
LastCommitRound: 2,
|
|
}, &tmcons.Message{
|
|
Sum: &tmcons.Message_NewRoundStep{
|
|
NewRoundStep: &tmcons.NewRoundStep{
|
|
Height: 2,
|
|
Round: 1,
|
|
Step: 1,
|
|
SecondsSinceStartTime: 1,
|
|
LastCommitRound: 2,
|
|
},
|
|
},
|
|
}, false},
|
|
|
|
{"successful NewValidBlockMessage", &NewValidBlockMessage{
|
|
Height: 1,
|
|
Round: 1,
|
|
BlockPartSetHeader: psh,
|
|
BlockParts: bits,
|
|
IsCommit: false,
|
|
}, &tmcons.Message{
|
|
Sum: &tmcons.Message_NewValidBlock{
|
|
NewValidBlock: &tmcons.NewValidBlock{
|
|
Height: 1,
|
|
Round: 1,
|
|
BlockPartSetHeader: pbPsh,
|
|
BlockParts: pbBits,
|
|
IsCommit: false,
|
|
},
|
|
},
|
|
}, false},
|
|
{"successful BlockPartMessage", &BlockPartMessage{
|
|
Height: 100,
|
|
Round: 1,
|
|
Part: &parts,
|
|
}, &tmcons.Message{
|
|
Sum: &tmcons.Message_BlockPart{
|
|
BlockPart: &tmcons.BlockPart{
|
|
Height: 100,
|
|
Round: 1,
|
|
Part: *pbParts,
|
|
},
|
|
},
|
|
}, false},
|
|
{"successful ProposalPOLMessage", &ProposalPOLMessage{
|
|
Height: 1,
|
|
ProposalPOLRound: 1,
|
|
ProposalPOL: bits,
|
|
}, &tmcons.Message{
|
|
Sum: &tmcons.Message_ProposalPol{
|
|
ProposalPol: &tmcons.ProposalPOL{
|
|
Height: 1,
|
|
ProposalPolRound: 1,
|
|
ProposalPol: *pbBits,
|
|
},
|
|
}}, false},
|
|
{"successful ProposalMessage", &ProposalMessage{
|
|
Proposal: &proposal,
|
|
}, &tmcons.Message{
|
|
Sum: &tmcons.Message_Proposal{
|
|
Proposal: &tmcons.Proposal{
|
|
Proposal: *pbProposal,
|
|
},
|
|
},
|
|
}, false},
|
|
{"successful VoteMessage", &VoteMessage{
|
|
Vote: vote,
|
|
}, &tmcons.Message{
|
|
Sum: &tmcons.Message_Vote{
|
|
Vote: &tmcons.Vote{
|
|
Vote: pbVote,
|
|
},
|
|
},
|
|
}, false},
|
|
{"successful VoteSetMaj23", &VoteSetMaj23Message{
|
|
Height: 1,
|
|
Round: 1,
|
|
Type: 1,
|
|
BlockID: bi,
|
|
}, &tmcons.Message{
|
|
Sum: &tmcons.Message_VoteSetMaj23{
|
|
VoteSetMaj23: &tmcons.VoteSetMaj23{
|
|
Height: 1,
|
|
Round: 1,
|
|
Type: 1,
|
|
BlockID: pbBi,
|
|
},
|
|
},
|
|
}, false},
|
|
{"successful VoteSetBits", &VoteSetBitsMessage{
|
|
Height: 1,
|
|
Round: 1,
|
|
Type: 1,
|
|
BlockID: bi,
|
|
Votes: bits,
|
|
}, &tmcons.Message{
|
|
Sum: &tmcons.Message_VoteSetBits{
|
|
VoteSetBits: &tmcons.VoteSetBits{
|
|
Height: 1,
|
|
Round: 1,
|
|
Type: 1,
|
|
BlockID: pbBi,
|
|
Votes: *pbBits,
|
|
},
|
|
},
|
|
}, false},
|
|
{"failure", nil, &tmcons.Message{}, true},
|
|
}
|
|
for _, tt := range testsCases {
|
|
tt := tt
|
|
t.Run(tt.testName, func(t *testing.T) {
|
|
pb, err := MsgToProto(tt.msg)
|
|
if tt.wantErr == true {
|
|
assert.Equal(t, err != nil, tt.wantErr)
|
|
return
|
|
}
|
|
assert.EqualValues(t, tt.want, pb, tt.testName)
|
|
|
|
msg, err := MsgFromProto(pb)
|
|
|
|
if !tt.wantErr {
|
|
require.NoError(t, err)
|
|
bcm := assert.Equal(t, tt.msg, msg, tt.testName)
|
|
assert.True(t, bcm, tt.testName)
|
|
} else {
|
|
require.Error(t, err, tt.testName)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWALMsgProto(t *testing.T) {
|
|
|
|
parts := types.Part{
|
|
Index: 1,
|
|
Bytes: []byte("test"),
|
|
Proof: merkle.Proof{
|
|
Total: 1,
|
|
Index: 1,
|
|
LeafHash: tmrand.Bytes(32),
|
|
Aunts: [][]byte{},
|
|
},
|
|
}
|
|
pbParts, err := parts.ToProto()
|
|
require.NoError(t, err)
|
|
|
|
testsCases := []struct {
|
|
testName string
|
|
msg WALMessage
|
|
want *tmcons.WALMessage
|
|
wantErr bool
|
|
}{
|
|
{"successful EventDataRoundState", types.EventDataRoundState{
|
|
Height: 2,
|
|
Round: 1,
|
|
Step: "ronies",
|
|
}, &tmcons.WALMessage{
|
|
Sum: &tmcons.WALMessage_EventDataRoundState{
|
|
EventDataRoundState: &tmproto.EventDataRoundState{
|
|
Height: 2,
|
|
Round: 1,
|
|
Step: "ronies",
|
|
},
|
|
},
|
|
}, false},
|
|
{"successful msgInfo", msgInfo{
|
|
Msg: &BlockPartMessage{
|
|
Height: 100,
|
|
Round: 1,
|
|
Part: &parts,
|
|
},
|
|
PeerID: types.NodeID("string"),
|
|
}, &tmcons.WALMessage{
|
|
Sum: &tmcons.WALMessage_MsgInfo{
|
|
MsgInfo: &tmcons.MsgInfo{
|
|
Msg: tmcons.Message{
|
|
Sum: &tmcons.Message_BlockPart{
|
|
BlockPart: &tmcons.BlockPart{
|
|
Height: 100,
|
|
Round: 1,
|
|
Part: *pbParts,
|
|
},
|
|
},
|
|
},
|
|
PeerID: "string",
|
|
},
|
|
},
|
|
}, false},
|
|
{"successful timeoutInfo", timeoutInfo{
|
|
Duration: time.Duration(100),
|
|
Height: 1,
|
|
Round: 1,
|
|
Step: 1,
|
|
}, &tmcons.WALMessage{
|
|
Sum: &tmcons.WALMessage_TimeoutInfo{
|
|
TimeoutInfo: &tmcons.TimeoutInfo{
|
|
Duration: time.Duration(100),
|
|
Height: 1,
|
|
Round: 1,
|
|
Step: 1,
|
|
},
|
|
},
|
|
}, false},
|
|
{"successful EndHeightMessage", EndHeightMessage{
|
|
Height: 1,
|
|
}, &tmcons.WALMessage{
|
|
Sum: &tmcons.WALMessage_EndHeight{
|
|
EndHeight: &tmcons.EndHeight{
|
|
Height: 1,
|
|
},
|
|
},
|
|
}, false},
|
|
{"failure", nil, &tmcons.WALMessage{}, true},
|
|
}
|
|
for _, tt := range testsCases {
|
|
tt := tt
|
|
t.Run(tt.testName, func(t *testing.T) {
|
|
pb, err := WALToProto(tt.msg)
|
|
if tt.wantErr == true {
|
|
assert.Equal(t, err != nil, tt.wantErr)
|
|
return
|
|
}
|
|
assert.EqualValues(t, tt.want, pb, tt.testName)
|
|
|
|
msg, err := WALFromProto(pb)
|
|
|
|
if !tt.wantErr {
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tt.msg, msg, tt.testName) // need the concrete type as WAL Message is a empty interface
|
|
} else {
|
|
require.Error(t, err, tt.testName)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// nolint:lll //ignore line length for tests
|
|
func TestConsMsgsVectors(t *testing.T) {
|
|
date := time.Date(2018, 8, 30, 12, 0, 0, 0, time.UTC)
|
|
psh := types.PartSetHeader{
|
|
Total: 1,
|
|
Hash: []byte("add_more_exclamation_marks_code-"),
|
|
}
|
|
pbPsh := psh.ToProto()
|
|
|
|
bi := types.BlockID{
|
|
Hash: []byte("add_more_exclamation_marks_code-"),
|
|
PartSetHeader: psh,
|
|
}
|
|
pbBi := bi.ToProto()
|
|
bits := bits.NewBitArray(1)
|
|
pbBits := bits.ToProto()
|
|
|
|
parts := types.Part{
|
|
Index: 1,
|
|
Bytes: []byte("test"),
|
|
Proof: merkle.Proof{
|
|
Total: 1,
|
|
Index: 1,
|
|
LeafHash: []byte("add_more_exclamation_marks_code-"),
|
|
Aunts: [][]byte{},
|
|
},
|
|
}
|
|
pbParts, err := parts.ToProto()
|
|
require.NoError(t, err)
|
|
|
|
proposal := types.Proposal{
|
|
Type: tmproto.ProposalType,
|
|
Height: 1,
|
|
Round: 1,
|
|
POLRound: 1,
|
|
BlockID: bi,
|
|
Timestamp: date,
|
|
Signature: []byte("add_more_exclamation"),
|
|
}
|
|
pbProposal := proposal.ToProto()
|
|
|
|
v := &types.Vote{
|
|
ValidatorAddress: []byte("add_more_exclamation"),
|
|
ValidatorIndex: 1,
|
|
Height: 1,
|
|
Round: 0,
|
|
Timestamp: date,
|
|
Type: tmproto.PrecommitType,
|
|
BlockID: bi,
|
|
}
|
|
vpb := v.ToProto()
|
|
|
|
testCases := []struct {
|
|
testName string
|
|
cMsg proto.Message
|
|
expBytes string
|
|
}{
|
|
{"NewRoundStep", &tmcons.Message{Sum: &tmcons.Message_NewRoundStep{NewRoundStep: &tmcons.NewRoundStep{
|
|
Height: 1,
|
|
Round: 1,
|
|
Step: 1,
|
|
SecondsSinceStartTime: 1,
|
|
LastCommitRound: 1,
|
|
}}}, "0a0a08011001180120012801"},
|
|
{"NewRoundStep Max", &tmcons.Message{Sum: &tmcons.Message_NewRoundStep{NewRoundStep: &tmcons.NewRoundStep{
|
|
Height: math.MaxInt64,
|
|
Round: math.MaxInt32,
|
|
Step: math.MaxUint32,
|
|
SecondsSinceStartTime: math.MaxInt64,
|
|
LastCommitRound: math.MaxInt32,
|
|
}}}, "0a2608ffffffffffffffff7f10ffffffff0718ffffffff0f20ffffffffffffffff7f28ffffffff07"},
|
|
{"NewValidBlock", &tmcons.Message{Sum: &tmcons.Message_NewValidBlock{
|
|
NewValidBlock: &tmcons.NewValidBlock{
|
|
Height: 1, Round: 1, BlockPartSetHeader: pbPsh, BlockParts: pbBits, IsCommit: false}}},
|
|
"1231080110011a24080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d22050801120100"},
|
|
{"Proposal", &tmcons.Message{Sum: &tmcons.Message_Proposal{Proposal: &tmcons.Proposal{Proposal: *pbProposal}}},
|
|
"1a720a7008201001180120012a480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d320608c0b89fdc053a146164645f6d6f72655f6578636c616d6174696f6e"},
|
|
{"ProposalPol", &tmcons.Message{Sum: &tmcons.Message_ProposalPol{
|
|
ProposalPol: &tmcons.ProposalPOL{Height: 1, ProposalPolRound: 1}}},
|
|
"2206080110011a00"},
|
|
{"BlockPart", &tmcons.Message{Sum: &tmcons.Message_BlockPart{
|
|
BlockPart: &tmcons.BlockPart{Height: 1, Round: 1, Part: *pbParts}}},
|
|
"2a36080110011a3008011204746573741a26080110011a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d"},
|
|
{"Vote", &tmcons.Message{Sum: &tmcons.Message_Vote{
|
|
Vote: &tmcons.Vote{Vote: vpb}}},
|
|
"32700a6e0802100122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d2a0608c0b89fdc0532146164645f6d6f72655f6578636c616d6174696f6e3801"},
|
|
{"HasVote", &tmcons.Message{Sum: &tmcons.Message_HasVote{
|
|
HasVote: &tmcons.HasVote{Height: 1, Round: 1, Type: tmproto.PrevoteType, Index: 1}}},
|
|
"3a080801100118012001"},
|
|
{"HasVote", &tmcons.Message{Sum: &tmcons.Message_HasVote{
|
|
HasVote: &tmcons.HasVote{Height: math.MaxInt64, Round: math.MaxInt32,
|
|
Type: tmproto.PrevoteType, Index: math.MaxInt32}}},
|
|
"3a1808ffffffffffffffff7f10ffffffff07180120ffffffff07"},
|
|
{"VoteSetMaj23", &tmcons.Message{Sum: &tmcons.Message_VoteSetMaj23{
|
|
VoteSetMaj23: &tmcons.VoteSetMaj23{Height: 1, Round: 1, Type: tmproto.PrevoteType, BlockID: pbBi}}},
|
|
"425008011001180122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d"},
|
|
{"VoteSetBits", &tmcons.Message{Sum: &tmcons.Message_VoteSetBits{
|
|
VoteSetBits: &tmcons.VoteSetBits{Height: 1, Round: 1, Type: tmproto.PrevoteType, BlockID: pbBi, Votes: *pbBits}}},
|
|
"4a5708011001180122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d2a050801120100"},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
bz, err := proto.Marshal(tc.cMsg)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tc.expBytes, hex.EncodeToString(bz))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVoteSetMaj23MessageValidateBasic(t *testing.T) {
|
|
const (
|
|
validSignedMsgType tmproto.SignedMsgType = 0x01
|
|
invalidSignedMsgType tmproto.SignedMsgType = 0x03
|
|
)
|
|
|
|
validBlockID := types.BlockID{}
|
|
invalidBlockID := types.BlockID{
|
|
Hash: bytes.HexBytes{},
|
|
PartSetHeader: types.PartSetHeader{
|
|
Total: 1,
|
|
Hash: []byte{0},
|
|
},
|
|
}
|
|
|
|
testCases := []struct { // nolint: maligned
|
|
expectErr bool
|
|
messageRound int32
|
|
messageHeight int64
|
|
testName string
|
|
messageType tmproto.SignedMsgType
|
|
messageBlockID types.BlockID
|
|
}{
|
|
{false, 0, 0, "Valid Message", validSignedMsgType, validBlockID},
|
|
{true, -1, 0, "Invalid Message", validSignedMsgType, validBlockID},
|
|
{true, 0, -1, "Invalid Message", validSignedMsgType, validBlockID},
|
|
{true, 0, 0, "Invalid Message", invalidSignedMsgType, validBlockID},
|
|
{true, 0, 0, "Invalid Message", validSignedMsgType, invalidBlockID},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
message := VoteSetMaj23Message{
|
|
Height: tc.messageHeight,
|
|
Round: tc.messageRound,
|
|
Type: tc.messageType,
|
|
BlockID: tc.messageBlockID,
|
|
}
|
|
|
|
assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVoteSetBitsMessageValidateBasic(t *testing.T) {
|
|
testCases := []struct {
|
|
malleateFn func(*VoteSetBitsMessage)
|
|
expErr string
|
|
}{
|
|
{func(msg *VoteSetBitsMessage) {}, ""},
|
|
{func(msg *VoteSetBitsMessage) { msg.Height = -1 }, "negative Height"},
|
|
{func(msg *VoteSetBitsMessage) { msg.Type = 0x03 }, "invalid Type"},
|
|
{func(msg *VoteSetBitsMessage) {
|
|
msg.BlockID = types.BlockID{
|
|
Hash: bytes.HexBytes{},
|
|
PartSetHeader: types.PartSetHeader{
|
|
Total: 1,
|
|
Hash: []byte{0},
|
|
},
|
|
}
|
|
}, "wrong BlockID: wrong PartSetHeader: wrong Hash:"},
|
|
{func(msg *VoteSetBitsMessage) { msg.Votes = bits.NewBitArray(types.MaxVotesCount + 1) },
|
|
"votes bit array is too big: 10001, max: 10000"},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
tc := tc
|
|
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
|
|
msg := &VoteSetBitsMessage{
|
|
Height: 1,
|
|
Round: 0,
|
|
Type: 0x01,
|
|
Votes: bits.NewBitArray(1),
|
|
BlockID: types.BlockID{},
|
|
}
|
|
|
|
tc.malleateFn(msg)
|
|
err := msg.ValidateBasic()
|
|
if tc.expErr != "" && assert.Error(t, err) {
|
|
assert.Contains(t, err.Error(), tc.expErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewRoundStepMessageValidateBasic(t *testing.T) {
|
|
testCases := []struct { // nolint: maligned
|
|
expectErr bool
|
|
messageRound int32
|
|
messageLastCommitRound int32
|
|
messageHeight int64
|
|
testName string
|
|
messageStep cstypes.RoundStepType
|
|
}{
|
|
{false, 0, 0, 0, "Valid Message", cstypes.RoundStepNewHeight},
|
|
{true, -1, 0, 0, "Negative round", cstypes.RoundStepNewHeight},
|
|
{true, 0, 0, -1, "Negative height", cstypes.RoundStepNewHeight},
|
|
{true, 0, 0, 0, "Invalid Step", cstypes.RoundStepCommit + 1},
|
|
// The following cases will be handled by ValidateHeight
|
|
{false, 0, 0, 1, "H == 1 but LCR != -1 ", cstypes.RoundStepNewHeight},
|
|
{false, 0, -1, 2, "H > 1 but LCR < 0", cstypes.RoundStepNewHeight},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
message := NewRoundStepMessage{
|
|
Height: tc.messageHeight,
|
|
Round: tc.messageRound,
|
|
Step: tc.messageStep,
|
|
LastCommitRound: tc.messageLastCommitRound,
|
|
}
|
|
|
|
err := message.ValidateBasic()
|
|
if tc.expectErr {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewRoundStepMessageValidateHeight(t *testing.T) {
|
|
initialHeight := int64(10)
|
|
testCases := []struct { // nolint: maligned
|
|
expectErr bool
|
|
messageLastCommitRound int32
|
|
messageHeight int64
|
|
testName string
|
|
}{
|
|
{false, 0, 11, "Valid Message"},
|
|
{true, 0, -1, "Negative height"},
|
|
{true, 0, 0, "Zero height"},
|
|
{true, 0, 10, "Initial height but LCR != -1 "},
|
|
{true, -1, 11, "Normal height but LCR < 0"},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
message := NewRoundStepMessage{
|
|
Height: tc.messageHeight,
|
|
Round: 0,
|
|
Step: cstypes.RoundStepNewHeight,
|
|
LastCommitRound: tc.messageLastCommitRound,
|
|
}
|
|
|
|
err := message.ValidateHeight(initialHeight)
|
|
if tc.expectErr {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewValidBlockMessageValidateBasic(t *testing.T) {
|
|
testCases := []struct {
|
|
malleateFn func(*NewValidBlockMessage)
|
|
expErr string
|
|
}{
|
|
{func(msg *NewValidBlockMessage) {}, ""},
|
|
{func(msg *NewValidBlockMessage) { msg.Height = -1 }, "negative Height"},
|
|
{func(msg *NewValidBlockMessage) { msg.Round = -1 }, "negative Round"},
|
|
{
|
|
func(msg *NewValidBlockMessage) { msg.BlockPartSetHeader.Total = 2 },
|
|
"blockParts bit array size 1 not equal to BlockPartSetHeader.Total 2",
|
|
},
|
|
{
|
|
func(msg *NewValidBlockMessage) {
|
|
msg.BlockPartSetHeader.Total = 0
|
|
msg.BlockParts = bits.NewBitArray(0)
|
|
},
|
|
"empty blockParts",
|
|
},
|
|
{
|
|
func(msg *NewValidBlockMessage) { msg.BlockParts = bits.NewBitArray(int(types.MaxBlockPartsCount) + 1) },
|
|
"blockParts bit array size 1602 not equal to BlockPartSetHeader.Total 1",
|
|
},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
tc := tc
|
|
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
|
|
msg := &NewValidBlockMessage{
|
|
Height: 1,
|
|
Round: 0,
|
|
BlockPartSetHeader: types.PartSetHeader{
|
|
Total: 1,
|
|
},
|
|
BlockParts: bits.NewBitArray(1),
|
|
}
|
|
|
|
tc.malleateFn(msg)
|
|
err := msg.ValidateBasic()
|
|
if tc.expErr != "" && assert.Error(t, err) {
|
|
assert.Contains(t, err.Error(), tc.expErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProposalPOLMessageValidateBasic(t *testing.T) {
|
|
testCases := []struct {
|
|
malleateFn func(*ProposalPOLMessage)
|
|
expErr string
|
|
}{
|
|
{func(msg *ProposalPOLMessage) {}, ""},
|
|
{func(msg *ProposalPOLMessage) { msg.Height = -1 }, "negative Height"},
|
|
{func(msg *ProposalPOLMessage) { msg.ProposalPOLRound = -1 }, "negative ProposalPOLRound"},
|
|
{func(msg *ProposalPOLMessage) { msg.ProposalPOL = bits.NewBitArray(0) }, "empty ProposalPOL bit array"},
|
|
{func(msg *ProposalPOLMessage) { msg.ProposalPOL = bits.NewBitArray(types.MaxVotesCount + 1) },
|
|
"proposalPOL bit array is too big: 10001, max: 10000"},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
tc := tc
|
|
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
|
|
msg := &ProposalPOLMessage{
|
|
Height: 1,
|
|
ProposalPOLRound: 1,
|
|
ProposalPOL: bits.NewBitArray(1),
|
|
}
|
|
|
|
tc.malleateFn(msg)
|
|
err := msg.ValidateBasic()
|
|
if tc.expErr != "" && assert.Error(t, err) {
|
|
assert.Contains(t, err.Error(), tc.expErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBlockPartMessageValidateBasic(t *testing.T) {
|
|
testPart := new(types.Part)
|
|
testPart.Proof.LeafHash = tmhash.Sum([]byte("leaf"))
|
|
testCases := []struct {
|
|
testName string
|
|
messageHeight int64
|
|
messageRound int32
|
|
messagePart *types.Part
|
|
expectErr bool
|
|
}{
|
|
{"Valid Message", 0, 0, testPart, false},
|
|
{"Invalid Message", -1, 0, testPart, true},
|
|
{"Invalid Message", 0, -1, testPart, true},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
message := BlockPartMessage{
|
|
Height: tc.messageHeight,
|
|
Round: tc.messageRound,
|
|
Part: tc.messagePart,
|
|
}
|
|
|
|
assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
|
})
|
|
}
|
|
|
|
message := BlockPartMessage{Height: 0, Round: 0, Part: new(types.Part)}
|
|
message.Part.Index = 1
|
|
|
|
assert.Equal(t, true, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
|
}
|
|
|
|
func TestHasVoteMessageValidateBasic(t *testing.T) {
|
|
const (
|
|
validSignedMsgType tmproto.SignedMsgType = 0x01
|
|
invalidSignedMsgType tmproto.SignedMsgType = 0x03
|
|
)
|
|
|
|
testCases := []struct { // nolint: maligned
|
|
expectErr bool
|
|
messageRound int32
|
|
messageIndex int32
|
|
messageHeight int64
|
|
testName string
|
|
messageType tmproto.SignedMsgType
|
|
}{
|
|
{false, 0, 0, 0, "Valid Message", validSignedMsgType},
|
|
{true, -1, 0, 0, "Invalid Message", validSignedMsgType},
|
|
{true, 0, -1, 0, "Invalid Message", validSignedMsgType},
|
|
{true, 0, 0, 0, "Invalid Message", invalidSignedMsgType},
|
|
{true, 0, 0, -1, "Invalid Message", validSignedMsgType},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
message := HasVoteMessage{
|
|
Height: tc.messageHeight,
|
|
Round: tc.messageRound,
|
|
Type: tc.messageType,
|
|
Index: tc.messageIndex,
|
|
}
|
|
|
|
assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
|
|
})
|
|
}
|
|
}
|