mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
310 lines
8.6 KiB
Go
310 lines
8.6 KiB
Go
// --- Start fork code ---
|
|
package trie
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
|
)
|
|
|
|
type Diff struct {
|
|
Key []byte
|
|
PreValue []byte
|
|
PostValue []byte
|
|
}
|
|
|
|
var branchIndices = []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}
|
|
|
|
func CompareTrie(left, right common.Hash, proofDB ethdb.KeyValueReader) ([]*Diff, error) {
|
|
resolveNode := func(hash common.Hash) (node, error) {
|
|
buf, _ := proofDB.Get(hash[:])
|
|
if buf == nil {
|
|
return nil, fmt.Errorf("proof node (hash %064x) missing", hash)
|
|
}
|
|
n, err := decodeNode(hash[:], buf)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("bad proof node %v", err)
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
leftNode, err := resolveNode(left)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rightNode, err := resolveNode(right)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return compareTrie(leftNode, rightNode, proofDB)
|
|
}
|
|
|
|
func compareTrie(left, right node, proofDB ethdb.KeyValueReader) ([]*Diff, error) {
|
|
// resolveNode retrieves and resolves trie node from merkle proof stream
|
|
resolveNode := func(hash common.Hash) (node, error) {
|
|
buf, _ := proofDB.Get(hash[:])
|
|
if buf == nil {
|
|
return nil, fmt.Errorf("proof node (hash %064x) missing", hash)
|
|
}
|
|
n, err := decodeNode(hash[:], buf)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("bad proof node %v", err)
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
// resolve hash nodes
|
|
if l, ok := left.(hashNode); ok {
|
|
n, err := resolveNode(common.BytesToHash(l))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return compareTrie(n, right, proofDB)
|
|
}
|
|
|
|
// resolve hash nodes
|
|
if r, ok := right.(hashNode); ok {
|
|
n, err := resolveNode(common.BytesToHash(r))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return compareTrie(left, n, proofDB)
|
|
}
|
|
|
|
switch l := left.(type) {
|
|
case nil:
|
|
switch r := right.(type) {
|
|
case nil:
|
|
// both nodes are nil => no diff
|
|
return nil, nil
|
|
case valueNode:
|
|
// left is nil, right is value => right value is a diff
|
|
return []*Diff{{PostValue: r}}, nil
|
|
case *shortNode:
|
|
// left is nil, right is short => look for diffs in the right child
|
|
diffs, err := compareTrie(nil, r.Val, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return extendDiffKeys(r.Key, diffs), nil
|
|
case *fullNode:
|
|
// left is nil, right is full => look for diffs in the right children
|
|
diffs := make([]*Diff, 0)
|
|
for i := 0; i < 15; i++ {
|
|
cldDiffs, err := compareTrie(nil, r.Children[i], proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, extendDiffKeys([]byte{branchIndices[i]}, cldDiffs)...)
|
|
}
|
|
return diffs, nil
|
|
|
|
}
|
|
case valueNode:
|
|
switch r := right.(type) {
|
|
case nil:
|
|
// left is value, right is nil => this is a diff
|
|
return []*Diff{{Key: []byte{}, PreValue: l}}, nil
|
|
case valueNode:
|
|
// left is value, right is value => compare the values
|
|
if bytes.Equal(l, r) {
|
|
return nil, nil
|
|
}
|
|
return []*Diff{{Key: []byte{}, PreValue: l, PostValue: r}}, nil
|
|
case *shortNode:
|
|
// left is value, right is short => everything is a diff
|
|
diffs, err := compareTrie(nil, r.Val, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = extendDiffKeys(r.Key, diffs)
|
|
diffs = append(diffs, &Diff{Key: []byte{}, PreValue: l})
|
|
return diffs, nil
|
|
case *fullNode:
|
|
// left is value, right is full => left value is a diff + look for diffs in the right children
|
|
diffs := []*Diff{{PreValue: l}}
|
|
for i := range 15 {
|
|
cldDiffs, err := compareTrie(nil, r.Children[i], proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, extendDiffKeys([]byte{branchIndices[i]}, cldDiffs)...)
|
|
}
|
|
return diffs, nil
|
|
}
|
|
case *shortNode:
|
|
switch r := right.(type) {
|
|
case nil:
|
|
// left is short, right is nil => look for diffs in the left child
|
|
diffs, err := compareTrie(l.Val, nil, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return extendDiffKeys(l.Key, diffs), nil
|
|
case *shortNode:
|
|
// both nodes are short
|
|
if bytes.Equal(l.Key, r.Key) {
|
|
// same key => compare the children
|
|
diffs, err := compareTrie(l.Val, r.Val, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return extendDiffKeys(l.Key, diffs), nil
|
|
}
|
|
|
|
if bytes.HasPrefix(l.Key, r.Key) {
|
|
// right is prefix of left
|
|
// compare the right child with the left shortened by the right key
|
|
// Note: the right child could be full and have common values with the left shortened
|
|
diffs, err := compareTrie(&shortNode{Key: l.Key[len(r.Key):], Val: l.Val}, r.Val, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return extendDiffKeys(r.Key, diffs), nil
|
|
}
|
|
|
|
if bytes.HasPrefix(r.Key, l.Key) {
|
|
// left is prefix of right
|
|
// same as above
|
|
diffs, err := compareTrie(l.Val, &shortNode{Key: r.Key[len(l.Key):], Val: r.Val}, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return extendDiffKeys(l.Key, diffs), nil
|
|
}
|
|
|
|
// left and right are different
|
|
// look for diffs in the left child and the right child
|
|
lDiffs, err := compareTrie(l.Val, nil, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lDiffs = extendDiffKeys(l.Key, lDiffs)
|
|
|
|
rDiffs, err := compareTrie(nil, r.Val, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rDiffs = extendDiffKeys(r.Key, rDiffs)
|
|
|
|
return append(lDiffs, rDiffs...), nil
|
|
case *fullNode:
|
|
// left is short, right is full
|
|
diffs := make([]*Diff, 0)
|
|
if len(l.Key) == 0 { // this should never happen when comparing two valid trie roots
|
|
// left is empty key => look for diffs in the left child and the right node
|
|
lDiffs, err := compareTrie(l.Val, nil, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, lDiffs...)
|
|
|
|
rDiffs, err := compareTrie(nil, r, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, rDiffs...)
|
|
|
|
return diffs, nil
|
|
}
|
|
|
|
for i := range 15 {
|
|
if l.Key[0] == branchIndices[i] {
|
|
// left key matches the branch index => compare the left node shortened by the index with the right child
|
|
cldDiffs, err := compareTrie(&shortNode{Key: l.Key[1:], Val: l.Val}, r.Children[i], proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, extendDiffKeys([]byte{branchIndices[i]}, cldDiffs)...)
|
|
} else {
|
|
// left key does not match the branch index => look for diffs in the right child
|
|
cldDiffs, err := compareTrie(nil, r.Children[i], proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, extendDiffKeys([]byte{branchIndices[i]}, cldDiffs)...)
|
|
}
|
|
}
|
|
|
|
return diffs, nil
|
|
}
|
|
case *fullNode:
|
|
switch r := right.(type) {
|
|
case nil:
|
|
// left is full, right is nil => look for diffs in the left children
|
|
diffs := make([]*Diff, 0)
|
|
for i := range 15 {
|
|
cldDiffs, err := compareTrie(l.Children[i], nil, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, extendDiffKeys([]byte{branchIndices[i]}, cldDiffs)...)
|
|
}
|
|
return diffs, nil
|
|
case *shortNode:
|
|
// left is full, right is short
|
|
diffs := make([]*Diff, 0)
|
|
if len(r.Key) == 0 { // this should never happen when comparing two valid trie roots
|
|
// right is empty key => look for diffs in the left children and the right node
|
|
lDiffs, err := compareTrie(l, nil, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, lDiffs...)
|
|
|
|
rDiffs, err := compareTrie(nil, r.Val, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, rDiffs...)
|
|
|
|
return diffs, nil
|
|
}
|
|
|
|
for i := range 15 {
|
|
if r.Key[0] == branchIndices[i] {
|
|
// right key matches the branch index => compare the left child with the right child shortened by the index
|
|
cldDiffs, err := compareTrie(l.Children[i], &shortNode{Key: r.Key[1:], Val: r.Val}, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, extendDiffKeys([]byte{branchIndices[i]}, cldDiffs)...)
|
|
} else {
|
|
// right key does not match the branch index => look for diffs in the left child
|
|
cldDiffs, err := compareTrie(l.Children[i], nil, proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, extendDiffKeys([]byte{branchIndices[i]}, cldDiffs)...)
|
|
}
|
|
}
|
|
|
|
return diffs, nil
|
|
case *fullNode:
|
|
// both nodes are full
|
|
diffs := make([]*Diff, 0)
|
|
for i := range 15 {
|
|
cldDiffs, err := compareTrie(l.Children[i], r.Children[i], proofDB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diffs = append(diffs, extendDiffKeys([]byte{branchIndices[i]}, cldDiffs)...)
|
|
}
|
|
return diffs, nil
|
|
}
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func extendDiffKeys(prefix []byte, diffs []*Diff) []*Diff {
|
|
for _, d := range diffs {
|
|
d.Key = append(prefix, d.Key...)
|
|
}
|
|
return diffs
|
|
}
|
|
|
|
// --- End fork code ---
|