mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 11:41:19 +00:00
304 lines
8.6 KiB
Go
304 lines
8.6 KiB
Go
package trie
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"runtime/debug"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/common"
|
|
"github.com/ledgerwatch/turbo-geth/common/dbutils"
|
|
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
|
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
|
"github.com/ledgerwatch/turbo-geth/metrics"
|
|
"github.com/ledgerwatch/turbo-geth/trie/rlphacks"
|
|
)
|
|
|
|
var (
|
|
trieResolveStatefulTimer = metrics.NewRegisteredTimer("trie/resolve/stateful", nil)
|
|
)
|
|
|
|
type hookFunction func(*ResolveRequest, node, common.Hash) error
|
|
|
|
type ResolverStateful struct {
|
|
rss []*ResolveSet
|
|
curr bytes.Buffer // Current key for the structure generation algorithm, as well as the input tape for the hash builder
|
|
succ bytes.Buffer
|
|
value bytes.Buffer // Current value to be used as the value tape for the hash builder
|
|
groups []uint16
|
|
reqIndices []int // Indices pointing back to request slice from slices returned by PrepareResolveParams
|
|
hb *HashBuilder
|
|
topLevels int // How many top levels of the trie to keep (not roll into hashes)
|
|
currentReq *ResolveRequest // Request currently being handled
|
|
currentRs *ResolveSet // ResolveSet currently being used
|
|
keyIdx int
|
|
fieldSet uint32 // fieldSet for the next invocation of genStructStep
|
|
a accounts.Account
|
|
leafData GenStructStepLeafData
|
|
accData GenStructStepAccountData
|
|
requests []*ResolveRequest
|
|
|
|
roots []node // roots of the tries that are being built
|
|
hookFunction hookFunction
|
|
}
|
|
|
|
func NewResolverStateful(topLevels int, requests []*ResolveRequest, hookFunction hookFunction) *ResolverStateful {
|
|
return &ResolverStateful{
|
|
topLevels: topLevels,
|
|
hb: NewHashBuilder(false),
|
|
reqIndices: []int{},
|
|
requests: requests,
|
|
hookFunction: hookFunction,
|
|
}
|
|
}
|
|
|
|
// Reset prepares the Resolver for reuse
|
|
func (tr *ResolverStateful) Reset(topLevels int, requests []*ResolveRequest, hookFunction hookFunction) {
|
|
tr.topLevels = topLevels
|
|
tr.requests = tr.requests[:0]
|
|
tr.reqIndices = tr.reqIndices[:0]
|
|
tr.keyIdx = 0
|
|
tr.currentReq = nil
|
|
tr.currentRs = nil
|
|
tr.fieldSet = 0
|
|
tr.rss = tr.rss[:0]
|
|
tr.requests = requests
|
|
tr.hookFunction = hookFunction
|
|
tr.curr.Reset()
|
|
tr.succ.Reset()
|
|
tr.value.Reset()
|
|
tr.groups = tr.groups[:0]
|
|
tr.a.Reset()
|
|
tr.hb.Reset()
|
|
}
|
|
|
|
func (tr *ResolverStateful) PopRoots() []node {
|
|
roots := tr.roots
|
|
tr.roots = nil
|
|
return roots
|
|
}
|
|
|
|
// PrepareResolveParams prepares information for the MultiWalk
|
|
func (tr *ResolverStateful) PrepareResolveParams() ([][]byte, []uint) {
|
|
// Remove requests strictly contained in the preceding ones
|
|
startkeys := [][]byte{}
|
|
fixedbits := []uint{}
|
|
tr.rss = tr.rss[:0]
|
|
if len(tr.requests) == 0 {
|
|
return startkeys, fixedbits
|
|
}
|
|
var prevReq *ResolveRequest
|
|
for i, req := range tr.requests {
|
|
if prevReq == nil ||
|
|
!bytes.Equal(req.contract, prevReq.contract) ||
|
|
!bytes.Equal(req.resolveHex[:req.resolvePos], prevReq.resolveHex[:prevReq.resolvePos]) {
|
|
|
|
tr.reqIndices = append(tr.reqIndices, i)
|
|
pLen := len(req.contract)
|
|
key := make([]byte, pLen+32)
|
|
copy(key[:], req.contract)
|
|
decodeNibbles(req.resolveHex[:req.resolvePos], key[pLen:])
|
|
startkeys = append(startkeys, key)
|
|
req.extResolvePos = req.resolvePos + 2*pLen
|
|
fixedbits = append(fixedbits, uint(4*req.extResolvePos))
|
|
prevReq = req
|
|
var minLength int
|
|
if req.resolvePos >= tr.topLevels {
|
|
minLength = 0
|
|
} else {
|
|
minLength = tr.topLevels - req.resolvePos
|
|
}
|
|
rs := NewResolveSet(minLength)
|
|
tr.rss = append(tr.rss, rs)
|
|
rs.AddHex(req.resolveHex[req.resolvePos:])
|
|
} else {
|
|
rs := tr.rss[len(tr.rss)-1]
|
|
rs.AddHex(req.resolveHex[req.resolvePos:])
|
|
}
|
|
}
|
|
tr.currentReq = tr.requests[tr.reqIndices[0]]
|
|
tr.currentRs = tr.rss[0]
|
|
return startkeys, fixedbits
|
|
}
|
|
|
|
func (tr *ResolverStateful) finaliseRoot() error {
|
|
tr.curr.Reset()
|
|
tr.curr.Write(tr.succ.Bytes())
|
|
tr.succ.Reset()
|
|
if tr.curr.Len() > 0 {
|
|
var err error
|
|
var data GenStructStepData
|
|
if tr.fieldSet == 0 {
|
|
tr.leafData.Value = rlphacks.RlpSerializableBytes(tr.value.Bytes())
|
|
data = &tr.leafData
|
|
} else {
|
|
tr.accData.FieldSet = tr.fieldSet
|
|
tr.accData.StorageSize = tr.a.StorageSize
|
|
tr.accData.Balance.Set(&tr.a.Balance)
|
|
tr.accData.Nonce = tr.a.Nonce
|
|
tr.accData.Incarnation = tr.a.Incarnation
|
|
data = &tr.accData
|
|
}
|
|
tr.groups, err = GenStructStep(tr.currentRs.HashOnly, tr.curr.Bytes(), tr.succ.Bytes(), tr.hb, data, tr.groups, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if tr.hb.hasRoot() {
|
|
hbRoot := tr.hb.root()
|
|
hbHash := tr.hb.rootHash()
|
|
return tr.hookFunction(tr.currentReq, hbRoot, hbHash)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (tr *ResolverStateful) RebuildTrie(
|
|
db ethdb.Database,
|
|
blockNr uint64,
|
|
accounts bool,
|
|
historical bool) error {
|
|
defer trieResolveStatefulTimer.UpdateSince(time.Now())
|
|
|
|
startkeys, fixedbits := tr.PrepareResolveParams()
|
|
if db == nil {
|
|
var b strings.Builder
|
|
fmt.Fprintf(&b, "ResolveWithDb(db=nil), accounts: %t\n", accounts)
|
|
for i, sk := range startkeys {
|
|
fmt.Fprintf(&b, "sk %x, bits: %d\n", sk, fixedbits[i])
|
|
}
|
|
return fmt.Errorf("unexpected resolution: %s at %s", b.String(), debug.Stack())
|
|
}
|
|
|
|
var err error
|
|
if accounts {
|
|
if historical {
|
|
err = db.MultiWalkAsOf(dbutils.AccountsBucket, dbutils.AccountsHistoryBucket, startkeys, fixedbits, blockNr+1, tr.WalkerAccounts)
|
|
} else {
|
|
err = db.MultiWalk(dbutils.AccountsBucket, startkeys, fixedbits, tr.WalkerAccounts)
|
|
}
|
|
} else {
|
|
if historical {
|
|
err = db.MultiWalkAsOf(dbutils.StorageBucket, dbutils.StorageHistoryBucket, startkeys, fixedbits, blockNr+1, tr.WalkerStorage)
|
|
} else {
|
|
err = db.MultiWalk(dbutils.StorageBucket, startkeys, fixedbits, tr.WalkerStorage)
|
|
}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = tr.finaliseRoot(); err != nil {
|
|
return fmt.Errorf("error in finaliseRoot, for block %d: %w", blockNr, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (tr *ResolverStateful) AttachRequestedCode(db ethdb.Getter, requests []*ResolveRequestForCode) error {
|
|
for _, req := range requests {
|
|
codeHash := req.codeHash
|
|
code, err := db.Get(dbutils.CodeBucket, codeHash[:])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if req.bytecode {
|
|
if err := req.t.UpdateAccountCode(req.addrHash[:], codeNode(code)); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := req.t.UpdateAccountCodeSize(req.addrHash[:], len(code)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (tr *ResolverStateful) WalkerAccounts(keyIdx int, k []byte, v []byte) error {
|
|
return tr.Walker(true, keyIdx, k, v)
|
|
}
|
|
|
|
func (tr *ResolverStateful) WalkerStorage(keyIdx int, k []byte, v []byte) error {
|
|
return tr.Walker(false, keyIdx, k, v)
|
|
}
|
|
|
|
// Walker - k, v - shouldn't be reused in the caller's code
|
|
func (tr *ResolverStateful) Walker(isAccount bool, keyIdx int, k []byte, v []byte) error {
|
|
//fmt.Printf("Walker: keyIdx: %d key:%x value:%x\n", keyIdx, k, v)
|
|
if keyIdx != tr.keyIdx {
|
|
if err := tr.finaliseRoot(); err != nil {
|
|
return err
|
|
}
|
|
tr.hb.Reset()
|
|
tr.groups = nil
|
|
tr.keyIdx = keyIdx
|
|
tr.currentReq = tr.requests[tr.reqIndices[keyIdx]]
|
|
tr.currentRs = tr.rss[keyIdx]
|
|
tr.curr.Reset()
|
|
}
|
|
if len(v) > 0 {
|
|
tr.curr.Reset()
|
|
tr.curr.Write(tr.succ.Bytes())
|
|
tr.succ.Reset()
|
|
skip := tr.currentReq.extResolvePos // how many first nibbles to skip
|
|
i := 0
|
|
for _, b := range k {
|
|
if i >= skip {
|
|
tr.succ.WriteByte(b / 16)
|
|
}
|
|
i++
|
|
if i >= skip {
|
|
tr.succ.WriteByte(b % 16)
|
|
}
|
|
i++
|
|
}
|
|
tr.succ.WriteByte(16)
|
|
if tr.curr.Len() > 0 {
|
|
var err error
|
|
var data GenStructStepData
|
|
if tr.fieldSet == 0 {
|
|
tr.leafData.Value = rlphacks.RlpSerializableBytes(tr.value.Bytes())
|
|
data = &tr.leafData
|
|
} else {
|
|
tr.accData.FieldSet = tr.fieldSet
|
|
tr.accData.StorageSize = tr.a.StorageSize
|
|
tr.accData.Balance.Set(&tr.a.Balance)
|
|
tr.accData.Nonce = tr.a.Nonce
|
|
tr.accData.Incarnation = tr.a.Incarnation
|
|
data = &tr.accData
|
|
}
|
|
tr.groups, err = GenStructStep(tr.currentRs.HashOnly, tr.curr.Bytes(), tr.succ.Bytes(), tr.hb, data, tr.groups, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// Remember the current key and value
|
|
if isAccount {
|
|
if err := tr.a.DecodeForStorage(v); err != nil {
|
|
return err
|
|
}
|
|
if tr.a.IsEmptyCodeHash() && tr.a.IsEmptyRoot() {
|
|
tr.fieldSet = AccountFieldSetNotContract
|
|
} else {
|
|
if tr.a.HasStorageSize {
|
|
tr.fieldSet = AccountFieldSetContractWithSize
|
|
} else {
|
|
tr.fieldSet = AccountFieldSetContract
|
|
}
|
|
// the first item ends up deepest on the stack, the seccond item - on the top
|
|
if err := tr.hb.hash(tr.a.CodeHash[:]); err != nil {
|
|
return err
|
|
}
|
|
if err := tr.hb.hash(tr.a.Root[:]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else {
|
|
tr.value.Reset()
|
|
tr.value.Write(v)
|
|
tr.fieldSet = AccountFieldSetNotAccount
|
|
}
|
|
}
|
|
return nil
|
|
}
|