diff --git a/core/vm/logger.go b/core/vm/logger.go index 623c0d563..75309da92 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -45,7 +45,6 @@ type LogConfig struct { DisableMemory bool // disable memory capture DisableStack bool // disable stack capture DisableStorage bool // disable storage capture - FullStorage bool // show full storage (slow) Limit int // maximum length of output, but zero means unlimited } @@ -136,14 +135,13 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui ) l.changedValues[contract.Address()][address] = value } - // copy a snapstot of the current memory state to a new buffer + // Copy a snapstot of the current memory state to a new buffer var mem []byte if !l.cfg.DisableMemory { mem = make([]byte, len(memory.Data())) copy(mem, memory.Data()) } - - // copy a snapshot of the current stack state to a new buffer + // Copy a snapshot of the current stack state to a new buffer var stck []*big.Int if !l.cfg.DisableStack { stck = make([]*big.Int, len(stack.Data())) @@ -151,26 +149,10 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui stck[i] = new(big.Int).Set(item) } } - - // Copy the storage based on the settings specified in the log config. If full storage - // is disabled (default) we can use the simple Storage.Copy method, otherwise we use - // the state object to query for all values (slow process). + // Copy a snapshot of the current storage to a new container var storage Storage if !l.cfg.DisableStorage { - if l.cfg.FullStorage { - storage = make(Storage) - // Get the contract account and loop over each storage entry. This may involve looping over - // the trie and is a very expensive process. - - env.StateDB.ForEachStorage(contract.Address(), func(key, value common.Hash) bool { - storage[key] = value - // Return true, indicating we'd like to continue. - return true - }) - } else { - // copy a snapshot of the current storage to a new container. - storage = l.changedValues[contract.Address()].Copy() - } + storage = l.changedValues[contract.Address()].Copy() } // create a new snaptshot of the EVM. log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, err} diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index b6fa31132..915f7177e 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -63,32 +63,8 @@ func TestStoreCapture(t *testing.T) { if len(logger.changedValues[contract.Address()]) == 0 { t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()])) } - exp := common.BigToHash(big.NewInt(1)) if logger.changedValues[contract.Address()][index] != exp { t.Errorf("expected %x, got %x", exp, logger.changedValues[contract.Address()][index]) } } - -func TestStorageCapture(t *testing.T) { - t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it") - var ( - ref = &dummyContractRef{} - contract = NewContract(ref, ref, new(big.Int), 0) - env = NewEVM(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false}) - logger = NewStructLogger(nil) - mem = NewMemory() - stack = newstack() - ) - - logger.CaptureState(env, 0, STOP, 0, 0, mem, stack, contract, 0, nil) - if ref.calledForEach { - t.Error("didn't expect for each to be called") - } - - logger = NewStructLogger(&LogConfig{FullStorage: true}) - logger.CaptureState(env, 0, STOP, 0, 0, mem, stack, contract, 0, nil) - if !ref.calledForEach { - t.Error("expected for each to be called") - } -} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 59a29d722..cf15d1c71 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -710,45 +710,52 @@ type ExecutionResult struct { // StructLogRes stores a structured log emitted by the EVM while replaying a // transaction in debug mode type StructLogRes struct { - Pc uint64 `json:"pc"` - Op string `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Depth int `json:"depth"` - Error error `json:"error"` - Stack []string `json:"stack"` - Memory []string `json:"memory"` - Storage map[string]string `json:"storage"` + Pc uint64 `json:"pc"` + Op string `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Depth int `json:"depth"` + Error error `json:"error,omitempty"` + Stack *[]string `json:"stack,omitempty"` + Memory *[]string `json:"memory,omitempty"` + Storage *map[string]string `json:"storage,omitempty"` } // formatLogs formats EVM returned structured logs for json output -func FormatLogs(structLogs []vm.StructLog) []StructLogRes { - formattedStructLogs := make([]StructLogRes, len(structLogs)) - for index, trace := range structLogs { - formattedStructLogs[index] = StructLogRes{ +func FormatLogs(logs []vm.StructLog) []StructLogRes { + formatted := make([]StructLogRes, len(logs)) + for index, trace := range logs { + formatted[index] = StructLogRes{ Pc: trace.Pc, Op: trace.Op.String(), Gas: trace.Gas, GasCost: trace.GasCost, Depth: trace.Depth, Error: trace.Err, - Stack: make([]string, len(trace.Stack)), - Storage: make(map[string]string), } - - for i, stackValue := range trace.Stack { - formattedStructLogs[index].Stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32)) + if trace.Stack != nil { + stack := make([]string, len(trace.Stack)) + for i, stackValue := range trace.Stack { + stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32)) + } + formatted[index].Stack = &stack } - - for i := 0; i+32 <= len(trace.Memory); i += 32 { - formattedStructLogs[index].Memory = append(formattedStructLogs[index].Memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) + if trace.Memory != nil { + memory := make([]string, 0, (len(trace.Memory)+31)/32) + for i := 0; i+32 <= len(trace.Memory); i += 32 { + memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) + } + formatted[index].Memory = &memory } - - for i, storageValue := range trace.Storage { - formattedStructLogs[index].Storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) + if trace.Storage != nil { + storage := make(map[string]string) + for i, storageValue := range trace.Storage { + storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) + } + formatted[index].Storage = &storage } } - return formattedStructLogs + return formatted } // rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are