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  }