eth/tracers: do the JSON serialization via .js to capture C faults
This commit is contained in:
		@@ -505,7 +505,7 @@ func (jst *Tracer) Stop(err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// call executes a method on a JS object, catching any errors, formatting and
 | 
					// call executes a method on a JS object, catching any errors, formatting and
 | 
				
			||||||
// returning them as error objects.
 | 
					// returning them as error objects.
 | 
				
			||||||
func (jst *Tracer) call(method string, args ...string) (json.RawMessage, error) {
 | 
					func (jst *Tracer) call(noret bool, method string, args ...string) (json.RawMessage, error) {
 | 
				
			||||||
	// Execute the JavaScript call and return any error
 | 
						// Execute the JavaScript call and return any error
 | 
				
			||||||
	jst.vm.PushString(method)
 | 
						jst.vm.PushString(method)
 | 
				
			||||||
	for _, arg := range args {
 | 
						for _, arg := range args {
 | 
				
			||||||
@@ -519,7 +519,21 @@ func (jst *Tracer) call(method string, args ...string) (json.RawMessage, error)
 | 
				
			|||||||
		return nil, errors.New(err)
 | 
							return nil, errors.New(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// No error occurred, extract return value and return
 | 
						// No error occurred, extract return value and return
 | 
				
			||||||
	return json.RawMessage(jst.vm.JsonEncode(-1)), nil
 | 
						if noret {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Push a JSON marshaller onto the stack. We can't marshal from the out-
 | 
				
			||||||
 | 
						// side because duktape can crash on large nestings and we can't catch
 | 
				
			||||||
 | 
						// C++ exceptions ourselves from Go. TODO(karalabe): Yuck, why wrap?!
 | 
				
			||||||
 | 
						jst.vm.PushString("(JSON.stringify)")
 | 
				
			||||||
 | 
						jst.vm.Eval()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						jst.vm.Swap(-1, -2)
 | 
				
			||||||
 | 
						if code = jst.vm.Pcall(1); code != 0 {
 | 
				
			||||||
 | 
							err := jst.vm.SafeToString(-1)
 | 
				
			||||||
 | 
							return nil, errors.New(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return json.RawMessage(jst.vm.SafeToString(-1)), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func wrapError(context string, err error) error {
 | 
					func wrapError(context string, err error) error {
 | 
				
			||||||
@@ -578,7 +592,7 @@ func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost
 | 
				
			|||||||
		*jst.errorValue = err.Error()
 | 
							*jst.errorValue = err.Error()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err := jst.call("step", "log", "db"); err != nil {
 | 
						if _, err := jst.call(true, "step", "log", "db"); err != nil {
 | 
				
			||||||
		jst.err = wrapError("step", err)
 | 
							jst.err = wrapError("step", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -592,7 +606,7 @@ func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost
 | 
				
			|||||||
	jst.errorValue = new(string)
 | 
						jst.errorValue = new(string)
 | 
				
			||||||
	*jst.errorValue = err.Error()
 | 
						*jst.errorValue = err.Error()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err := jst.call("fault", "log", "db"); err != nil {
 | 
						if _, err := jst.call(true, "fault", "log", "db"); err != nil {
 | 
				
			||||||
		jst.err = wrapError("fault", err)
 | 
							jst.err = wrapError("fault", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -640,7 +654,7 @@ func (jst *Tracer) GetResult() (json.RawMessage, error) {
 | 
				
			|||||||
	jst.vm.PutPropString(jst.stateObject, "ctx")
 | 
						jst.vm.PutPropString(jst.stateObject, "ctx")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Finalize the trace and return the results
 | 
						// Finalize the trace and return the results
 | 
				
			||||||
	result, err := jst.call("result", "ctx", "db")
 | 
						result, err := jst.call(false, "result", "ctx", "db")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		jst.err = wrapError("result", err)
 | 
							jst.err = wrapError("result", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,7 +78,7 @@ func runTrace(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestTracer(t *testing.T) {
 | 
					func TestTracer(t *testing.T) {
 | 
				
			||||||
	execTracer := func(code string) []byte {
 | 
						execTracer := func(code string) ([]byte, string) {
 | 
				
			||||||
		t.Helper()
 | 
							t.Helper()
 | 
				
			||||||
		ctx := &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
 | 
							ctx := &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
 | 
				
			||||||
		tracer, err := New(code, ctx.txCtx)
 | 
							tracer, err := New(code, ctx.txCtx)
 | 
				
			||||||
@@ -87,13 +87,14 @@ func TestTracer(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		ret, err := runTrace(tracer, ctx)
 | 
							ret, err := runTrace(tracer, ctx)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatal(err)
 | 
								return nil, err.Error() // Stringify to allow comparison without nil checks
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return ret
 | 
							return ret, ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for i, tt := range []struct {
 | 
						for i, tt := range []struct {
 | 
				
			||||||
		code string
 | 
							code string
 | 
				
			||||||
		want string
 | 
							want string
 | 
				
			||||||
 | 
							fail string
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{ // tests that we don't panic on bad arguments to memory access
 | 
							{ // tests that we don't panic on bad arguments to memory access
 | 
				
			||||||
			code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}",
 | 
								code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}",
 | 
				
			||||||
@@ -116,10 +117,13 @@ func TestTracer(t *testing.T) {
 | 
				
			|||||||
		}, { // tests intrinsic gas
 | 
							}, { // tests intrinsic gas
 | 
				
			||||||
			code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed+'.'+ctx.intrinsicGas; }}",
 | 
								code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed+'.'+ctx.intrinsicGas; }}",
 | 
				
			||||||
			want: `"100000.6.21000"`,
 | 
								want: `"100000.6.21000"`,
 | 
				
			||||||
 | 
							}, { // tests too deep object / serialization crash
 | 
				
			||||||
 | 
								code: "{step: function() {}, fault: function() {}, result: function() { var o={}; var x=o; for (var i=0; i<1000; i++){	o.foo={}; o=o.foo; } return x; }}",
 | 
				
			||||||
 | 
								fail: "RangeError: json encode recursion limit    in server-side tracer function 'result'",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	} {
 | 
						} {
 | 
				
			||||||
		if have := execTracer(tt.code); tt.want != string(have) {
 | 
							if have, err := execTracer(tt.code); tt.want != string(have) || tt.fail != err {
 | 
				
			||||||
			t.Errorf("testcase %d: expected return value to be %s got %s\n\tcode: %v", i, tt.want, string(have), tt.code)
 | 
								t.Errorf("testcase %d: expected return value to be '%s' got '%s', error to be '%s' got '%s'\n\tcode: %v", i, tt.want, string(have), tt.fail, err, tt.code)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user