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 }