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. | ||||
| 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() | ||||
|   | ||||
| @@ -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 | ||||
| 	} | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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()])) | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user