Skip to content

Commit db48539

Browse files
authored
eth/tracers: expose gas used in tx to js tracer ethereum#22038 (#1249)
1 parent f87caf0 commit db48539

File tree

6 files changed

+48
-119
lines changed

6 files changed

+48
-119
lines changed

eth/api_tracer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ func (api *DebugAPI) traceTx(ctx context.Context, message core.Message, txctx *t
718718
return nil, err
719719
}
720720
}
721-
if t, err := tracers.New(*config.Tracer, txctx, config.TracerConfig); err != nil {
721+
if t, err := tracers.New(*config.Tracer, txContext, txctx, config.TracerConfig); err != nil {
722722
return nil, err
723723
} else {
724724
deadlineCtx, cancel := context.WithTimeout(ctx, timeout)

eth/tracers/testing/calltrace_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
110110
}
111111
statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
112112
)
113-
tracer, err := tracers.New(tracerName, new(tracers.Context), test.TracerConfig)
113+
tracer, err := tracers.New(tracerName, txContext, new(tracers.Context), test.TracerConfig)
114114
if err != nil {
115115
t.Fatalf("failed to create call tracer: %v", err)
116116
}
@@ -223,7 +223,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
223223
b.ReportAllocs()
224224
b.ResetTimer()
225225
for i := 0; i < b.N; i++ {
226-
tracer, err := tracers.New(tracerName, new(tracers.Context), nil)
226+
tracer, err := tracers.New(tracerName, txContext, new(tracers.Context), nil)
227227
if err != nil {
228228
b.Fatalf("failed to create call tracer: %v", err)
229229
}
@@ -292,7 +292,7 @@ func testContractTracer(tracerName string, dirPath string, t *testing.T) {
292292
}
293293
statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
294294
)
295-
tracer, err := tracers.New(tracerName, new(tracers.Context), test.TracerConfig)
295+
tracer, err := tracers.New(tracerName, txContext, new(tracers.Context), test.TracerConfig)
296296
if err != nil {
297297
t.Fatalf("failed to create call tracer: %v", err)
298298
}

eth/tracers/tracer.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ type Context struct {
411411
// which must evaluate to an expression returning an object with 'step', 'fault'
412412
// and 'result' functions.
413413
// TODO gerui rename to private func
414-
func NewJsTracer(code string, ctx *Context) (*JsTracer, error) {
414+
func NewJsTracer(code string, txCtx vm.TxContext, ctx *Context) (*JsTracer, error) {
415415
tracer := &JsTracer{
416416
vm: duktape.New(),
417417
ctx: make(map[string]interface{}),
@@ -428,6 +428,8 @@ func NewJsTracer(code string, ctx *Context) (*JsTracer, error) {
428428
frame: newFrame(),
429429
frameResult: newFrameResult(),
430430
}
431+
tracer.ctx["gasPrice"] = txCtx.GasPrice
432+
431433
if ctx.BlockHash != (common.Hash{}) {
432434
tracer.ctx["blockHash"] = ctx.BlockHash
433435

eth/tracers/tracer_test.go

Lines changed: 37 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package tracers
1818

1919
import (
20-
"bytes"
2120
"encoding/json"
2221
"errors"
2322
"math/big"
@@ -55,16 +54,17 @@ type vmContext struct {
5554
txContext vm.TxContext
5655
}
5756

58-
func runTrace(tracer Tracer, blockNumber *big.Int, chaincfg *params.ChainConfig) (json.RawMessage, error) {
57+
func testCtx() *vmContext {
58+
return &vmContext{ctx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txContext: vm.TxContext{GasPrice: big.NewInt(100000)}}
59+
}
60+
61+
func runTrace(tracer Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) {
62+
env := vm.NewEVM(vmctx.ctx, vmctx.txContext, &dummyStatedb{}, nil, chaincfg, vm.Config{Tracer: tracer})
5963
var (
60-
startGas uint64 = 10000
61-
value = big.NewInt(0)
62-
ctx = vm.BlockContext{BlockNumber: blockNumber}
63-
txContext = vm.TxContext{GasPrice: big.NewInt(100000)}
64+
startGas uint64 = 10000
65+
value = big.NewInt(0)
6466
)
6567

66-
env := vm.NewEVM(ctx, txContext, &dummyStatedb{}, nil, chaincfg, vm.Config{Tracer: tracer})
67-
6868
contract := vm.NewContract(account{}, account{}, value, startGas)
6969
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
7070

@@ -80,11 +80,12 @@ func runTrace(tracer Tracer, blockNumber *big.Int, chaincfg *params.ChainConfig)
8080
func TestTracer(t *testing.T) {
8181
execTracer := func(code string) ([]byte, string) {
8282
t.Helper()
83-
tracer, err := New(code, new(Context), nil)
83+
ctx := &vmContext{ctx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txContext: vm.TxContext{GasPrice: big.NewInt(100000)}}
84+
tracer, err := New(code, ctx.txContext, new(Context), nil)
8485
if err != nil {
8586
t.Fatal(err)
8687
}
87-
ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig)
88+
ret, err := runTrace(tracer, ctx, params.TestChainConfig)
8889
if err != nil {
8990
return nil, err.Error() // Stringify to allow comparison without nil checks
9091
}
@@ -131,21 +132,23 @@ func TestHalt(t *testing.T) {
131132
t.Skip("duktape doesn't support abortion")
132133

133134
timeout := errors.New("stahp")
134-
tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", new(Context), nil)
135+
vmctx := testCtx()
136+
tracer, err := New("{step: function() { while(1); }, result: function() { return null; }}", vmctx.txContext, new(Context), nil)
135137
if err != nil {
136138
t.Fatal(err)
137139
}
138140
go func() {
139141
time.Sleep(1 * time.Second)
140142
tracer.Stop(timeout)
141143
}()
142-
if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" {
144+
if _, err = runTrace(tracer, vmctx, params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" {
143145
t.Errorf("Expected timeout error, got %v", err)
144146
}
145147
}
146148

147149
func TestHaltBetweenSteps(t *testing.T) {
148-
tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", new(Context), nil)
150+
vmctx := testCtx()
151+
tracer, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }}", vmctx.txContext, new(Context), nil)
149152
if err != nil {
150153
t.Fatal(err)
151154
}
@@ -166,23 +169,18 @@ func TestHaltBetweenSteps(t *testing.T) {
166169
// TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb
167170
// in 'result'
168171
func TestNoStepExec(t *testing.T) {
169-
runEmptyTrace := func(tracer Tracer) (json.RawMessage, error) {
170-
ctx := vm.BlockContext{BlockNumber: big.NewInt(1)}
171-
txContext := vm.TxContext{GasPrice: big.NewInt(100000)}
172-
env := vm.NewEVM(ctx, txContext, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: tracer})
173-
startGas := uint64(10000)
174-
contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas)
175-
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, big.NewInt(0))
176-
tracer.CaptureEnd(nil, startGas-contract.Gas, 1, nil)
177-
return tracer.GetResult()
178-
}
179172
execTracer := func(code string) []byte {
180173
t.Helper()
181-
tracer, err := New(code, new(Context), nil)
174+
txContext := vm.TxContext{GasPrice: big.NewInt(100000)}
175+
tracer, err := New(code, txContext, new(Context), nil)
182176
if err != nil {
183177
t.Fatal(err)
184178
}
185-
ret, err := runEmptyTrace(tracer)
179+
ctx := vm.BlockContext{BlockNumber: big.NewInt(1)}
180+
env := vm.NewEVM(ctx, txContext, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: tracer})
181+
tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0))
182+
tracer.CaptureEnd(nil, 0, 1, nil)
183+
ret, err := tracer.GetResult()
186184
if err != nil {
187185
t.Fatal(err)
188186
}
@@ -224,20 +222,25 @@ func TestIsPrecompile(t *testing.T) {
224222
chaincfg.ByzantiumBlock = big.NewInt(100)
225223
chaincfg.IstanbulBlock = big.NewInt(200)
226224
chaincfg.BerlinBlock = big.NewInt(300)
227-
tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context), nil)
225+
txCtx := vm.TxContext{GasPrice: big.NewInt(100000)}
226+
227+
tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", txCtx, new(Context), nil)
228228
if err != nil {
229229
t.Fatal(err)
230230
}
231-
res, err := runTrace(tracer, big.NewInt(150), chaincfg)
231+
232+
blockCtx := vm.BlockContext{BlockNumber: big.NewInt(150)}
233+
res, err := runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
232234
if err != nil {
233235
t.Error(err)
234236
}
235237
if string(res) != "false" {
236238
t.Errorf("Tracer should not consider blake2f as precompile in byzantium")
237239
}
238240

239-
tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context), nil)
240-
res, err = runTrace(tracer, big.NewInt(250), chaincfg)
241+
blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)}
242+
tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", txCtx, new(Context), nil)
243+
res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
241244
if err != nil {
242245
t.Error(err)
243246
}
@@ -247,16 +250,18 @@ func TestIsPrecompile(t *testing.T) {
247250
}
248251

249252
func TestEnterExit(t *testing.T) {
253+
txCtx := vm.TxContext{GasPrice: big.NewInt(100000)}
254+
250255
// test that either both or none of enter() and exit() are defined
251-
if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(Context), nil); err == nil {
256+
if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", txCtx, new(Context), nil); err == nil {
252257
t.Fatal("tracer creation should've failed without exit() definition")
253258
}
254-
if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(Context), nil); err != nil {
259+
if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", txCtx, new(Context), nil); err != nil {
255260
t.Fatal(err)
256261
}
257262

258263
// test that the enter and exit method are correctly invoked and the values passed
259-
tracer, err := New("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(Context), nil)
264+
tracer, err := New("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", txCtx, new(Context), nil)
260265
if err != nil {
261266
t.Fatal(err)
262267
}
@@ -277,81 +282,3 @@ func TestEnterExit(t *testing.T) {
277282
t.Errorf("Number of invocations of enter() and exit() is wrong. Have %s, want %s\n", have, want)
278283
}
279284
}
280-
281-
// TestRegressionPanicSlice tests that we don't panic on bad arguments to memory access
282-
func TestRegressionPanicSlice(t *testing.T) {
283-
tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}", new(Context), nil)
284-
if err != nil {
285-
t.Fatal(err)
286-
}
287-
if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil {
288-
t.Fatal(err)
289-
}
290-
}
291-
292-
// TestRegressionPanicPeek tests that we don't panic on bad arguments to stack peeks
293-
func TestRegressionPanicPeek(t *testing.T) {
294-
tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}", new(Context), nil)
295-
if err != nil {
296-
t.Fatal(err)
297-
}
298-
if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil {
299-
t.Fatal(err)
300-
}
301-
}
302-
303-
// TestRegressionPanicGetUint tests that we don't panic on bad arguments to memory getUint
304-
func TestRegressionPanicGetUint(t *testing.T) {
305-
tracer, err := New("{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}", new(Context), nil)
306-
if err != nil {
307-
t.Fatal(err)
308-
}
309-
if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil {
310-
t.Fatal(err)
311-
}
312-
}
313-
314-
func TestTracing(t *testing.T) {
315-
tracer, err := New("{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}", new(Context), nil)
316-
if err != nil {
317-
t.Fatal(err)
318-
}
319-
320-
ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig)
321-
if err != nil {
322-
t.Fatal(err)
323-
}
324-
if !bytes.Equal(ret, []byte("3")) {
325-
t.Errorf("Expected return value to be 3, got %s", string(ret))
326-
}
327-
}
328-
329-
func TestStack(t *testing.T) {
330-
tracer, err := New("{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}", new(Context), nil)
331-
if err != nil {
332-
t.Fatal(err)
333-
}
334-
335-
ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig)
336-
if err != nil {
337-
t.Fatal(err)
338-
}
339-
if !bytes.Equal(ret, []byte("[0,1,2]")) {
340-
t.Errorf("Expected return value to be [0,1,2], got %s", string(ret))
341-
}
342-
}
343-
344-
func TestOpcodes(t *testing.T) {
345-
tracer, err := New("{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}", new(Context), nil)
346-
if err != nil {
347-
t.Fatal(err)
348-
}
349-
350-
ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig)
351-
if err != nil {
352-
t.Fatal(err)
353-
}
354-
if !bytes.Equal(ret, []byte("[\"PUSH1\",\"PUSH1\",\"STOP\"]")) {
355-
t.Errorf("Expected return value to be [\"PUSH1\",\"PUSH1\",\"STOP\"], got %s", string(ret))
356-
}
357-
}

eth/tracers/tracers.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func RegisterNativeTracer(name string, ctor ctorFn) {
5757
// instantiated and returned
5858
// 3. Otherwise, the code is interpreted as the js code of a js-tracer, and
5959
// is evaluated and returned.
60-
func New(code string, ctx *Context, cfg json.RawMessage) (Tracer, error) {
60+
func New(code string, txCtx vm.TxContext, ctx *Context, cfg json.RawMessage) (Tracer, error) {
6161
// Resolve native tracer
6262
if fn, ok := nativeTracers[code]; ok {
6363
return fn(cfg)
@@ -66,7 +66,7 @@ func New(code string, ctx *Context, cfg json.RawMessage) (Tracer, error) {
6666
if tracer, ok := jsTracers[code]; ok {
6767
code = tracer
6868
}
69-
return NewJsTracer(code, ctx)
69+
return NewJsTracer(code, txCtx, ctx)
7070
}
7171

7272
// camel converts a snake cased input string into a camel cased output.

eth/tracers/tracers_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func TestZeroValueToNotExitCall(t *testing.T) {
148148
}
149149
statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc)
150150
// Create the tracer, the EVM environment and run it
151-
tracer, err := New("callTracer", new(Context), nil)
151+
tracer, err := New("callTracer", txContext, new(Context), nil)
152152
if err != nil {
153153
t.Fatalf("failed to create call tracer: %v", err)
154154
}
@@ -233,7 +233,7 @@ func TestPrestateTracerCreate2(t *testing.T) {
233233
statedb := tests.MakePreState(db, alloc)
234234

235235
// Create the tracer, the EVM environment and run it
236-
tracer, err := New("prestateTracer", new(Context), nil)
236+
tracer, err := New("prestateTracer", txContext, new(Context), nil)
237237
if err != nil {
238238
t.Fatalf("failed to create call tracer: %v", err)
239239
}

0 commit comments

Comments
 (0)