first commit

This commit is contained in:
Jae Kwon
2018-07-05 01:18:41 -07:00
parent b51ed132f7
commit 4f7565ba7b
16 changed files with 597 additions and 160 deletions
+6
View File
@@ -0,0 +1,6 @@
#! /bin/bash
protoc --gogo_out=. -I $GOPATH/src/ -I . -I $GOPATH/src/github.com/gogo/protobuf/protobuf merkle.proto
echo "--> adding nolint declarations to protobuf generated files"
awk '/package merkle/ { print "//nolint: gas"; print; next }1' merkle.pb.go > merkle.pb.go.new
mv merkle.pb.go.new merkle.pb.go
+101
View File
@@ -0,0 +1,101 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: merkle.proto
/*
Package merkle is a generated protocol buffer package.
It is generated from these files:
merkle.proto
It has these top-level messages:
ProofOp
Proof
*/
//nolint: gas
package merkle
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type ProofOp struct {
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
}
func (m *ProofOp) Reset() { *m = ProofOp{} }
func (m *ProofOp) String() string { return proto.CompactTextString(m) }
func (*ProofOp) ProtoMessage() {}
func (*ProofOp) Descriptor() ([]byte, []int) { return fileDescriptorMerkle, []int{0} }
func (m *ProofOp) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *ProofOp) GetKey() string {
if m != nil {
return m.Key
}
return ""
}
func (m *ProofOp) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
type Proof struct {
Ops []ProofOp `protobuf:"bytes,1,rep,name=ops" json:"ops"`
}
func (m *Proof) Reset() { *m = Proof{} }
func (m *Proof) String() string { return proto.CompactTextString(m) }
func (*Proof) ProtoMessage() {}
func (*Proof) Descriptor() ([]byte, []int) { return fileDescriptorMerkle, []int{1} }
func (m *Proof) GetOps() []ProofOp {
if m != nil {
return m.Ops
}
return nil
}
func init() {
proto.RegisterType((*ProofOp)(nil), "merkle.ProofOp")
proto.RegisterType((*Proof)(nil), "merkle.Proof")
}
func init() { proto.RegisterFile("merkle.proto", fileDescriptorMerkle) }
var fileDescriptorMerkle = []byte{
// 168 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xc9, 0x4d, 0x2d, 0xca,
0xce, 0x49, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x83, 0xf0, 0xa4, 0x74, 0xd3, 0x33,
0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0xd3, 0xf3, 0xd3, 0xf3, 0xf5, 0xc1, 0xd2,
0x49, 0xa5, 0x69, 0x60, 0x1e, 0x98, 0x03, 0x66, 0x41, 0xb4, 0x29, 0x39, 0x73, 0xb1, 0x07, 0x14,
0xe5, 0xe7, 0xa7, 0xf9, 0x17, 0x08, 0x09, 0x71, 0xb1, 0x94, 0x54, 0x16, 0xa4, 0x4a, 0x30, 0x2a,
0x30, 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, 0x02, 0x5c, 0xcc, 0xd9, 0xa9, 0x95, 0x12, 0x4c, 0x60,
0x21, 0x10, 0x13, 0xa4, 0x2a, 0x25, 0xb1, 0x24, 0x51, 0x82, 0x59, 0x81, 0x51, 0x83, 0x27, 0x08,
0xcc, 0x56, 0x32, 0xe0, 0x62, 0x05, 0x1b, 0x22, 0xa4, 0xce, 0xc5, 0x9c, 0x5f, 0x50, 0x2c, 0xc1,
0xa8, 0xc0, 0xac, 0xc1, 0x6d, 0xc4, 0xaf, 0x07, 0x75, 0x20, 0xd4, 0x02, 0x27, 0x96, 0x13, 0xf7,
0xe4, 0x19, 0x82, 0x40, 0x2a, 0x92, 0xd8, 0xc0, 0xb6, 0x1b, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff,
0x52, 0x58, 0x00, 0xa7, 0xc4, 0x00, 0x00, 0x00,
}
+19
View File
@@ -0,0 +1,19 @@
syntax = "proto3";
package merkle;
// For more information on gogo.proto, see:
// https://github.com/gogo/protobuf/blob/master/extensions.md
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
//----------------------------------------
// Message types
message ProofOp {
string type = 1;
string key = 2;
bytes data = 3;
}
message Proof {
repeated ProofOp ops = 1 [(gogoproto.nullable)=false];
}
+140
View File
@@ -0,0 +1,140 @@
package merkle
import (
"bytes"
"reflect"
"github.com/tendermint/go-amino"
cmn "github.com/tendermint/tmlibs/common"
)
//----------------------------------------
// Extension of ProofOp (defined in merkle.proto)
func (po ProofOp) Bytes() []byte {
bz, err := amino.MarshalBinary(po)
if err != nil {
panic(err)
}
return bz
}
//----------------------------------------
// ProofOp gets converted to an instance of ProofOperator:
type ProofOperator interface {
Run([][]byte) ([][]byte, error)
GetKey() string
ProofOp() ProofOp
}
//----------------------------------------
// Operations on a list of ProofOperators
type ProofOperators []ProofOperator
func (poz ProofOperators) VerifyValue(root []byte, value []byte, keys ...string) (err error) {
return poz.Verify(root, [][]byte{value}, keys...)
}
func (poz ProofOperators) Verify(root []byte, args [][]byte, keys ...string) (err error) {
for i, op := range poz {
key := op.GetKey()
if key != "" {
if keys[0] != key {
return cmn.NewError("Key mismatch on operation #%d: expected %+v but %+v", i, []byte(keys[0]), []byte(key))
}
keys = keys[1:]
}
args, err = op.Run(args)
if err != nil {
return
}
}
if !bytes.Equal(root, args[0]) {
return cmn.NewError("Calculated root hash is invalid: expected %+v but %+v", root, args[0])
}
return nil
}
//----------------------------------------
// ProofRuntime - main entrypoint
type OpDecoder func(ProofOp) (ProofOperator, error)
type ProofRuntime struct {
decoders map[string]OpDecoder
}
func NewProofRuntime() *ProofRuntime {
return &ProofRuntime{
decoders: make(map[string]OpDecoder),
}
}
func (prt *ProofRuntime) RegisterAminoOpDecoder(typ string, opType reflect.Type) {
prt.RegisterOpDecoder(
typ,
func(pop ProofOp) (ProofOperator, error) {
newOp := reflect.New(opType).Elem().Interface()
err := cdc.UnmarshalBinary(pop.Data, &newOp)
if err != nil {
return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data")
}
return newOp.(ProofOperator), nil
},
)
}
func (prt *ProofRuntime) RegisterOpDecoder(typ string, dec OpDecoder) {
_, ok := prt.decoders[typ]
if ok {
panic("already registered for type " + typ)
}
prt.decoders[typ] = dec
}
func (prt *ProofRuntime) Decode(pop ProofOp) (ProofOperator, error) {
decoder := prt.decoders[pop.Type]
if decoder == nil {
return nil, cmn.NewError("unrecognized proof type %v", pop.Type)
}
return decoder(pop)
}
func (prt *ProofRuntime) DecodeProof(proof *Proof) (poz ProofOperators, err error) {
poz = ProofOperators(nil)
for _, pop := range proof.Ops {
operator, err := prt.Decode(pop)
if err != nil {
return nil, cmn.ErrorWrap(err, "decoding a proof operator")
}
poz = append(poz, operator)
}
return
}
func (prt *ProofRuntime) VerifyValue(proof *Proof, root []byte, value []byte, keys ...string) (err error) {
return prt.Verify(proof, root, [][]byte{value}, keys...)
}
func (prt *ProofRuntime) Verify(proof *Proof, root []byte, args [][]byte, keys ...string) (err error) {
poz, err := prt.DecodeProof(proof)
if err != nil {
return cmn.ErrorWrap(err, "decoding proof")
}
return poz.Verify(root, args, keys...)
}
// DefaultProofRuntime only knows about Simple value
// proofs.
// To use e.g. IAVL proofs, register op-decoders as
// defined in the IAVL package.
func DefaultProofRuntime() (prt *ProofRuntime) {
prt = NewProofRuntime()
prt.RegisterAminoOpDecoder(
ProofOpSimpleValue,
reflect.TypeOf(SimpleValueOp{}),
)
return
}
+79
View File
@@ -0,0 +1,79 @@
package merkle
import (
"bytes"
"fmt"
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tmlibs/common"
)
const ProofOpSimpleValue = "simple:v"
// SimpleValueOp takes a key and a single value as argument and
// produces the root hash. The corresponding tree structure is
// the SimpleMap tree. SimpleMap takes a Hasher, and currently
// Tendermint uses aminoHasher. SimpleValueOp should support
// the hash function as used in aminoHasher. TODO support
// additional hash functions here as options/args to this
// operator.
//
// If the produced root hash matches the expected hash, the
// proof is good.
type SimpleValueOp struct {
// encoded in ProofOp.Key, not .Data
key string
// To encode in ProofOp.Data
Proof *SimpleProof `json:"simple-proof"`
}
var _ ProofOperator = SimpleValueOp{}
func NewSimpleValueOp(key string, proof *SimpleProof) SimpleValueOp {
return SimpleValueOp{
key: key,
Proof: proof,
}
}
func (op SimpleValueOp) String() string {
return fmt.Sprintf("SimpleValueOp{%v}", op.GetKey())
}
func (op SimpleValueOp) Run(args [][]byte) ([][]byte, error) {
if len(args) != 1 {
return nil, cmn.NewError("expected 1 arg, got %v", len(args))
}
value := args[0]
hasher := tmhash.New()
hasher.Write(value) // does not error
vhash := hasher.Sum(nil)
// Wrap <op.key, vhash> to hash the KVPair.
hasher = tmhash.New()
encodeByteSlice(hasher, []byte(op.key)) // does not error
encodeByteSlice(hasher, []byte(vhash)) // does not error
kvhash := hasher.Sum(nil)
if !bytes.Equal(kvhash, op.Proof.LeafHash) {
return nil, cmn.NewError("leaf hash mismatch: want %X got %X", op.Proof.LeafHash, kvhash)
}
return [][]byte{
op.Proof.ComputeRootHash(),
}, nil
}
func (op SimpleValueOp) GetKey() string {
return op.key
}
func (op SimpleValueOp) ProofOp() ProofOp {
bz := cdc.MustMarshalBinary(op)
return ProofOp{
Type: ProofOpSimpleValue,
Key: op.key,
Data: bz,
}
}
+43 -8
View File
@@ -3,11 +3,22 @@ package merkle
import (
"bytes"
"fmt"
cmn "github.com/tendermint/tmlibs/common"
)
// SimpleProof represents a simple merkle proof.
// SimpleProof represents a simple Merkle proof.
// NOTE: The convention for proofs is to include leaf hashes but to
// exclude the root hash.
// This convention is implemented across IAVL range proofs as well.
// Keep this consistent unless there's a very good reason to change
// everything. This also affects the generalized proof system as
// well.
type SimpleProof struct {
Aunts [][]byte `json:"aunts"` // Hashes from leaf's sibling to a root's child.
Total int `json:"total"` // Total number of items.
Index int `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.
}
// SimpleProofsFromHashers computes inclusion proof for given items.
@@ -18,7 +29,10 @@ func SimpleProofsFromHashers(items []Hasher) (rootHash []byte, proofs []*SimpleP
proofs = make([]*SimpleProof, len(items))
for i, trail := range trails {
proofs[i] = &SimpleProof{
Aunts: trail.FlattenAunts(),
Total: len(items),
Index: i,
LeafHash: trail.Hash,
Aunts: trail.FlattenAunts(),
}
}
return
@@ -49,11 +63,32 @@ func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs map[strin
return
}
// Verify that leafHash is a leaf hash of the simple-merkle-tree
// which hashes to rootHash.
func (sp *SimpleProof) Verify(index int, total int, leafHash []byte, rootHash []byte) bool {
computedHash := computeHashFromAunts(index, total, leafHash, sp.Aunts)
return computedHash != nil && bytes.Equal(computedHash, rootHash)
// Verify that the SimpleProof proves the root hash.
func (sp *SimpleProof) Verify(rootHash []byte, index int, total int, leafHash []byte) error {
if sp.Index != index {
return cmn.NewError("invalid index: wanted %v got %v", index, sp.Index)
}
if sp.Total != total {
return cmn.NewError("invalid total: wanted %v got %v", total, sp.Total)
}
if !bytes.Equal(sp.LeafHash, leafHash) {
return cmn.NewError("invalid leaf hash: wanted %X got %X", leafHash, sp.LeafHash)
}
computedHash := sp.ComputeRootHash()
if !bytes.Equal(computedHash, rootHash) {
return cmn.NewError("invalid root hash: wanted %X got %X", rootHash, computedHash)
}
return nil
}
// Compute the root hash given a leaf hash. Does not verify the result.
func (sp *SimpleProof) ComputeRootHash() []byte {
return computeHashFromAunts(
sp.Index,
sp.Total,
sp.LeafHash,
sp.Aunts,
)
}
// String implements the stringer interface for SimpleProof.
+16
View File
@@ -0,0 +1,16 @@
package merkle
import (
"github.com/tendermint/go-amino"
)
var cdc *amino.Codec
func init() {
cdc = amino.NewCodec()
RegisterWire(cdc)
}
func RegisterWire(cdc *amino.Codec) {
// Nothing to do.
}