mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 11:41:19 +00:00
224 lines
6.5 KiB
Go
224 lines
6.5 KiB
Go
package trie
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/common"
|
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
|
)
|
|
|
|
var emptyHash [32]byte
|
|
|
|
type ResolveFunc func(*Resolver) error
|
|
|
|
// Resolver looks up (resolves) some keys and corresponding values from a database.
|
|
// One resolver per trie (prefix).
|
|
// See also ResolveRequest in trie.go
|
|
type Resolver struct {
|
|
accounts bool // Is this a resolver for accounts or for storage
|
|
historical bool
|
|
collectWitnesses bool // if true, stores witnesses for all the subtries that are being resolved
|
|
blockNr uint64
|
|
topLevels int // How many top levels of the trie to keep (not roll into hashes)
|
|
requests []*ResolveRequest
|
|
codeRequests []*ResolveRequestForCode
|
|
witnesses []*Witness // list of witnesses for resolved subtries, nil if `collectWitnesses` is false
|
|
}
|
|
|
|
func NewResolver(topLevels int, forAccounts bool, blockNr uint64) *Resolver {
|
|
tr := Resolver{
|
|
accounts: forAccounts,
|
|
requests: []*ResolveRequest{},
|
|
codeRequests: []*ResolveRequestForCode{},
|
|
blockNr: blockNr,
|
|
topLevels: topLevels,
|
|
}
|
|
return &tr
|
|
}
|
|
|
|
func (tr *Resolver) Reset(topLevels int, forAccounts bool, blockNr uint64) {
|
|
tr.topLevels = topLevels
|
|
tr.accounts = forAccounts
|
|
tr.blockNr = blockNr
|
|
tr.requests = tr.requests[:0]
|
|
tr.codeRequests = tr.codeRequests[:0]
|
|
tr.witnesses = nil
|
|
tr.collectWitnesses = false
|
|
tr.historical = false
|
|
}
|
|
|
|
func (tr *Resolver) CollectWitnesses(c bool) {
|
|
tr.collectWitnesses = c
|
|
}
|
|
|
|
// PopCollectedWitnesses returns all the collected witnesses and clears the storage in this resolver
|
|
func (tr *Resolver) PopCollectedWitnesses() []*Witness {
|
|
result := tr.witnesses
|
|
tr.witnesses = nil
|
|
return result
|
|
}
|
|
|
|
func (tr *Resolver) SetHistorical(h bool) {
|
|
tr.historical = h
|
|
}
|
|
|
|
// AddCodeRequest add a request for code resolution
|
|
func (tr *Resolver) AddCodeRequest(req *ResolveRequestForCode) {
|
|
tr.codeRequests = append(tr.codeRequests, req)
|
|
}
|
|
|
|
// Resolver implements sort.Interface
|
|
// and sorts by resolve requests
|
|
// (more general requests come first)
|
|
func (tr *Resolver) Len() int {
|
|
return len(tr.requests)
|
|
}
|
|
|
|
func min(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (tr *Resolver) Less(i, j int) bool {
|
|
ci := tr.requests[i]
|
|
cj := tr.requests[j]
|
|
m := min(ci.resolvePos, cj.resolvePos)
|
|
c := bytes.Compare(ci.contract, cj.contract)
|
|
if c != 0 {
|
|
return c < 0
|
|
}
|
|
c = bytes.Compare(ci.resolveHex[:m], cj.resolveHex[:m])
|
|
if c != 0 {
|
|
return c < 0
|
|
}
|
|
return ci.resolvePos < cj.resolvePos
|
|
}
|
|
|
|
func (tr *Resolver) Swap(i, j int) {
|
|
tr.requests[i], tr.requests[j] = tr.requests[j], tr.requests[i]
|
|
}
|
|
|
|
func (tr *Resolver) AddRequest(req *ResolveRequest) {
|
|
tr.requests = append(tr.requests, req)
|
|
}
|
|
|
|
func (tr *Resolver) Print() {
|
|
for _, req := range tr.requests {
|
|
fmt.Printf("%s\n", req.String())
|
|
}
|
|
}
|
|
|
|
// Various values of the account field set
|
|
const (
|
|
AccountFieldNonceOnly uint32 = 0x01
|
|
AccountFieldBalanceOnly uint32 = 0x02
|
|
AccountFieldRootOnly uint32 = 0x04
|
|
AccountFieldCodeHashOnly uint32 = 0x08
|
|
AccountFieldSSizeOnly uint32 = 0x10
|
|
AccountFieldSetNotAccount uint32 = 0x00
|
|
AccountFieldSetNotContract uint32 = 0x03 // Bit 0 is set for nonce, bit 1 is set for balance
|
|
AccountFieldSetContract uint32 = 0x0f // Bits 0-3 are set for nonce, balance, storageRoot and codeHash
|
|
AccountFieldSetContractWithSize uint32 = 0x1f // Bits 0-4 are set for nonce, balance, storageRoot, codeHash and storageSize
|
|
)
|
|
|
|
// ResolveWithDb resolves and hooks subtries using a state database.
|
|
func (tr *Resolver) ResolveWithDb(db ethdb.Database, blockNr uint64, trace bool) error {
|
|
if !tr.historical {
|
|
return tr.ResolveStatefulCached(db, blockNr, trace)
|
|
}
|
|
|
|
return tr.ResolveStateful(db, blockNr)
|
|
}
|
|
|
|
func (tr *Resolver) ResolveStateful(db ethdb.Database, blockNr uint64) error {
|
|
var hf hookFunction
|
|
if tr.collectWitnesses {
|
|
hf = tr.extractWitnessAndHookSubtrie
|
|
} else {
|
|
hf = hookSubtrie
|
|
}
|
|
|
|
sort.Stable(tr)
|
|
|
|
resolver := NewResolverStateful(tr.topLevels, tr.requests, hf)
|
|
if err := resolver.RebuildTrie(db, blockNr, tr.accounts, tr.historical); err != nil {
|
|
return err
|
|
}
|
|
return resolver.AttachRequestedCode(db, tr.codeRequests)
|
|
}
|
|
|
|
func (tr *Resolver) ResolveStatefulCached(db ethdb.Database, blockNr uint64, trace bool) error {
|
|
var hf hookFunction
|
|
if tr.collectWitnesses {
|
|
hf = tr.extractWitnessAndHookSubtrie
|
|
} else {
|
|
hf = hookSubtrie
|
|
}
|
|
|
|
sort.Stable(tr)
|
|
|
|
resolver := NewResolverStatefulCached(tr.topLevels, tr.requests, hf)
|
|
if err := resolver.RebuildTrie(db, blockNr, tr.accounts, tr.historical, trace); err != nil {
|
|
return err
|
|
}
|
|
return resolver.AttachRequestedCode(db, tr.codeRequests)
|
|
}
|
|
|
|
// ResolveStateless resolves and hooks subtries using a witnesses database instead of
|
|
// the state DB.
|
|
func (tr *Resolver) ResolveStateless(db WitnessStorage, blockNr uint64, trieLimit uint32, startPos int64) (int64, error) {
|
|
sort.Stable(tr)
|
|
resolver := NewResolverStateless(tr.requests, hookSubtrie)
|
|
// we expect CodeNodes to be already attached to the trie in stateless resolution
|
|
return resolver.RebuildTrie(db, blockNr, trieLimit, startPos)
|
|
}
|
|
|
|
func hookSubtrie(currentReq *ResolveRequest, hbRoot node, hbHash common.Hash) error {
|
|
if currentReq.RequiresRLP {
|
|
hasher := newHasher(false)
|
|
defer returnHasherToPool(hasher)
|
|
h, err := hasher.hashChildren(hbRoot, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
currentReq.NodeRLP = h
|
|
}
|
|
|
|
var hookKey []byte
|
|
if currentReq.contract == nil {
|
|
hookKey = currentReq.resolveHex[:currentReq.resolvePos]
|
|
} else {
|
|
contractHex := keybytesToHex(currentReq.contract)
|
|
contractHex = contractHex[:len(contractHex)-1-16] // Remove terminal nibble and incarnation bytes
|
|
hookKey = append(contractHex, currentReq.resolveHex[:currentReq.resolvePos]...)
|
|
}
|
|
|
|
//fmt.Printf("hookKey: %x, %s\n", hookKey, hbRoot.fstring(""))
|
|
currentReq.t.hook(hookKey, hbRoot)
|
|
if len(currentReq.resolveHash) > 0 && !bytes.Equal(currentReq.resolveHash, hbHash[:]) {
|
|
return fmt.Errorf("mismatching hash: %s %x for prefix %x, resolveHex %x, resolvePos %d",
|
|
currentReq.resolveHash, hbHash, currentReq.contract, currentReq.resolveHex, currentReq.resolvePos)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (tr *Resolver) extractWitnessAndHookSubtrie(currentReq *ResolveRequest, hbRoot node, hbHash common.Hash) error {
|
|
if tr.witnesses == nil {
|
|
tr.witnesses = make([]*Witness, 0)
|
|
}
|
|
|
|
witness, err := extractWitnessFromRootNode(hbRoot, tr.blockNr, false /*tr.hb.trace*/, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("error while extracting witness for resolver: %w", err)
|
|
}
|
|
|
|
tr.witnesses = append(tr.witnesses, witness)
|
|
|
|
return hookSubtrie(currentReq, hbRoot, hbHash)
|
|
}
|