浏览代码

core/vm: adds refund as part of the json standard trace (#17910)

This adds the global accumulated refund counter to the standard
json output as a numeric json value. Previously this was not very
interesting since it was not used much, but with the new sstore
gas changes the value is a lot more interesting from a consensus
investigation perspective.
Martin Holst Swende 7 年之前
父节点
当前提交
4c0883e20d
共有 6 个文件被更改,包括 79 次插入51 次删除
  1. 9 8
      cmd/evm/json_logger.go
  2. 30 22
      core/vm/gen_structlog.go
  3. 12 11
      core/vm/logger.go
  4. 8 3
      core/vm/logger_test.go
  5. 11 5
      eth/tracers/tracer.go
  6. 9 2
      eth/tracers/tracer_test.go

+ 9 - 8
cmd/evm/json_logger.go

@@ -45,14 +45,15 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create
 // CaptureState outputs state information on the logger.
 func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
 	log := vm.StructLog{
-		Pc:         pc,
-		Op:         op,
-		Gas:        gas,
-		GasCost:    cost,
-		MemorySize: memory.Len(),
-		Storage:    nil,
-		Depth:      depth,
-		Err:        err,
+		Pc:            pc,
+		Op:            op,
+		Gas:           gas,
+		GasCost:       cost,
+		MemorySize:    memory.Len(),
+		Storage:       nil,
+		Depth:         depth,
+		RefundCounter: env.StateDB.GetRefund(),
+		Err:           err,
 	}
 	if !l.cfg.DisableMemory {
 		log.Memory = memory.Data()

+ 30 - 22
core/vm/gen_structlog.go

@@ -13,20 +13,22 @@ import (
 
 var _ = (*structLogMarshaling)(nil)
 
+// MarshalJSON marshals as JSON.
 func (s StructLog) MarshalJSON() ([]byte, error) {
 	type StructLog struct {
-		Pc          uint64                      `json:"pc"`
-		Op          OpCode                      `json:"op"`
-		Gas         math.HexOrDecimal64         `json:"gas"`
-		GasCost     math.HexOrDecimal64         `json:"gasCost"`
-		Memory      hexutil.Bytes               `json:"memory"`
-		MemorySize  int                         `json:"memSize"`
-		Stack       []*math.HexOrDecimal256     `json:"stack"`
-		Storage     map[common.Hash]common.Hash `json:"-"`
-		Depth       int                         `json:"depth"`
-		Err         error                       `json:"-"`
-		OpName      string                      `json:"opName"`
-		ErrorString string                      `json:"error"`
+		Pc            uint64                      `json:"pc"`
+		Op            OpCode                      `json:"op"`
+		Gas           math.HexOrDecimal64         `json:"gas"`
+		GasCost       math.HexOrDecimal64         `json:"gasCost"`
+		Memory        hexutil.Bytes               `json:"memory"`
+		MemorySize    int                         `json:"memSize"`
+		Stack         []*math.HexOrDecimal256     `json:"stack"`
+		Storage       map[common.Hash]common.Hash `json:"-"`
+		Depth         int                         `json:"depth"`
+		RefundCounter uint64                      `json:"refund"`
+		Err           error                       `json:"-"`
+		OpName        string                      `json:"opName"`
+		ErrorString   string                      `json:"error"`
 	}
 	var enc StructLog
 	enc.Pc = s.Pc
@@ -43,24 +45,27 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
 	}
 	enc.Storage = s.Storage
 	enc.Depth = s.Depth
+	enc.RefundCounter = s.RefundCounter
 	enc.Err = s.Err
 	enc.OpName = s.OpName()
 	enc.ErrorString = s.ErrorString()
 	return json.Marshal(&enc)
 }
 
+// UnmarshalJSON unmarshals from JSON.
 func (s *StructLog) UnmarshalJSON(input []byte) error {
 	type StructLog struct {
-		Pc         *uint64                     `json:"pc"`
-		Op         *OpCode                     `json:"op"`
-		Gas        *math.HexOrDecimal64        `json:"gas"`
-		GasCost    *math.HexOrDecimal64        `json:"gasCost"`
-		Memory     *hexutil.Bytes              `json:"memory"`
-		MemorySize *int                        `json:"memSize"`
-		Stack      []*math.HexOrDecimal256     `json:"stack"`
-		Storage    map[common.Hash]common.Hash `json:"-"`
-		Depth      *int                        `json:"depth"`
-		Err        error                       `json:"-"`
+		Pc            *uint64                     `json:"pc"`
+		Op            *OpCode                     `json:"op"`
+		Gas           *math.HexOrDecimal64        `json:"gas"`
+		GasCost       *math.HexOrDecimal64        `json:"gasCost"`
+		Memory        *hexutil.Bytes              `json:"memory"`
+		MemorySize    *int                        `json:"memSize"`
+		Stack         []*math.HexOrDecimal256     `json:"stack"`
+		Storage       map[common.Hash]common.Hash `json:"-"`
+		Depth         *int                        `json:"depth"`
+		RefundCounter *uint64                     `json:"refund"`
+		Err           error                       `json:"-"`
 	}
 	var dec StructLog
 	if err := json.Unmarshal(input, &dec); err != nil {
@@ -96,6 +101,9 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
 	if dec.Depth != nil {
 		s.Depth = *dec.Depth
 	}
+	if dec.RefundCounter != nil {
+		s.RefundCounter = *dec.RefundCounter
+	}
 	if dec.Err != nil {
 		s.Err = dec.Err
 	}

+ 12 - 11
core/vm/logger.go

@@ -56,16 +56,17 @@ type LogConfig struct {
 // StructLog is emitted to the EVM each cycle and lists information about the current internal state
 // prior to the execution of the statement.
 type StructLog struct {
-	Pc         uint64                      `json:"pc"`
-	Op         OpCode                      `json:"op"`
-	Gas        uint64                      `json:"gas"`
-	GasCost    uint64                      `json:"gasCost"`
-	Memory     []byte                      `json:"memory"`
-	MemorySize int                         `json:"memSize"`
-	Stack      []*big.Int                  `json:"stack"`
-	Storage    map[common.Hash]common.Hash `json:"-"`
-	Depth      int                         `json:"depth"`
-	Err        error                       `json:"-"`
+	Pc            uint64                      `json:"pc"`
+	Op            OpCode                      `json:"op"`
+	Gas           uint64                      `json:"gas"`
+	GasCost       uint64                      `json:"gasCost"`
+	Memory        []byte                      `json:"memory"`
+	MemorySize    int                         `json:"memSize"`
+	Stack         []*big.Int                  `json:"stack"`
+	Storage       map[common.Hash]common.Hash `json:"-"`
+	Depth         int                         `json:"depth"`
+	RefundCounter uint64                      `json:"refund"`
+	Err           error                       `json:"-"`
 }
 
 // overrides for gencodec
@@ -177,7 +178,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
 		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}
+	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err}
 
 	l.logs = append(l.logs, log)
 	return nil

+ 8 - 3
core/vm/logger_test.go

@@ -21,6 +21,7 @@ import (
 	"testing"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/state"
 	"github.com/ethereum/go-ethereum/params"
 )
 
@@ -41,9 +42,15 @@ func (d *dummyContractRef) SetBalance(*big.Int)        {}
 func (d *dummyContractRef) SetNonce(uint64)            {}
 func (d *dummyContractRef) Balance() *big.Int          { return new(big.Int) }
 
+type dummyStatedb struct {
+	state.StateDB
+}
+
+func (dummyStatedb) GetRefund() uint64 { return 1337 }
+
 func TestStoreCapture(t *testing.T) {
 	var (
-		env      = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
+		env      = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{})
 		logger   = NewStructLogger(nil)
 		mem      = NewMemory()
 		stack    = newstack()
@@ -51,9 +58,7 @@ func TestStoreCapture(t *testing.T) {
 	)
 	stack.push(big.NewInt(1))
 	stack.push(big.NewInt(0))
-
 	var index common.Hash
-
 	logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil)
 	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()]))

+ 11 - 5
eth/tracers/tracer.go

@@ -290,11 +290,12 @@ type Tracer struct {
 	contractWrapper *contractWrapper // Wrapper around the contract object
 	dbWrapper       *dbWrapper       // Wrapper around the VM environment
 
-	pcValue    *uint   // Swappable pc value wrapped by a log accessor
-	gasValue   *uint   // Swappable gas value wrapped by a log accessor
-	costValue  *uint   // Swappable cost value wrapped by a log accessor
-	depthValue *uint   // Swappable depth value wrapped by a log accessor
-	errorValue *string // Swappable error value wrapped by a log accessor
+	pcValue     *uint   // Swappable pc value wrapped by a log accessor
+	gasValue    *uint   // Swappable gas value wrapped by a log accessor
+	costValue   *uint   // Swappable cost value wrapped by a log accessor
+	depthValue  *uint   // Swappable depth value wrapped by a log accessor
+	errorValue  *string // Swappable error value wrapped by a log accessor
+	refundValue *uint   // Swappable refund value wrapped by a log accessor
 
 	ctx map[string]interface{} // Transaction context gathered throughout execution
 	err error                  // Error, if one has occurred
@@ -323,6 +324,7 @@ func New(code string) (*Tracer, error) {
 		gasValue:        new(uint),
 		costValue:       new(uint),
 		depthValue:      new(uint),
+		refundValue:     new(uint),
 	}
 	// Set up builtins for this environment
 	tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
@@ -442,6 +444,9 @@ func New(code string) (*Tracer, error) {
 	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 })
 	tracer.vm.PutPropString(logObject, "getDepth")
 
+	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 })
+	tracer.vm.PutPropString(logObject, "getRefund")
+
 	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int {
 		if tracer.errorValue != nil {
 			ctx.PushString(*tracer.errorValue)
@@ -527,6 +532,7 @@ func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost
 		*jst.gasValue = uint(gas)
 		*jst.costValue = uint(cost)
 		*jst.depthValue = uint(depth)
+		*jst.refundValue = uint(env.StateDB.GetRefund())
 
 		jst.errorValue = nil
 		if err != nil {

+ 9 - 2
eth/tracers/tracer_test.go

@@ -25,6 +25,7 @@ import (
 	"time"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/state"
 	"github.com/ethereum/go-ethereum/core/vm"
 	"github.com/ethereum/go-ethereum/params"
 )
@@ -43,8 +44,14 @@ func (account) ReturnGas(*big.Int)                                  {}
 func (account) SetCode(common.Hash, []byte)                         {}
 func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
 
+type dummyStatedb struct {
+	state.StateDB
+}
+
+func (dummyStatedb) GetRefund() uint64 { return 1337 }
+
 func runTrace(tracer *Tracer) (json.RawMessage, error) {
-	env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+	env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
 
 	contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
 	contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
@@ -126,7 +133,7 @@ func TestHaltBetweenSteps(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+	env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
 	contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
 
 	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil)