core, eth: split eth package, implement snap protocol (#21482)
This commit splits the eth package, separating the handling of eth and snap protocols. It also includes the capability to run snap sync (https://github.com/ethereum/devp2p/blob/master/caps/snap.md) , but does not enable it by default. Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: Martin Holst Swende <martin@swende.se>
This commit is contained in:
104
trie/proof.go
104
trie/proof.go
@ -426,7 +426,7 @@ func hasRightElement(node node, key []byte) bool {
|
||||
|
||||
// VerifyRangeProof checks whether the given leaf nodes and edge proof
|
||||
// can prove the given trie leaves range is matched with the specific root.
|
||||
// Besides, the range should be consecutive(no gap inside) and monotonic
|
||||
// Besides, the range should be consecutive (no gap inside) and monotonic
|
||||
// increasing.
|
||||
//
|
||||
// Note the given proof actually contains two edge proofs. Both of them can
|
||||
@ -454,96 +454,136 @@ 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) (error, bool) {
|
||||
func VerifyRangeProof(rootHash common.Hash, firstKey []byte, lastKey []byte, keys [][]byte, values [][]byte, proof ethdb.KeyValueReader) (ethdb.KeyValueStore, *Trie, *KeyValueNotary, bool, error) {
|
||||
if len(keys) != len(values) {
|
||||
return fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values)), false
|
||||
return nil, nil, nil, 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 errors.New("range is not monotonically increasing"), false
|
||||
return nil, nil, nil, 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 {
|
||||
emptytrie, err := New(common.Hash{}, NewDatabase(memorydb.New()))
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = NewDatabase(diskdb)
|
||||
)
|
||||
tr, err := New(common.Hash{}, triedb)
|
||||
if err != nil {
|
||||
return err, false
|
||||
return nil, nil, nil, false, err
|
||||
}
|
||||
for index, key := range keys {
|
||||
emptytrie.TryUpdate(key, values[index])
|
||||
tr.TryUpdate(key, values[index])
|
||||
}
|
||||
if emptytrie.Hash() != rootHash {
|
||||
return fmt.Errorf("invalid proof, want hash %x, got %x", rootHash, emptytrie.Hash()), false
|
||||
if tr.Hash() != rootHash {
|
||||
return nil, nil, nil, false, fmt.Errorf("invalid proof, want hash %x, got %x", rootHash, tr.Hash())
|
||||
}
|
||||
return nil, false // no more element.
|
||||
// Proof seems valid, serialize all the nodes into the database
|
||||
if _, err := tr.Commit(nil); err != nil {
|
||||
return nil, nil, nil, false, err
|
||||
}
|
||||
if err := triedb.Commit(rootHash, false, nil); err != nil {
|
||||
return nil, nil, nil, false, err
|
||||
}
|
||||
return diskdb, tr, notary, 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, proof, true)
|
||||
root, val, err := proofToPath(rootHash, nil, firstKey, notary, true)
|
||||
if err != nil {
|
||||
return err, false
|
||||
return nil, nil, nil, false, err
|
||||
}
|
||||
if val != nil || hasRightElement(root, firstKey) {
|
||||
return errors.New("more entries available"), false
|
||||
return nil, nil, nil, false, errors.New("more entries available")
|
||||
}
|
||||
return nil, false
|
||||
// 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()
|
||||
tr := &Trie{
|
||||
db: NewDatabase(diskdb),
|
||||
root: root,
|
||||
}
|
||||
return diskdb, tr, notary, 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, proof, false)
|
||||
root, val, err := proofToPath(rootHash, nil, firstKey, notary, false)
|
||||
if err != nil {
|
||||
return err, false
|
||||
return nil, nil, nil, false, err
|
||||
}
|
||||
if !bytes.Equal(firstKey, keys[0]) {
|
||||
return errors.New("correct proof but invalid key"), false
|
||||
return nil, nil, nil, false, errors.New("correct proof but invalid key")
|
||||
}
|
||||
if !bytes.Equal(val, values[0]) {
|
||||
return errors.New("correct proof but invalid data"), false
|
||||
return nil, nil, nil, false, errors.New("correct proof but invalid data")
|
||||
}
|
||||
return nil, hasRightElement(root, firstKey)
|
||||
// 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()
|
||||
tr := &Trie{
|
||||
db: NewDatabase(diskdb),
|
||||
root: root,
|
||||
}
|
||||
return diskdb, tr, notary, 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 errors.New("invalid edge keys"), false
|
||||
return nil, nil, nil, false, errors.New("invalid edge keys")
|
||||
}
|
||||
// todo(rjl493456442) different length edge keys should be supported
|
||||
if len(firstKey) != len(lastKey) {
|
||||
return errors.New("inconsistent edge keys"), false
|
||||
return nil, nil, nil, 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, proof, true)
|
||||
root, _, err := proofToPath(rootHash, nil, firstKey, notary, true)
|
||||
if err != nil {
|
||||
return err, false
|
||||
return nil, nil, nil, 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, proof, true)
|
||||
root, _, err = proofToPath(rootHash, root, lastKey, notary, true)
|
||||
if err != nil {
|
||||
return err, false
|
||||
return nil, nil, nil, false, err
|
||||
}
|
||||
// Remove all internal references. All the removed parts should
|
||||
// be re-filled(or re-constructed) by the given leaves range.
|
||||
if err := unsetInternal(root, firstKey, lastKey); err != nil {
|
||||
return err, false
|
||||
return nil, nil, nil, false, err
|
||||
}
|
||||
// Rebuild the trie with the leave stream, the shape of trie
|
||||
// Rebuild the trie with the leaf stream, the shape of trie
|
||||
// should be same with the original one.
|
||||
newtrie := &Trie{root: root, db: NewDatabase(memorydb.New())}
|
||||
var (
|
||||
diskdb = memorydb.New()
|
||||
triedb = NewDatabase(diskdb)
|
||||
)
|
||||
tr := &Trie{root: root, db: triedb}
|
||||
for index, key := range keys {
|
||||
newtrie.TryUpdate(key, values[index])
|
||||
tr.TryUpdate(key, values[index])
|
||||
}
|
||||
if newtrie.Hash() != rootHash {
|
||||
return fmt.Errorf("invalid proof, want hash %x, got %x", rootHash, newtrie.Hash()), false
|
||||
if tr.Hash() != rootHash {
|
||||
return nil, nil, nil, false, fmt.Errorf("invalid proof, want hash %x, got %x", rootHash, tr.Hash())
|
||||
}
|
||||
return nil, hasRightElement(root, keys[len(keys)-1])
|
||||
// Proof seems valid, serialize all the nodes into the database
|
||||
if _, err := tr.Commit(nil); err != nil {
|
||||
return nil, nil, nil, false, err
|
||||
}
|
||||
if err := triedb.Commit(rootHash, false, nil); err != nil {
|
||||
return nil, nil, nil, false, err
|
||||
}
|
||||
return diskdb, tr, notary, hasRightElement(root, keys[len(keys)-1]), nil
|
||||
}
|
||||
|
||||
// get returns the child of the given node. Return nil if the
|
||||
|
Reference in New Issue
Block a user