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