erigon-pulse/trie/witness_builder.go

292 lines
6.5 KiB
Go

package trie
import (
"fmt"
"math/big"
"github.com/ledgerwatch/turbo-geth/common"
)
type HashNodeFunc func(node, bool, []byte) (int, error)
type HashOnly interface {
HashOnly([]byte) bool
Current() []byte
}
type MerklePathLimiter struct {
HashOnly HashOnly
HashFunc HashNodeFunc
}
type CodeMap map[common.Hash][]byte
type WitnessBuilder struct {
root node
blockNr uint64
trace bool
codeMap CodeMap
operands []WitnessOperator
}
func NewWitnessBuilder(root node, blockNr uint64, trace bool, codeMap CodeMap) *WitnessBuilder {
return &WitnessBuilder{
root: root,
blockNr: blockNr,
trace: trace,
codeMap: codeMap,
operands: make([]WitnessOperator, 0),
}
}
func (b *WitnessBuilder) Build(limiter *MerklePathLimiter) (*Witness, error) {
err := b.makeBlockWitness(b.root, []byte{}, limiter, true)
witness := NewWitness(b.operands)
b.operands = nil
return witness, err
}
func (b *WitnessBuilder) addLeafOp(key []byte, value []byte) error {
if b.trace {
fmt.Printf("LEAF_VALUE: k %x v:%x\n", key, value)
}
var op OperatorLeafValue
op.Key = make([]byte, len(key))
copy(op.Key[:], key)
if value != nil {
op.Value = make([]byte, len(value))
copy(op.Value[:], value)
}
b.operands = append(b.operands, &op)
return nil
}
func (b *WitnessBuilder) addAccountLeafOp(key []byte, accountNode *accountNode) error {
if b.trace {
fmt.Printf("LEAF_ACCOUNT: k %x acc:%v\n", key, accountNode)
}
var op OperatorLeafAccount
op.Key = make([]byte, len(key))
copy(op.Key[:], key)
op.Nonce = accountNode.Nonce
op.Balance = big.NewInt(0)
op.Balance.SetBytes(accountNode.Balance.Bytes())
if !accountNode.IsEmptyRoot() || !accountNode.IsEmptyCodeHash() {
op.HasCode = true
op.HasStorage = true
}
b.operands = append(b.operands, &op)
return nil
}
func (b *WitnessBuilder) addExtensionOp(key []byte) error {
if b.trace {
fmt.Printf("EXTENSION: k %x\n", key)
}
var op OperatorExtension
op.Key = make([]byte, len(key))
copy(op.Key[:], key)
b.operands = append(b.operands, &op)
return nil
}
func (b *WitnessBuilder) makeHashNode(n node, force bool, hashNodeFunc HashNodeFunc) (hashNode, error) {
switch n := n.(type) {
case hashNode:
return n, nil
default:
var hash common.Hash
if _, err := hashNodeFunc(n, force, hash[:]); err != nil {
return nil, err
}
return hashNode(hash[:]), nil
}
}
func (b *WitnessBuilder) addHashOp(n hashNode) error {
if b.trace {
fmt.Printf("HASH: type: %T v %x\n", n, n)
}
var op OperatorHash
op.Hash = common.BytesToHash(n)
b.operands = append(b.operands, &op)
return nil
}
func (b *WitnessBuilder) addBranchOp(mask uint32) error {
if b.trace {
fmt.Printf("BRANCH: mask=%b\n", mask)
}
var op OperatorBranch
op.Mask = mask
b.operands = append(b.operands, &op)
return nil
}
func (b *WitnessBuilder) addCodeOp(code []byte) error {
if b.trace {
fmt.Printf("CODE: len=%d\n", len(code))
}
var op OperatorCode
op.Code = make([]byte, len(code))
copy(op.Code, code)
b.operands = append(b.operands, &op)
return nil
}
func (b *WitnessBuilder) addEmptyRoot() error {
if b.trace {
fmt.Printf("EMPTY ROOT\n")
}
b.operands = append(b.operands, &OperatorEmptyRoot{})
return nil
}
func (b *WitnessBuilder) processAccountCode(n *accountNode) error {
if n.IsEmptyRoot() && n.IsEmptyCodeHash() {
return nil
}
code, ok := b.codeMap[n.CodeHash]
if !ok {
return b.addHashOp(hashNode(n.CodeHash[:]))
}
return b.addCodeOp(code)
}
func (b *WitnessBuilder) processAccountStorage(n *accountNode, hex []byte, limiter *MerklePathLimiter) error {
if n.IsEmptyRoot() && n.IsEmptyCodeHash() {
return nil
}
if n.storage == nil {
return b.addEmptyRoot()
}
// Here we substitute rs parameter for storageRs, because it needs to become the default
return b.makeBlockWitness(n.storage, hex, limiter, true)
}
func (b *WitnessBuilder) makeBlockWitness(
nd node, hex []byte, limiter *MerklePathLimiter, force bool) error {
processAccountNode := func(key []byte, storageKey []byte, n *accountNode) error {
if err := b.processAccountCode(n); err != nil {
return err
}
if err := b.processAccountStorage(n, storageKey, limiter); err != nil {
return err
}
return b.addAccountLeafOp(key, n)
}
switch n := nd.(type) {
case nil:
return nil
case valueNode:
return b.addLeafOp(hex, n)
case *accountNode:
return processAccountNode(hex, hex, n)
case *shortNode:
h := n.Key
// Remove terminator
if h[len(h)-1] == 16 {
h = h[:len(h)-1]
}
hexVal := concat(hex, h...)
switch v := n.Val.(type) {
case valueNode:
return b.addLeafOp(n.Key, v[:])
case *accountNode:
return processAccountNode(n.Key, hexVal, v)
default:
if err := b.makeBlockWitness(n.Val, hexVal, limiter, false); err != nil {
return err
}
return b.addExtensionOp(n.Key)
}
case *duoNode:
hashOnly := limiter != nil && limiter.HashOnly.HashOnly(hex) // Save this because rs can move on to other keys during the recursive invocation
if b.trace {
fmt.Printf("b.hashOnly.HashOnly(%x) -> %v\n", hex, hashOnly)
}
if hashOnly {
hn, err := b.makeHashNode(n, force, limiter.HashFunc)
if err != nil {
return err
}
return b.addHashOp(hn)
}
i1, i2 := n.childrenIdx()
if err := b.makeBlockWitness(n.child1, expandKeyHex(hex, i1), limiter, false); err != nil {
return err
}
if err := b.makeBlockWitness(n.child2, expandKeyHex(hex, i2), limiter, false); err != nil {
return err
}
return b.addBranchOp(n.mask)
case *fullNode:
hashOnly := limiter != nil && limiter.HashOnly.HashOnly(hex) // Save this because rs can move on to other keys during the recursive invocation
if hashOnly {
hn, err := b.makeHashNode(n, force, limiter.HashFunc)
if err != nil {
return err
}
return b.addHashOp(hn)
}
var mask uint32
for i, child := range n.Children {
if child != nil {
if err := b.makeBlockWitness(child, expandKeyHex(hex, byte(i)), limiter, false); err != nil {
return err
}
mask |= (uint32(1) << uint(i))
}
}
return b.addBranchOp(mask)
case hashNode:
hashOnly := limiter == nil || limiter.HashOnly.HashOnly(hex)
if !hashOnly {
if c := limiter.HashOnly.Current(); len(c) == len(hex)+1 && c[len(c)-1] == 16 {
hashOnly = true
}
}
if hashOnly {
return b.addHashOp(n)
}
return fmt.Errorf("unexpected hashNode: %s, at hex: %x, rs.Current: %x (%d)", n, hex, limiter.HashOnly.Current(), len(hex))
default:
return fmt.Errorf("unexpected node type: %T", nd)
}
}
func expandKeyHex(hex []byte, nibble byte) []byte {
result := make([]byte, len(hex)+1)
copy(result, hex)
result[len(hex)] = nibble
return result
}