Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 0 additions & 57 deletions trie/notary.go

This file was deleted.

82 changes: 29 additions & 53 deletions trie/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,125 +463,101 @@ func hasRightElement(node node, key []byte) bool {
//
// Except returning the error to indicate the proof is valid or not, the function will
// also return a flag to indicate whether there exists more accounts/slots in the trie.
func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, keys [][]byte, values [][]byte, proof ethdb.KeyValueReader) (ethdb.KeyValueStore, bool, error) {
//
// Note: This method does not verify that the proof is of minimal form. If the input
// proofs are 'bloated' with neighbour leaves or random data, aside from the 'useful'
// data, then the proof will still be accepted.
func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, keys [][]byte, values [][]byte, proof ethdb.KeyValueReader) (bool, error) {
if len(keys) != len(values) {
return nil, false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
}
// Ensure the received batch is monotonic increasing.
for i := 0; i < len(keys)-1; i++ {
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
return nil, false, errors.New("range is not monotonically increasing")
return false, errors.New("range is not monotonically increasing")
}
}
// Create a key-value notary to track which items from the given proof the
// range prover actually needed to verify the data
notary := newKeyValueNotary(proof)

// Special case, there is no edge proof at all. The given range is expected
// to be the whole leaf-set in the trie.
if proof == nil {
var (
diskdb = memorydb.New()
tr = NewStackTrie(diskdb)
)
tr := NewStackTrie(nil)
for index, key := range keys {
tr.TryUpdate(key, values[index])
}
if have, want := tr.Hash(), rootHash; have != want {
return nil, false, fmt.Errorf("invalid proof, want hash %x, got %x", want, have)
}
// Proof seems valid, serialize remaining nodes into the database
if _, err := tr.Commit(); err != nil {
return nil, false, err
return false, fmt.Errorf("invalid proof, want hash %x, got %x", want, have)
}
return diskdb, false, nil // No more elements
return false, nil // No more elements
}
// Special case, there is a provided edge proof but zero key/value
// pairs, ensure there are no more accounts / slots in the trie.
if len(keys) == 0 {
root, val, err := proofToPath(rootHash, nil, firstKey, notary, true)
root, val, err := proofToPath(rootHash, nil, firstKey, proof, true)
if err != nil {
return nil, false, err
return false, err
}
if val != nil || hasRightElement(root, firstKey) {
return nil, false, errors.New("more entries available")
return false, errors.New("more entries available")
}
// Since the entire proof is a single path, we can construct a trie and a
// node database directly out of the inputs, no need to generate them
diskdb := notary.Accessed()
return diskdb, hasRightElement(root, firstKey), nil
return hasRightElement(root, firstKey), nil
}
// Special case, there is only one element and two edge keys are same.
// In this case, we can't construct two edge paths. So handle it here.
if len(keys) == 1 && bytes.Equal(firstKey, lastKey) {
root, val, err := proofToPath(rootHash, nil, firstKey, notary, false)
root, val, err := proofToPath(rootHash, nil, firstKey, proof, false)
if err != nil {
return nil, false, err
return false, err
}
if !bytes.Equal(firstKey, keys[0]) {
return nil, false, errors.New("correct proof but invalid key")
return false, errors.New("correct proof but invalid key")
}
if !bytes.Equal(val, values[0]) {
return nil, false, errors.New("correct proof but invalid data")
return false, errors.New("correct proof but invalid data")
}
// Since the entire proof is a single path, we can construct a trie and a
// node database directly out of the inputs, no need to generate them
diskdb := notary.Accessed()
return diskdb, hasRightElement(root, firstKey), nil
return hasRightElement(root, firstKey), nil
}
// Ok, in all other cases, we require two edge paths available.
// First check the validity of edge keys.
if bytes.Compare(firstKey, lastKey) >= 0 {
return nil, false, errors.New("invalid edge keys")
return false, errors.New("invalid edge keys")
}
// todo(rjl493456442) different length edge keys should be supported
if len(firstKey) != len(lastKey) {
return nil, false, errors.New("inconsistent edge keys")
return false, errors.New("inconsistent edge keys")
}
// Convert the edge proofs to edge trie paths. Then we can
// have the same tree architecture with the original one.
// For the first edge proof, non-existent proof is allowed.
root, _, err := proofToPath(rootHash, nil, firstKey, notary, true)
root, _, err := proofToPath(rootHash, nil, firstKey, proof, true)
if err != nil {
return nil, false, err
return false, err
}
// Pass the root node here, the second path will be merged
// with the first one. For the last edge proof, non-existent
// proof is also allowed.
root, _, err = proofToPath(rootHash, root, lastKey, notary, true)
root, _, err = proofToPath(rootHash, root, lastKey, proof, true)
if err != nil {
return nil, false, err
return false, err
}
// Remove all internal references. All the removed parts should
// be re-filled(or re-constructed) by the given leaves range.
empty, err := unsetInternal(root, firstKey, lastKey)
if err != nil {
return nil, false, err
return false, err
}
// Rebuild the trie with the leaf stream, the shape of trie
// should be same with the original one.
var (
diskdb = memorydb.New()
triedb = NewDatabase(diskdb)
)
tr := &Trie{root: root, Db: triedb}
tr := &Trie{root: root, Db: NewDatabase(memorydb.New())}
if empty {
tr.root = nil
}
for index, key := range keys {
tr.TryUpdate(key, values[index])
}
if tr.Hash() != rootHash {
return nil, false, fmt.Errorf("invalid proof, want hash %x, got %x", rootHash, tr.Hash())
}
// Proof seems valid, serialize all the nodes into the database
if _, _, err := tr.Commit(nil); err != nil {
return nil, false, err
}
if err := triedb.Commit(rootHash, false); err != nil {
return nil, false, err
return false, fmt.Errorf("invalid proof, want hash %x, got %x", rootHash, tr.Hash())
}
return diskdb, hasRightElement(root, keys[len(keys)-1]), nil
return hasRightElement(root, keys[len(keys)-1]), nil
}

// get returns the child of the given Node. Return nil if the
Expand Down
Loading