mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +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
|
||||
}
|
||||
if len(records) == 1 {
|
||||
return decodeNodeUnsafe(nil, records[0].Value)
|
||||
return buildLeafFromRecord(records[0])
|
||||
}
|
||||
|
||||
var (
|
||||
newnode fullNode
|
||||
curnode *fullNode
|
||||
)
|
||||
for _, record := range records {
|
||||
curnode = &newnode
|
||||
resolved, err := decodeNodeUnsafe(nil, record.Value)
|
||||
var newnode fullNode
|
||||
for i, record := range records {
|
||||
if err := validateRecordPath(record.Path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
// It's not needed to resurrect all nodes, nodes
|
||||
// not along the path of what has been asked can
|
||||
// be updated as expired. This is for v2.
|
||||
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
|
||||
}
|
||||
}
|
||||
child, err := insertTrieNode(newnode.Children[key[0]], key[1:], valueNode(record.Value))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newnode.Children[key[0]] = child
|
||||
}
|
||||
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