diff --git a/crypto/merkle/simple_proof.go b/crypto/merkle/simple_proof.go index 2f784314f..7c262e3f6 100644 --- a/crypto/merkle/simple_proof.go +++ b/crypto/merkle/simple_proof.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/tendermint/tendermint/crypto/tmhash" + tmmerkle "github.com/tendermint/tendermint/proto/crypto/merkle" ) const ( @@ -23,8 +24,8 @@ const ( // everything. This also affects the generalized proof system as // well. type SimpleProof struct { - Total int `json:"total"` // Total number of items. - Index int `json:"index"` // Index of item to prove. + Total int64 `json:"total"` // Total number of items. + Index int64 `json:"index"` // Index of item to prove. LeafHash []byte `json:"leaf_hash"` // Hash of item value. Aunts [][]byte `json:"aunts"` // Hashes from leaf's sibling to a root's child. } @@ -37,8 +38,8 @@ func SimpleProofsFromByteSlices(items [][]byte) (rootHash []byte, proofs []*Simp proofs = make([]*SimpleProof, len(items)) for i, trail := range trails { proofs[i] = &SimpleProof{ - Total: len(items), - Index: i, + Total: int64(len(items)), + Index: int64(i), LeafHash: trail.Hash, Aunts: trail.FlattenAunts(), } @@ -115,10 +116,38 @@ func (sp *SimpleProof) ValidateBasic() error { return nil } +func (sp *SimpleProof) ToProto() *tmmerkle.SimpleProof { + if sp == nil { + return nil + } + pb := new(tmmerkle.SimpleProof) + + pb.Total = sp.Total + pb.Index = sp.Index + pb.LeafHash = sp.LeafHash + pb.Aunts = sp.Aunts + + return pb +} + +func SimpleProofFromProto(pb *tmmerkle.SimpleProof) (*SimpleProof, error) { + if pb == nil { + return nil, errors.New("nil proof") + } + sp := new(SimpleProof) + + sp.Total = pb.Total + sp.Index = pb.Index + sp.LeafHash = pb.LeafHash + sp.Aunts = pb.Aunts + + return sp, sp.ValidateBasic() +} + // Use the leafHash and innerHashes to get the root merkle hash. // If the length of the innerHashes slice isn't exactly correct, the result is nil. // Recursive impl. -func computeHashFromAunts(index int, total int, leafHash []byte, innerHashes [][]byte) []byte { +func computeHashFromAunts(index, total int64, leafHash []byte, innerHashes [][]byte) []byte { if index >= total || index < 0 || total <= 0 { return nil } @@ -192,7 +221,7 @@ func trailsFromByteSlices(items [][]byte) (trails []*SimpleProofNode, root *Simp trail := &SimpleProofNode{leafHash(items[0]), nil, nil, nil} return []*SimpleProofNode{trail}, trail default: - k := getSplitPoint(len(items)) + k := getSplitPoint(int64(len(items))) lefts, leftRoot := trailsFromByteSlices(items[:k]) rights, rightRoot := trailsFromByteSlices(items[k:]) rootHash := innerHash(leftRoot.Hash, rightRoot.Hash) diff --git a/crypto/merkle/simple_tree.go b/crypto/merkle/simple_tree.go index d2b931ec7..c9c478406 100644 --- a/crypto/merkle/simple_tree.go +++ b/crypto/merkle/simple_tree.go @@ -13,7 +13,7 @@ func SimpleHashFromByteSlices(items [][]byte) []byte { case 1: return leafHash(items[0]) default: - k := getSplitPoint(len(items)) + k := getSplitPoint(int64(len(items))) left := SimpleHashFromByteSlices(items[:k]) right := SimpleHashFromByteSlices(items[k:]) return innerHash(left, right) @@ -92,13 +92,13 @@ func SimpleHashFromByteSlicesIterative(input [][]byte) []byte { } // getSplitPoint returns the largest power of 2 less than length -func getSplitPoint(length int) int { +func getSplitPoint(length int64) int64 { if length < 1 { panic("Trying to split a tree with size < 1") } uLength := uint(length) bitlen := bits.Len(uLength) - k := 1 << uint(bitlen-1) + k := int64(1 << uint(bitlen-1)) if k == length { k >>= 1 } diff --git a/crypto/merkle/simple_tree_test.go b/crypto/merkle/simple_tree_test.go index 665017a97..d6d4345e8 100644 --- a/crypto/merkle/simple_tree_test.go +++ b/crypto/merkle/simple_tree_test.go @@ -37,9 +37,9 @@ func TestSimpleProof(t *testing.T) { proof := proofs[i] // Check total/index - require.Equal(t, proof.Index, i, "Unmatched indicies: %d vs %d", proof.Index, i) + require.EqualValues(t, proof.Index, i, "Unmatched indicies: %d vs %d", proof.Index, i) - require.Equal(t, proof.Total, total, "Unmatched totals: %d vs %d", proof.Total, total) + require.EqualValues(t, proof.Total, total, "Unmatched totals: %d vs %d", proof.Total, total) // Verify success err := proof.Verify(rootHash, item) @@ -108,7 +108,7 @@ func BenchmarkSimpleHashAlternatives(b *testing.B) { func Test_getSplitPoint(t *testing.T) { tests := []struct { - length int + length int64 want int }{ {1, 0}, @@ -125,6 +125,6 @@ func Test_getSplitPoint(t *testing.T) { } for _, tt := range tests { got := getSplitPoint(tt.length) - require.Equal(t, tt.want, got, "getSplitPoint(%d) = %v, want %v", tt.length, got, tt.want) + require.EqualValues(t, tt.want, got, "getSplitPoint(%d) = %v, want %v", tt.length, got, tt.want) } } diff --git a/types/block.go b/types/block.go index b555fe6db..fab1614da 100644 --- a/types/block.go +++ b/types/block.go @@ -202,6 +202,58 @@ func (b *Block) StringShort() string { return fmt.Sprintf("Block#%v", b.Hash()) } +// ToProto converts Block to protobuf +func (b *Block) ToProto() (*tmproto.Block, error) { + if b == nil { + return nil, errors.New("nil Block") + } + + pb := new(tmproto.Block) + + pb.Header = *b.Header.ToProto() + pb.LastCommit = b.LastCommit.ToProto() + pb.Data = b.Data.ToProto() + + protoEvidence, err := b.Evidence.ToProto() + if err != nil { + return nil, err + } + pb.Evidence = *protoEvidence + + return pb, nil +} + +// FromProto sets a protobuf Block to the given pointer. +// It returns an error if the block is invalid. +func BlockFromProto(bp *tmproto.Block) (*Block, error) { + if bp == nil { + return nil, errors.New("nil block") + } + + b := new(Block) + h, err := HeaderFromProto(&bp.Header) + if err != nil { + return nil, err + } + b.Header = h + data, err := DataFromProto(&bp.Data) + if err != nil { + return nil, err + } + b.Data = data + b.Evidence.FromProto(&bp.Evidence) + + if bp.LastCommit != nil { + lc, err := CommitFromProto(bp.LastCommit) + if err != nil { + return nil, err + } + b.LastCommit = lc + } + + return b, b.ValidateBasic() +} + //----------------------------------------------------------- // These methods are for Protobuf Compatibility @@ -1050,6 +1102,48 @@ func (data *Data) StringIndented(indent string) string { indent, data.hash) } +// ToProto converts Data to protobuf +func (data *Data) ToProto() tmproto.Data { + tp := new(tmproto.Data) + + if len(data.Txs) > 0 { + txBzs := make([][]byte, len(data.Txs)) + for i := range data.Txs { + txBzs[i] = data.Txs[i] + } + tp.Txs = txBzs + } + + if data.hash != nil { + tp.Hash = data.hash + } + + return *tp +} + +// DataFromProto takes a protobud representation of Data & +// returns the native type. +func DataFromProto(dp *tmproto.Data) (Data, error) { + if dp == nil { + return Data{}, errors.New("nil data") + } + data := new(Data) + + if len(dp.Txs) > 0 { + txBzs := make(Txs, len(dp.Txs)) + for i := range dp.Txs { + txBzs[i] = Tx(dp.Txs[i]) + } + data.Txs = txBzs + } else { + data.Txs = Txs{} + } + + data.hash = dp.Hash + + return *data, nil +} + //----------------------------------------------------------------------------- // EvidenceData contains any evidence of malicious wrong-doing by validators @@ -1088,6 +1182,51 @@ func (data *EvidenceData) StringIndented(indent string) string { indent, data.hash) } +// ToProto converts EvidenceData to protobuf +func (data *EvidenceData) ToProto() (*tmproto.EvidenceData, error) { + if data == nil { + return nil, errors.New("nil evidence data") + } + + evi := new(tmproto.EvidenceData) + eviBzs := make([]tmproto.Evidence, len(data.Evidence)) + for i := range data.Evidence { + protoEvi, err := EvidenceToProto(data.Evidence[i]) + if err != nil { + return nil, err + } + eviBzs[i] = *protoEvi + } + evi.Evidence = eviBzs + + if data.hash != nil { + evi.Hash = data.hash + } + + return evi, nil +} + +// FromProto sets a protobuf EvidenceData to the given pointer. +func (data *EvidenceData) FromProto(eviData *tmproto.EvidenceData) error { + if eviData == nil { + return errors.New("nil evidenceData") + } + + eviBzs := make(EvidenceList, len(eviData.Evidence)) + for i := range eviData.Evidence { + evi, err := EvidenceFromProto(&eviData.Evidence[i]) + if err != nil { + return err + } + eviBzs[i] = evi + } + data.Evidence = eviBzs + + data.hash = eviData.GetHash() + + return nil +} + //-------------------------------------------------------------------------------- // BlockID diff --git a/types/block_meta.go b/types/block_meta.go index 2b6787dc2..3591a1847 100644 --- a/types/block_meta.go +++ b/types/block_meta.go @@ -2,7 +2,10 @@ package types import ( "bytes" + "errors" "fmt" + + tmproto "github.com/tendermint/tendermint/proto/types" ) // BlockMeta contains meta information. @@ -23,6 +26,45 @@ func NewBlockMeta(block *Block, blockParts *PartSet) *BlockMeta { } } +func (bm *BlockMeta) ToProto() *tmproto.BlockMeta { + if bm == nil { + return nil + } + + pb := &tmproto.BlockMeta{ + BlockID: bm.BlockID.ToProto(), + BlockSize: int64(bm.BlockSize), + Header: *bm.Header.ToProto(), + NumTxs: int64(bm.NumTxs), + } + return pb +} + +func BlockMetaFromProto(pb *tmproto.BlockMeta) (*BlockMeta, error) { + if pb == nil { + return nil, errors.New("blockmeta is empty") + } + + bm := new(BlockMeta) + + bi, err := BlockIDFromProto(&pb.BlockID) + if err != nil { + return nil, err + } + + h, err := HeaderFromProto(&pb.Header) + if err != nil { + return nil, err + } + + bm.BlockID = *bi + bm.BlockSize = int(pb.BlockSize) + bm.Header = h + bm.NumTxs = int(pb.NumTxs) + + return bm, bm.ValidateBasic() +} + //----------------------------------------------------------- // These methods are for Protobuf Compatibility diff --git a/types/block_test.go b/types/block_test.go index 2da6d2853..3bbf79883 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -630,6 +630,75 @@ func makeRandHeader() Header { return h } +func TestBlockProtoBuf(t *testing.T) { + h := tmrand.Int63() + c1 := randCommit(time.Now()) + b1 := MakeBlock(h, []Tx{Tx([]byte{1})}, &Commit{Signatures: []CommitSig{}}, []Evidence{}) + b1.ProposerAddress = tmrand.Bytes(crypto.AddressSize) + b2 := MakeBlock(h, []Tx{Tx([]byte{1})}, c1, []Evidence{}) + b2.ProposerAddress = tmrand.Bytes(crypto.AddressSize) + evi := NewMockEvidence(b2.Height, time.Now(), tmrand.Bytes(32)) + b2.Evidence = EvidenceData{Evidence: EvidenceList{evi}} + b2.EvidenceHash = b2.Evidence.Hash() + b3 := MakeBlock(h, []Tx{}, c1, []Evidence{}) + b3.ProposerAddress = tmrand.Bytes(crypto.AddressSize) + testCases := []struct { + msg string + b1 *Block + expPass bool + expPass2 bool + }{ + {"nil block", nil, false, false}, + {"b1", b1, true, true}, + {"b2", b2, true, true}, + {"b3", b3, true, true}, + } + for _, tc := range testCases { + pb, err := tc.b1.ToProto() + if tc.expPass { + require.NoError(t, err, tc.msg) + } else { + require.Error(t, err, tc.msg) + } + block, err := BlockFromProto(pb) + if tc.expPass2 { + require.NoError(t, err, tc.msg) + require.EqualValues(t, tc.b1.Header, block.Header, tc.msg) + require.EqualValues(t, tc.b1.Data, block.Data, tc.msg) + require.EqualValues(t, tc.b1.Evidence, block.Evidence, tc.msg) + require.EqualValues(t, *tc.b1.LastCommit, *block.LastCommit, tc.msg) + } else { + require.Error(t, err, tc.msg) + } + } +} + +func TestDataProtoBuf(t *testing.T) { + data := &Data{Txs: Txs{Tx([]byte{1}), Tx([]byte{2}), Tx([]byte{3})}} + _ = data.Hash() + data2 := &Data{Txs: Txs{}} + _ = data2.Hash() + + testCases := []struct { + msg string + data1 *Data + expPass bool + }{ + {"success", data, true}, + {"success data2", data2, true}, + } + for _, tc := range testCases { + protoData := tc.data1.ToProto() + d, err := DataFromProto(&protoData) + if tc.expPass { + require.NoError(t, err, tc.msg) + require.EqualValues(t, tc.data1, &d, tc.msg) + } else { + require.Error(t, err, tc.msg) + } + } +} + func TestHeaderProto(t *testing.T) { h1 := makeRandHeader() tc := []struct { @@ -710,6 +779,45 @@ func TestSignedHeaderProtoBuf(t *testing.T) { } } +func TestEvidenceDataProtoBuf(t *testing.T) { + val := NewMockPV() + blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) + blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) + const chainID = "mychain" + v := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, time.Now()) + v2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 2, 0x01, blockID2, time.Now()) + ev := NewDuplicateVoteEvidence(v2, v) + data := &EvidenceData{Evidence: EvidenceList{ev}} + _ = data.Hash() + testCases := []struct { + msg string + data1 *EvidenceData + expPass1 bool + expPass2 bool + }{ + {"success", data, true, true}, + {"empty evidenceData", &EvidenceData{Evidence: EvidenceList{}}, true, true}, + {"fail nil Data", nil, false, false}, + } + + for _, tc := range testCases { + protoData, err := tc.data1.ToProto() + if tc.expPass1 { + require.NoError(t, err, tc.msg) + } else { + require.Error(t, err, tc.msg) + } + + eviD := new(EvidenceData) + err = eviD.FromProto(protoData) + if tc.expPass2 { + require.NoError(t, err, tc.msg) + require.Equal(t, tc.data1, eviD, tc.msg) + } else { + require.Error(t, err, tc.msg) + } + } +} func TestCommitProtoBuf(t *testing.T) { commit := randCommit(time.Now()) diff --git a/types/part_set.go b/types/part_set.go index 66236b4d0..0cf319b6e 100644 --- a/types/part_set.go +++ b/types/part_set.go @@ -51,6 +51,37 @@ func (part *Part) StringIndented(indent string) string { indent) } +func (part *Part) ToProto() (*tmproto.Part, error) { + if part == nil { + return nil, errors.New("nil part") + } + pb := new(tmproto.Part) + proof := part.Proof.ToProto() + + pb.Index = part.Index + pb.Bytes = part.Bytes + pb.Proof = *proof + + return pb, nil +} + +func PartFromProto(pb *tmproto.Part) (*Part, error) { + if pb == nil { + return nil, errors.New("nil part") + } + + part := new(Part) + proof, err := merkle.SimpleProofFromProto(&pb.Proof) + if err != nil { + return nil, err + } + part.Index = pb.Index + part.Bytes = pb.Bytes + part.Proof = *proof + + return part, part.ValidateBasic() +} + //------------------------------------- type PartSetHeader struct { diff --git a/types/part_set_test.go b/types/part_set_test.go index 5b1c0668c..16ab388a1 100644 --- a/types/part_set_test.go +++ b/types/part_set_test.go @@ -157,3 +157,35 @@ func TestParSetHeaderProtoBuf(t *testing.T) { } } } + +func TestPartProtoBuf(t *testing.T) { + + proof := merkle.SimpleProof{ + Total: 1, + Index: 1, + LeafHash: tmrand.Bytes(32), + } + testCases := []struct { + msg string + ps1 *Part + expPass bool + }{ + {"failure empty", &Part{}, false}, + {"failure nil", nil, false}, + {"success", + &Part{Index: 1, Bytes: tmrand.Bytes(32), Proof: proof}, true}, + } + + for _, tc := range testCases { + proto, err := tc.ps1.ToProto() + if tc.expPass { + require.NoError(t, err, tc.msg) + } + + p, err := PartFromProto(proto) + if tc.expPass { + require.NoError(t, err) + require.Equal(t, tc.ps1, p, tc.msg) + } + } +} diff --git a/types/tx_test.go b/types/tx_test.go index 6bf190aed..969fed95c 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -68,8 +68,8 @@ func TestValidTxProof(t *testing.T) { for i := range txs { tx := []byte(txs[i]) proof := txs.Proof(i) - assert.Equal(t, i, proof.Proof.Index, "%d: %d", h, i) - assert.Equal(t, len(txs), proof.Proof.Total, "%d: %d", h, i) + assert.EqualValues(t, i, proof.Proof.Index, "%d: %d", h, i) + assert.EqualValues(t, len(txs), proof.Proof.Total, "%d: %d", h, i) assert.EqualValues(t, root, proof.RootHash, "%d: %d", h, i) assert.EqualValues(t, tx, proof.Data, "%d: %d", h, i) assert.EqualValues(t, txs[i].Hash(), proof.Leaf(), "%d: %d", h, i)