trie: separate verification with/without proofs

This commit is contained in:
Martin Holst Swende 2023-09-28 11:49:27 +02:00
parent 3dc45a3e1d
commit 1fb6eb15dd
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
3 changed files with 41 additions and 28 deletions

View file

@ -228,16 +228,8 @@ func (dl *diskLayer) proveRange(ctx *generatorContext, trieId *trie.ID, prefix [
// The snap state is exhausted, pass the entire key/val set for verification
root := trieId.Root
if origin == nil && !diskMore {
stackTr := trie.NewStackTrie(nil)
for i, key := range keys {
stackTr.Update(key, vals[i])
}
if gotRoot := stackTr.Hash(); gotRoot != root {
return &proofResult{
keys: keys,
vals: vals,
proofErr: fmt.Errorf("wrong root: have %#x want %#x", gotRoot, root),
}, nil
if err := trie.VerifyStandaloneRange(root, keys, vals); err != nil {
return &proofResult{keys: keys, vals: vals, proofErr: err}, nil
}
return &proofResult{keys: keys, vals: vals}, nil
}

View file

@ -2645,12 +2645,10 @@ func (s *Syncer) OnStorage(peer SyncPeer, id uint64, hashes [][]common.Hash, slo
nodes = append(nodes, node)
}
}
var err error
if len(nodes) == 0 {
// No proof has been attached, the response must cover the entire key
// space and hash to the origin root.
_, err = trie.VerifyRangeProof(req.roots[i], nil, nil, keys, slots[i], nil)
if err != nil {
if err := trie.VerifyStandaloneRange(req.roots[i], keys, slots[i]); err != nil {
s.scheduleRevertStorageRequest(req) // reschedule request
logger.Warn("Storage slots failed proof", "err", err)
return err
@ -2658,9 +2656,11 @@ func (s *Syncer) OnStorage(peer SyncPeer, id uint64, hashes [][]common.Hash, slo
} else {
// A proof was attached, the response is only partial, check that the
// returned data is indeed part of the storage trie
proofdb := nodes.NodeSet()
var end []byte
var (
proofdb = nodes.NodeSet()
err error
end []byte
)
if len(keys) > 0 {
end = keys[len(keys)-1]
}

View file

@ -482,6 +482,11 @@ func hasRightElement(node node, key []byte) bool {
// 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 proof == nil {
// Special case, there is no edge proof at all. The given range is expected
// to be the whole leaf-set in the trie.
return false, VerifyStandaloneRange(rootHash, keys, values)
}
if len(keys) != len(values) {
return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
}
@ -496,18 +501,6 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, key
return false, errors.New("range contains deletion")
}
}
// 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 {
tr := NewStackTrie(nil)
for index, key := range keys {
tr.Update(key, values[index])
}
if have, want := tr.Hash(), rootHash; have != want {
return false, fmt.Errorf("invalid proof, want hash %x, got %x", want, have)
}
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 {
@ -579,6 +572,34 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, key
return hasRightElement(tr.root, keys[len(keys)-1]), nil
}
// VerifyStandaloneRange checks whether a trie built with the given leaf nodes
// matches with the specific root.
// The range must be monotonically increasing and contain no deletions.
func VerifyStandaloneRange(rootHash common.Hash, keys [][]byte, values [][]byte) error {
if len(keys) != len(values) {
return fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
}
// Ensure the received batch is monotonic increasing and contains no deletions
for i := 0; i < len(keys)-1; i++ {
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
return errors.New("range is not monotonically increasing")
}
}
for _, value := range values {
if len(value) == 0 {
return errors.New("range contains deletion")
}
}
tr := NewStackTrie(nil)
for index, key := range keys {
tr.Update(key, values[index])
}
if have, want := tr.Hash(), rootHash; have != want {
return fmt.Errorf("invalid proof, want hash %x, got %x", want, have)
}
return nil // No more elements
}
// get returns the child of the given node. Return nil if the
// node with specified key doesn't exist at all.
//