forked from cerc-io/plugeth
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:
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user