proto: add more to/from (#4956)

## Description

adding in some more to/from methods/functions

Closes: #XXX
This commit is contained in:
Marko
2020-06-05 06:42:04 +02:00
committed by GitHub
parent dc49dcc1c1
commit ee91312d34
9 changed files with 396 additions and 15 deletions

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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())

View File

@@ -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 {

View File

@@ -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)
}
}
}

View File

@@ -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)