Commit KeyPath
This commit is contained in:
parent
3a5a7a776e
commit
7495528fb8
|
@ -32,10 +32,13 @@ type ProofOperator interface {
|
|||
|
||||
type ProofOperators []ProofOperator
|
||||
|
||||
// XXX Reorder value/keys.
|
||||
// XXX Replace keys with keyString (the result of KeyPath.String())
|
||||
func (poz ProofOperators) VerifyValue(root []byte, value []byte, keys ...string) (err error) {
|
||||
return poz.Verify(root, [][]byte{value}, keys...)
|
||||
}
|
||||
|
||||
// XXX Replace keys with keyString (the result of KeyPath.String())
|
||||
func (poz ProofOperators) Verify(root []byte, args [][]byte, keys ...string) (err error) {
|
||||
for i, op := range poz {
|
||||
key := op.GetKey()
|
||||
|
@ -99,22 +102,22 @@ func (prt *ProofRuntime) DecodeProof(proof *Proof) (poz ProofOperators, err erro
|
|||
return
|
||||
}
|
||||
|
||||
// XXX Reorder value/keys, and figure out how to merge keys into a single string
|
||||
// after figuring out encoding between bytes/string.
|
||||
// XXX Reorder value/keys.
|
||||
// XXX Replace keys with keyString (the result of KeyPath.String()).
|
||||
func (prt *ProofRuntime) VerifyValue(proof *Proof, root []byte, value []byte, keys ...string) (err error) {
|
||||
return prt.Verify(proof, root, [][]byte{value}, keys...)
|
||||
}
|
||||
|
||||
// XXX Reorder value/keys, and figure out how to merge keys into a single string
|
||||
// after figuring out encoding between bytes/string.
|
||||
// XXX Reorder value/keys.
|
||||
// XXX Replace keys with keyString (the result of KeyPath.String()).
|
||||
// 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, root []byte, keys ...string) (err error) {
|
||||
return prt.Verify(proof, root, nil, keys...)
|
||||
}
|
||||
|
||||
// XXX Reorder value/keys, and figure out how to merge keys into a single string
|
||||
// after figuring out encoding between bytes/string.
|
||||
// XXX Reorder value/keys.
|
||||
// XXX Replace keys with keyString (the result of KeyPath.String()).
|
||||
func (prt *ProofRuntime) Verify(proof *Proof, root []byte, args [][]byte, keys ...string) (err error) {
|
||||
poz, err := prt.DecodeProof(proof)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
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
|
||||
)
|
||||
|
||||
type KeyPath struct {
|
||||
keys [][]byte
|
||||
encs []keyEncoding
|
||||
}
|
||||
|
||||
func (pth *KeyPath) AppendKey(key []byte, enc keyEncoding) {
|
||||
pth.keys = append(pth.keys, key)
|
||||
pth.encs = append(pth.encs, enc)
|
||||
}
|
||||
|
||||
func (pth *KeyPath) String() string {
|
||||
res := ""
|
||||
for i := 0; i < len(pth.keys); i++ {
|
||||
key, enc := pth.keys[i], pth.encs[i]
|
||||
switch enc {
|
||||
case KeyEncodingURL:
|
||||
res += "/" + url.PathEscape(string(key))
|
||||
case KeyEncodingHex:
|
||||
res += "/x:" + fmt.Sprintf("%X", key)
|
||||
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
|
||||
}
|
Loading…
Reference in New Issue