diff options
Diffstat (limited to 'trie')
-rw-r--r-- | trie/encoding.go | 21 | ||||
-rw-r--r-- | trie/encoding_test.go | 6 | ||||
-rw-r--r-- | trie/errors.go | 18 | ||||
-rw-r--r-- | trie/proof.go | 24 | ||||
-rw-r--r-- | trie/trie.go | 5 |
5 files changed, 59 insertions, 15 deletions
diff --git a/trie/encoding.go b/trie/encoding.go index 3c172b843..761bad188 100644 --- a/trie/encoding.go +++ b/trie/encoding.go @@ -69,6 +69,27 @@ func compactHexDecode(str []byte) []byte { return nibbles } +// compactHexEncode encodes a series of nibbles into a byte array +func compactHexEncode(nibbles []byte) []byte { + nl := len(nibbles) + if nl == 0 { + return nil + } + if nibbles[nl-1] == 16 { + nl-- + } + l := (nl + 1) / 2 + var str = make([]byte, l) + for i, _ := range str { + b := nibbles[i*2] * 16 + if nl > i*2 { + b += nibbles[i*2+1] + } + str[i] = b + } + return str +} + func decodeCompact(key []byte) []byte { l := len(key) / 2 var res = make([]byte, l) diff --git a/trie/encoding_test.go b/trie/encoding_test.go index 061d48d58..2f125ef2f 100644 --- a/trie/encoding_test.go +++ b/trie/encoding_test.go @@ -57,6 +57,12 @@ func (s *TrieEncodingSuite) TestCompactHexDecode(c *checker.C) { c.Assert(res, checker.DeepEquals, exp) } +func (s *TrieEncodingSuite) TestCompactHexEncode(c *checker.C) { + exp := []byte("verb") + res := compactHexEncode([]byte{7, 6, 6, 5, 7, 2, 6, 2, 16}) + c.Assert(res, checker.DeepEquals, exp) +} + func (s *TrieEncodingSuite) TestCompactDecode(c *checker.C) { // odd compact decode exp := []byte{1, 2, 3, 4, 5} diff --git a/trie/errors.go b/trie/errors.go index a0f58f28f..fd0e6f0a9 100644 --- a/trie/errors.go +++ b/trie/errors.go @@ -27,13 +27,23 @@ import ( // information necessary for retrieving the missing node through an ODR service. // // NodeHash is the hash of the missing node +// // RootHash is the original root of the trie that contains the node -// KeyPrefix is the prefix that leads from the root to the missing node (hex encoded) -// KeySuffix (optional) contains the rest of the key we were looking for, gives a -// hint on which further nodes should also be retrieved (hex encoded) +// +// Key is a binary-encoded key that contains the prefix that leads to the first +// missing node and optionally a suffix that hints on which further nodes should +// also be retrieved +// +// PrefixLen is the nibble length of the key prefix that leads from the root to +// the missing node +// +// SuffixLen is the nibble length of the remaining part of the key that hints on +// which further nodes should also be retrieved (can be zero when there are no +// such hints in the error message) type MissingNodeError struct { RootHash, NodeHash common.Hash - KeyPrefix, KeySuffix []byte + Key []byte + PrefixLen, SuffixLen int } func (err *MissingNodeError) Error() string { diff --git a/trie/proof.go b/trie/proof.go index 2e88bb50b..0f9264942 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -17,29 +17,30 @@ import ( // also included in the last node and can be retrieved by verifying // the proof. // -// The returned proof is nil if the trie does not contain a value for key. -// For existing keys, the proof will have at least one element. +// If the trie does not contain a value for key, the returned proof +// contains all nodes of the longest existing prefix of the key +// (at least the root node), ending with the node that proves the +// absence of the key. func (t *Trie) Prove(key []byte) []rlp.RawValue { // Collect all nodes on the path to key. key = compactHexDecode(key) nodes := []node{} tn := t.root - for len(key) > 0 { + for len(key) > 0 && tn != nil { switch n := tn.(type) { case shortNode: if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { // The trie doesn't contain the key. - return nil + tn = nil + } else { + tn = n.Val + key = key[len(n.Key):] } - tn = n.Val - key = key[len(n.Key):] nodes = append(nodes, n) case fullNode: tn = n[key[0]] key = key[1:] nodes = append(nodes, n) - case nil: - return nil case hashNode: var err error tn, err = t.resolveHash(n, nil, nil) @@ -93,7 +94,12 @@ func VerifyProof(rootHash common.Hash, key []byte, proof []rlp.RawValue) (value keyrest, cld := get(n, key) switch cld := cld.(type) { case nil: - return nil, fmt.Errorf("key mismatch at proof node %d", i) + if i != len(proof)-1 { + return nil, fmt.Errorf("key mismatch at proof node %d", i) + } else { + // The trie doesn't contain the key. + return nil, nil + } case hashNode: key = keyrest wantHash = cld diff --git a/trie/trie.go b/trie/trie.go index 717296e27..9dfde4529 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -394,8 +394,9 @@ func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) { return nil, &MissingNodeError{ RootHash: t.originalRoot, NodeHash: common.BytesToHash(n), - KeyPrefix: prefix, - KeySuffix: suffix, + Key: compactHexEncode(append(prefix, suffix...)), + PrefixLen: len(prefix), + SuffixLen: len(suffix), } } dec := mustDecodeNode(n, enc) |