You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
5.4 KiB

  1. package types
  2. import (
  3. "context"
  4. "math"
  5. "testing"
  6. "time"
  7. "github.com/gogo/protobuf/proto"
  8. "github.com/stretchr/testify/assert"
  9. "github.com/stretchr/testify/require"
  10. "github.com/tendermint/tendermint/crypto/tmhash"
  11. "github.com/tendermint/tendermint/internal/libs/protoio"
  12. tmrand "github.com/tendermint/tendermint/libs/rand"
  13. tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
  14. )
  15. var (
  16. testProposal *Proposal
  17. pbp *tmproto.Proposal
  18. )
  19. func init() {
  20. var stamp, err = time.Parse(TimeFormat, "2018-02-11T07:09:22.765Z")
  21. if err != nil {
  22. panic(err)
  23. }
  24. testProposal = &Proposal{
  25. Height: 12345,
  26. Round: 23456,
  27. BlockID: BlockID{Hash: []byte("--June_15_2020_amino_was_removed"),
  28. PartSetHeader: PartSetHeader{Total: 111, Hash: []byte("--June_15_2020_amino_was_removed")}},
  29. POLRound: -1,
  30. Timestamp: stamp,
  31. }
  32. pbp = testProposal.ToProto()
  33. }
  34. func TestProposalSignable(t *testing.T) {
  35. chainID := "test_chain_id"
  36. signBytes := ProposalSignBytes(chainID, pbp)
  37. pb := CanonicalizeProposal(chainID, pbp)
  38. expected, err := protoio.MarshalDelimited(&pb)
  39. require.NoError(t, err)
  40. require.Equal(t, expected, signBytes, "Got unexpected sign bytes for Proposal")
  41. }
  42. func TestProposalString(t *testing.T) {
  43. str := testProposal.String()
  44. expected := `Proposal{12345/23456 (2D2D4A756E655F31355F323032305F616D696E6F5F7761735F72656D6F766564:111:2D2D4A756E65, -1) 000000000000 @ 2018-02-11T07:09:22.765Z}` //nolint:lll // ignore line length for tests
  45. if str != expected {
  46. t.Errorf("got unexpected string for Proposal. Expected:\n%v\nGot:\n%v", expected, str)
  47. }
  48. }
  49. func TestProposalVerifySignature(t *testing.T) {
  50. privVal := NewMockPV()
  51. pubKey, err := privVal.GetPubKey(context.Background())
  52. require.NoError(t, err)
  53. prop := NewProposal(
  54. 4, 2, 2,
  55. BlockID{tmrand.Bytes(tmhash.Size), PartSetHeader{777, tmrand.Bytes(tmhash.Size)}})
  56. p := prop.ToProto()
  57. signBytes := ProposalSignBytes("test_chain_id", p)
  58. // sign it
  59. err = privVal.SignProposal(context.Background(), "test_chain_id", p)
  60. require.NoError(t, err)
  61. prop.Signature = p.Signature
  62. // verify the same proposal
  63. valid := pubKey.VerifySignature(signBytes, prop.Signature)
  64. require.True(t, valid)
  65. // serialize, deserialize and verify again....
  66. newProp := new(tmproto.Proposal)
  67. pb := prop.ToProto()
  68. bs, err := proto.Marshal(pb)
  69. require.NoError(t, err)
  70. err = proto.Unmarshal(bs, newProp)
  71. require.NoError(t, err)
  72. np, err := ProposalFromProto(newProp)
  73. require.NoError(t, err)
  74. // verify the transmitted proposal
  75. newSignBytes := ProposalSignBytes("test_chain_id", pb)
  76. require.Equal(t, string(signBytes), string(newSignBytes))
  77. valid = pubKey.VerifySignature(newSignBytes, np.Signature)
  78. require.True(t, valid)
  79. }
  80. func BenchmarkProposalWriteSignBytes(b *testing.B) {
  81. for i := 0; i < b.N; i++ {
  82. ProposalSignBytes("test_chain_id", pbp)
  83. }
  84. }
  85. func BenchmarkProposalSign(b *testing.B) {
  86. privVal := NewMockPV()
  87. for i := 0; i < b.N; i++ {
  88. err := privVal.SignProposal(context.Background(), "test_chain_id", pbp)
  89. if err != nil {
  90. b.Error(err)
  91. }
  92. }
  93. }
  94. func BenchmarkProposalVerifySignature(b *testing.B) {
  95. privVal := NewMockPV()
  96. err := privVal.SignProposal(context.Background(), "test_chain_id", pbp)
  97. require.NoError(b, err)
  98. pubKey, err := privVal.GetPubKey(context.Background())
  99. require.NoError(b, err)
  100. for i := 0; i < b.N; i++ {
  101. pubKey.VerifySignature(ProposalSignBytes("test_chain_id", pbp), testProposal.Signature)
  102. }
  103. }
  104. func TestProposalValidateBasic(t *testing.T) {
  105. privVal := NewMockPV()
  106. testCases := []struct {
  107. testName string
  108. malleateProposal func(*Proposal)
  109. expectErr bool
  110. }{
  111. {"Good Proposal", func(p *Proposal) {}, false},
  112. {"Invalid Type", func(p *Proposal) { p.Type = tmproto.PrecommitType }, true},
  113. {"Invalid Height", func(p *Proposal) { p.Height = -1 }, true},
  114. {"Invalid Round", func(p *Proposal) { p.Round = -1 }, true},
  115. {"Invalid POLRound", func(p *Proposal) { p.POLRound = -2 }, true},
  116. {"Invalid BlockId", func(p *Proposal) {
  117. p.BlockID = BlockID{[]byte{1, 2, 3}, PartSetHeader{111, []byte("blockparts")}}
  118. }, true},
  119. {"Invalid Signature", func(p *Proposal) {
  120. p.Signature = make([]byte, 0)
  121. }, true},
  122. {"Too big Signature", func(p *Proposal) {
  123. p.Signature = make([]byte, MaxSignatureSize+1)
  124. }, true},
  125. }
  126. blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash")))
  127. for _, tc := range testCases {
  128. tc := tc
  129. t.Run(tc.testName, func(t *testing.T) {
  130. prop := NewProposal(
  131. 4, 2, 2,
  132. blockID)
  133. p := prop.ToProto()
  134. err := privVal.SignProposal(context.Background(), "test_chain_id", p)
  135. prop.Signature = p.Signature
  136. require.NoError(t, err)
  137. tc.malleateProposal(prop)
  138. assert.Equal(t, tc.expectErr, prop.ValidateBasic() != nil, "Validate Basic had an unexpected result")
  139. })
  140. }
  141. }
  142. func TestProposalProtoBuf(t *testing.T) {
  143. proposal := NewProposal(1, 2, 3, makeBlockID([]byte("hash"), 2, []byte("part_set_hash")))
  144. proposal.Signature = []byte("sig")
  145. proposal2 := NewProposal(1, 2, 3, BlockID{})
  146. testCases := []struct {
  147. msg string
  148. p1 *Proposal
  149. expPass bool
  150. }{
  151. {"success", proposal, true},
  152. {"success", proposal2, false}, // blcokID cannot be empty
  153. {"empty proposal failure validatebasic", &Proposal{}, false},
  154. {"nil proposal", nil, false},
  155. }
  156. for _, tc := range testCases {
  157. protoProposal := tc.p1.ToProto()
  158. p, err := ProposalFromProto(protoProposal)
  159. if tc.expPass {
  160. require.NoError(t, err)
  161. require.Equal(t, tc.p1, p, tc.msg)
  162. } else {
  163. require.Error(t, err)
  164. }
  165. }
  166. }