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.

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