core, eth, internal: include read storage entries in structlog output (#21204)

* core, eth, internal: extend structLog tracer

* core/vm, internal: add storage view

* core, internal: add slots to storage directly

* core: remove useless

* core: address martin's comment

* core/vm: fix tests
pull/21208/head
gary rong 4 years ago committed by GitHub
parent e9ba536d85
commit 4a19c0e7b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 61
      core/vm/logger.go
  2. 8
      core/vm/logger_test.go
  3. 14
      eth/fetcher/tx_fetcher.go

@ -42,7 +42,6 @@ func (s Storage) Copy() Storage {
for key, value := range s { for key, value := range s {
cpy[key] = value cpy[key] = value
} }
return cpy return cpy
} }
@ -118,16 +117,16 @@ type Tracer interface {
type StructLogger struct { type StructLogger struct {
cfg LogConfig cfg LogConfig
logs []StructLog storage map[common.Address]Storage
changedValues map[common.Address]Storage logs []StructLog
output []byte output []byte
err error err error
} }
// NewStructLogger returns a new logger // NewStructLogger returns a new logger
func NewStructLogger(cfg *LogConfig) *StructLogger { func NewStructLogger(cfg *LogConfig) *StructLogger {
logger := &StructLogger{ logger := &StructLogger{
changedValues: make(map[common.Address]Storage), storage: make(map[common.Address]Storage),
} }
if cfg != nil { if cfg != nil {
logger.cfg = *cfg logger.cfg = *cfg
@ -142,28 +141,12 @@ func (l *StructLogger) CaptureStart(from common.Address, to common.Address, crea
// CaptureState logs a new structured log message and pushes it out to the environment // CaptureState logs a new structured log message and pushes it out to the environment
// //
// CaptureState also tracks SSTORE ops to track dirty values. // CaptureState also tracks SLOAD/SSTORE ops to track storage change.
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error { func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
// check if already accumulated the specified number of logs // check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
return errTraceLimitReached return errTraceLimitReached
} }
// initialise new changed values storage container for this contract
// if not present.
if l.changedValues[contract.Address()] == nil {
l.changedValues[contract.Address()] = make(Storage)
}
// capture SSTORE opcodes and determine the changed value and store
// it in the local storage container.
if op == SSTORE && stack.len() >= 2 {
var (
value = common.Hash(stack.data[stack.len()-2].Bytes32())
address = common.Hash(stack.data[stack.len()-1].Bytes32())
)
l.changedValues[contract.Address()][address] = value
}
// Copy a snapshot of the current memory state to a new buffer // Copy a snapshot of the current memory state to a new buffer
var mem []byte var mem []byte
if !l.cfg.DisableMemory { if !l.cfg.DisableMemory {
@ -178,19 +161,39 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
stck[i] = new(big.Int).Set(item.ToBig()) stck[i] = new(big.Int).Set(item.ToBig())
} }
} }
// Copy a snapshot of the current storage to a new container
var storage Storage
if !l.cfg.DisableStorage {
storage = l.changedValues[contract.Address()].Copy()
}
var rstack []uint64 var rstack []uint64
if !l.cfg.DisableStack && rStack != nil { if !l.cfg.DisableStack && rStack != nil {
rstck := make([]uint64, len(rStack.data)) rstck := make([]uint64, len(rStack.data))
copy(rstck, rStack.data) copy(rstck, rStack.data)
} }
// Copy a snapshot of the current storage to a new container
var storage Storage
if !l.cfg.DisableStorage {
// initialise new changed values storage container for this contract
// if not present.
if l.storage[contract.Address()] == nil {
l.storage[contract.Address()] = make(Storage)
}
// capture SLOAD opcodes and record the read entry in the local storage
if op == SLOAD && stack.len() >= 1 {
var (
address = common.Hash(stack.data[stack.len()-1].Bytes32())
value = env.StateDB.GetState(contract.Address(), address)
)
l.storage[contract.Address()][address] = value
}
// capture SSTORE opcodes and record the written entry in the local storage.
if op == SSTORE && stack.len() >= 2 {
var (
value = common.Hash(stack.data[stack.len()-2].Bytes32())
address = common.Hash(stack.data[stack.len()-1].Bytes32())
)
l.storage[contract.Address()][address] = value
}
storage = l.storage[contract.Address()].Copy()
}
// create a new snapshot of the EVM. // create a new snapshot of the EVM.
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, storage, depth, env.StateDB.GetRefund(), err} log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, storage, depth, env.StateDB.GetRefund(), err}
l.logs = append(l.logs, log) l.logs = append(l.logs, log)
return nil return nil
} }

@ -62,11 +62,11 @@ func TestStoreCapture(t *testing.T) {
stack.push(uint256.NewInt()) stack.push(uint256.NewInt())
var index common.Hash var index common.Hash
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, contract, 0, nil) logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, contract, 0, nil)
if len(logger.changedValues[contract.Address()]) == 0 { if len(logger.storage[contract.Address()]) == 0 {
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()])) t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()]))
} }
exp := common.BigToHash(big.NewInt(1)) exp := common.BigToHash(big.NewInt(1))
if logger.changedValues[contract.Address()][index] != exp { if logger.storage[contract.Address()][index] != exp {
t.Errorf("expected %x, got %x", exp, logger.changedValues[contract.Address()][index]) t.Errorf("expected %x, got %x", exp, logger.storage[contract.Address()][index])
} }
} }

@ -387,7 +387,7 @@ func (f *TxFetcher) loop() {
if announces := f.announces[ann.origin]; announces != nil { if announces := f.announces[ann.origin]; announces != nil {
announces[hash] = struct{}{} announces[hash] = struct{}{}
} else { } else {
f.announces[ann.origin] = map[common.Hash]struct{}{hash: struct{}{}} f.announces[ann.origin] = map[common.Hash]struct{}{hash: {}}
} }
continue continue
} }
@ -400,7 +400,7 @@ func (f *TxFetcher) loop() {
if announces := f.announces[ann.origin]; announces != nil { if announces := f.announces[ann.origin]; announces != nil {
announces[hash] = struct{}{} announces[hash] = struct{}{}
} else { } else {
f.announces[ann.origin] = map[common.Hash]struct{}{hash: struct{}{}} f.announces[ann.origin] = map[common.Hash]struct{}{hash: {}}
} }
continue continue
} }
@ -413,18 +413,18 @@ func (f *TxFetcher) loop() {
if waitslots := f.waitslots[ann.origin]; waitslots != nil { if waitslots := f.waitslots[ann.origin]; waitslots != nil {
waitslots[hash] = struct{}{} waitslots[hash] = struct{}{}
} else { } else {
f.waitslots[ann.origin] = map[common.Hash]struct{}{hash: struct{}{}} f.waitslots[ann.origin] = map[common.Hash]struct{}{hash: {}}
} }
continue continue
} }
// Transaction unknown to the fetcher, insert it into the waiting list // Transaction unknown to the fetcher, insert it into the waiting list
f.waitlist[hash] = map[string]struct{}{ann.origin: struct{}{}} f.waitlist[hash] = map[string]struct{}{ann.origin: {}}
f.waittime[hash] = f.clock.Now() f.waittime[hash] = f.clock.Now()
if waitslots := f.waitslots[ann.origin]; waitslots != nil { if waitslots := f.waitslots[ann.origin]; waitslots != nil {
waitslots[hash] = struct{}{} waitslots[hash] = struct{}{}
} else { } else {
f.waitslots[ann.origin] = map[common.Hash]struct{}{hash: struct{}{}} f.waitslots[ann.origin] = map[common.Hash]struct{}{hash: {}}
} }
} }
// If a new item was added to the waitlist, schedule it into the fetcher // If a new item was added to the waitlist, schedule it into the fetcher
@ -434,7 +434,7 @@ func (f *TxFetcher) loop() {
// If this peer is new and announced something already queued, maybe // If this peer is new and announced something already queued, maybe
// request transactions from them // request transactions from them
if !oldPeer && len(f.announces[ann.origin]) > 0 { if !oldPeer && len(f.announces[ann.origin]) > 0 {
f.scheduleFetches(timeoutTimer, timeoutTrigger, map[string]struct{}{ann.origin: struct{}{}}) f.scheduleFetches(timeoutTimer, timeoutTrigger, map[string]struct{}{ann.origin: {}})
} }
case <-waitTrigger: case <-waitTrigger:
@ -452,7 +452,7 @@ func (f *TxFetcher) loop() {
if announces := f.announces[peer]; announces != nil { if announces := f.announces[peer]; announces != nil {
announces[hash] = struct{}{} announces[hash] = struct{}{}
} else { } else {
f.announces[peer] = map[common.Hash]struct{}{hash: struct{}{}} f.announces[peer] = map[common.Hash]struct{}{hash: {}}
} }
delete(f.waitslots[peer], hash) delete(f.waitslots[peer], hash)
if len(f.waitslots[peer]) == 0 { if len(f.waitslots[peer]) == 0 {

Loading…
Cancel
Save