Skip to content

Commit

Permalink
Dragonberry fix
Browse files Browse the repository at this point in the history
  • Loading branch information
haasted committed Oct 14, 2022
1 parent 67ae28d commit 601cd6c
Show file tree
Hide file tree
Showing 10 changed files with 5,628 additions and 2 deletions.
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ require (

// https://docs.cosmos.network/v0.41/core/grpc_rest.html#grpc-server
replace google.golang.org/grpc => google.golang.org/grpc v1.33.2
replace github.com/confio/ics23/go => ./ics23

replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1

// replace github.com/e-money/bep3 => ../bep3
1 change: 1 addition & 0 deletions ics23/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vendor
20 changes: 20 additions & 0 deletions ics23/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.PHONY: protoc test

# make sure we turn on go modules
export GO111MODULE := on

# PROTOC_FLAGS := -I=.. -I=./vendor -I=$(GOPATH)/src
PROTOC_FLAGS := -I=.. -I=$(GOPATH)/src

test:
go test .

protoc:
# @go mod vendor
protoc --gocosmos_out=plugins=interfacetype+grpc,Mgoogle/protobuf/any.proto=github.com/gogo/protobuf/types:. $(PROTOC_FLAGS) ../proofs.proto

install-proto-dep:
@echo "Installing protoc-gen-gocosmos..."
@go install github.com/regen-network/cosmos-proto/protoc-gen-gocosmos


157 changes: 157 additions & 0 deletions ics23/compress.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package ics23

// IsCompressed returns true if the proof was compressed
func IsCompressed(proof *CommitmentProof) bool {
return proof.GetCompressed() != nil
}

// Compress will return a CompressedBatchProof if the input is BatchProof
// Otherwise it will return the input.
// This is safe to call multiple times (idempotent)
func Compress(proof *CommitmentProof) *CommitmentProof {
batch := proof.GetBatch()
if batch == nil {
return proof
}
return &CommitmentProof{
Proof: &CommitmentProof_Compressed{
Compressed: compress(batch),
},
}
}

// Decompress will return a BatchProof if the input is CompressedBatchProof
// Otherwise it will return the input.
// This is safe to call multiple times (idempotent)
func Decompress(proof *CommitmentProof) *CommitmentProof {
comp := proof.GetCompressed()
if comp != nil {
return &CommitmentProof{
Proof: &CommitmentProof_Batch{
Batch: decompress(comp),
},
}
}
return proof
}

func compress(batch *BatchProof) *CompressedBatchProof {
var centries []*CompressedBatchEntry
var lookup []*InnerOp
registry := make(map[string]int32)

for _, entry := range batch.Entries {
centry := compressEntry(entry, &lookup, registry)
centries = append(centries, centry)
}

return &CompressedBatchProof{
Entries: centries,
LookupInners: lookup,
}
}

func compressEntry(entry *BatchEntry, lookup *[]*InnerOp, registry map[string]int32) *CompressedBatchEntry {
if exist := entry.GetExist(); exist != nil {
return &CompressedBatchEntry{
Proof: &CompressedBatchEntry_Exist{
Exist: compressExist(exist, lookup, registry),
},
}
}

non := entry.GetNonexist()
return &CompressedBatchEntry{
Proof: &CompressedBatchEntry_Nonexist{
Nonexist: &CompressedNonExistenceProof{
Key: non.Key,
Left: compressExist(non.Left, lookup, registry),
Right: compressExist(non.Right, lookup, registry),
},
},
}
}

func compressExist(exist *ExistenceProof, lookup *[]*InnerOp, registry map[string]int32) *CompressedExistenceProof {
if exist == nil {
return nil
}
res := &CompressedExistenceProof{
Key: exist.Key,
Value: exist.Value,
Leaf: exist.Leaf,
Path: make([]int32, len(exist.Path)),
}
for i, step := range exist.Path {
res.Path[i] = compressStep(step, lookup, registry)
}
return res
}

func compressStep(step *InnerOp, lookup *[]*InnerOp, registry map[string]int32) int32 {
bz, err := step.Marshal()
if err != nil {
panic(err)
}
sig := string(bz)

// load from cache if there
if num, ok := registry[sig]; ok {
return num
}

// create new step if not there
num := int32(len(*lookup))
*lookup = append(*lookup, step)
registry[sig] = num
return num
}

func decompress(comp *CompressedBatchProof) *BatchProof {
lookup := comp.LookupInners

var entries []*BatchEntry

for _, centry := range comp.Entries {
entry := decompressEntry(centry, lookup)
entries = append(entries, entry)
}

return &BatchProof{
Entries: entries,
}
}

// TendermintSpec constrains the format from proofs-tendermint (crypto/merkle SimpleProof)
var TendermintSpec = &ProofSpec{
LeafSpec: &LeafOp{
Prefix: []byte{0},
PrehashKey: HashOp_NO_HASH,
Hash: HashOp_SHA256,
PrehashValue: HashOp_SHA256,
Length: LengthOp_VAR_PROTO,
},
InnerSpec: &InnerSpec{
ChildOrder: []int32{0, 1},
MinPrefixLength: 1,
MaxPrefixLength: 1,
ChildSize: 32, // (no length byte)
Hash: HashOp_SHA256,
},
}

func decompressExist(exist *CompressedExistenceProof, lookup []*InnerOp) *ExistenceProof {
if exist == nil {
return nil
}
res := &ExistenceProof{
Key: exist.Key,
Value: exist.Value,
Leaf: exist.Leaf,
Path: make([]*InnerOp, len(exist.Path)),
}
for i, step := range exist.Path {
res.Path[i] = lookup[step]
}
return res
}
9 changes: 9 additions & 0 deletions ics23/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module github.com/confio/ics23/go

go 1.14

require (
github.com/gogo/protobuf v1.3.1
github.com/pkg/errors v0.8.1
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
)
14 changes: 14 additions & 0 deletions ics23/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
174 changes: 174 additions & 0 deletions ics23/ics23.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/**
This implements the client side functions as specified in
https://github.com/cosmos/ics/tree/master/spec/ics-023-vector-commitments
In particular:
// Assumes ExistenceProof
type verifyMembership = (root: CommitmentRoot, proof: CommitmentProof, key: Key, value: Value) => boolean
// Assumes NonExistenceProof
type verifyNonMembership = (root: CommitmentRoot, proof: CommitmentProof, key: Key) => boolean
// Assumes BatchProof - required ExistenceProofs may be a subset of all items proven
type batchVerifyMembership = (root: CommitmentRoot, proof: CommitmentProof, items: Map<Key, Value>) => boolean
// Assumes BatchProof - required NonExistenceProofs may be a subset of all items proven
type batchVerifyNonMembership = (root: CommitmentRoot, proof: CommitmentProof, keys: Set<Key>) => boolean
We make an adjustment to accept a Spec to ensure the provided proof is in the format of the expected merkle store.
This can avoid an range of attacks on fake preimages, as we need to be careful on how to map key, value -> leaf
and determine neighbors
*/
package ics23

import (
"bytes"
"fmt"
)

// CommitmentRoot is a byte slice that represents the merkle root of a tree that can be used to validate proofs
type CommitmentRoot []byte

// VerifyMembership returns true iff
// proof is (contains) an ExistenceProof for the given key and value AND
// calculating the root for the ExistenceProof matches the provided CommitmentRoot
func VerifyMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentProof, key []byte, value []byte) bool {
// decompress it before running code (no-op if not compressed)
proof = Decompress(proof)
ep := getExistProofForKey(proof, key)
if ep == nil {
return false
}
err := ep.Verify(spec, root, key, value)
return err == nil
}

// VerifyNonMembership returns true iff
// proof is (contains) a NonExistenceProof
// both left and right sub-proofs are valid existence proofs (see above) or nil
// left and right proofs are neighbors (or left/right most if one is nil)
// provided key is between the keys of the two proofs
func VerifyNonMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentProof, key []byte) bool {
// decompress it before running code (no-op if not compressed)
proof = Decompress(proof)
np := getNonExistProofForKey(proof, key)
if np == nil {
return false
}
err := np.Verify(spec, root, key)
return err == nil
}

// BatchVerifyMembership will ensure all items are also proven by the CommitmentProof (which should be a BatchProof,
// unless there is one item, when a ExistenceProof may work)
func BatchVerifyMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentProof, items map[string][]byte) bool {
// decompress it before running code (no-op if not compressed) - once for batch
proof = Decompress(proof)
for k, v := range items {
valid := VerifyMembership(spec, root, proof, []byte(k), v)
if !valid {
return false
}
}
return true
}

// BatchVerifyNonMembership will ensure all items are also proven to not be in the Commitment by the CommitmentProof
// (which should be a BatchProof, unless there is one item, when a NonExistenceProof may work)
func BatchVerifyNonMembership(spec *ProofSpec, root CommitmentRoot, proof *CommitmentProof, keys [][]byte) bool {
// decompress it before running code (no-op if not compressed) - once for batch
proof = Decompress(proof)
for _, k := range keys {
valid := VerifyNonMembership(spec, root, proof, k)
if !valid {
return false
}
}
return true
}

// CombineProofs takes a number of commitment proofs (simple or batch) and
// converts them into a batch and compresses them.
//
// This is designed for proof generation libraries to create efficient batches
func CombineProofs(proofs []*CommitmentProof) (*CommitmentProof, error) {
var entries []*BatchEntry

for _, proof := range proofs {
if ex := proof.GetExist(); ex != nil {
entry := &BatchEntry{
Proof: &BatchEntry_Exist{
Exist: ex,
},
}
entries = append(entries, entry)
} else if non := proof.GetNonexist(); non != nil {
entry := &BatchEntry{
Proof: &BatchEntry_Nonexist{
Nonexist: non,
},
}
entries = append(entries, entry)
} else if batch := proof.GetBatch(); batch != nil {
entries = append(entries, batch.Entries...)
} else if comp := proof.GetCompressed(); comp != nil {
decomp := Decompress(proof)
entries = append(entries, decomp.GetBatch().Entries...)
} else {
return nil, fmt.Errorf("proof neither exist or nonexist: %#v", proof.GetProof())
}
}

batch := &CommitmentProof{
Proof: &CommitmentProof_Batch{
Batch: &BatchProof{
Entries: entries,
},
},
}

return Compress(batch), nil
}

func getExistProofForKey(proof *CommitmentProof, key []byte) *ExistenceProof {
switch p := proof.Proof.(type) {
case *CommitmentProof_Exist:
ep := p.Exist
if bytes.Equal(ep.Key, key) {
return ep
}
case *CommitmentProof_Batch:
for _, sub := range p.Batch.Entries {
if ep := sub.GetExist(); ep != nil && bytes.Equal(ep.Key, key) {
return ep
}
}
}
return nil
}

func getNonExistProofForKey(proof *CommitmentProof, key []byte) *NonExistenceProof {
switch p := proof.Proof.(type) {
case *CommitmentProof_Nonexist:
np := p.Nonexist
if isLeft(np.Left, key) && isRight(np.Right, key) {
return np
}
case *CommitmentProof_Batch:
for _, sub := range p.Batch.Entries {
if np := sub.GetNonexist(); np != nil && isLeft(np.Left, key) && isRight(np.Right, key) {
return np
}
}
}
return nil
}

func isLeft(left *ExistenceProof, key []byte) bool {
return left == nil || bytes.Compare(left.Key, key) < 0
}

func isRight(right *ExistenceProof, key []byte) bool {
return right == nil || bytes.Compare(right.Key, key) > 0
}
Loading

0 comments on commit 601cd6c

Please sign in to comment.