mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 18:30:45 +00:00
fix leaf reconstruction
This commit is contained in:
parent
68621ae05a
commit
9606dbcb19
1 changed files with 97 additions and 19 deletions
|
|
@ -67,31 +67,109 @@ func archiveRecordsToNode(records []*archive.Record) (node, error) {
|
||||||
return nil, archive.EmptyArchiveRecord
|
return nil, archive.EmptyArchiveRecord
|
||||||
}
|
}
|
||||||
if len(records) == 1 {
|
if len(records) == 1 {
|
||||||
return decodeNodeUnsafe(nil, records[0].Value)
|
return buildLeafFromRecord(records[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var newnode fullNode
|
||||||
newnode fullNode
|
for i, record := range records {
|
||||||
curnode *fullNode
|
if err := validateRecordPath(record.Path); err != nil {
|
||||||
)
|
return nil, err
|
||||||
for _, record := range records {
|
}
|
||||||
curnode = &newnode
|
|
||||||
resolved, err := decodeNodeUnsafe(nil, record.Value)
|
// we are not in the case of a single leaf node, so each
|
||||||
|
// path should be at least 2 nibbles (terminator included)
|
||||||
|
if len(record.Path) < 2 || !hasTerm(record.Path) {
|
||||||
|
return nil, fmt.Errorf("invalid record path for non-leaf node #%d: %v", i, record.Path)
|
||||||
|
}
|
||||||
|
key, err := normalizeRecordKey(record.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// It's not needed to resurrect all nodes, nodes
|
child, err := insertTrieNode(newnode.Children[key[0]], key[1:], valueNode(record.Value))
|
||||||
// not along the path of what has been asked can
|
if err != nil {
|
||||||
// be updated as expired. This is for v2.
|
return nil, err
|
||||||
for i, b := range record.Path {
|
|
||||||
if curnode.Children[b] == nil {
|
|
||||||
if i < len(record.Path)-1 {
|
|
||||||
curnode.Children[b] = &fullNode{}
|
|
||||||
} else {
|
|
||||||
curnode.Children[b] = resolved
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
newnode.Children[key[0]] = child
|
||||||
}
|
}
|
||||||
return &newnode, nil
|
return &newnode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateRecordPath(path []byte) error {
|
||||||
|
for i, b := range path {
|
||||||
|
if b > 16 {
|
||||||
|
return fmt.Errorf("invalid nibble in record path: %d", b)
|
||||||
|
}
|
||||||
|
if b == 16 && i != len(path)-1 {
|
||||||
|
return fmt.Errorf("terminator nibble in middle of record path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildLeafFromRecord(record *archive.Record) (node, error) {
|
||||||
|
key, err := normalizeRecordKey(record.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &shortNode{Key: key, Val: valueNode(record.Value)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizeRecordKey ensures the record path is a hex-nibble key suitable for
|
||||||
|
// leaf insertion by guaranteeing a single terminator nibble and preserving any
|
||||||
|
// already-terminated path. Empty paths are normalized to a sole terminator.
|
||||||
|
func normalizeRecordKey(path []byte) ([]byte, error) {
|
||||||
|
if len(path) == 0 {
|
||||||
|
return []byte{16}, nil
|
||||||
|
}
|
||||||
|
if hasTerm(path) {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
key := append([]byte{}, path...)
|
||||||
|
key = append(key, 16)
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertTrieNode(n node, key []byte, value node) (node, error) {
|
||||||
|
if len(key) == 0 {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
switch n := n.(type) {
|
||||||
|
case *shortNode:
|
||||||
|
matchlen := prefixLen(key, n.Key)
|
||||||
|
if matchlen == len(n.Key) {
|
||||||
|
nn, err := insertTrieNode(n.Val, key[matchlen:], value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &shortNode{Key: n.Key, Val: nn}, nil
|
||||||
|
}
|
||||||
|
branch := &fullNode{}
|
||||||
|
var err error
|
||||||
|
branch.Children[n.Key[matchlen]], err = insertTrieNode(nil, n.Key[matchlen+1:], n.Val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
branch.Children[key[matchlen]], err = insertTrieNode(nil, key[matchlen+1:], value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if matchlen == 0 {
|
||||||
|
return branch, nil
|
||||||
|
}
|
||||||
|
return &shortNode{Key: key[:matchlen], Val: branch}, nil
|
||||||
|
|
||||||
|
case *fullNode:
|
||||||
|
child, err := insertTrieNode(n.Children[key[0]], key[1:], value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n.Children[key[0]] = child
|
||||||
|
return n, nil
|
||||||
|
|
||||||
|
case nil:
|
||||||
|
return &shortNode{Key: key, Val: value}, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid node type in trie insert: %T", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue