github.com/vipernet-xyz/tm@v0.34.24/crypto/merkle/proof_key_path.go (about)

     1  package merkle
     2  
     3  import (
     4  	"encoding/hex"
     5  	"errors"
     6  	"fmt"
     7  	"net/url"
     8  	"strings"
     9  )
    10  
    11  /*
    12  
    13  	For generalized Merkle proofs, each layer of the proof may require an
    14  	optional key.  The key may be encoded either by URL-encoding or
    15  	(upper-case) hex-encoding.
    16  	TODO: In the future, more encodings may be supported, like base32 (e.g.
    17  	/32:)
    18  
    19  	For example, for a Cosmos-SDK application where the first two proof layers
    20  	are ValueOps, and the third proof layer is an IAVLValueOp, the keys
    21  	might look like:
    22  
    23  	0: []byte("App")
    24  	1: []byte("IBC")
    25  	2: []byte{0x01, 0x02, 0x03}
    26  
    27  	Assuming that we know that the first two layers are always ASCII texts, we
    28  	probably want to use URLEncoding for those, whereas the third layer will
    29  	require HEX encoding for efficient representation.
    30  
    31  	kp := new(KeyPath)
    32  	kp.AppendKey([]byte("App"), KeyEncodingURL)
    33  	kp.AppendKey([]byte("IBC"), KeyEncodingURL)
    34  	kp.AppendKey([]byte{0x01, 0x02, 0x03}, KeyEncodingURL)
    35  	kp.String() // Should return "/App/IBC/x:010203"
    36  
    37  	NOTE: Key paths must begin with a `/`.
    38  
    39  	NOTE: All encodings *MUST* work compatibly, such that you can choose to use
    40  	whatever encoding, and the decoded keys will always be the same.  In other
    41  	words, it's just as good to encode all three keys using URL encoding or HEX
    42  	encoding... it just wouldn't be optimal in terms of readability or space
    43  	efficiency.
    44  
    45  	NOTE: Punycode will never be supported here, because not all values can be
    46  	decoded.  For example, no string decodes to the string "xn--blah" in
    47  	Punycode.
    48  
    49  */
    50  
    51  type keyEncoding int
    52  
    53  const (
    54  	KeyEncodingURL keyEncoding = iota
    55  	KeyEncodingHex
    56  	KeyEncodingMax // Number of known encodings. Used for testing
    57  )
    58  
    59  type Key struct {
    60  	name []byte
    61  	enc  keyEncoding
    62  }
    63  
    64  type KeyPath []Key
    65  
    66  func (pth KeyPath) AppendKey(key []byte, enc keyEncoding) KeyPath {
    67  	return append(pth, Key{key, enc})
    68  }
    69  
    70  func (pth KeyPath) String() string {
    71  	res := ""
    72  	for _, key := range pth {
    73  		switch key.enc {
    74  		case KeyEncodingURL:
    75  			res += "/" + url.PathEscape(string(key.name))
    76  		case KeyEncodingHex:
    77  			res += "/x:" + fmt.Sprintf("%X", key.name)
    78  		default:
    79  			panic("unexpected key encoding type")
    80  		}
    81  	}
    82  	return res
    83  }
    84  
    85  // Decode a path to a list of keys. Path must begin with `/`.
    86  // Each key must use a known encoding.
    87  func KeyPathToKeys(path string) (keys [][]byte, err error) {
    88  	if path == "" || path[0] != '/' {
    89  		return nil, errors.New("key path string must start with a forward slash '/'")
    90  	}
    91  	parts := strings.Split(path[1:], "/")
    92  	keys = make([][]byte, len(parts))
    93  	for i, part := range parts {
    94  		if strings.HasPrefix(part, "x:") {
    95  			hexPart := part[2:]
    96  			key, err := hex.DecodeString(hexPart)
    97  			if err != nil {
    98  				return nil, fmt.Errorf("decoding hex-encoded part #%d: /%s: %w", i, part, err)
    99  			}
   100  			keys[i] = key
   101  		} else {
   102  			key, err := url.PathUnescape(part)
   103  			if err != nil {
   104  				return nil, fmt.Errorf("decoding url-encoded part #%d: /%s: %w", i, part, err)
   105  			}
   106  			keys[i] = []byte(key) // TODO Test this with random bytes, I'm not sure that it works for arbitrary bytes...
   107  		}
   108  	}
   109  	return keys, nil
   110  }