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.
This commit is contained in:
		
				
					committed by
					
						 Felix Lange
						Felix Lange
					
				
			
			
				
	
			
			
			
						parent
						
							3088c122d8
						
					
				
				
					commit
					4c0883e20d
				
			| @@ -45,14 +45,15 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create | |||||||
| // CaptureState outputs state information on the logger. | // 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 { | 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{ | 	log := vm.StructLog{ | ||||||
| 		Pc:         pc, | 		Pc:            pc, | ||||||
| 		Op:         op, | 		Op:            op, | ||||||
| 		Gas:        gas, | 		Gas:           gas, | ||||||
| 		GasCost:    cost, | 		GasCost:       cost, | ||||||
| 		MemorySize: memory.Len(), | 		MemorySize:    memory.Len(), | ||||||
| 		Storage:    nil, | 		Storage:       nil, | ||||||
| 		Depth:      depth, | 		Depth:         depth, | ||||||
| 		Err:        err, | 		RefundCounter: env.StateDB.GetRefund(), | ||||||
|  | 		Err:           err, | ||||||
| 	} | 	} | ||||||
| 	if !l.cfg.DisableMemory { | 	if !l.cfg.DisableMemory { | ||||||
| 		log.Memory = memory.Data() | 		log.Memory = memory.Data() | ||||||
|   | |||||||
| @@ -13,20 +13,22 @@ import ( | |||||||
|  |  | ||||||
| var _ = (*structLogMarshaling)(nil) | var _ = (*structLogMarshaling)(nil) | ||||||
|  |  | ||||||
|  | // MarshalJSON marshals as JSON. | ||||||
| func (s StructLog) MarshalJSON() ([]byte, error) { | func (s StructLog) MarshalJSON() ([]byte, error) { | ||||||
| 	type StructLog struct { | 	type StructLog struct { | ||||||
| 		Pc          uint64                      `json:"pc"` | 		Pc            uint64                      `json:"pc"` | ||||||
| 		Op          OpCode                      `json:"op"` | 		Op            OpCode                      `json:"op"` | ||||||
| 		Gas         math.HexOrDecimal64         `json:"gas"` | 		Gas           math.HexOrDecimal64         `json:"gas"` | ||||||
| 		GasCost     math.HexOrDecimal64         `json:"gasCost"` | 		GasCost       math.HexOrDecimal64         `json:"gasCost"` | ||||||
| 		Memory      hexutil.Bytes               `json:"memory"` | 		Memory        hexutil.Bytes               `json:"memory"` | ||||||
| 		MemorySize  int                         `json:"memSize"` | 		MemorySize    int                         `json:"memSize"` | ||||||
| 		Stack       []*math.HexOrDecimal256     `json:"stack"` | 		Stack         []*math.HexOrDecimal256     `json:"stack"` | ||||||
| 		Storage     map[common.Hash]common.Hash `json:"-"` | 		Storage       map[common.Hash]common.Hash `json:"-"` | ||||||
| 		Depth       int                         `json:"depth"` | 		Depth         int                         `json:"depth"` | ||||||
| 		Err         error                       `json:"-"` | 		RefundCounter uint64                      `json:"refund"` | ||||||
| 		OpName      string                      `json:"opName"` | 		Err           error                       `json:"-"` | ||||||
| 		ErrorString string                      `json:"error"` | 		OpName        string                      `json:"opName"` | ||||||
|  | 		ErrorString   string                      `json:"error"` | ||||||
| 	} | 	} | ||||||
| 	var enc StructLog | 	var enc StructLog | ||||||
| 	enc.Pc = s.Pc | 	enc.Pc = s.Pc | ||||||
| @@ -43,24 +45,27 @@ func (s StructLog) MarshalJSON() ([]byte, error) { | |||||||
| 	} | 	} | ||||||
| 	enc.Storage = s.Storage | 	enc.Storage = s.Storage | ||||||
| 	enc.Depth = s.Depth | 	enc.Depth = s.Depth | ||||||
|  | 	enc.RefundCounter = s.RefundCounter | ||||||
| 	enc.Err = s.Err | 	enc.Err = s.Err | ||||||
| 	enc.OpName = s.OpName() | 	enc.OpName = s.OpName() | ||||||
| 	enc.ErrorString = s.ErrorString() | 	enc.ErrorString = s.ErrorString() | ||||||
| 	return json.Marshal(&enc) | 	return json.Marshal(&enc) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // UnmarshalJSON unmarshals from JSON. | ||||||
| func (s *StructLog) UnmarshalJSON(input []byte) error { | func (s *StructLog) UnmarshalJSON(input []byte) error { | ||||||
| 	type StructLog struct { | 	type StructLog struct { | ||||||
| 		Pc         *uint64                     `json:"pc"` | 		Pc            *uint64                     `json:"pc"` | ||||||
| 		Op         *OpCode                     `json:"op"` | 		Op            *OpCode                     `json:"op"` | ||||||
| 		Gas        *math.HexOrDecimal64        `json:"gas"` | 		Gas           *math.HexOrDecimal64        `json:"gas"` | ||||||
| 		GasCost    *math.HexOrDecimal64        `json:"gasCost"` | 		GasCost       *math.HexOrDecimal64        `json:"gasCost"` | ||||||
| 		Memory     *hexutil.Bytes              `json:"memory"` | 		Memory        *hexutil.Bytes              `json:"memory"` | ||||||
| 		MemorySize *int                        `json:"memSize"` | 		MemorySize    *int                        `json:"memSize"` | ||||||
| 		Stack      []*math.HexOrDecimal256     `json:"stack"` | 		Stack         []*math.HexOrDecimal256     `json:"stack"` | ||||||
| 		Storage    map[common.Hash]common.Hash `json:"-"` | 		Storage       map[common.Hash]common.Hash `json:"-"` | ||||||
| 		Depth      *int                        `json:"depth"` | 		Depth         *int                        `json:"depth"` | ||||||
| 		Err        error                       `json:"-"` | 		RefundCounter *uint64                     `json:"refund"` | ||||||
|  | 		Err           error                       `json:"-"` | ||||||
| 	} | 	} | ||||||
| 	var dec StructLog | 	var dec StructLog | ||||||
| 	if err := json.Unmarshal(input, &dec); err != nil { | 	if err := json.Unmarshal(input, &dec); err != nil { | ||||||
| @@ -96,6 +101,9 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { | |||||||
| 	if dec.Depth != nil { | 	if dec.Depth != nil { | ||||||
| 		s.Depth = *dec.Depth | 		s.Depth = *dec.Depth | ||||||
| 	} | 	} | ||||||
|  | 	if dec.RefundCounter != nil { | ||||||
|  | 		s.RefundCounter = *dec.RefundCounter | ||||||
|  | 	} | ||||||
| 	if dec.Err != nil { | 	if dec.Err != nil { | ||||||
| 		s.Err = dec.Err | 		s.Err = dec.Err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -56,16 +56,17 @@ type LogConfig struct { | |||||||
| // StructLog is emitted to the EVM each cycle and lists information about the current internal state | // StructLog is emitted to the EVM each cycle and lists information about the current internal state | ||||||
| // prior to the execution of the statement. | // prior to the execution of the statement. | ||||||
| type StructLog struct { | type StructLog struct { | ||||||
| 	Pc         uint64                      `json:"pc"` | 	Pc            uint64                      `json:"pc"` | ||||||
| 	Op         OpCode                      `json:"op"` | 	Op            OpCode                      `json:"op"` | ||||||
| 	Gas        uint64                      `json:"gas"` | 	Gas           uint64                      `json:"gas"` | ||||||
| 	GasCost    uint64                      `json:"gasCost"` | 	GasCost       uint64                      `json:"gasCost"` | ||||||
| 	Memory     []byte                      `json:"memory"` | 	Memory        []byte                      `json:"memory"` | ||||||
| 	MemorySize int                         `json:"memSize"` | 	MemorySize    int                         `json:"memSize"` | ||||||
| 	Stack      []*big.Int                  `json:"stack"` | 	Stack         []*big.Int                  `json:"stack"` | ||||||
| 	Storage    map[common.Hash]common.Hash `json:"-"` | 	Storage       map[common.Hash]common.Hash `json:"-"` | ||||||
| 	Depth      int                         `json:"depth"` | 	Depth         int                         `json:"depth"` | ||||||
| 	Err        error                       `json:"-"` | 	RefundCounter uint64                      `json:"refund"` | ||||||
|  | 	Err           error                       `json:"-"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // overrides for gencodec | // 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() | 		storage = l.changedValues[contract.Address()].Copy() | ||||||
| 	} | 	} | ||||||
| 	// create a new snaptshot of the EVM. | 	// 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) | 	l.logs = append(l.logs, log) | ||||||
| 	return nil | 	return nil | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/ethereum/go-ethereum/common" | 	"github.com/ethereum/go-ethereum/common" | ||||||
|  | 	"github.com/ethereum/go-ethereum/core/state" | ||||||
| 	"github.com/ethereum/go-ethereum/params" | 	"github.com/ethereum/go-ethereum/params" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -41,9 +42,15 @@ func (d *dummyContractRef) SetBalance(*big.Int)        {} | |||||||
| func (d *dummyContractRef) SetNonce(uint64)            {} | func (d *dummyContractRef) SetNonce(uint64)            {} | ||||||
| func (d *dummyContractRef) Balance() *big.Int          { return new(big.Int) } | 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) { | func TestStoreCapture(t *testing.T) { | ||||||
| 	var ( | 	var ( | ||||||
| 		env      = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) | 		env      = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{}) | ||||||
| 		logger   = NewStructLogger(nil) | 		logger   = NewStructLogger(nil) | ||||||
| 		mem      = NewMemory() | 		mem      = NewMemory() | ||||||
| 		stack    = newstack() | 		stack    = newstack() | ||||||
| @@ -51,9 +58,7 @@ func TestStoreCapture(t *testing.T) { | |||||||
| 	) | 	) | ||||||
| 	stack.push(big.NewInt(1)) | 	stack.push(big.NewInt(1)) | ||||||
| 	stack.push(big.NewInt(0)) | 	stack.push(big.NewInt(0)) | ||||||
|  |  | ||||||
| 	var index common.Hash | 	var index common.Hash | ||||||
|  |  | ||||||
| 	logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil) | 	logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil) | ||||||
| 	if len(logger.changedValues[contract.Address()]) == 0 { | 	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()])) | 		t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()])) | ||||||
|   | |||||||
| @@ -290,11 +290,12 @@ type Tracer struct { | |||||||
| 	contractWrapper *contractWrapper // Wrapper around the contract object | 	contractWrapper *contractWrapper // Wrapper around the contract object | ||||||
| 	dbWrapper       *dbWrapper       // Wrapper around the VM environment | 	dbWrapper       *dbWrapper       // Wrapper around the VM environment | ||||||
|  |  | ||||||
| 	pcValue    *uint   // Swappable pc 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 | 	gasValue    *uint   // Swappable gas value wrapped by a log accessor | ||||||
| 	costValue  *uint   // Swappable cost 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 | 	depthValue  *uint   // Swappable depth value wrapped by a log accessor | ||||||
| 	errorValue *string // Swappable error 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 | 	ctx map[string]interface{} // Transaction context gathered throughout execution | ||||||
| 	err error                  // Error, if one has occurred | 	err error                  // Error, if one has occurred | ||||||
| @@ -323,6 +324,7 @@ func New(code string) (*Tracer, error) { | |||||||
| 		gasValue:        new(uint), | 		gasValue:        new(uint), | ||||||
| 		costValue:       new(uint), | 		costValue:       new(uint), | ||||||
| 		depthValue:      new(uint), | 		depthValue:      new(uint), | ||||||
|  | 		refundValue:     new(uint), | ||||||
| 	} | 	} | ||||||
| 	// Set up builtins for this environment | 	// Set up builtins for this environment | ||||||
| 	tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int { | 	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.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 }) | ||||||
| 	tracer.vm.PutPropString(logObject, "getDepth") | 	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 { | 	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { | ||||||
| 		if tracer.errorValue != nil { | 		if tracer.errorValue != nil { | ||||||
| 			ctx.PushString(*tracer.errorValue) | 			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.gasValue = uint(gas) | ||||||
| 		*jst.costValue = uint(cost) | 		*jst.costValue = uint(cost) | ||||||
| 		*jst.depthValue = uint(depth) | 		*jst.depthValue = uint(depth) | ||||||
|  | 		*jst.refundValue = uint(env.StateDB.GetRefund()) | ||||||
|  |  | ||||||
| 		jst.errorValue = nil | 		jst.errorValue = nil | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/ethereum/go-ethereum/common" | 	"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/core/vm" | ||||||
| 	"github.com/ethereum/go-ethereum/params" | 	"github.com/ethereum/go-ethereum/params" | ||||||
| ) | ) | ||||||
| @@ -43,8 +44,14 @@ func (account) ReturnGas(*big.Int)                                  {} | |||||||
| func (account) SetCode(common.Hash, []byte)                         {} | func (account) SetCode(common.Hash, []byte)                         {} | ||||||
| func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} | 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) { | 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 := vm.NewContract(account{}, account{}, big.NewInt(0), 10000) | ||||||
| 	contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} | 	contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} | ||||||
| @@ -126,7 +133,7 @@ func TestHaltBetweenSteps(t *testing.T) { | |||||||
| 		t.Fatal(err) | 		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) | 	contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0) | ||||||
|  |  | ||||||
| 	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil) | 	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user