Compare commits

...

8 Commits

Author SHA1 Message Date
mossid
f31a6f043f add test 2018-07-16 19:25:04 -07:00
mossid
b16ce7e0c2 in progress 2018-07-12 23:50:39 -07:00
Jae Kwon
7495528fb8 Commit KeyPath 2018-07-12 21:59:13 -07:00
Jae Kwon
3a5a7a776e fixed all tests 2018-07-12 00:27:50 -07:00
Jae Kwon
77a51e738c Fixing tests... 2018-07-10 02:40:34 -07:00
Jae Kwon
ad873c761e Trusted -> \!Prove 2018-07-09 12:53:51 -07:00
Jae Kwon
c039b610f1 Fix tests 2018-07-07 22:01:20 -07:00
Jae Kwon
4f7565ba7b first commit 2018-07-05 01:18:41 -07:00
36 changed files with 988 additions and 380 deletions

11
Gopkg.lock generated
View File

@@ -302,6 +302,15 @@
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412"
version = "0.10.1"
[[projects]]
name = "github.com/tendermint/iavl"
packages = [
".",
"sha256truncated"
]
revision = "866a229a90d86b07af23ffa156c3fa1396416574"
version = "v0.10.0-rc1"
[[projects]]
name = "github.com/tendermint/tmlibs"
packages = [
@@ -423,6 +432,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "c25289282b94abc7f0c390e592e5e1636b7f26cb4773863ac39cde7fdc7b5bdf"
inputs-digest = "ece6ed2d29c510b27b489c93a94e68e4aa5db840c16c6b8b7563d81fed4072fe"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -73,6 +73,10 @@
name = "github.com/tendermint/go-amino"
version = "~0.10.1"
[[constraint]]
name = "github.com/tendermint/iavl"
version = "0.10.0-rc1"
[[override]]
name = "github.com/tendermint/tmlibs"
version = "~0.9.0"

View File

@@ -22,6 +22,7 @@ import (
servertest "github.com/tendermint/tendermint/abci/tests/server"
"github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/abci/version"
"github.com/tendermint/tendermint/crypto/merkle"
)
// client is a global variable so it can be reused by the console
@@ -100,7 +101,7 @@ type queryResponse struct {
Key []byte
Value []byte
Height int64
Proof []byte
Proof *merkle.Proof
}
func Execute() error {
@@ -740,7 +741,7 @@ func printResponse(cmd *cobra.Command, args []string, rsp response) {
fmt.Printf("-> value.hex: %X\n", rsp.Query.Value)
}
if rsp.Query.Proof != nil {
fmt.Printf("-> proof: %X\n", rsp.Query.Proof)
fmt.Printf("-> proof: %#v\n", rsp.Query.Proof)
}
}
}

View File

@@ -6,4 +6,5 @@ const (
CodeTypeEncodingError uint32 = 1
CodeTypeBadNonce uint32 = 2
CodeTypeUnauthorized uint32 = 3
CodeTypeUnknownError uint32 = 4
)

View File

@@ -2,31 +2,33 @@ package kvstore
import (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"github.com/tendermint/iavl"
"github.com/tendermint/tendermint/abci/example/code"
"github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
)
var (
stateKey = []byte("stateKey")
kvPairPrefixKey = []byte("kvPairKey:")
stateKey = []byte("stateKey")
)
type State struct {
db dbm.DB
db dbm.DB
tree *iavl.VersionedTree
Size int64 `json:"size"`
Height int64 `json:"height"`
AppHash []byte `json:"app_hash"`
}
func loadState(db dbm.DB) State {
func loadState(db dbm.DB) *State {
stateBytes := db.Get(stateKey)
var state State
var state = new(State)
if len(stateBytes) != 0 {
err := json.Unmarshal(stateBytes, &state)
if err != nil {
@@ -34,10 +36,22 @@ func loadState(db dbm.DB) State {
}
}
state.db = db
state.tree = iavl.NewVersionedTree(db, 0)
state.tree.LoadVersion(state.Height)
return state
}
func saveState(state State) {
func saveState(state *State) {
hash, version, err := state.tree.SaveVersion()
if err != nil {
panic(err)
}
state.AppHash = hash
if state.Height+1 != version {
panic("should not happen, expected version to be 1+state.Height.")
}
state.Height = version
stateBytes, err := json.Marshal(state)
if err != nil {
panic(err)
@@ -45,10 +59,6 @@ func saveState(state State) {
state.db.Set(stateKey, stateBytes)
}
func prefixKey(key []byte) []byte {
return append(kvPairPrefixKey, key...)
}
//---------------------------------------------------
var _ types.Application = (*KVStoreApplication)(nil)
@@ -56,7 +66,7 @@ var _ types.Application = (*KVStoreApplication)(nil)
type KVStoreApplication struct {
types.BaseApplication
state State
state *State
}
func NewKVStoreApplication() *KVStoreApplication {
@@ -77,11 +87,11 @@ func (app *KVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
} else {
key, value = tx, tx
}
app.state.db.Set(prefixKey(key), value)
app.state.tree.Set(key, value)
app.state.Size += 1
tags := []cmn.KVPair{
{[]byte("app.creator"), []byte("jae")},
{[]byte("app.creator"), []byte("Cosmoshi Netowoko")},
{[]byte("app.key"), key},
}
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags}
@@ -92,30 +102,48 @@ func (app *KVStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx {
}
func (app *KVStoreApplication) Commit() types.ResponseCommit {
// Using a memdb - just return the big endian size of the db
appHash := make([]byte, 8)
binary.PutVarint(appHash, app.state.Size)
app.state.AppHash = appHash
app.state.Height += 1
saveState(app.state)
return types.ResponseCommit{Data: appHash}
return types.ResponseCommit{Data: app.state.AppHash}
}
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
key := reqQuery.Data
height := app.state.Height
if reqQuery.Height != 0 {
height = reqQuery.Height
}
if reqQuery.Prove {
value := app.state.db.Get(prefixKey(reqQuery.Data))
resQuery.Index = -1 // TODO make Proof return index
resQuery.Key = reqQuery.Data
value, proof, err := app.state.tree.GetVersionedWithProof(key, height)
if err != nil {
resQuery.Code = code.CodeTypeUnknownError
resQuery.Log = err.Error()
return
}
resQuery.Height = height
resQuery.Index = proof.LeftIndex() // TODO make Proof return index
resQuery.Key = key
resQuery.Value = value
if value != nil {
resQuery.Proof = &merkle.Proof{
// XXX key encoding
Ops: []merkle.ProofOp{iavl.NewIAVLValueOp(string(key), proof).ProofOp()},
}
resQuery.Log = "exists"
} else {
resQuery.Proof = &merkle.Proof{
// XXX key encoding
Ops: []merkle.ProofOp{iavl.NewIAVLAbsenceOp(string(key), proof).ProofOp()},
}
resQuery.Log = "does not exist"
}
return
} else {
value := app.state.db.Get(prefixKey(reqQuery.Data))
index, value := app.state.tree.GetVersioned(key, height)
resQuery.Height = height
resQuery.Index = int64(index) // TODO GetVersioned64?
resQuery.Key = key
resQuery.Value = value
resQuery.Proof = nil
if value != nil {
resQuery.Log = "exists"
} else {

View File

@@ -18,11 +18,13 @@ import (
)
func testKVStore(t *testing.T, app types.Application, tx []byte, key, value string) {
ar := app.DeliverTx(tx)
require.False(t, ar.IsErr(), ar)
rdtx := app.DeliverTx(tx)
require.False(t, rdtx.IsErr(), rdtx)
// repeating tx doesn't raise error
ar = app.DeliverTx(tx)
require.False(t, ar.IsErr(), ar)
rdtx = app.DeliverTx(tx)
require.False(t, rdtx.IsErr(), rdtx)
rc := app.Commit()
require.NotNil(t, rc.Data)
// make sure query is fine
resQuery := app.Query(types.RequestQuery{
@@ -252,7 +254,7 @@ func TestClientServer(t *testing.T) {
// set up socket app
kvstore := NewKVStoreApplication()
client, server, err := makeSocketClientServer(kvstore, "kvstore-socket")
require.Nil(t, err)
require.Nil(t, err, "%+v", err)
defer server.Stop()
defer client.Stop()
@@ -281,13 +283,16 @@ func runClientTests(t *testing.T, client abcicli.Client) {
}
func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) {
ar, err := app.DeliverTxSync(tx)
rdtx, err := app.DeliverTxSync(tx)
require.NoError(t, err)
require.False(t, ar.IsErr(), ar)
require.False(t, rdtx.IsErr(), rdtx)
// repeating tx doesn't raise error
ar, err = app.DeliverTxSync(tx)
rdtx, err = app.DeliverTxSync(tx)
require.NoError(t, err)
require.False(t, ar.IsErr(), ar)
require.False(t, rdtx.IsErr(), rdtx)
rc, err := app.CommitSync()
require.NoError(t, err)
require.NotNil(t, rc.Data)
// make sure query is fine
resQuery, err := app.QuerySync(types.RequestQuery{

View File

@@ -168,7 +168,7 @@ func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.Respon
}
// update
return app.updateValidator(types.Ed25519Validator(pubkey, int64(power)))
return app.updateValidator(types.Ed25519Validator(pubkey, power))
}
// add, update, or remove a validator

View File

@@ -51,6 +51,7 @@ import fmt "fmt"
import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
import common "github.com/tendermint/tmlibs/common"
import merkle "github.com/tendermint/tendermint/crypto/merkle"
import context "golang.org/x/net/context"
import grpc "google.golang.org/grpc"
@@ -1288,13 +1289,13 @@ func (m *ResponseInitChain) GetValidators() []Validator {
type ResponseQuery struct {
Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
// bytes data = 2; // use "value" instead.
Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"`
Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"`
Index int64 `protobuf:"varint,5,opt,name=index,proto3" json:"index,omitempty"`
Key []byte `protobuf:"bytes,6,opt,name=key,proto3" json:"key,omitempty"`
Value []byte `protobuf:"bytes,7,opt,name=value,proto3" json:"value,omitempty"`
Proof []byte `protobuf:"bytes,8,opt,name=proof,proto3" json:"proof,omitempty"`
Height int64 `protobuf:"varint,9,opt,name=height,proto3" json:"height,omitempty"`
Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"`
Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"`
Index int64 `protobuf:"varint,5,opt,name=index,proto3" json:"index,omitempty"`
Key []byte `protobuf:"bytes,6,opt,name=key,proto3" json:"key,omitempty"`
Value []byte `protobuf:"bytes,7,opt,name=value,proto3" json:"value,omitempty"`
Proof *merkle.Proof `protobuf:"bytes,8,opt,name=proof" json:"proof,omitempty"`
Height int64 `protobuf:"varint,9,opt,name=height,proto3" json:"height,omitempty"`
}
func (m *ResponseQuery) Reset() { *m = ResponseQuery{} }
@@ -1344,7 +1345,7 @@ func (m *ResponseQuery) GetValue() []byte {
return nil
}
func (m *ResponseQuery) GetProof() []byte {
func (m *ResponseQuery) GetProof() *merkle.Proof {
if m != nil {
return m.Proof
}
@@ -2335,121 +2336,123 @@ var _ABCIApplication_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("types/types.proto", fileDescriptorTypes) }
var fileDescriptorTypes = []byte{
// 1846 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x58, 0xcd, 0x6e, 0x1b, 0xc9,
0x11, 0x16, 0xff, 0x39, 0xa5, 0x1f, 0xd2, 0x2d, 0xdb, 0xa2, 0xb9, 0x08, 0x6c, 0x0c, 0x02, 0xaf,
0x9c, 0xd5, 0x8a, 0x89, 0x76, 0x6d, 0xd8, 0xbb, 0xc9, 0x22, 0x92, 0xd6, 0x59, 0x0a, 0x9b, 0x1f,
0x65, 0xec, 0x75, 0x80, 0x5c, 0x88, 0x26, 0xa7, 0x45, 0x0e, 0x4c, 0xce, 0xcc, 0x4e, 0x37, 0xb5,
0x94, 0x6f, 0xb9, 0x2f, 0x72, 0xcd, 0x39, 0x2f, 0x90, 0x43, 0x80, 0xbc, 0x42, 0x90, 0x97, 0x88,
0x0f, 0x49, 0x4e, 0x79, 0x89, 0x04, 0x55, 0xdd, 0xf3, 0xab, 0xa1, 0xe1, 0x38, 0xc7, 0xbd, 0x48,
0x5d, 0x5d, 0x55, 0x3d, 0x5d, 0xc5, 0xaa, 0xaf, 0xaa, 0x1a, 0x6e, 0xa8, 0xab, 0x50, 0xc8, 0x01,
0xfd, 0x3d, 0x0c, 0xa3, 0x40, 0x05, 0xac, 0x41, 0x44, 0xff, 0xc3, 0xa9, 0xa7, 0x66, 0xcb, 0xf1,
0xe1, 0x24, 0x58, 0x0c, 0xa6, 0xc1, 0x34, 0x18, 0x10, 0x77, 0xbc, 0xbc, 0x20, 0x8a, 0x08, 0x5a,
0x69, 0xad, 0xfe, 0x20, 0x23, 0xae, 0x84, 0xef, 0x8a, 0x68, 0xe1, 0xf9, 0x6a, 0xa0, 0x16, 0x73,
0x6f, 0x2c, 0x07, 0x93, 0x60, 0xb1, 0x08, 0xfc, 0xec, 0x67, 0xec, 0xbf, 0xd6, 0xa1, 0xe5, 0x88,
0xaf, 0x97, 0x42, 0x2a, 0xb6, 0x0f, 0x75, 0x31, 0x99, 0x05, 0xbd, 0xea, 0xbd, 0xca, 0xfe, 0xe6,
0x11, 0x3b, 0xd4, 0x72, 0x86, 0xfb, 0x74, 0x32, 0x0b, 0x86, 0x1b, 0x0e, 0x49, 0xb0, 0x0f, 0xa0,
0x71, 0x31, 0x5f, 0xca, 0x59, 0xaf, 0x46, 0xa2, 0xbb, 0x79, 0xd1, 0x9f, 0x21, 0x6b, 0xb8, 0xe1,
0x68, 0x19, 0x3c, 0xd6, 0xf3, 0x2f, 0x82, 0x5e, 0xbd, 0xec, 0xd8, 0x33, 0xff, 0x82, 0x8e, 0x45,
0x09, 0xf6, 0x18, 0x40, 0x0a, 0x35, 0x0a, 0x42, 0xe5, 0x05, 0x7e, 0xaf, 0x41, 0xf2, 0x7b, 0x79,
0xf9, 0x67, 0x42, 0xfd, 0x8a, 0xd8, 0xc3, 0x0d, 0xc7, 0x92, 0x31, 0x81, 0x9a, 0x9e, 0xef, 0xa9,
0xd1, 0x64, 0xc6, 0x3d, 0xbf, 0xd7, 0x2c, 0xd3, 0x3c, 0xf3, 0x3d, 0x75, 0x8a, 0x6c, 0xd4, 0xf4,
0x62, 0x02, 0x4d, 0xf9, 0x7a, 0x29, 0xa2, 0xab, 0x5e, 0xab, 0xcc, 0x94, 0x5f, 0x23, 0x0b, 0x4d,
0x21, 0x19, 0xf6, 0x29, 0x6c, 0x8e, 0xc5, 0xd4, 0xf3, 0x47, 0xe3, 0x79, 0x30, 0x79, 0xd9, 0x6b,
0x93, 0x4a, 0x2f, 0xaf, 0x72, 0x82, 0x02, 0x27, 0xc8, 0x1f, 0x6e, 0x38, 0x30, 0x4e, 0x28, 0x76,
0x04, 0xed, 0xc9, 0x4c, 0x4c, 0x5e, 0x8e, 0xd4, 0xaa, 0x67, 0x91, 0xe6, 0xad, 0xbc, 0xe6, 0x29,
0x72, 0x9f, 0xaf, 0x86, 0x1b, 0x4e, 0x6b, 0xa2, 0x97, 0x68, 0x97, 0x2b, 0xe6, 0xde, 0xa5, 0x88,
0x50, 0x6b, 0xb7, 0xcc, 0xae, 0xcf, 0x35, 0x9f, 0xf4, 0x2c, 0x37, 0x26, 0xd8, 0x43, 0xb0, 0x84,
0xef, 0x9a, 0x8b, 0x6e, 0x92, 0xe2, 0xed, 0xc2, 0x2f, 0xea, 0xbb, 0xf1, 0x35, 0xdb, 0xc2, 0xac,
0xd9, 0x21, 0x34, 0x31, 0x4a, 0x3c, 0xd5, 0xdb, 0x22, 0x9d, 0x9b, 0x85, 0x2b, 0x12, 0x6f, 0xb8,
0xe1, 0x18, 0xa9, 0x93, 0x16, 0x34, 0x2e, 0xf9, 0x7c, 0x29, 0xec, 0xf7, 0x61, 0x33, 0x13, 0x29,
0xac, 0x07, 0xad, 0x85, 0x90, 0x92, 0x4f, 0x45, 0xaf, 0x72, 0xaf, 0xb2, 0x6f, 0x39, 0x31, 0x69,
0xef, 0xc0, 0x56, 0x36, 0x4e, 0x32, 0x8a, 0x18, 0x0b, 0xa8, 0x78, 0x29, 0x22, 0x89, 0x01, 0x60,
0x14, 0x0d, 0x69, 0x7f, 0x02, 0xdd, 0x62, 0x10, 0xb0, 0x2e, 0xd4, 0x5e, 0x8a, 0x2b, 0x23, 0x89,
0x4b, 0x76, 0xd3, 0x5c, 0x88, 0xa2, 0xd8, 0x72, 0xcc, 0xed, 0xfe, 0x55, 0x49, 0x94, 0x93, 0x38,
0x60, 0x0c, 0xea, 0xca, 0x5b, 0xe8, 0x0b, 0xd6, 0x1c, 0x5a, 0xb3, 0x3b, 0xf8, 0x23, 0x71, 0xcf,
0x1f, 0x79, 0xae, 0x39, 0xa1, 0x45, 0xf4, 0x99, 0xcb, 0x8e, 0xa1, 0x3b, 0x09, 0x7c, 0x29, 0x7c,
0xb9, 0x94, 0xa3, 0x90, 0x47, 0x7c, 0x21, 0x4d, 0xfc, 0xc7, 0x8e, 0x3d, 0x8d, 0xd9, 0xe7, 0xc4,
0x75, 0x3a, 0x93, 0xfc, 0x06, 0x7b, 0x04, 0x70, 0xc9, 0xe7, 0x9e, 0xcb, 0x55, 0x10, 0xc9, 0x5e,
0xfd, 0x5e, 0x6d, 0x7f, 0xf3, 0xa8, 0x6b, 0x94, 0x5f, 0xc4, 0x8c, 0x93, 0xfa, 0xdf, 0x5e, 0xdf,
0xdd, 0x70, 0x32, 0x92, 0xec, 0x3e, 0x74, 0x78, 0x18, 0x8e, 0xa4, 0xe2, 0x4a, 0x8c, 0xc6, 0x57,
0x4a, 0x48, 0xca, 0x8e, 0x2d, 0x67, 0x9b, 0x87, 0xe1, 0x33, 0xdc, 0x3d, 0xc1, 0x4d, 0xdb, 0x4d,
0x7c, 0x4b, 0x81, 0x8b, 0x16, 0xba, 0x5c, 0x71, 0xb2, 0x70, 0xcb, 0xa1, 0x35, 0xee, 0x85, 0x5c,
0xcd, 0x8c, 0x75, 0xb4, 0x66, 0xb7, 0xa1, 0x39, 0x13, 0xde, 0x74, 0xa6, 0xc8, 0xa0, 0x9a, 0x63,
0x28, 0x74, 0x66, 0x18, 0x05, 0x97, 0x82, 0x72, 0xb7, 0xed, 0x68, 0xc2, 0xfe, 0x7b, 0x05, 0x6e,
0x5c, 0x0b, 0x76, 0x3c, 0x77, 0xc6, 0xe5, 0x2c, 0xfe, 0x16, 0xae, 0xd9, 0x07, 0x78, 0x2e, 0x77,
0x45, 0x64, 0x30, 0x65, 0xdb, 0xd8, 0x3a, 0xa4, 0x4d, 0x63, 0xa8, 0x11, 0x61, 0x3f, 0xc9, 0x39,
0xa7, 0x46, 0xce, 0x89, 0x63, 0xfd, 0x99, 0x37, 0xf5, 0x3d, 0x7f, 0xfa, 0x26, 0x1f, 0x0d, 0xe1,
0xe6, 0xf8, 0xea, 0x15, 0xf7, 0x95, 0xe7, 0x8b, 0xd1, 0x35, 0x2f, 0x77, 0xcc, 0x41, 0x4f, 0x2f,
0x3d, 0x57, 0xf8, 0x13, 0x61, 0x0e, 0xd8, 0x4d, 0x54, 0x92, 0xa3, 0xa5, 0x7d, 0x0f, 0x76, 0xf2,
0x19, 0xc9, 0x76, 0xa0, 0xaa, 0x56, 0xc6, 0xb2, 0xaa, 0x5a, 0xd9, 0x76, 0x12, 0x4d, 0x49, 0xf6,
0x5d, 0x93, 0x79, 0x00, 0x9d, 0x42, 0xa2, 0x65, 0xdc, 0x5c, 0xc9, 0xba, 0xd9, 0xee, 0xc0, 0x76,
0x2e, 0xbf, 0xec, 0x6f, 0x1b, 0xd0, 0x76, 0x84, 0x0c, 0x31, 0x7c, 0xd8, 0x63, 0xb0, 0xc4, 0x6a,
0x22, 0x34, 0x28, 0x56, 0x0a, 0x90, 0xa3, 0x65, 0x9e, 0xc6, 0x7c, 0xc4, 0x80, 0x44, 0x98, 0x3d,
0xc8, 0x01, 0xfa, 0x6e, 0x51, 0x29, 0x8b, 0xe8, 0x07, 0x79, 0x44, 0xbf, 0x59, 0x90, 0x2d, 0x40,
0xfa, 0x83, 0x1c, 0xa4, 0x17, 0x0f, 0xce, 0x61, 0xfa, 0x93, 0x12, 0x4c, 0x2f, 0x5e, 0x7f, 0x0d,
0xa8, 0x3f, 0x29, 0x01, 0xf5, 0xde, 0xb5, 0x6f, 0x95, 0xa2, 0xfa, 0x41, 0x1e, 0xd5, 0x8b, 0xe6,
0x14, 0x60, 0xfd, 0xc7, 0x65, 0xb0, 0x7e, 0xa7, 0xa0, 0xb3, 0x16, 0xd7, 0x3f, 0xba, 0x86, 0xeb,
0xb7, 0x0b, 0xaa, 0x25, 0xc0, 0xfe, 0x24, 0x07, 0xec, 0x50, 0x6a, 0xdb, 0x1a, 0x64, 0x7f, 0x74,
0x1d, 0xd9, 0xf7, 0x8a, 0x3f, 0x6d, 0x19, 0xb4, 0x0f, 0x0a, 0xd0, 0x7e, 0xab, 0x78, 0xcb, 0xb5,
0xd8, 0xfe, 0x00, 0xf3, 0xbd, 0x10, 0x69, 0x88, 0x0d, 0x22, 0x8a, 0x82, 0xc8, 0x80, 0xaf, 0x26,
0xec, 0x7d, 0x44, 0xa0, 0x34, 0xbe, 0xde, 0x50, 0x07, 0x28, 0xe8, 0x33, 0xd1, 0x65, 0xff, 0xa1,
0x92, 0xea, 0x52, 0x29, 0xc8, 0xa2, 0x97, 0x65, 0xd0, 0x2b, 0x53, 0x1e, 0xaa, 0xb9, 0xf2, 0xc0,
0x7e, 0x00, 0x37, 0xe6, 0x5c, 0x2a, 0xed, 0x97, 0x51, 0x0e, 0xce, 0x3a, 0xc8, 0xd0, 0x0e, 0xd1,
0xb8, 0xf6, 0x21, 0xec, 0x66, 0x64, 0x11, 0x5a, 0x09, 0xba, 0xea, 0x94, 0xbc, 0xdd, 0x44, 0xfa,
0x38, 0x0c, 0x87, 0x5c, 0xce, 0xec, 0x5f, 0xa4, 0xf6, 0xa7, 0xa5, 0x87, 0x41, 0x7d, 0x12, 0xb8,
0xda, 0xac, 0x6d, 0x87, 0xd6, 0x58, 0x8e, 0xe6, 0xc1, 0x94, 0xbe, 0x6a, 0x39, 0xb8, 0x44, 0xa9,
0x24, 0x53, 0x2c, 0x9d, 0x12, 0xf6, 0xef, 0x2b, 0xe9, 0x79, 0x69, 0x35, 0x2a, 0x2b, 0x2f, 0x95,
0xff, 0xa7, 0xbc, 0x54, 0xdf, 0xb6, 0xbc, 0xd8, 0x7f, 0xa9, 0xa4, 0xbf, 0x45, 0x52, 0x38, 0xde,
0xcd, 0x38, 0x0c, 0x0b, 0xcf, 0x77, 0xc5, 0x8a, 0x52, 0xbd, 0xe6, 0x68, 0x22, 0xae, 0xd3, 0x4d,
0x72, 0x70, 0xbe, 0x4e, 0xb7, 0x68, 0x4f, 0x13, 0xa6, 0xe0, 0x04, 0x17, 0x94, 0x83, 0x5b, 0x8e,
0x26, 0x32, 0xb8, 0x69, 0xe5, 0x70, 0xf3, 0x1c, 0xd8, 0xf5, 0xec, 0x64, 0x9f, 0x40, 0x5d, 0xf1,
0x29, 0x3a, 0x0f, 0xed, 0xdf, 0x39, 0xd4, 0x5d, 0xef, 0xe1, 0x97, 0x2f, 0xce, 0xb9, 0x17, 0x9d,
0xdc, 0x46, 0xeb, 0xff, 0xfd, 0xfa, 0xee, 0x0e, 0xca, 0x1c, 0x04, 0x0b, 0x4f, 0x89, 0x45, 0xa8,
0xae, 0x1c, 0xd2, 0xb1, 0xff, 0x53, 0x41, 0xd4, 0xce, 0x65, 0x6d, 0xa9, 0x2f, 0xe2, 0xd0, 0xac,
0x66, 0x0a, 0xeb, 0xdb, 0xf9, 0xe7, 0x7b, 0x00, 0x53, 0x2e, 0x47, 0xdf, 0x70, 0x5f, 0x09, 0xd7,
0x38, 0xc9, 0x9a, 0x72, 0xf9, 0x1b, 0xda, 0xc0, 0xfe, 0x03, 0xd9, 0x4b, 0x29, 0x5c, 0xf2, 0x56,
0xcd, 0x69, 0x4d, 0xb9, 0xfc, 0x4a, 0x0a, 0x37, 0xb1, 0xab, 0xf5, 0xbf, 0xdb, 0xc5, 0xf6, 0xa1,
0x76, 0x21, 0x84, 0x41, 0xb6, 0x6e, 0xa2, 0x7a, 0xf6, 0xe8, 0x63, 0x52, 0xd6, 0x21, 0x81, 0x22,
0xf6, 0xef, 0xaa, 0x69, 0x70, 0xa6, 0xc5, 0xed, 0xbb, 0xe5, 0x83, 0x7f, 0x52, 0xb7, 0x98, 0x87,
0x52, 0x76, 0x0a, 0x37, 0x92, 0x94, 0x19, 0x2d, 0x43, 0x97, 0x63, 0x17, 0x56, 0x79, 0x63, 0x8e,
0x75, 0x13, 0x85, 0xaf, 0xb4, 0x3c, 0xfb, 0x25, 0xec, 0x15, 0x92, 0x3c, 0x39, 0xaa, 0xfa, 0xc6,
0x5c, 0xbf, 0x95, 0xcf, 0xf5, 0xf8, 0xbc, 0xd8, 0x1f, 0xb5, 0x77, 0x88, 0xf5, 0xef, 0x63, 0x9b,
0x93, 0x85, 0xfe, 0xb2, 0x5f, 0xd4, 0xfe, 0x63, 0x05, 0x3a, 0x85, 0xcb, 0xb0, 0x01, 0x80, 0x46,
0x4e, 0xe9, 0xbd, 0x12, 0x06, 0xa4, 0x62, 0x1f, 0x90, 0xb3, 0x9e, 0x79, 0xaf, 0x84, 0x63, 0x8d,
0xe3, 0x25, 0xbb, 0x0f, 0x2d, 0xb5, 0xd2, 0xd2, 0xf9, 0x46, 0xf0, 0xf9, 0x8a, 0x44, 0x9b, 0x8a,
0xfe, 0xb3, 0x87, 0xb0, 0xa5, 0x0f, 0x9e, 0x06, 0x52, 0x7a, 0xa1, 0x69, 0x46, 0x58, 0xf6, 0xe8,
0x2f, 0x88, 0xe3, 0x6c, 0x8e, 0x53, 0xc2, 0xfe, 0x2d, 0x58, 0xc9, 0x67, 0xd9, 0x7b, 0x60, 0x2d,
0xf8, 0xca, 0x74, 0xc9, 0x78, 0xb7, 0x86, 0xd3, 0x5e, 0xf0, 0x15, 0x35, 0xc8, 0x6c, 0x0f, 0x5a,
0xc8, 0x54, 0x2b, 0xed, 0xef, 0x86, 0xd3, 0x5c, 0xf0, 0xd5, 0xf3, 0x55, 0xc2, 0x98, 0x72, 0x19,
0xb7, 0xc0, 0x0b, 0xbe, 0xfa, 0x82, 0x4b, 0xfb, 0x33, 0x68, 0xea, 0x4b, 0xbe, 0xd5, 0xc1, 0xa8,
0x5f, 0xcd, 0xe9, 0xff, 0x14, 0x36, 0x33, 0xf7, 0x66, 0x3f, 0x82, 0x5b, 0xda, 0xc2, 0x90, 0x47,
0x8a, 0x3c, 0x92, 0x3b, 0x90, 0x11, 0xf3, 0x9c, 0x47, 0x0a, 0x3f, 0xa9, 0x9b, 0xfa, 0x3f, 0x57,
0xa1, 0xa9, 0x1b, 0x66, 0x76, 0x3f, 0x33, 0x9d, 0x50, 0x55, 0x3c, 0xd9, 0xfc, 0xc7, 0xeb, 0xbb,
0x2d, 0x2a, 0x20, 0x67, 0x9f, 0xa7, 0xa3, 0x4a, 0x0a, 0x98, 0xd5, 0x5c, 0x3f, 0x1f, 0x4f, 0x3c,
0xb5, 0xcc, 0xc4, 0xb3, 0x07, 0x2d, 0x7f, 0xb9, 0x20, 0x97, 0xd4, 0xb5, 0x4b, 0xfc, 0xe5, 0x02,
0x5d, 0xf2, 0x1e, 0x58, 0x2a, 0x50, 0x7c, 0x4e, 0x2c, 0x9d, 0xa4, 0x6d, 0xda, 0x40, 0xe6, 0x7d,
0xe8, 0x64, 0xab, 0x2d, 0x56, 0x4f, 0x0d, 0xee, 0xdb, 0x69, 0xad, 0xc5, 0x09, 0xe0, 0x7d, 0xe8,
0xa4, 0x85, 0x46, 0xcb, 0x69, 0xc0, 0xdf, 0x49, 0xb7, 0x49, 0xf0, 0x0e, 0xb4, 0x93, 0x3a, 0xac,
0xc1, 0xbf, 0xc5, 0x75, 0xf9, 0xc5, 0xc1, 0x39, 0x8c, 0x82, 0x30, 0x90, 0x22, 0x32, 0x0d, 0xd6,
0xba, 0x84, 0x4b, 0xe4, 0x6c, 0x0f, 0xac, 0x84, 0x89, 0x4d, 0x03, 0x77, 0xdd, 0x48, 0x48, 0x69,
0xfa, 0xf3, 0x98, 0x64, 0x07, 0xd0, 0x0a, 0x97, 0xe3, 0x11, 0xd6, 0xa6, 0x7c, 0x60, 0x9e, 0x2f,
0xc7, 0x5f, 0x8a, 0xab, 0x78, 0x42, 0x09, 0x89, 0xa2, 0xea, 0x14, 0x7c, 0x23, 0x22, 0xe3, 0x3f,
0x4d, 0xd8, 0x0a, 0xba, 0xc5, 0xf1, 0x84, 0x7d, 0x0c, 0x56, 0x62, 0x5f, 0x21, 0x41, 0x8a, 0x77,
0x4e, 0x05, 0xb1, 0x85, 0x91, 0xde, 0xd4, 0x17, 0xee, 0x28, 0xf5, 0x2d, 0xdd, 0xab, 0xed, 0x74,
0x34, 0xe3, 0xe7, 0xb1, 0x73, 0xed, 0x1f, 0x42, 0x53, 0xdf, 0x91, 0x7e, 0xd4, 0xab, 0x30, 0xee,
0xaf, 0x68, 0x5d, 0x9a, 0xc9, 0x7f, 0xaa, 0x40, 0x3b, 0x1e, 0x7f, 0x4a, 0x95, 0x72, 0x97, 0xae,
0xbe, 0xed, 0xa5, 0xd7, 0xcd, 0x8e, 0x71, 0xac, 0xd5, 0x33, 0xb1, 0x76, 0x00, 0x4c, 0x87, 0xd4,
0x65, 0xa0, 0x3c, 0x7f, 0x3a, 0xd2, 0xde, 0xd4, 0xb1, 0xd5, 0x25, 0xce, 0x0b, 0x62, 0x9c, 0xe3,
0xfe, 0xd1, 0xb7, 0x0d, 0xe8, 0x1c, 0x9f, 0x9c, 0x9e, 0x1d, 0x87, 0xe1, 0xdc, 0x9b, 0x70, 0xea,
0xba, 0x06, 0x50, 0xa7, 0xbe, 0xb2, 0xe4, 0x75, 0xaa, 0x5f, 0x36, 0xe0, 0xb0, 0x23, 0x68, 0x50,
0x7b, 0xc9, 0xca, 0x1e, 0xa9, 0xfa, 0xa5, 0x73, 0x0e, 0x7e, 0x44, 0x37, 0xa0, 0xd7, 0xdf, 0xaa,
0xfa, 0x65, 0xc3, 0x0e, 0xfb, 0x0c, 0xac, 0xb4, 0x31, 0x5c, 0xf7, 0x62, 0xd5, 0x5f, 0x3b, 0xf6,
0xa0, 0x7e, 0x5a, 0x6b, 0xd7, 0xbd, 0xef, 0xf4, 0xd7, 0xce, 0x07, 0xec, 0x31, 0xb4, 0xe2, 0x6e,
0xa5, 0xfc, 0x4d, 0xa9, 0xbf, 0x66, 0x24, 0x41, 0xf7, 0xe8, 0x8e, 0xaf, 0xec, 0xe1, 0xab, 0x5f,
0x3a, 0x37, 0xb1, 0x87, 0xd0, 0x34, 0x05, 0xa3, 0xf4, 0x75, 0xa8, 0x5f, 0x3e, 0x58, 0xa0, 0x91,
0x69, 0xb7, 0xbb, 0xee, 0x71, 0xae, 0xbf, 0x76, 0xc0, 0x63, 0xc7, 0x00, 0x99, 0x2e, 0x6f, 0xed,
0xab, 0x5b, 0x7f, 0xfd, 0xe0, 0xc6, 0x3e, 0x85, 0x76, 0x3a, 0x8c, 0x97, 0xbf, 0x86, 0xf5, 0xd7,
0xcd, 0x52, 0xe3, 0x26, 0xbd, 0x98, 0x7e, 0xf4, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe5, 0xf3,
0xb2, 0x34, 0xad, 0x15, 0x00, 0x00,
// 1874 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x58, 0x4b, 0x93, 0x1b, 0x49,
0x11, 0x1e, 0xbd, 0xd5, 0x39, 0x0f, 0xc9, 0x35, 0xb6, 0x47, 0xd6, 0x06, 0x61, 0x47, 0x43, 0x78,
0xc7, 0xec, 0xec, 0x08, 0x66, 0xd7, 0x0e, 0x7b, 0x0d, 0x1b, 0xcc, 0xcc, 0x9a, 0xd5, 0xc4, 0xf2,
0x18, 0xda, 0x5e, 0x13, 0xc1, 0x45, 0x51, 0x52, 0xd7, 0xb4, 0x3a, 0xac, 0x7e, 0x6c, 0x57, 0x69,
0x56, 0xf2, 0x8d, 0xfb, 0x06, 0x57, 0xce, 0xfc, 0x01, 0x0e, 0xfc, 0x06, 0x82, 0xe0, 0x4f, 0xe0,
0x03, 0x70, 0xe2, 0x4f, 0x40, 0x54, 0x56, 0xf5, 0x53, 0xdd, 0x0e, 0xb3, 0x1c, 0xb9, 0xcc, 0x54,
0x56, 0x66, 0x56, 0x57, 0xa6, 0x32, 0xbf, 0xcc, 0x2c, 0xb8, 0x21, 0xd6, 0x21, 0xe3, 0x23, 0xfc,
0x7b, 0x1c, 0x46, 0x81, 0x08, 0x48, 0x0b, 0x89, 0xe1, 0x87, 0x8e, 0x2b, 0xe6, 0xcb, 0xe9, 0xf1,
0x2c, 0xf0, 0x46, 0x4e, 0xe0, 0x04, 0x23, 0xe4, 0x4e, 0x97, 0x57, 0x48, 0x21, 0x81, 0x2b, 0xa5,
0x35, 0x1c, 0x65, 0xc4, 0x05, 0xf3, 0x6d, 0x16, 0x79, 0xae, 0x2f, 0x46, 0xc2, 0x5b, 0xb8, 0x53,
0x3e, 0x9a, 0x05, 0x9e, 0x17, 0xf8, 0xd9, 0xcf, 0x0c, 0x9f, 0x56, 0x28, 0xa4, 0xcb, 0x59, 0xb4,
0x0e, 0x45, 0x30, 0xf2, 0x58, 0xf4, 0x6a, 0xc1, 0xf4, 0x3f, 0xa5, 0x6c, 0xfe, 0xa5, 0x09, 0x1d,
0x8b, 0x7d, 0xb5, 0x64, 0x5c, 0x90, 0x43, 0x68, 0xb2, 0xd9, 0x3c, 0x18, 0xd4, 0xef, 0xd5, 0x0e,
0xb7, 0x4f, 0xc8, 0xb1, 0xfa, 0x88, 0xe6, 0x3e, 0x9b, 0xcd, 0x83, 0xf1, 0x96, 0x85, 0x12, 0xe4,
0x03, 0x68, 0x5d, 0x2d, 0x96, 0x7c, 0x3e, 0x68, 0xa0, 0xe8, 0x7e, 0x5e, 0xf4, 0xa7, 0x92, 0x35,
0xde, 0xb2, 0x94, 0x8c, 0x3c, 0xd6, 0xf5, 0xaf, 0x82, 0x41, 0xb3, 0xec, 0xd8, 0x0b, 0xff, 0x0a,
0x8f, 0x95, 0x12, 0xe4, 0x31, 0x00, 0x67, 0x62, 0x12, 0x84, 0xc2, 0x0d, 0xfc, 0x41, 0x0b, 0xe5,
0x0f, 0xf2, 0xf2, 0xcf, 0x99, 0xf8, 0x25, 0xb2, 0xc7, 0x5b, 0x96, 0xc1, 0x63, 0x42, 0x6a, 0xba,
0xbe, 0x2b, 0x26, 0xb3, 0x39, 0x75, 0xfd, 0x41, 0xbb, 0x4c, 0xf3, 0xc2, 0x77, 0xc5, 0xb9, 0x64,
0x4b, 0x4d, 0x37, 0x26, 0xa4, 0x29, 0x5f, 0x2d, 0x59, 0xb4, 0x1e, 0x74, 0xca, 0x4c, 0xf9, 0x95,
0x64, 0x49, 0x53, 0x50, 0x86, 0x3c, 0x85, 0xed, 0x29, 0x73, 0x5c, 0x7f, 0x32, 0x5d, 0x04, 0xb3,
0x57, 0x83, 0x2e, 0xaa, 0x0c, 0xf2, 0x2a, 0x67, 0x52, 0xe0, 0x4c, 0xf2, 0xc7, 0x5b, 0x16, 0x4c,
0x13, 0x8a, 0x9c, 0x40, 0x77, 0x36, 0x67, 0xb3, 0x57, 0x13, 0xb1, 0x1a, 0x18, 0xa8, 0x79, 0x2b,
0xaf, 0x79, 0x2e, 0xb9, 0x2f, 0x56, 0xe3, 0x2d, 0xab, 0x33, 0x53, 0x4b, 0x69, 0x97, 0xcd, 0x16,
0xee, 0x35, 0x8b, 0xa4, 0xd6, 0x7e, 0x99, 0x5d, 0x9f, 0x29, 0x3e, 0xea, 0x19, 0x76, 0x4c, 0x90,
0x87, 0x60, 0x30, 0xdf, 0xd6, 0x17, 0xdd, 0x46, 0xc5, 0xdb, 0x85, 0x5f, 0xd4, 0xb7, 0xe3, 0x6b,
0x76, 0x99, 0x5e, 0x93, 0x63, 0x68, 0xcb, 0x10, 0x73, 0xc5, 0x60, 0x07, 0x75, 0x6e, 0x16, 0xae,
0x88, 0xbc, 0xf1, 0x96, 0xa5, 0xa5, 0xce, 0x3a, 0xd0, 0xba, 0xa6, 0x8b, 0x25, 0x33, 0xdf, 0x87,
0xed, 0x4c, 0xa4, 0x90, 0x01, 0x74, 0x3c, 0xc6, 0x39, 0x75, 0xd8, 0xa0, 0x76, 0xaf, 0x76, 0x68,
0x58, 0x31, 0x69, 0xee, 0xc1, 0x4e, 0x36, 0x4e, 0x32, 0x8a, 0x32, 0x16, 0xa4, 0xe2, 0x35, 0x8b,
0xb8, 0x0c, 0x00, 0xad, 0xa8, 0x49, 0xf3, 0x13, 0xe8, 0x17, 0x83, 0x80, 0xf4, 0xa1, 0xf1, 0x8a,
0xad, 0xb5, 0xa4, 0x5c, 0x92, 0x9b, 0xfa, 0x42, 0x18, 0xc5, 0x86, 0xa5, 0x6f, 0xf7, 0xcf, 0x5a,
0xa2, 0x9c, 0xc4, 0x01, 0x21, 0xd0, 0x14, 0xae, 0xa7, 0x2e, 0xd8, 0xb0, 0x70, 0x4d, 0xee, 0xc8,
0x1f, 0x89, 0xba, 0xfe, 0xc4, 0xb5, 0xf5, 0x09, 0x1d, 0xa4, 0x2f, 0x6c, 0x72, 0x0a, 0xfd, 0x59,
0xe0, 0x73, 0xe6, 0xf3, 0x25, 0x9f, 0x84, 0x34, 0xa2, 0x1e, 0xd7, 0xf1, 0x1f, 0x3b, 0xf6, 0x3c,
0x66, 0x5f, 0x22, 0xd7, 0xea, 0xcd, 0xf2, 0x1b, 0xe4, 0x11, 0xc0, 0x35, 0x5d, 0xb8, 0x36, 0x15,
0x41, 0xc4, 0x07, 0xcd, 0x7b, 0x8d, 0xc3, 0xed, 0x93, 0xbe, 0x56, 0x7e, 0x19, 0x33, 0xce, 0x9a,
0x7f, 0x7d, 0x73, 0x77, 0xcb, 0xca, 0x48, 0x92, 0xfb, 0xd0, 0xa3, 0x61, 0x38, 0xe1, 0x82, 0x0a,
0x36, 0x99, 0xae, 0x05, 0xe3, 0x98, 0x1d, 0x3b, 0xd6, 0x2e, 0x0d, 0xc3, 0xe7, 0x72, 0xf7, 0x4c,
0x6e, 0x9a, 0x76, 0xe2, 0x5b, 0x0c, 0x5c, 0x69, 0xa1, 0x4d, 0x05, 0x45, 0x0b, 0x77, 0x2c, 0x5c,
0xcb, 0xbd, 0x90, 0x8a, 0xb9, 0xb6, 0x0e, 0xd7, 0xe4, 0x36, 0xb4, 0xe7, 0xcc, 0x75, 0xe6, 0x02,
0x0d, 0x6a, 0x58, 0x9a, 0x92, 0xce, 0x0c, 0xa3, 0xe0, 0x9a, 0x61, 0xee, 0x76, 0x2d, 0x45, 0x98,
0x7f, 0xab, 0xc1, 0x8d, 0x8d, 0x60, 0x97, 0xe7, 0xce, 0x29, 0x9f, 0xc7, 0xdf, 0x92, 0x6b, 0xf2,
0x81, 0x3c, 0x97, 0xda, 0x2c, 0xd2, 0x98, 0xb2, 0xab, 0x6d, 0x1d, 0xe3, 0xa6, 0x36, 0x54, 0x8b,
0x90, 0x1f, 0xe7, 0x9c, 0xd3, 0x40, 0xe7, 0xc4, 0xb1, 0xfe, 0xdc, 0x75, 0x7c, 0xd7, 0x77, 0xde,
0xe6, 0xa3, 0x31, 0xdc, 0x9c, 0xae, 0x5f, 0x53, 0x5f, 0xb8, 0x3e, 0x9b, 0x6c, 0x78, 0xb9, 0xa7,
0x0f, 0x7a, 0x76, 0xed, 0xda, 0xcc, 0x9f, 0x31, 0x7d, 0xc0, 0x7e, 0xa2, 0x92, 0x1c, 0xcd, 0xcd,
0x7b, 0xb0, 0x97, 0xcf, 0x48, 0xb2, 0x07, 0x75, 0xb1, 0xd2, 0x96, 0xd5, 0xc5, 0xca, 0x34, 0x93,
0x68, 0x4a, 0xb2, 0x6f, 0x43, 0xe6, 0x01, 0xf4, 0x0a, 0x89, 0x96, 0x71, 0x73, 0x2d, 0xeb, 0x66,
0xb3, 0x07, 0xbb, 0xb9, 0xfc, 0x32, 0xbf, 0x69, 0x41, 0xd7, 0x62, 0x3c, 0x94, 0xe1, 0x43, 0x1e,
0x83, 0xc1, 0x56, 0x33, 0xa6, 0x40, 0xb1, 0x56, 0x80, 0x1c, 0x25, 0xf3, 0x2c, 0xe6, 0x4b, 0x0c,
0x48, 0x84, 0xc9, 0x83, 0x1c, 0xa0, 0xef, 0x17, 0x95, 0xb2, 0x88, 0x7e, 0x94, 0x47, 0xf4, 0x9b,
0x05, 0xd9, 0x02, 0xa4, 0x3f, 0xc8, 0x41, 0x7a, 0xf1, 0xe0, 0x1c, 0xa6, 0x3f, 0x29, 0xc1, 0xf4,
0xe2, 0xf5, 0x2b, 0x40, 0xfd, 0x49, 0x09, 0xa8, 0x0f, 0x36, 0xbe, 0x55, 0x8a, 0xea, 0x47, 0x79,
0x54, 0x2f, 0x9a, 0x53, 0x80, 0xf5, 0x1f, 0x95, 0xc1, 0xfa, 0x9d, 0x82, 0x4e, 0x25, 0xae, 0x7f,
0xb4, 0x81, 0xeb, 0xb7, 0x0b, 0xaa, 0x25, 0xc0, 0xfe, 0x24, 0x07, 0xec, 0x50, 0x6a, 0x5b, 0x05,
0xb2, 0x3f, 0xda, 0x44, 0xf6, 0x83, 0xe2, 0x4f, 0x5b, 0x06, 0xed, 0xa3, 0x02, 0xb4, 0xdf, 0x2a,
0xde, 0xb2, 0x12, 0xdb, 0x1f, 0xc8, 0x7c, 0x2f, 0x44, 0x9a, 0xc4, 0x06, 0x16, 0x45, 0x41, 0xa4,
0xc1, 0x57, 0x11, 0xe6, 0xa1, 0x44, 0xa0, 0x34, 0xbe, 0xde, 0x52, 0x07, 0x30, 0xe8, 0x33, 0xd1,
0x65, 0xfe, 0xbe, 0x96, 0xea, 0x62, 0x29, 0xc8, 0xa2, 0x97, 0xa1, 0xd1, 0x2b, 0x53, 0x1e, 0xea,
0xb9, 0xf2, 0x40, 0xbe, 0x0f, 0x37, 0x16, 0x94, 0x0b, 0xe5, 0x97, 0x49, 0x0e, 0xce, 0x7a, 0x92,
0xa1, 0x1c, 0xa2, 0x70, 0xed, 0x43, 0xd8, 0xcf, 0xc8, 0x4a, 0x68, 0x45, 0xe8, 0x6a, 0x62, 0xf2,
0xf6, 0x13, 0xe9, 0xd3, 0x30, 0x1c, 0x53, 0x3e, 0x37, 0x7f, 0x9e, 0xda, 0x9f, 0x96, 0x1e, 0x02,
0xcd, 0x59, 0x60, 0x2b, 0xb3, 0x76, 0x2d, 0x5c, 0xcb, 0x72, 0xb4, 0x08, 0x1c, 0xfc, 0xaa, 0x61,
0xc9, 0xa5, 0x94, 0x4a, 0x32, 0xc5, 0x50, 0x29, 0x61, 0xfe, 0xae, 0x96, 0x9e, 0x97, 0x56, 0xa3,
0xb2, 0xf2, 0x52, 0xfb, 0x5f, 0xca, 0x4b, 0xfd, 0x5d, 0xcb, 0x8b, 0xf9, 0xe7, 0x5a, 0xfa, 0x5b,
0x24, 0x85, 0xe3, 0xdb, 0x19, 0x27, 0xc3, 0xc2, 0xf5, 0x6d, 0xb6, 0xc2, 0x54, 0x6f, 0x58, 0x8a,
0x88, 0xeb, 0x74, 0x1b, 0x1d, 0x9c, 0xaf, 0xd3, 0x1d, 0xdc, 0x53, 0x04, 0xf9, 0x2e, 0x16, 0x9c,
0xe0, 0x4a, 0xe7, 0xe0, 0xee, 0xb1, 0x6e, 0x56, 0x2f, 0xe5, 0xa6, 0xa5, 0x78, 0x19, 0x18, 0x35,
0x72, 0x30, 0x7a, 0x09, 0x64, 0x33, 0x59, 0xc9, 0x27, 0xd0, 0x14, 0xd4, 0x91, 0xbe, 0x94, 0xee,
0xd8, 0x3b, 0x56, 0x1d, 0xf4, 0xf1, 0x17, 0x2f, 0x2f, 0xa9, 0x1b, 0x9d, 0xdd, 0x96, 0xce, 0xf8,
0xd7, 0x9b, 0xbb, 0x7b, 0x52, 0xe6, 0x28, 0xf0, 0x5c, 0xc1, 0xbc, 0x50, 0xac, 0x2d, 0xd4, 0x31,
0xff, 0x5d, 0x93, 0x20, 0x9e, 0x4b, 0xe2, 0x52, 0xd7, 0xc4, 0x91, 0x5a, 0xcf, 0xd4, 0xd9, 0x77,
0x73, 0xd7, 0x77, 0x00, 0x1c, 0xca, 0x27, 0x5f, 0x53, 0x5f, 0x30, 0x5b, 0xfb, 0xcc, 0x70, 0x28,
0xff, 0x35, 0x6e, 0xc8, 0x76, 0x44, 0xb2, 0x97, 0x9c, 0xd9, 0xe8, 0xbc, 0x86, 0xd5, 0x71, 0x28,
0xff, 0x92, 0x33, 0x3b, 0xb1, 0xab, 0xf3, 0xdf, 0xdb, 0x45, 0x0e, 0xa1, 0x71, 0xc5, 0x98, 0x76,
0x72, 0x3f, 0x51, 0xbd, 0x78, 0xf4, 0x31, 0x2a, 0xab, 0x08, 0x91, 0x22, 0xe6, 0x6f, 0xeb, 0x69,
0xac, 0xa6, 0xb5, 0xee, 0xff, 0xcb, 0x07, 0xff, 0xc0, 0xe6, 0x31, 0x8f, 0xac, 0xe4, 0x1c, 0x6e,
0x24, 0x19, 0x34, 0x59, 0x86, 0x36, 0x95, 0x4d, 0x59, 0xed, 0xad, 0x29, 0xd7, 0x4f, 0x14, 0xbe,
0x54, 0xf2, 0xe4, 0x17, 0x70, 0x50, 0xc8, 0xf9, 0xe4, 0xa8, 0xfa, 0x5b, 0x53, 0xff, 0x56, 0x3e,
0xf5, 0xe3, 0xf3, 0x62, 0x7f, 0x34, 0xbe, 0x45, 0xac, 0x7f, 0x4f, 0x76, 0x3d, 0xd9, 0x4a, 0x50,
0xf6, 0x8b, 0x9a, 0x7f, 0xa8, 0x41, 0xaf, 0x70, 0x19, 0x32, 0x02, 0x50, 0x40, 0xca, 0xdd, 0xd7,
0x4c, 0x63, 0x56, 0xec, 0x03, 0x74, 0xd6, 0x73, 0xf7, 0x35, 0xb3, 0x8c, 0x69, 0xbc, 0x24, 0xf7,
0xa1, 0x23, 0x56, 0x4a, 0x3a, 0xdf, 0x17, 0xbe, 0x58, 0xa1, 0x68, 0x5b, 0xe0, 0x7f, 0xf2, 0x10,
0x76, 0xd4, 0xc1, 0x4e, 0xc0, 0xb9, 0x1b, 0xea, 0xde, 0x84, 0x64, 0x8f, 0xfe, 0x1c, 0x39, 0xd6,
0xf6, 0x34, 0x25, 0xcc, 0xdf, 0x80, 0x91, 0x7c, 0x96, 0xbc, 0x07, 0x86, 0x47, 0x57, 0xba, 0x69,
0x96, 0x77, 0x6b, 0x59, 0x5d, 0x8f, 0xae, 0xb0, 0x5f, 0x26, 0x07, 0xd0, 0x91, 0x4c, 0xb1, 0x52,
0xfe, 0x6e, 0x59, 0x6d, 0x8f, 0xae, 0x5e, 0xac, 0x12, 0x86, 0x43, 0x79, 0xdc, 0x11, 0x7b, 0x74,
0xf5, 0x39, 0xe5, 0xe6, 0xa7, 0xd0, 0x56, 0x97, 0x7c, 0xa7, 0x83, 0xa5, 0x7e, 0x3d, 0xa7, 0xff,
0x13, 0xd8, 0xce, 0xdc, 0x9b, 0xfc, 0x10, 0x6e, 0x29, 0x0b, 0x43, 0x1a, 0x09, 0xf4, 0x48, 0xee,
0x40, 0x82, 0xcc, 0x4b, 0x1a, 0x09, 0xf9, 0x49, 0xd5, 0xe3, 0xff, 0xa9, 0x0e, 0x6d, 0xd5, 0x3f,
0x93, 0xfb, 0x99, 0x61, 0x05, 0x8b, 0xe4, 0xd9, 0xf6, 0xdf, 0xdf, 0xdc, 0xed, 0x60, 0x3d, 0xb9,
0xf8, 0x2c, 0x9d, 0x5c, 0x52, 0xc0, 0xac, 0xe7, 0xda, 0xfb, 0x78, 0x00, 0x6a, 0x64, 0x06, 0xa0,
0x03, 0xe8, 0xf8, 0x4b, 0x0f, 0x5d, 0xd2, 0x54, 0x2e, 0xf1, 0x97, 0x9e, 0x74, 0xc9, 0x7b, 0x60,
0x88, 0x40, 0xd0, 0x05, 0xb2, 0x54, 0x92, 0x76, 0x71, 0x43, 0x32, 0xef, 0x43, 0x2f, 0x5b, 0x7c,
0x65, 0x31, 0x55, 0x58, 0xbf, 0x9b, 0x96, 0x5e, 0x39, 0x10, 0xbc, 0x0f, 0xbd, 0xb4, 0xee, 0x28,
0x39, 0x85, 0xff, 0x7b, 0xe9, 0x36, 0x0a, 0xde, 0x81, 0x6e, 0x52, 0x96, 0xbb, 0x28, 0xd1, 0xa1,
0xaa, 0x1a, 0xcb, 0x39, 0x3a, 0x8c, 0x82, 0x30, 0xe0, 0x2c, 0xd2, 0xfd, 0x56, 0x55, 0xc2, 0x25,
0x72, 0xa6, 0x0b, 0x46, 0xc2, 0x94, 0x3d, 0x04, 0xb5, 0xed, 0x88, 0x71, 0xae, 0xdb, 0xf5, 0x98,
0x24, 0x47, 0xd0, 0x09, 0x97, 0xd3, 0x89, 0x2c, 0x55, 0xf9, 0xc0, 0xbc, 0x5c, 0x4e, 0xbf, 0x60,
0xeb, 0x78, 0x60, 0x09, 0x91, 0xc2, 0xe9, 0x28, 0xf8, 0x9a, 0x45, 0xda, 0x7f, 0x8a, 0x30, 0x05,
0xf4, 0x8b, 0xd3, 0x0a, 0xf9, 0x18, 0x8c, 0xc4, 0xbe, 0x42, 0x82, 0x14, 0xef, 0x9c, 0x0a, 0xca,
0x8e, 0x86, 0xbb, 0x8e, 0xcf, 0xec, 0x49, 0xea, 0x5b, 0xbc, 0x57, 0xd7, 0xea, 0x29, 0xc6, 0xcf,
0x62, 0xe7, 0x9a, 0x3f, 0x80, 0xb6, 0xba, 0x23, 0xfe, 0xa8, 0xeb, 0x30, 0x6e, 0xb7, 0x70, 0x5d,
0x9a, 0xc9, 0x7f, 0xac, 0x41, 0x37, 0x9e, 0x86, 0x4a, 0x95, 0x72, 0x97, 0xae, 0xbf, 0xeb, 0xa5,
0xab, 0x46, 0xc9, 0x38, 0xd6, 0x9a, 0x99, 0x58, 0x3b, 0x02, 0xa2, 0x42, 0xea, 0x3a, 0x10, 0xae,
0xef, 0x4c, 0x94, 0x37, 0x55, 0x6c, 0xf5, 0x91, 0xf3, 0x12, 0x19, 0x97, 0x72, 0xff, 0xe4, 0x9b,
0x16, 0xf4, 0x4e, 0xcf, 0xce, 0x2f, 0x4e, 0xc3, 0x70, 0xe1, 0xce, 0x28, 0x36, 0x61, 0x23, 0x68,
0x62, 0x9b, 0x59, 0xf2, 0x58, 0x35, 0x2c, 0x9b, 0x77, 0xc8, 0x09, 0xb4, 0xb0, 0xdb, 0x24, 0x65,
0x6f, 0x56, 0xc3, 0xd2, 0xb1, 0x47, 0x7e, 0x44, 0xf5, 0xa3, 0x9b, 0x4f, 0x57, 0xc3, 0xb2, 0xd9,
0x87, 0x7c, 0x0a, 0x46, 0xda, 0x27, 0x56, 0x3d, 0x60, 0x0d, 0x2b, 0xa7, 0x20, 0xa9, 0x9f, 0xd6,
0xda, 0xaa, 0xe7, 0x9e, 0x61, 0xe5, 0xb8, 0x40, 0x1e, 0x43, 0x27, 0xee, 0x56, 0xca, 0x9f, 0x98,
0x86, 0x15, 0x13, 0x8a, 0x74, 0x8f, 0x6a, 0x00, 0xcb, 0xde, 0xc1, 0x86, 0xa5, 0x63, 0x14, 0x79,
0x08, 0x6d, 0x5d, 0x30, 0x4a, 0x1f, 0x8b, 0x86, 0xe5, 0x73, 0x86, 0x34, 0x32, 0x6d, 0x7e, 0xab,
0xde, 0xea, 0x86, 0x95, 0xf3, 0x1e, 0x39, 0x05, 0xc8, 0x74, 0x79, 0x95, 0x8f, 0x70, 0xc3, 0xea,
0x39, 0x8e, 0x3c, 0x85, 0x6e, 0x3a, 0x9b, 0x97, 0x3f, 0x8e, 0x0d, 0xab, 0x46, 0xab, 0x69, 0x1b,
0x1f, 0x50, 0x3f, 0xfa, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x11, 0x49, 0xc5, 0xab, 0xf9, 0x15,
0x00, 0x00,
}

View File

@@ -5,6 +5,7 @@ package types;
// https://github.com/gogo/protobuf/blob/master/extensions.md
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "github.com/tendermint/tmlibs/common/types.proto";
import "github.com/tendermint/tendermint/crypto/merkle/merkle.proto";
// This file is copied from http://github.com/tendermint/abci
// NOTE: When using custom types, mind the warnings.
@@ -143,7 +144,7 @@ message ResponseQuery {
int64 index = 5;
bytes key = 6;
bytes value = 7;
bytes proof = 8;
merkle.Proof proof = 8;
int64 height = 9;
}

View File

@@ -39,7 +39,7 @@ const (
// genesis, chain_id, priv_val
var config *cfg.Config // NOTE: must be reset for each _test.go file
var ensureTimeout = time.Second * 1 // must be in seconds because CreateEmptyBlocksInterval is
var ensureTimeout = time.Second * 5 // must be in seconds because CreateEmptyBlocksInterval is
func ensureDir(dir string, mode os.FileMode) {
if err := cmn.EnsureDir(dir, mode); err != nil {

6
crypto/merkle/compile.sh Normal file
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

100
crypto/merkle/merkle.pb.go Normal file
View File

@@ -0,0 +1,100 @@
// Code generated by protoc-gen-go. 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
*/
package merkle
import proto "github.com/golang/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.ProtoPackageIsVersion2 // please upgrade the proto package
type ProofOp struct {
Type string `protobuf:"bytes,1,opt,name=type" json:"type,omitempty"`
Key []byte `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 fileDescriptor0, []int{0} }
func (m *ProofOp) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *ProofOp) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
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,omitempty"`
}
func (m *Proof) Reset() { *m = Proof{} }
func (m *Proof) String() string { return proto.CompactTextString(m) }
func (*Proof) ProtoMessage() {}
func (*Proof) Descriptor() ([]byte, []int) { return fileDescriptor0, []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", fileDescriptor0) }
var fileDescriptor0 = []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, 0x0a,
0x8c, 0x1a, 0x3c, 0x41, 0x20, 0x26, 0x48, 0x55, 0x4a, 0x62, 0x49, 0xa2, 0x04, 0x33, 0x58, 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,
0xe0, 0x7d, 0x7c, 0x4e, 0xc4, 0x00, 0x00, 0x00,
}

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;
bytes key = 2;
bytes data = 3;
}
message Proof {
repeated ProofOp ops = 1 [(gogoproto.nullable)=false];
}

124
crypto/merkle/proof.go Normal file
View File

@@ -0,0 +1,124 @@
package merkle
import (
"bytes"
cmn "github.com/tendermint/tmlibs/common"
)
//----------------------------------------
// ProofOp gets converted to an instance of ProofOperator:
type ProofOperator interface {
Run([][]byte) ([][]byte, error)
GetKey() []byte
ProofOp() ProofOp
}
//----------------------------------------
// Operations on a list of ProofOperators
type ProofOperators []ProofOperator
func (poz ProofOperators) VerifyValue(root []byte, keypath string, value []byte) (err error) {
return poz.Verify(root, keypath, [][]byte{value})
}
func (poz ProofOperators) Verify(root []byte, keypath string, args [][]byte) (err error) {
keys, err := KeyPathToKeys(keypath)
if err != nil {
return
}
for i, op := range poz {
key := op.GetKey()
if len(key) != 0 {
if !bytes.Equal(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])
}
if len(keys) != 0 {
return cmn.NewError("Keypath not consumed all")
}
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) 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, keypath string, value []byte) (err error) {
return prt.Verify(proof, root, keypath, [][]byte{value})
}
// TODO In the long run we'll need a method of classifcation of ops,
// whether existence or absence or perhaps a third?
func (prt *ProofRuntime) VerifyAbsence(proof *Proof, keypath string, root []byte) (err error) {
return prt.Verify(proof, root, keypath, nil)
}
func (prt *ProofRuntime) Verify(proof *Proof, root []byte, keypath string, args [][]byte) (err error) {
poz, err := prt.DecodeProof(proof)
if err != nil {
return cmn.ErrorWrap(err, "decoding proof")
}
return poz.Verify(root, keypath, args)
}
// 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.RegisterOpDecoder(ProofOpSimpleValue, SimpleValueOpDecoder)
return
}

View File

@@ -0,0 +1,107 @@
package merkle
import (
"encoding/hex"
"fmt"
"net/url"
"strings"
cmn "github.com/tendermint/tmlibs/common"
)
/*
For generalized Merkle proofs, each layer of the proof may require an
optional key. The key may be encoded either by URL-encoding or
(upper-case) hex-encoding.
TODO: In the future, more encodings may be supported, like base32 (e.g.
/32:)
For example, for a Cosmos-SDK application where the first two proof layers
are SimpleValueOps, and the third proof layer is an IAVLValueOp, the keys
might look like:
0: []byte("App")
1: []byte("IBC")
2: []byte{0x01, 0x02, 0x03}
Assuming that we know that the first two layers are always ASCII texts, we
probably want to use URLEncoding for those, whereas the third layer will
require HEX encoding for efficient representation.
kp := new(KeyPath)
kp.AppendKey([]byte("App"), KeyEncodingURL)
kp.AppendKey([]byte("IBC"), KeyEncodingURL)
kp.AppendKey([]byte{0x01, 0x02, 0x03}, KeyEncodingURL)
kp.String() // Should return "/App/IBC/x:010203"
NOTE: All encodings *MUST* work compatibly, such that you can choose to use
whatever encoding, and the decoded keys will always be the same. In other
words, it's just as good to encode all three keys using URL encoding or HEX
encoding... it just wouldn't be optimal in terms of readability or space
efficiency.
NOTE: Punycode will never be supported here, because not all values can be
decoded. For example, no string decodes to the string "xn--blah" in
Punycode.
*/
type keyEncoding int
const (
KeyEncodingURL keyEncoding = iota
KeyEncodingHex
KeyEncodingMax
)
type Key struct {
name []byte
enc keyEncoding
}
type KeyPath []Key
func (pth KeyPath) AppendKey(key []byte, enc keyEncoding) KeyPath {
return append(pth, Key{key, enc})
}
func (pth KeyPath) String() string {
res := ""
for _, key := range pth {
switch key.enc {
case KeyEncodingURL:
res += "/" + url.PathEscape(string(key.name))
case KeyEncodingHex:
res += "/x:" + fmt.Sprintf("%X", key.name)
default:
panic("unexpected key encoding type")
}
}
return res
}
func KeyPathToKeys(path string) (keys [][]byte, err error) {
if path == "" || path[0] != '/' {
return nil, cmn.NewError("key path string must start with a forward slash '/'")
}
parts := strings.Split(path[1:], "/")
keys = make([][]byte, len(parts))
for i, part := range parts {
if strings.HasPrefix(part, "x:") {
hexPart := part[2:]
key, err := hex.DecodeString(hexPart)
if err != nil {
return nil, cmn.ErrorWrap(err, "decoding hex-encoded part #%d: /%s", i, part)
}
keys[i] = key
} else {
key, err := url.PathUnescape(part)
if err != nil {
return nil, cmn.ErrorWrap(err, "decoding url-encoded part #%d: /%s", i, part)
}
keys[i] = []byte(key) // TODO Test this with random bytes, I'm not sure that it works for arbitrary bytes...
}
}
return keys, nil
}

View File

@@ -0,0 +1,41 @@
package merkle
import (
"math/rand"
"testing"
"github.com/stretchr/testify/require"
)
func TestKeyPath(t *testing.T) {
var path KeyPath
keys := make([][]byte, 10)
alphanum := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
for d := 0; d < 1e4; d++ {
path = nil
for i := range keys {
enc := keyEncoding(rand.Intn(int(KeyEncodingMax)))
keys[i] = make([]byte, rand.Uint32()%20)
switch enc {
case KeyEncodingURL:
for j := range keys[i] {
keys[i][j] = alphanum[rand.Intn(len(alphanum))]
}
case KeyEncodingHex:
rand.Read(keys[i])
default:
panic("Unexpected encoding")
}
path = path.AppendKey(keys[i], enc)
}
res, err := KeyPathToKeys(path.String())
require.Nil(t, err)
for i, key := range keys {
require.Equal(t, key, res[i])
}
}
}

View File

@@ -0,0 +1,91 @@
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.
key []byte
// To encode in ProofOp.Data
Proof *SimpleProof `json:"simple-proof"`
}
var _ ProofOperator = SimpleValueOp{}
func NewSimpleValueOp(key []byte, proof *SimpleProof) SimpleValueOp {
return SimpleValueOp{
key: key,
Proof: proof,
}
}
func SimpleValueOpDecoder(pop ProofOp) (ProofOperator, error) {
if pop.Type != ProofOpSimpleValue {
return nil, cmn.NewError("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpSimpleValue)
}
var op SimpleValueOp // a bit strange as we'll discard this, but it works.
err := cdc.UnmarshalBinary(pop.Data, &op)
if err != nil {
return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into SimpleValueOp")
}
return NewSimpleValueOp(pop.Key, op.Proof), nil
}
func (op SimpleValueOp) ProofOp() ProofOp {
bz := cdc.MustMarshalBinary(op)
return ProofOp{
Type: ProofOpSimpleValue,
Key: op.key,
Data: bz,
}
}
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() []byte {
return op.key
}

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.

View File

@@ -6,8 +6,8 @@ import (
cmn "github.com/tendermint/tmlibs/common"
. "github.com/tendermint/tmlibs/test"
"testing"
"github.com/tendermint/tendermint/crypto/tmhash"
"testing"
)
type testItem []byte
@@ -39,16 +39,16 @@ func TestSimpleProof(t *testing.T) {
proof := proofs[i]
// Verify success
ok := proof.Verify(i, total, itemHash, rootHash)
if !ok {
t.Errorf("Verification failed for index %v.", i)
err := proof.Verify(rootHash, i, total, itemHash)
if err != nil {
t.Errorf("Verification failed: %v.", err)
}
// Wrong item index should make it fail
{
ok = proof.Verify((i+1)%total, total, itemHash, rootHash)
if ok {
t.Errorf("Expected verification to fail for wrong index %v.", i)
err = proof.Verify(rootHash, (i+1)%total, total, itemHash)
if err == nil {
t.Errorf("Expected verification to fail for wrong index %v", i)
}
}
@@ -56,9 +56,9 @@ func TestSimpleProof(t *testing.T) {
origAunts := proof.Aunts
proof.Aunts = append(proof.Aunts, cmn.RandBytes(32))
{
ok = proof.Verify(i, total, itemHash, rootHash)
if ok {
t.Errorf("Expected verification to fail for wrong trail length.")
err = proof.Verify(rootHash, i, total, itemHash)
if err == nil {
t.Errorf("Expected verification to fail for wrong trail length")
}
}
proof.Aunts = origAunts
@@ -66,22 +66,22 @@ func TestSimpleProof(t *testing.T) {
// Trail too short should make it fail
proof.Aunts = proof.Aunts[0 : len(proof.Aunts)-1]
{
ok = proof.Verify(i, total, itemHash, rootHash)
if ok {
t.Errorf("Expected verification to fail for wrong trail length.")
err = proof.Verify(rootHash, i, total, itemHash)
if err == nil {
t.Errorf("Expected verification to fail for wrong trail length")
}
}
proof.Aunts = origAunts
// Mutating the itemHash should make it fail.
ok = proof.Verify(i, total, MutateByteSlice(itemHash), rootHash)
if ok {
err = proof.Verify(rootHash, i, total, MutateByteSlice(itemHash))
if err == nil {
t.Errorf("Expected verification to fail for mutated leaf hash")
}
// Mutating the rootHash should make it fail.
ok = proof.Verify(i, total, itemHash, MutateByteSlice(rootHash))
if ok {
err = proof.Verify(MutateByteSlice(rootHash), i, total, itemHash)
if err == nil {
t.Errorf("Expected verification to fail for mutated root hash")
}
}

16
crypto/merkle/wire.go Normal file
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.
}

View File

@@ -408,7 +408,7 @@ In go:
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
if reqQuery.Prove {
value, proof, exists := app.state.Proof(reqQuery.Data)
value, proof, exists := app.state.GetWithProof(reqQuery.Data)
resQuery.Index = -1 // TODO make Proof return index
resQuery.Key = reqQuery.Data
resQuery.Value = value
@@ -437,22 +437,20 @@ In Java:
ResponseQuery requestQuery(RequestQuery req) {
final boolean isProveQuery = req.getProve();
final ResponseQuery.Builder responseBuilder = ResponseQuery.newBuilder();
byte[] queryData = req.getData().toByteArray();
if (isProveQuery) {
com.app.example.ProofResult proofResult = generateProof(req.getData().toByteArray());
final byte[] proofAsByteArray = proofResult.getAsByteArray();
responseBuilder.setProof(ByteString.copyFrom(proofAsByteArray));
com.app.example.QueryResultWithProof result = generateQueryResultWithProof(queryData);
responseBuilder.setIndex(result.getLeftIndex());
responseBuilder.setKey(req.getData());
responseBuilder.setValue(ByteString.copyFrom(proofResult.getData()));
responseBuilder.setValue(result.getValueOrNull(0));
responseBuilder.setHeight(result.getHeight());
responseBuilder.setProof(result.getProof());
responseBuilder.setLog(result.getLogValue());
} else {
byte[] queryData = req.getData().toByteArray();
final com.app.example.QueryResult result = generateQueryResult(queryData);
com.app.example.QueryResult result = generateQueryResult(queryData);
responseBuilder.setIndex(result.getIndex());
responseBuilder.setValue(ByteString.copyFrom(result.getValue()));
responseBuilder.setValue(result.getValue());
responseBuilder.setLog(result.getLogValue());
}

View File

@@ -41,6 +41,12 @@ func (e errMissingValidators) Error() string {
e.chainID, e.height)
}
type errEmptyTree struct{}
func (e errEmptyTree) Error() string {
return "Tree is empty"
}
//----------------------------------------
// Methods for above error types
@@ -110,3 +116,18 @@ func IsErrMissingValidators(err error) bool {
}
return false
}
//-----------------
// ErrEmptyTree
func ErrEmptyTree() error {
return cmn.ErrorWrap(errEmptyTree{}, "")
}
func IsErrEmptyTree(err error) bool {
if err_, ok := err.(cmn.Error); ok {
_, ok := err_.Data().(errEmptyTree)
return ok
}
return false
}

View File

@@ -1,24 +0,0 @@
package proxy
import (
cmn "github.com/tendermint/tmlibs/common"
)
type errNoData struct{}
func (e errNoData) Error() string {
return "No data returned for query"
}
// IsErrNoData checks whether an error is due to a query returning empty data
func IsErrNoData(err error) bool {
if err_, ok := err.(cmn.Error); ok {
_, ok := err_.Data().(errNoData)
return ok
}
return false
}
func ErrNoData() error {
return cmn.ErrorWrap(errNoData{}, "")
}

23
lite/proxy/proof.go Normal file
View File

@@ -0,0 +1,23 @@
package proxy
import (
"github.com/tendermint/iavl"
"github.com/tendermint/tendermint/crypto/merkle"
)
func defaultProofRuntime() *merkle.ProofRuntime {
prt := merkle.NewProofRuntime()
prt.RegisterOpDecoder(
merkle.ProofOpSimpleValue,
merkle.SimpleValueOpDecoder,
)
prt.RegisterOpDecoder(
iavl.ProofOpIAVLValue,
iavl.IAVLValueOpDecoder,
)
prt.RegisterOpDecoder(
iavl.ProofOpIAVLAbsence,
iavl.IAVLAbsenceOpDecoder,
)
return prt
}

View File

@@ -3,127 +3,95 @@ package proxy
import (
"fmt"
"github.com/pkg/errors"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/lite"
lerr "github.com/tendermint/tendermint/lite/errors"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types"
)
// KeyProof represents a proof of existence or absence of a single key.
// Copied from iavl repo. TODO
type KeyProof interface {
// Verify verfies the proof is valid. To verify absence,
// the value should be nil.
Verify(key, value, root []byte) error
// Root returns the root hash of the proof.
Root() []byte
// Serialize itself
Bytes() []byte
}
// GetWithProof will query the key on the given node, and verify it has
// a valid proof, as defined by the certifier.
//
// If there is any error in checking, returns an error.
// If val is non-empty, proof should be KeyExistsProof
// If val is empty, proof should be KeyMissingProof
func GetWithProof(key []byte, reqHeight int64, node rpcclient.Client,
func GetWithProof(prt *merkle.ProofRuntime, key []byte, reqHeight int64, node rpcclient.Client,
cert lite.Certifier) (
val cmn.HexBytes, height int64, proof KeyProof, err error) {
val cmn.HexBytes, height int64, proof *merkle.Proof, err error) {
if reqHeight < 0 {
err = errors.Errorf("Height cannot be negative")
err = cmn.NewError("Height cannot be negative")
return
}
_resp, proof, err := GetWithProofOptions("/key", key,
rpcclient.ABCIQueryOptions{Height: int64(reqHeight)},
res, err := GetWithProofOptions(prt, "/key", key,
rpcclient.ABCIQueryOptions{Height: int64(reqHeight), Prove: true},
node, cert)
if _resp != nil {
resp := _resp.Response
val, height = resp.Value, resp.Height
if err != nil {
return
}
resp := res.Response
val, height, proof = resp.Value, resp.Height, resp.Proof
return val, height, proof, err
}
// GetWithProofOptions is useful if you want full access to the ABCIQueryOptions
func GetWithProofOptions(path string, key []byte, opts rpcclient.ABCIQueryOptions,
// GetWithProofOptions is useful if you want full access to the ABCIQueryOptions.
// XXX Usage of path? It's not used, and sometimes it's /, sometimes /key, sometimes /store.
func GetWithProofOptions(prt *merkle.ProofRuntime, path string, key []byte, opts rpcclient.ABCIQueryOptions,
node rpcclient.Client, cert lite.Certifier) (
*ctypes.ResultABCIQuery, KeyProof, error) {
*ctypes.ResultABCIQuery, error) {
_resp, err := node.ABCIQueryWithOptions(path, key, opts)
if !opts.Prove {
return nil, cmn.NewError("require ABCIQueryOptions.Prove to be true")
}
res, err := node.ABCIQueryWithOptions(path, key, opts)
if err != nil {
return nil, nil, err
return nil, err
}
resp := _resp.Response
resp := res.Response
// make sure the proof is the proper height
// Validate the response, e.g. height.
if resp.IsErr() {
err = errors.Errorf("Query error for key %d: %d", key, resp.Code)
return nil, nil, err
err = cmn.NewError("Query error for key %d: %d", key, resp.Code)
return nil, err
}
if len(resp.Key) == 0 || len(resp.Proof) == 0 {
return nil, nil, ErrNoData()
if len(resp.Key) == 0 || resp.Proof == nil {
return nil, lerr.ErrEmptyTree()
}
if resp.Height == 0 {
return nil, nil, errors.New("Height returned is zero")
return nil, cmn.NewError("Height returned is zero")
}
// AppHash for height H is in header H+1
signedHeader, err := GetCertifiedCommit(resp.Height+1, node, cert)
if err != nil {
return nil, nil, err
}
_ = signedHeader
return &ctypes.ResultABCIQuery{Response: resp}, nil, nil
/* // TODO refactor so iavl stuff is not in tendermint core
// https://github.com/tendermint/tendermint/issues/1183
if len(resp.Value) > 0 {
// The key was found, construct a proof of existence.
proof, err := iavl.ReadKeyProof(resp.Proof)
if err != nil {
return nil, nil, errors.Wrap(err, "Error reading proof")
}
eproof, ok := proof.(*iavl.KeyExistsProof)
if !ok {
return nil, nil, errors.New("Expected KeyExistsProof for non-empty value")
}
// Validate the proof against the certified header to ensure data integrity.
err = eproof.Verify(resp.Key, resp.Value, signedHeader.AppHash)
if err != nil {
return nil, nil, errors.Wrap(err, "Couldn't verify proof")
}
return &ctypes.ResultABCIQuery{Response: resp}, eproof, nil
}
// The key wasn't found, construct a proof of non-existence.
proof, err := iavl.ReadKeyProof(resp.Proof)
if err != nil {
return nil, nil, errors.Wrap(err, "Error reading proof")
}
aproof, ok := proof.(*iavl.KeyAbsentProof)
if !ok {
return nil, nil, errors.New("Expected KeyAbsentProof for empty Value")
return nil, err
}
// Validate the proof against the certified header to ensure data integrity.
err = aproof.Verify(resp.Key, nil, signedHeader.AppHash)
if err != nil {
return nil, nil, errors.Wrap(err, "Couldn't verify proof")
if resp.Value != nil {
// Value exists
// XXX How do we encode the key into a string...
err = prt.VerifyValue(resp.Proof, signedHeader.AppHash, resp.Value, string(resp.Key))
if err != nil {
return nil, cmn.ErrorWrap(err, "Couldn't verify value proof")
}
return &ctypes.ResultABCIQuery{Response: resp}, nil
} else {
// Value absent
// Validate the proof against the certified header to ensure data integrity.
// XXX How do we encode the key into a string...
err = prt.VerifyAbsence(resp.Proof, signedHeader.AppHash, string(resp.Key))
if err != nil {
return nil, cmn.ErrorWrap(err, "Couldn't verify absence proof")
}
return &ctypes.ResultABCIQuery{Response: resp}, nil
}
return &ctypes.ResultABCIQuery{Response: resp}, aproof, ErrNoData()
*/
}
// GetCertifiedCommit gets the signed header for a given height and certifies

View File

@@ -4,12 +4,12 @@ import (
"fmt"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/abci/example/kvstore"
"github.com/tendermint/tendermint/lite"
certclient "github.com/tendermint/tendermint/lite/client"
nm "github.com/tendermint/tendermint/node"
@@ -20,6 +20,7 @@ import (
var node *nm.Node
var chainID = "tendermint_test" // TODO use from config.
var waitForEventTimeout = 5 * time.Second
// TODO fix tests!!
@@ -38,70 +39,85 @@ func kvstoreTx(k, v []byte) []byte {
return []byte(fmt.Sprintf("%s=%s", k, v))
}
func _TestAppProofs(t *testing.T) {
func TestAppProofs(t *testing.T) {
assert, require := assert.New(t), require.New(t)
prt := defaultProofRuntime()
cl := client.NewLocal(node)
client.WaitForHeight(cl, 1, nil)
// This sets up our trust on the node based on some past point.
source := certclient.NewProvider(chainID, cl)
seed, err := source.LatestFullCommit(chainID, 1, 1)
require.NoError(err, "%#v", err)
cert := lite.NewBaseCertifier(chainID, seed.Height(), seed.Validators)
// Wait for tx confirmation.
done := make(chan int64)
go func() {
evtTyp := types.EventTx
_, err = client.WaitForOneEvent(cl, evtTyp, waitForEventTimeout)
require.Nil(err, "%#v", err)
close(done)
}()
// Submit a transaction.
k := []byte("my-key")
v := []byte("my-value")
tx := kvstoreTx(k, v)
br, err := cl.BroadcastTxCommit(tx)
require.NoError(err, "%+v", err)
require.NoError(err, "%#v", err)
require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx)
require.EqualValues(0, br.DeliverTx.Code)
brh := br.Height
// This sets up our trust on the node based on some past point.
source := certclient.NewProvider(chainID, cl)
seed, err := source.LatestFullCommit(chainID, brh-2, brh-2)
require.NoError(err, "%+v", err)
cert := lite.NewBaseCertifier("my-chain", seed.Height(), seed.Validators)
client.WaitForHeight(cl, 3, nil)
// Fetch latest after tx commit.
<-done
latest, err := source.LatestFullCommit(chainID, 1, 1<<63-1)
require.NoError(err, "%+v", err)
require.NoError(err, "%#v", err)
rootHash := latest.SignedHeader.AppHash
if rootHash == nil {
// Fetch one block later, AppHash hasn't been committed yet.
// TODO find a way to avoid doing this.
client.WaitForHeight(cl, latest.SignedHeader.Height+1, nil)
latest, err = source.LatestFullCommit(chainID, latest.SignedHeader.Height+1, 1<<63-1)
require.NoError(err, "%#v", err)
rootHash = latest.SignedHeader.AppHash
}
require.NotNil(rootHash)
// verify a query before the tx block has no data (and valid non-exist proof)
bs, height, proof, err := GetWithProof(k, brh-1, cl, cert)
fmt.Println(bs, height, proof, err)
require.NotNil(err)
require.True(IsErrNoData(err), err.Error())
bs, height, proof, err := GetWithProof(prt, k, brh-1, cl, cert)
require.NoError(err, "%#v", err)
// require.NotNil(proof)
// TODO: Ensure that *some* keys will be there, ensuring that proof is nil,
// (currently there's a race condition)
// and ensure that proof proves absence of k.
require.Nil(bs)
// but given that block it is good
bs, height, proof, err = GetWithProof(k, brh, cl, cert)
require.NoError(err, "%+v", err)
bs, height, proof, err = GetWithProof(prt, k, brh, cl, cert)
require.NoError(err, "%#v", err)
require.NotNil(proof)
require.True(height >= int64(latest.Height()))
require.Equal(height, brh)
// Alexis there is a bug here, somehow the above code gives us rootHash = nil
// and proof.Verify doesn't care, while proofNotExists.Verify fails.
// I am hacking this in to make it pass, but please investigate further.
rootHash = proof.Root()
//err = wire.ReadBinaryBytes(bs, &data)
//require.NoError(err, "%+v", err)
assert.EqualValues(v, bs)
err = proof.Verify(k, bs, rootHash)
assert.NoError(err, "%+v", err)
err = prt.VerifyValue(proof, rootHash, bs, string(k)) // XXX key encoding
assert.NoError(err, "%#v", err)
// Test non-existing key.
missing := []byte("my-missing-key")
bs, _, proof, err = GetWithProof(missing, 0, cl, cert)
require.True(IsErrNoData(err))
bs, _, proof, err = GetWithProof(prt, missing, 0, cl, cert)
require.NoError(err)
require.Nil(bs)
require.NotNil(proof)
err = proof.Verify(missing, nil, rootHash)
assert.NoError(err, "%+v", err)
err = proof.Verify(k, nil, rootHash)
assert.Error(err)
err = prt.Verify(proof, rootHash, nil, string(missing)) // XXX VerifyAbsence(), keyencoding
assert.NoError(err, "%#v", err)
err = prt.Verify(proof, rootHash, nil, string(k)) // XXX VerifyAbsence(), keyencoding
assert.Error(err, "%#v", err)
}
func _TestTxProofs(t *testing.T) {
func TestTxProofs(t *testing.T) {
assert, require := assert.New(t), require.New(t)
cl := client.NewLocal(node)
@@ -109,15 +125,15 @@ func _TestTxProofs(t *testing.T) {
tx := kvstoreTx([]byte("key-a"), []byte("value-a"))
br, err := cl.BroadcastTxCommit(tx)
require.NoError(err, "%+v", err)
require.NoError(err, "%#v", err)
require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx)
require.EqualValues(0, br.DeliverTx.Code)
brh := br.Height
source := certclient.NewProvider(chainID, cl)
seed, err := source.LatestFullCommit(chainID, brh-2, brh-2)
require.NoError(err, "%+v", err)
cert := lite.NewBaseCertifier("my-chain", seed.Height(), seed.Validators)
require.NoError(err, "%#v", err)
cert := lite.NewBaseCertifier(chainID, seed.Height(), seed.Validators)
// First let's make sure a bogus transaction hash returns a valid non-existence proof.
key := types.Tx([]byte("bogus")).Hash()
@@ -128,12 +144,12 @@ func _TestTxProofs(t *testing.T) {
// Now let's check with the real tx hash.
key = types.Tx(tx).Hash()
res, err = cl.Tx(key, true)
require.NoError(err, "%+v", err)
require.NoError(err, "%#v", err)
require.NotNil(res)
err = res.Proof.Validate(key)
assert.NoError(err, "%+v", err)
assert.NoError(err, "%#v", err)
commit, err := GetCertifiedCommit(br.Height, cl, cert)
require.Nil(err, "%+v", err)
require.Nil(err, "%#v", err)
require.Equal(res.Proof.RootHash, commit.Header.DataHash)
}

View File

@@ -3,6 +3,7 @@ package proxy
import (
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/lite"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
@@ -15,6 +16,7 @@ var _ rpcclient.Client = Wrapper{}
type Wrapper struct {
rpcclient.Client
cert *lite.InquiringCertifier
prt *merkle.ProofRuntime
}
// SecureClient uses a given certifier to wrap an connection to an untrusted
@@ -22,7 +24,8 @@ type Wrapper struct {
//
// If it is wrapping an HTTP rpcclient, it will also wrap the websocket interface
func SecureClient(c rpcclient.Client, cert *lite.InquiringCertifier) Wrapper {
wrap := Wrapper{c, cert}
prt := defaultProofRuntime()
wrap := Wrapper{c, cert, prt}
// TODO: no longer possible as no more such interface exposed....
// if we wrap http client, then we can swap out the event switch to filter
// if hc, ok := c.(*rpcclient.HTTP); ok {
@@ -36,7 +39,7 @@ func SecureClient(c rpcclient.Client, cert *lite.InquiringCertifier) Wrapper {
func (w Wrapper) ABCIQueryWithOptions(path string, data cmn.HexBytes,
opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
res, _, err := GetWithProofOptions(path, data, opts, w.Client, w.cert)
res, err := GetWithProofOptions(w.prt, path, data, opts, w.Client, w.cert)
return res, err
}

View File

@@ -74,7 +74,7 @@ func (c *HTTP) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuer
func (c *HTTP) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
result := new(ctypes.ResultABCIQuery)
_, err := c.rpc.Call("abci_query",
map[string]interface{}{"path": path, "data": data, "height": opts.Height, "trusted": opts.Trusted},
map[string]interface{}{"path": path, "data": data, "height": opts.Height, "prove": opts.Prove},
result)
if err != nil {
return nil, errors.Wrap(err, "ABCIQuery")

View File

@@ -61,7 +61,7 @@ func (c *Local) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQue
}
func (Local) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
return core.ABCIQuery(path, data, opts.Height, opts.Trusted)
return core.ABCIQuery(path, data, opts.Height, opts.Prove)
}
func (Local) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {

View File

@@ -31,10 +31,13 @@ func (a ABCIApp) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQu
}
func (a ABCIApp) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
q := a.App.Query(abci.RequestQuery{data, path, opts.Height, opts.Trusted})
q := a.App.Query(abci.RequestQuery{data, path, opts.Height, opts.Prove})
return &ctypes.ResultABCIQuery{q}, nil
}
// NOTE: Caller should call a.App.Commit() separately,
// this function does not actually wait for a commit.
// TODO: Make it wait for a commit and set res.Height appropriately.
func (a ABCIApp) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
res := ctypes.ResultBroadcastTxCommit{}
res.CheckTx = a.App.CheckTx(tx)
@@ -42,6 +45,7 @@ func (a ABCIApp) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit
return &res, nil
}
res.DeliverTx = a.App.DeliverTx(tx)
res.Height = -1 // TODO
return &res, nil
}
@@ -86,7 +90,7 @@ func (m ABCIMock) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQ
}
func (m ABCIMock) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
res, err := m.Query.GetResponse(QueryArgs{path, data, opts.Height, opts.Trusted})
res, err := m.Query.GetResponse(QueryArgs{path, data, opts.Height, opts.Prove})
if err != nil {
return nil, err
}
@@ -133,10 +137,10 @@ func NewABCIRecorder(client client.ABCIClient) *ABCIRecorder {
}
type QueryArgs struct {
Path string
Data cmn.HexBytes
Height int64
Trusted bool
Path string
Data cmn.HexBytes
Height int64
Prove bool
}
func (r *ABCIRecorder) addCall(call Call) {
@@ -161,7 +165,7 @@ func (r *ABCIRecorder) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts
res, err := r.Client.ABCIQueryWithOptions(path, data, opts)
r.addCall(Call{
Name: "abci_query",
Args: QueryArgs{path, data, opts.Height, opts.Trusted},
Args: QueryArgs{path, data, opts.Height, opts.Prove},
Response: res,
Error: err,
})

View File

@@ -51,7 +51,7 @@ func TestABCIMock(t *testing.T) {
assert.Equal("foobar", err.Error())
// query always returns the response
_query, err := m.ABCIQueryWithOptions("/", nil, client.ABCIQueryOptions{Trusted: true})
_query, err := m.ABCIQueryWithOptions("/", nil, client.ABCIQueryOptions{Prove: false})
query := _query.Response
require.Nil(err)
require.NotNil(query)
@@ -98,7 +98,7 @@ func TestABCIRecorder(t *testing.T) {
_, err := r.ABCIInfo()
assert.Nil(err, "expected no err on info")
_, err = r.ABCIQueryWithOptions("path", cmn.HexBytes("data"), client.ABCIQueryOptions{Trusted: false})
_, err = r.ABCIQueryWithOptions("path", cmn.HexBytes("data"), client.ABCIQueryOptions{Prove: false})
assert.NotNil(err, "expected error on query")
require.Equal(2, len(r.Calls))
@@ -122,7 +122,7 @@ func TestABCIRecorder(t *testing.T) {
require.True(ok)
assert.Equal("path", qa.Path)
assert.EqualValues("data", qa.Data)
assert.False(qa.Trusted)
assert.False(qa.Prove)
// now add some broadcasts (should all err)
txs := []types.Tx{{1}, {2}, {3}}
@@ -173,9 +173,17 @@ func TestABCIApp(t *testing.T) {
require.NotNil(res.DeliverTx)
assert.True(res.DeliverTx.IsOK())
// commit
// TODO: This may not be necessary in the future
if res.Height == -1 {
m.App.Commit()
}
// check the key
_qres, err := m.ABCIQueryWithOptions("/key", cmn.HexBytes(key), client.ABCIQueryOptions{Trusted: true})
_qres, err := m.ABCIQueryWithOptions("/key", cmn.HexBytes(key), client.ABCIQueryOptions{Prove: true})
qres := _qres.Response
require.Nil(err)
assert.EqualValues(value, qres.Value)
// XXX Check proof
}

View File

@@ -1,3 +1,5 @@
package mock
/*
package mock returns a Client implementation that
accepts various (mock) implementations of the various methods.
@@ -11,7 +13,6 @@ For real clients, you probably want the "http" package. If you
want to directly call a tendermint node in process, you can use the
"local" package.
*/
package mock
import (
"reflect"
@@ -87,7 +88,7 @@ func (c Client) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQue
}
func (c Client) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
return core.ABCIQuery(path, data, opts.Height, opts.Trusted)
return core.ABCIQuery(path, data, opts.Height, opts.Prove)
}
func (c Client) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {

View File

@@ -166,10 +166,10 @@ func TestAppCalls(t *testing.T) {
if err := client.WaitForHeight(c, apph, nil); err != nil {
t.Error(err)
}
_qres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Trusted: true})
_qres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Prove: false})
qres := _qres.Response
if assert.Nil(err) && assert.True(qres.IsOK()) {
// assert.Equal(k, data.GetKey()) // only returned for proofs
assert.Equal(k, qres.Key)
assert.EqualValues(v, qres.Value)
}
@@ -221,10 +221,12 @@ func TestAppCalls(t *testing.T) {
assert.Equal(block.Block.LastCommit, commit2.Commit)
// and we got a proof that works!
_pres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Trusted: false})
_pres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Prove: true})
pres := _pres.Response
assert.Nil(err)
assert.True(pres.IsOK())
// XXX Test proof
}
}
@@ -357,7 +359,7 @@ func TestTxSearch(t *testing.T) {
require.Len(t, result.Txs, 0)
// we query using a tag (see kvstore application)
result, err = c.TxSearch("app.creator='jae'", false, 1, 30)
result, err = c.TxSearch("app.creator='Cosmoshi Netowoko'", false, 1, 30)
require.Nil(t, err, "%+v", err)
if len(result.Txs) == 0 {
t.Fatal("expected a lot of transactions")

View File

@@ -3,10 +3,9 @@ package client
// ABCIQueryOptions can be used to provide options for ABCIQuery call other
// than the DefaultABCIQueryOptions.
type ABCIQueryOptions struct {
Height int64
Trusted bool
Height int64
Prove bool
}
// DefaultABCIQueryOptions are latest height (0) and trusted equal to false
// (which will result in a proof being returned).
var DefaultABCIQueryOptions = ABCIQueryOptions{Height: 0, Trusted: false}
// DefaultABCIQueryOptions are latest height (0) and prove false.
var DefaultABCIQueryOptions = ABCIQueryOptions{Height: 0, Prove: false}

View File

@@ -10,7 +10,7 @@ import (
// Query the application for some information.
//
// ```shell
// curl 'localhost:26657/abci_query?path=""&data="abcd"&trusted=false'
// curl 'localhost:26657/abci_query?path=""&data="abcd"&prove=false'
// ```
//
// ```go
@@ -27,7 +27,6 @@ import (
// "response": {
// "log": "exists",
// "height": 0,
// "proof": "010114FED0DAD959F36091AD761C922ABA3CBF1D8349990101020103011406AA2262E2F448242DF2C2607C3CDC705313EE3B0001149D16177BC71E445476174622EA559715C293740C",
// "value": "61626364",
// "key": "61626364",
// "index": -1,
@@ -45,14 +44,14 @@ import (
// |-----------+--------+---------+----------+------------------------------------------------|
// | path | string | false | false | Path to the data ("/a/b/c") |
// | data | []byte | false | true | Data |
// | height | int64 | 0 | false | Height (0 means latest) |
// | trusted | bool | false | false | Does not include a proof of the data inclusion |
func ABCIQuery(path string, data cmn.HexBytes, height int64, trusted bool) (*ctypes.ResultABCIQuery, error) {
// | height | int64 | 0 | false | Height (0 means latest) |
// | prove | bool | false | false | Includes proof if true |
func ABCIQuery(path string, data cmn.HexBytes, height int64, prove bool) (*ctypes.ResultABCIQuery, error) {
resQuery, err := proxyAppQuery.QuerySync(abci.RequestQuery{
Path: path,
Data: data,
Height: height,
Prove: !trusted,
Prove: prove,
})
if err != nil {
return nil, err

View File

@@ -8,9 +8,9 @@ import (
"sync"
"time"
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/merkle"
"golang.org/x/crypto/ripemd160"
)
// Block defines the atomic unit of a Tendermint blockchain.
@@ -615,7 +615,7 @@ type hasher struct {
}
func (h hasher) Hash() []byte {
hasher := ripemd160.New()
hasher := tmhash.New()
if h.item != nil && !cmn.IsTypedNil(h.item) && !cmn.IsEmpty(h.item) {
bz, err := cdc.MarshalBinaryBare(h.item)
if err != nil {
@@ -627,7 +627,6 @@ func (h hasher) Hash() []byte {
}
}
return hasher.Sum(nil)
}
func aminoHash(item interface{}) []byte {