diff --git a/core/state/database.go b/core/state/database.go index e81f56ea55f..669ed7d760f 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -253,7 +253,7 @@ func NewTrieDbState(root common.Hash, db kv.Tx, blockNr uint64, stateReader Stat blockNr: blockNr, retainListBuilder: trie.NewRetainListBuilder(), tp: tp, - pw: &PreimageWriter{db: db, savePreimages: true}, + pw: &PreimageWriter{savePreimages: true}, hashBuilder: trie.NewHashBuilder(false), incarnationMap: make(map[common.Address]uint64), } @@ -301,7 +301,7 @@ func (tds *TrieDbState) Copy() *TrieDbState { db: tds.db, blockNr: n, tp: tp, - pw: &PreimageWriter{db: tds.db, savePreimages: true}, + pw: &PreimageWriter{savePreimages: true}, hashBuilder: trie.NewHashBuilder(false), incarnationMap: make(map[common.Address]uint64), } @@ -439,6 +439,20 @@ func (tds *TrieDbState) buildStorageReads() common.StorageKeys { return storageTouches } +func (tds *TrieDbState) BuildStorageReads() map[common.Address][]common.Hash { + storageReads := make(map[common.Address][]common.Hash) + for storageKey := range tds.aggregateBuffer.storageReads { + addr := common.BytesToAddress(tds.pw.GetPreimage(common.BytesToHash(storageKey[:length.Hash]))) + + if _, ok := storageReads[addr]; !ok { + storageReads[addr] = make([]common.Hash, 0) + } + + storageReads[addr] = append(storageReads[addr], common.BytesToHash(storageKey[length.Hash+length.Incarnation:])) + } + return storageReads +} + // buildStorageWrites builds a sorted list of all storage key hashes that were modified within the // period for which we are aggregating updates. It skips the updates that // were nullified by subsequent updates - best example is the @@ -482,6 +496,14 @@ func (tds *TrieDbState) buildCodeTouches() map[common.Hash]common.Hash { return tds.aggregateBuffer.codeReads } +func (tds *TrieDbState) BuildCodeReads() map[common.Address]common.Hash { + addresses := make(map[common.Address]common.Hash) + for addrHash, codeHash := range tds.aggregateBuffer.codeReads { + addresses[common.BytesToAddress(tds.pw.GetPreimage(addrHash))] = codeHash + } + return addresses +} + func (tds *TrieDbState) buildCodeSizeTouches() map[common.Hash]common.Hash { return tds.aggregateBuffer.codeSizeReads } @@ -498,6 +520,15 @@ func (tds *TrieDbState) buildAccountReads() common.Hashes { return accountTouches } +func (tds *TrieDbState) BuildAddressReads() []common.Address { + accountTouches := tds.buildAccountReads() + addresses := make([]common.Address, len(accountTouches)) + for i, addrHash := range accountTouches { + addresses[i] = common.BytesToAddress(tds.pw.GetPreimage(addrHash)) + } + return addresses +} + // buildAccountWrites builds a sorted list of all address hashes that were modified within the // period for which we are aggregating updates. func (tds *TrieDbState) buildAccountWrites() (common.Hashes, []*accounts.Account, [][]byte) { diff --git a/core/state/database_writer.go b/core/state/database_writer.go index 48c73eb3bd5..c1196c07faa 100644 --- a/core/state/database_writer.go +++ b/core/state/database_writer.go @@ -1,14 +1,11 @@ package state import ( - "fmt" - "github.com/ledgerwatch/erigon-lib/common" - "github.com/ledgerwatch/erigon-lib/kv" ) type PreimageWriter struct { - db kv.Getter + preImageMap map[string][]byte savePreimages bool } @@ -33,20 +30,24 @@ func (pw *PreimageWriter) HashKey(key *common.Hash, save bool) (common.Hash, err } func (pw *PreimageWriter) savePreimage(save bool, hash []byte, preimage []byte) error { - if !save || !pw.savePreimages { - return nil - } - // Following check is to minimise the overwriting the same value of preimage - // in the database, which would cause extra write churn - if p, _ := pw.db.GetOne(kv.PreimagePrefix, hash); p != nil { + if !pw.savePreimages { return nil } - putter, ok := pw.db.(kv.Putter) + if pw.preImageMap == nil { + pw.preImageMap = make(map[string][]byte) + } - if !ok { - return fmt.Errorf("db does not support putter interface") + if _, ok := pw.preImageMap[string(hash)]; !ok { + pw.preImageMap[string(hash)] = preimage } - return putter.Put(kv.PreimagePrefix, hash, preimage) + return nil +} + +func (pw *PreimageWriter) GetPreimage(hash common.Hash) []byte { + if pw.preImageMap == nil { + return nil + } + return pw.preImageMap[string(hash[:])] } diff --git a/core/state/intra_block_state.go b/core/state/intra_block_state.go index a4682215282..78717a65a0b 100644 --- a/core/state/intra_block_state.go +++ b/core/state/intra_block_state.go @@ -111,6 +111,14 @@ func (sdb *IntraBlockState) SetTrace(trace bool) { sdb.trace = trace } +func (sdb *IntraBlockState) SetStateReader(stateReader StateReader) { + sdb.stateReader = stateReader +} + +func (sdb *IntraBlockState) GetStateReader() StateReader { + return sdb.stateReader +} + func (sdb *IntraBlockState) SetDisableBalanceInc(disable bool) { sdb.disableBalanceInc = disable } diff --git a/eth/tracers/native/zero.go b/eth/tracers/native/zero.go index dc357f7c090..722a50f7514 100644 --- a/eth/tracers/native/zero.go +++ b/eth/tracers/native/zero.go @@ -11,6 +11,7 @@ import ( "github.com/holiman/uint256" libcommon "github.com/ledgerwatch/erigon-lib/common" + corestate "github.com/ledgerwatch/erigon/core/state" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/core/vm" "github.com/ledgerwatch/erigon/crypto" @@ -203,6 +204,28 @@ func (t *zeroTracer) CaptureTxEnd(restGas uint64) { t.tx.Meta.GasUsed = t.gasLimit - restGas *t.ctx.CumulativeGasUsed += t.tx.Meta.GasUsed + ibs := t.env.IntraBlockState() + + tds := ibs.(*corestate.IntraBlockState).GetStateReader().(*corestate.TrieDbState) + + addressReads := tds.BuildAddressReads() + for _, addr := range addressReads { + t.addAccountToTrace(addr) + } + + storageReads := tds.BuildStorageReads() + for addr, keys := range storageReads { + for _, key := range keys { + t.addSLOADToAccount(addr, key) + } + } + + codeReads := tds.BuildCodeReads() + + for addr := range codeReads { + t.addAccountToTrace(addr) + } + for addr := range t.tx.Traces { trace := t.tx.Traces[addr] hasLiveAccount := t.env.IntraBlockState().HasLiveAccount(addr) @@ -267,7 +290,7 @@ func (t *zeroTracer) CaptureTxEnd(restGas uint64) { // DELEGATECALL, CALL, STATICCALL, CALLCODE, EXTCODECOPY, EXTCODEHASH, EXTCODESIZE opCodes := []vm.OpCode{vm.DELEGATECALL, vm.CALL, vm.STATICCALL, vm.CALLCODE, vm.EXTCODECOPY, vm.EXTCODEHASH, vm.EXTCODESIZE} - keep := false + _, keep := codeReads[addr] for _, opCode := range opCodes { if _, ok := t.addrOpCodes[addr][opCode]; ok { keep = true @@ -288,6 +311,8 @@ func (t *zeroTracer) CaptureTxEnd(restGas uint64) { } } + tds.ClearUpdates() + receipt := &types.Receipt{Type: t.ctx.Txn.Type(), CumulativeGasUsed: *t.ctx.CumulativeGasUsed} receipt.Status = t.txStatus receipt.TxHash = t.ctx.Txn.Hash() diff --git a/turbo/jsonrpc/tracing.go b/turbo/jsonrpc/tracing.go index 7f14601709c..e4bfb6acfdc 100644 --- a/turbo/jsonrpc/tracing.go +++ b/turbo/jsonrpc/tracing.go @@ -95,6 +95,12 @@ func (api *PrivateDebugAPIImpl) traceBlock(ctx context.Context, blockNrOrHash rp return err } + if config.Tracer != nil && *config.Tracer == "zeroTracer" { + reader := ibs.GetStateReader() + tds := state.NewTrieDbState(common.Hash{}, tx, blockNumber, reader) + ibs.SetStateReader(tds) + } + signer := types.MakeSigner(chainConfig, block.NumberU64(), block.Time()) rules := chainConfig.Rules(block.NumberU64(), block.Time()) stream.WriteArrayStart()