github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/eth/tracers/js/goja.go (about) 1 // Copyright 2022 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package js 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "math/big" 24 "slices" 25 26 "github.com/dop251/goja" 27 "github.com/ethereum/go-ethereum/core/tracing" 28 "github.com/ethereum/go-ethereum/core/types" 29 "github.com/ethereum/go-ethereum/eth/tracers" 30 "github.com/ethereum/go-ethereum/eth/tracers/internal" 31 "github.com/holiman/uint256" 32 33 "github.com/ethereum/go-ethereum/common" 34 "github.com/ethereum/go-ethereum/common/hexutil" 35 "github.com/ethereum/go-ethereum/core/vm" 36 "github.com/ethereum/go-ethereum/crypto" 37 jsassets "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers" 38 ) 39 40 var assetTracers = make(map[string]string) 41 42 // init retrieves the JavaScript transaction tracers included in go-ethereum. 43 func init() { 44 var err error 45 assetTracers, err = jsassets.Load() 46 if err != nil { 47 panic(err) 48 } 49 type ctorFn = func(*tracers.Context, json.RawMessage) (*tracers.Tracer, error) 50 lookup := func(code string) ctorFn { 51 return func(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) { 52 return newJsTracer(code, ctx, cfg) 53 } 54 } 55 for name, code := range assetTracers { 56 tracers.DefaultDirectory.Register(name, lookup(code), true) 57 } 58 tracers.DefaultDirectory.RegisterJSEval(newJsTracer) 59 } 60 61 // bigIntProgram is compiled once and the exported function mostly invoked to convert 62 // hex strings into big ints. 63 var bigIntProgram = goja.MustCompile("bigInt", bigIntegerJS, false) 64 65 type toBigFn = func(vm *goja.Runtime, val string) (goja.Value, error) 66 type toBufFn = func(vm *goja.Runtime, val []byte) (goja.Value, error) 67 type fromBufFn = func(vm *goja.Runtime, buf goja.Value, allowString bool) ([]byte, error) 68 69 func toBuf(vm *goja.Runtime, bufType goja.Value, val []byte) (goja.Value, error) { 70 // bufType is usually Uint8Array. This is equivalent to `new Uint8Array(val)` in JS. 71 return vm.New(bufType, vm.ToValue(vm.NewArrayBuffer(val))) 72 } 73 74 func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString bool) ([]byte, error) { 75 obj := buf.ToObject(vm) 76 switch obj.ClassName() { 77 case "String": 78 if !allowString { 79 break 80 } 81 return common.FromHex(obj.String()), nil 82 83 case "Array": 84 var b []byte 85 if err := vm.ExportTo(buf, &b); err != nil { 86 return nil, err 87 } 88 return b, nil 89 90 case "Object": 91 if !obj.Get("constructor").SameAs(bufType) { 92 break 93 } 94 b := obj.Export().([]byte) 95 return b, nil 96 } 97 return nil, errors.New("invalid buffer type") 98 } 99 100 // jsTracer is an implementation of the Tracer interface which evaluates 101 // JS functions on the relevant EVM hooks. It uses Goja as its JS engine. 102 type jsTracer struct { 103 vm *goja.Runtime 104 env *tracing.VMContext 105 toBig toBigFn // Converts a hex string into a JS bigint 106 toBuf toBufFn // Converts a []byte into a JS buffer 107 fromBuf fromBufFn // Converts an array, hex string or Uint8Array to a []byte 108 ctx map[string]goja.Value // KV-bag passed to JS in `result` 109 activePrecompiles []common.Address // List of active precompiles at current block 110 traceStep bool // True if tracer object exposes a `step()` method 111 traceFrame bool // True if tracer object exposes the `enter()` and `exit()` methods 112 err error // Any error that should stop tracing 113 obj *goja.Object // Trace object 114 115 // Methods exposed by tracer 116 result goja.Callable 117 fault goja.Callable 118 step goja.Callable 119 enter goja.Callable 120 exit goja.Callable 121 122 // Underlying structs being passed into JS 123 log *steplog 124 frame *callframe 125 frameResult *callframeResult 126 127 // Goja-wrapping of types prepared for JS consumption 128 logValue goja.Value 129 dbValue goja.Value 130 frameValue goja.Value 131 frameResultValue goja.Value 132 } 133 134 // newJsTracer instantiates a new JS tracer instance. code is a 135 // Javascript snippet which evaluates to an expression returning 136 // an object with certain methods: 137 // 138 // The methods `result` and `fault` are required to be present. 139 // The methods `step`, `enter`, and `exit` are optional, but note that 140 // `enter` and `exit` always go together. 141 func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) { 142 vm := goja.New() 143 // By default field names are exported to JS as is, i.e. capitalized. 144 vm.SetFieldNameMapper(goja.UncapFieldNameMapper()) 145 t := &jsTracer{ 146 vm: vm, 147 ctx: make(map[string]goja.Value), 148 } 149 150 t.setTypeConverters() 151 t.setBuiltinFunctions() 152 153 if ctx == nil { 154 ctx = new(tracers.Context) 155 } 156 if ctx.BlockHash != (common.Hash{}) { 157 blockHash, err := t.toBuf(vm, ctx.BlockHash.Bytes()) 158 if err != nil { 159 return nil, err 160 } 161 t.ctx["blockHash"] = blockHash 162 if ctx.TxHash != (common.Hash{}) { 163 t.ctx["txIndex"] = vm.ToValue(ctx.TxIndex) 164 txHash, err := t.toBuf(vm, ctx.TxHash.Bytes()) 165 if err != nil { 166 return nil, err 167 } 168 t.ctx["txHash"] = txHash 169 } 170 } 171 172 ret, err := vm.RunString("(" + code + ")") 173 if err != nil { 174 return nil, err 175 } 176 // Check tracer's interface for required and optional methods. 177 obj := ret.ToObject(vm) 178 result, ok := goja.AssertFunction(obj.Get("result")) 179 if !ok { 180 return nil, errors.New("trace object must expose a function result()") 181 } 182 fault, ok := goja.AssertFunction(obj.Get("fault")) 183 if !ok { 184 return nil, errors.New("trace object must expose a function fault()") 185 } 186 step, ok := goja.AssertFunction(obj.Get("step")) 187 t.traceStep = ok 188 enter, hasEnter := goja.AssertFunction(obj.Get("enter")) 189 exit, hasExit := goja.AssertFunction(obj.Get("exit")) 190 if hasEnter != hasExit { 191 return nil, errors.New("trace object must expose either both or none of enter() and exit()") 192 } 193 t.traceFrame = hasEnter 194 t.obj = obj 195 t.step = step 196 t.enter = enter 197 t.exit = exit 198 t.result = result 199 t.fault = fault 200 201 // Pass in config 202 if setup, ok := goja.AssertFunction(obj.Get("setup")); ok { 203 cfgStr := "{}" 204 if cfg != nil { 205 cfgStr = string(cfg) 206 } 207 if _, err := setup(obj, vm.ToValue(cfgStr)); err != nil { 208 return nil, err 209 } 210 } 211 // Setup objects carrying data to JS. These are created once and re-used. 212 t.log = &steplog{ 213 vm: vm, 214 op: &opObj{vm: vm}, 215 memory: &memoryObj{vm: vm, toBig: t.toBig, toBuf: t.toBuf}, 216 stack: &stackObj{vm: vm, toBig: t.toBig}, 217 contract: &contractObj{vm: vm, toBig: t.toBig, toBuf: t.toBuf}, 218 } 219 t.frame = &callframe{vm: vm, toBig: t.toBig, toBuf: t.toBuf} 220 t.frameResult = &callframeResult{vm: vm, toBuf: t.toBuf} 221 t.frameValue = t.frame.setupObject() 222 t.frameResultValue = t.frameResult.setupObject() 223 t.logValue = t.log.setupObject() 224 225 return &tracers.Tracer{ 226 Hooks: &tracing.Hooks{ 227 OnTxStart: t.OnTxStart, 228 OnTxEnd: t.OnTxEnd, 229 OnEnter: t.OnEnter, 230 OnExit: t.OnExit, 231 OnOpcode: t.OnOpcode, 232 OnFault: t.OnFault, 233 }, 234 GetResult: t.GetResult, 235 Stop: t.Stop, 236 }, nil 237 } 238 239 // OnTxStart implements the Tracer interface and is invoked at the beginning of 240 // transaction processing. 241 func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { 242 t.env = env 243 // Need statedb access for db object 244 db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf} 245 t.dbValue = db.setupObject() 246 // Update list of precompiles based on current block 247 rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) 248 t.activePrecompiles = vm.ActivePrecompiles(rules) 249 t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64()) 250 t.ctx["gas"] = t.vm.ToValue(tx.Gas()) 251 gasPriceBig, err := t.toBig(t.vm, env.GasPrice.String()) 252 if err != nil { 253 t.err = err 254 return 255 } 256 t.ctx["gasPrice"] = gasPriceBig 257 } 258 259 // OnTxEnd implements the Tracer interface and is invoked at the end of 260 // transaction processing. 261 func (t *jsTracer) OnTxEnd(receipt *types.Receipt, err error) { 262 if t.err != nil { 263 return 264 } 265 if err != nil { 266 // Don't override vm error 267 if _, ok := t.ctx["error"]; !ok { 268 t.ctx["error"] = t.vm.ToValue(err.Error()) 269 } 270 return 271 } 272 t.ctx["gasUsed"] = t.vm.ToValue(receipt.GasUsed) 273 } 274 275 // onStart implements the Tracer interface to initialize the tracing operation. 276 func (t *jsTracer) onStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 277 if t.err != nil { 278 return 279 } 280 if create { 281 t.ctx["type"] = t.vm.ToValue("CREATE") 282 } else { 283 t.ctx["type"] = t.vm.ToValue("CALL") 284 } 285 fromVal, err := t.toBuf(t.vm, from.Bytes()) 286 if err != nil { 287 t.err = err 288 return 289 } 290 t.ctx["from"] = fromVal 291 toVal, err := t.toBuf(t.vm, to.Bytes()) 292 if err != nil { 293 t.err = err 294 return 295 } 296 t.ctx["to"] = toVal 297 inputVal, err := t.toBuf(t.vm, input) 298 if err != nil { 299 t.err = err 300 return 301 } 302 t.ctx["input"] = inputVal 303 valueBig, err := t.toBig(t.vm, value.String()) 304 if err != nil { 305 t.err = err 306 return 307 } 308 t.ctx["value"] = valueBig 309 } 310 311 // OnOpcode implements the Tracer interface to trace a single step of VM execution. 312 func (t *jsTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { 313 if !t.traceStep { 314 return 315 } 316 if t.err != nil { 317 return 318 } 319 320 log := t.log 321 log.op.op = vm.OpCode(op) 322 log.memory.memory = scope.MemoryData() 323 log.stack.stack = scope.StackData() 324 log.contract.scope = scope 325 log.pc = pc 326 log.gas = gas 327 log.cost = cost 328 log.refund = t.env.StateDB.GetRefund() 329 log.depth = depth 330 log.err = err 331 if _, err := t.step(t.obj, t.logValue, t.dbValue); err != nil { 332 t.onError("step", err) 333 } 334 } 335 336 // OnFault implements the Tracer interface to trace an execution fault 337 func (t *jsTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) { 338 if t.err != nil { 339 return 340 } 341 // Other log fields have been already set as part of the last OnOpcode. 342 t.log.err = err 343 if _, err := t.fault(t.obj, t.logValue, t.dbValue); err != nil { 344 t.onError("fault", err) 345 } 346 } 347 348 // onEnd is called after the call finishes to finalize the tracing. 349 func (t *jsTracer) onEnd(output []byte, gasUsed uint64, err error, reverted bool) { 350 if t.err != nil { 351 return 352 } 353 if err != nil { 354 t.ctx["error"] = t.vm.ToValue(err.Error()) 355 } 356 outputVal, err := t.toBuf(t.vm, output) 357 if err != nil { 358 t.err = err 359 return 360 } 361 t.ctx["output"] = outputVal 362 } 363 364 // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). 365 func (t *jsTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 366 if t.err != nil { 367 return 368 } 369 if depth == 0 { 370 t.onStart(from, to, vm.OpCode(typ) == vm.CREATE, input, gas, value) 371 return 372 } 373 if !t.traceFrame { 374 return 375 } 376 377 t.frame.typ = vm.OpCode(typ).String() 378 t.frame.from = from 379 t.frame.to = to 380 t.frame.input = common.CopyBytes(input) 381 t.frame.gas = uint(gas) 382 t.frame.value = nil 383 if value != nil { 384 t.frame.value = new(big.Int).SetBytes(value.Bytes()) 385 } 386 387 if _, err := t.enter(t.obj, t.frameValue); err != nil { 388 t.onError("enter", err) 389 } 390 } 391 392 // OnExit is called when EVM exits a scope, even if the scope didn't 393 // execute any code. 394 func (t *jsTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { 395 if t.err != nil { 396 return 397 } 398 if depth == 0 { 399 t.onEnd(output, gasUsed, err, reverted) 400 return 401 } 402 if !t.traceFrame { 403 return 404 } 405 406 t.frameResult.gasUsed = uint(gasUsed) 407 t.frameResult.output = common.CopyBytes(output) 408 t.frameResult.err = err 409 410 if _, err := t.exit(t.obj, t.frameResultValue); err != nil { 411 t.onError("exit", err) 412 } 413 } 414 415 // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error 416 func (t *jsTracer) GetResult() (json.RawMessage, error) { 417 if t.err != nil { 418 return nil, t.err 419 } 420 ctx := t.vm.ToValue(t.ctx) 421 res, err := t.result(t.obj, ctx, t.dbValue) 422 if err != nil { 423 return nil, wrapError("result", err) 424 } 425 encoded, err := json.Marshal(res) 426 if err != nil { 427 return nil, err 428 } 429 return encoded, t.err 430 } 431 432 // Stop terminates execution of the tracer at the first opportune moment. 433 func (t *jsTracer) Stop(err error) { 434 t.vm.Interrupt(err) 435 } 436 437 // onError is called anytime the running JS code is interrupted 438 // and returns an error. It in turn pings the EVM to cancel its 439 // execution. 440 func (t *jsTracer) onError(context string, err error) { 441 t.err = wrapError(context, err) 442 } 443 444 func wrapError(context string, err error) error { 445 return fmt.Errorf("%v in server-side tracer function '%v'", err, context) 446 } 447 448 // setBuiltinFunctions injects Go functions which are available to tracers into the environment. 449 // It depends on type converters having been set up. 450 func (t *jsTracer) setBuiltinFunctions() { 451 vm := t.vm 452 // TODO: load console from goja-nodejs 453 vm.Set("toHex", func(v goja.Value) string { 454 b, err := t.fromBuf(vm, v, false) 455 if err != nil { 456 vm.Interrupt(err) 457 return "" 458 } 459 return hexutil.Encode(b) 460 }) 461 vm.Set("toWord", func(v goja.Value) goja.Value { 462 // TODO: add test with []byte len < 32 or > 32 463 b, err := t.fromBuf(vm, v, true) 464 if err != nil { 465 vm.Interrupt(err) 466 return nil 467 } 468 b = common.BytesToHash(b).Bytes() 469 res, err := t.toBuf(vm, b) 470 if err != nil { 471 vm.Interrupt(err) 472 return nil 473 } 474 return res 475 }) 476 vm.Set("toAddress", func(v goja.Value) goja.Value { 477 a, err := t.fromBuf(vm, v, true) 478 if err != nil { 479 vm.Interrupt(err) 480 return nil 481 } 482 a = common.BytesToAddress(a).Bytes() 483 res, err := t.toBuf(vm, a) 484 if err != nil { 485 vm.Interrupt(err) 486 return nil 487 } 488 return res 489 }) 490 vm.Set("toContract", func(from goja.Value, nonce uint) goja.Value { 491 a, err := t.fromBuf(vm, from, true) 492 if err != nil { 493 vm.Interrupt(err) 494 return nil 495 } 496 addr := common.BytesToAddress(a) 497 b := crypto.CreateAddress(addr, uint64(nonce)).Bytes() 498 res, err := t.toBuf(vm, b) 499 if err != nil { 500 vm.Interrupt(err) 501 return nil 502 } 503 return res 504 }) 505 vm.Set("toContract2", func(from goja.Value, salt string, initcode goja.Value) goja.Value { 506 a, err := t.fromBuf(vm, from, true) 507 if err != nil { 508 vm.Interrupt(err) 509 return nil 510 } 511 addr := common.BytesToAddress(a) 512 code, err := t.fromBuf(vm, initcode, true) 513 if err != nil { 514 vm.Interrupt(err) 515 return nil 516 } 517 code = common.CopyBytes(code) 518 codeHash := crypto.Keccak256(code) 519 b := crypto.CreateAddress2(addr, common.HexToHash(salt), codeHash).Bytes() 520 res, err := t.toBuf(vm, b) 521 if err != nil { 522 vm.Interrupt(err) 523 return nil 524 } 525 return res 526 }) 527 vm.Set("isPrecompiled", func(v goja.Value) bool { 528 a, err := t.fromBuf(vm, v, true) 529 if err != nil { 530 vm.Interrupt(err) 531 return false 532 } 533 return slices.Contains(t.activePrecompiles, common.BytesToAddress(a)) 534 }) 535 vm.Set("slice", func(slice goja.Value, start, end int64) goja.Value { 536 b, err := t.fromBuf(vm, slice, false) 537 if err != nil { 538 vm.Interrupt(err) 539 return nil 540 } 541 if start < 0 || start > end || end > int64(len(b)) { 542 vm.Interrupt(fmt.Sprintf("Tracer accessed out of bound memory: available %d, offset %d, size %d", len(b), start, end-start)) 543 return nil 544 } 545 res, err := t.toBuf(vm, b[start:end]) 546 if err != nil { 547 vm.Interrupt(err) 548 return nil 549 } 550 return res 551 }) 552 } 553 554 // setTypeConverters sets up utilities for converting Go types into those 555 // suitable for JS consumption. 556 func (t *jsTracer) setTypeConverters() error { 557 // Inject bigint logic. 558 // TODO: To be replaced after goja adds support for native JS bigint. 559 toBigCode, err := t.vm.RunProgram(bigIntProgram) 560 if err != nil { 561 return err 562 } 563 // Used to create JS bigint objects from go. 564 toBigFn, ok := goja.AssertFunction(toBigCode) 565 if !ok { 566 return errors.New("failed to bind bigInt func") 567 } 568 toBigWrapper := func(vm *goja.Runtime, val string) (goja.Value, error) { 569 return toBigFn(goja.Undefined(), vm.ToValue(val)) 570 } 571 t.toBig = toBigWrapper 572 // NOTE: We need this workaround to create JS buffers because 573 // goja doesn't at the moment expose constructors for typed arrays. 574 // 575 // Cache uint8ArrayType once to be used every time for less overhead. 576 uint8ArrayType := t.vm.Get("Uint8Array") 577 toBufWrapper := func(vm *goja.Runtime, val []byte) (goja.Value, error) { 578 return toBuf(vm, uint8ArrayType, val) 579 } 580 t.toBuf = toBufWrapper 581 fromBufWrapper := func(vm *goja.Runtime, buf goja.Value, allowString bool) ([]byte, error) { 582 return fromBuf(vm, uint8ArrayType, buf, allowString) 583 } 584 t.fromBuf = fromBufWrapper 585 return nil 586 } 587 588 type opObj struct { 589 vm *goja.Runtime 590 op vm.OpCode 591 } 592 593 func (o *opObj) ToNumber() int { 594 return int(o.op) 595 } 596 597 func (o *opObj) ToString() string { 598 return o.op.String() 599 } 600 601 func (o *opObj) IsPush() bool { 602 return o.op.IsPush() 603 } 604 605 func (o *opObj) setupObject() *goja.Object { 606 obj := o.vm.NewObject() 607 obj.Set("toNumber", o.vm.ToValue(o.ToNumber)) 608 obj.Set("toString", o.vm.ToValue(o.ToString)) 609 obj.Set("isPush", o.vm.ToValue(o.IsPush)) 610 return obj 611 } 612 613 type memoryObj struct { 614 memory []byte 615 vm *goja.Runtime 616 toBig toBigFn 617 toBuf toBufFn 618 } 619 620 func (mo *memoryObj) Slice(begin, end int64) goja.Value { 621 b, err := mo.slice(begin, end) 622 if err != nil { 623 mo.vm.Interrupt(err) 624 return nil 625 } 626 res, err := mo.toBuf(mo.vm, b) 627 if err != nil { 628 mo.vm.Interrupt(err) 629 return nil 630 } 631 return res 632 } 633 634 // slice returns the requested range of memory as a byte slice. 635 func (mo *memoryObj) slice(begin, end int64) ([]byte, error) { 636 if end == begin { 637 return []byte{}, nil 638 } 639 if end < begin || begin < 0 { 640 return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end) 641 } 642 slice, err := internal.GetMemoryCopyPadded(mo.memory, begin, end-begin) 643 if err != nil { 644 return nil, err 645 } 646 return slice, nil 647 } 648 649 func (mo *memoryObj) GetUint(addr int64) goja.Value { 650 value, err := mo.getUint(addr) 651 if err != nil { 652 mo.vm.Interrupt(err) 653 return nil 654 } 655 res, err := mo.toBig(mo.vm, value.String()) 656 if err != nil { 657 mo.vm.Interrupt(err) 658 return nil 659 } 660 return res 661 } 662 663 // getUint returns the 32 bytes at the specified address interpreted as a uint. 664 func (mo *memoryObj) getUint(addr int64) (*big.Int, error) { 665 if len(mo.memory) < int(addr)+32 || addr < 0 { 666 return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", len(mo.memory), addr, 32) 667 } 668 return new(big.Int).SetBytes(internal.MemoryPtr(mo.memory, addr, 32)), nil 669 } 670 671 func (mo *memoryObj) Length() int { 672 return len(mo.memory) 673 } 674 675 func (mo *memoryObj) setupObject() *goja.Object { 676 o := mo.vm.NewObject() 677 o.Set("slice", mo.vm.ToValue(mo.Slice)) 678 o.Set("getUint", mo.vm.ToValue(mo.GetUint)) 679 o.Set("length", mo.vm.ToValue(mo.Length)) 680 return o 681 } 682 683 type stackObj struct { 684 stack []uint256.Int 685 vm *goja.Runtime 686 toBig toBigFn 687 } 688 689 func (s *stackObj) Peek(idx int) goja.Value { 690 value, err := s.peek(idx) 691 if err != nil { 692 s.vm.Interrupt(err) 693 return nil 694 } 695 res, err := s.toBig(s.vm, value.String()) 696 if err != nil { 697 s.vm.Interrupt(err) 698 return nil 699 } 700 return res 701 } 702 703 // peek returns the nth-from-the-top element of the stack. 704 func (s *stackObj) peek(idx int) (*big.Int, error) { 705 if len(s.stack) <= idx || idx < 0 { 706 return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack), idx) 707 } 708 return internal.StackBack(s.stack, idx).ToBig(), nil 709 } 710 711 func (s *stackObj) Length() int { 712 return len(s.stack) 713 } 714 715 func (s *stackObj) setupObject() *goja.Object { 716 o := s.vm.NewObject() 717 o.Set("peek", s.vm.ToValue(s.Peek)) 718 o.Set("length", s.vm.ToValue(s.Length)) 719 return o 720 } 721 722 type dbObj struct { 723 db tracing.StateDB 724 vm *goja.Runtime 725 toBig toBigFn 726 toBuf toBufFn 727 fromBuf fromBufFn 728 } 729 730 func (do *dbObj) GetBalance(addrSlice goja.Value) goja.Value { 731 a, err := do.fromBuf(do.vm, addrSlice, false) 732 if err != nil { 733 do.vm.Interrupt(err) 734 return nil 735 } 736 addr := common.BytesToAddress(a) 737 value := do.db.GetBalance(addr) 738 res, err := do.toBig(do.vm, value.String()) 739 if err != nil { 740 do.vm.Interrupt(err) 741 return nil 742 } 743 return res 744 } 745 746 func (do *dbObj) GetNonce(addrSlice goja.Value) uint64 { 747 a, err := do.fromBuf(do.vm, addrSlice, false) 748 if err != nil { 749 do.vm.Interrupt(err) 750 return 0 751 } 752 addr := common.BytesToAddress(a) 753 return do.db.GetNonce(addr) 754 } 755 756 func (do *dbObj) GetCode(addrSlice goja.Value) goja.Value { 757 a, err := do.fromBuf(do.vm, addrSlice, false) 758 if err != nil { 759 do.vm.Interrupt(err) 760 return nil 761 } 762 addr := common.BytesToAddress(a) 763 code := do.db.GetCode(addr) 764 res, err := do.toBuf(do.vm, code) 765 if err != nil { 766 do.vm.Interrupt(err) 767 return nil 768 } 769 return res 770 } 771 772 func (do *dbObj) GetState(addrSlice goja.Value, hashSlice goja.Value) goja.Value { 773 a, err := do.fromBuf(do.vm, addrSlice, false) 774 if err != nil { 775 do.vm.Interrupt(err) 776 return nil 777 } 778 addr := common.BytesToAddress(a) 779 h, err := do.fromBuf(do.vm, hashSlice, false) 780 if err != nil { 781 do.vm.Interrupt(err) 782 return nil 783 } 784 hash := common.BytesToHash(h) 785 state := do.db.GetState(addr, hash).Bytes() 786 res, err := do.toBuf(do.vm, state) 787 if err != nil { 788 do.vm.Interrupt(err) 789 return nil 790 } 791 return res 792 } 793 794 func (do *dbObj) Exists(addrSlice goja.Value) bool { 795 a, err := do.fromBuf(do.vm, addrSlice, false) 796 if err != nil { 797 do.vm.Interrupt(err) 798 return false 799 } 800 addr := common.BytesToAddress(a) 801 return do.db.Exist(addr) 802 } 803 804 func (do *dbObj) setupObject() *goja.Object { 805 o := do.vm.NewObject() 806 o.Set("getBalance", do.vm.ToValue(do.GetBalance)) 807 o.Set("getNonce", do.vm.ToValue(do.GetNonce)) 808 o.Set("getCode", do.vm.ToValue(do.GetCode)) 809 o.Set("getState", do.vm.ToValue(do.GetState)) 810 o.Set("exists", do.vm.ToValue(do.Exists)) 811 return o 812 } 813 814 type contractObj struct { 815 scope tracing.OpContext 816 vm *goja.Runtime 817 toBig toBigFn 818 toBuf toBufFn 819 } 820 821 func (co *contractObj) GetCaller() goja.Value { 822 caller := co.scope.Caller().Bytes() 823 res, err := co.toBuf(co.vm, caller) 824 if err != nil { 825 co.vm.Interrupt(err) 826 return nil 827 } 828 return res 829 } 830 831 func (co *contractObj) GetAddress() goja.Value { 832 addr := co.scope.Address().Bytes() 833 res, err := co.toBuf(co.vm, addr) 834 if err != nil { 835 co.vm.Interrupt(err) 836 return nil 837 } 838 return res 839 } 840 841 func (co *contractObj) GetValue() goja.Value { 842 value := co.scope.CallValue() 843 res, err := co.toBig(co.vm, value.String()) 844 if err != nil { 845 co.vm.Interrupt(err) 846 return nil 847 } 848 return res 849 } 850 851 func (co *contractObj) GetInput() goja.Value { 852 input := common.CopyBytes(co.scope.CallInput()) 853 res, err := co.toBuf(co.vm, input) 854 if err != nil { 855 co.vm.Interrupt(err) 856 return nil 857 } 858 return res 859 } 860 861 func (co *contractObj) setupObject() *goja.Object { 862 o := co.vm.NewObject() 863 o.Set("getCaller", co.vm.ToValue(co.GetCaller)) 864 o.Set("getAddress", co.vm.ToValue(co.GetAddress)) 865 o.Set("getValue", co.vm.ToValue(co.GetValue)) 866 o.Set("getInput", co.vm.ToValue(co.GetInput)) 867 return o 868 } 869 870 type callframe struct { 871 vm *goja.Runtime 872 toBig toBigFn 873 toBuf toBufFn 874 875 typ string 876 from common.Address 877 to common.Address 878 input []byte 879 gas uint 880 value *big.Int 881 } 882 883 func (f *callframe) GetType() string { 884 return f.typ 885 } 886 887 func (f *callframe) GetFrom() goja.Value { 888 from := f.from.Bytes() 889 res, err := f.toBuf(f.vm, from) 890 if err != nil { 891 f.vm.Interrupt(err) 892 return nil 893 } 894 return res 895 } 896 897 func (f *callframe) GetTo() goja.Value { 898 to := f.to.Bytes() 899 res, err := f.toBuf(f.vm, to) 900 if err != nil { 901 f.vm.Interrupt(err) 902 return nil 903 } 904 return res 905 } 906 907 func (f *callframe) GetInput() goja.Value { 908 input := f.input 909 res, err := f.toBuf(f.vm, input) 910 if err != nil { 911 f.vm.Interrupt(err) 912 return nil 913 } 914 return res 915 } 916 917 func (f *callframe) GetGas() uint { 918 return f.gas 919 } 920 921 func (f *callframe) GetValue() goja.Value { 922 if f.value == nil { 923 return goja.Undefined() 924 } 925 res, err := f.toBig(f.vm, f.value.String()) 926 if err != nil { 927 f.vm.Interrupt(err) 928 return nil 929 } 930 return res 931 } 932 933 func (f *callframe) setupObject() *goja.Object { 934 o := f.vm.NewObject() 935 o.Set("getType", f.vm.ToValue(f.GetType)) 936 o.Set("getFrom", f.vm.ToValue(f.GetFrom)) 937 o.Set("getTo", f.vm.ToValue(f.GetTo)) 938 o.Set("getInput", f.vm.ToValue(f.GetInput)) 939 o.Set("getGas", f.vm.ToValue(f.GetGas)) 940 o.Set("getValue", f.vm.ToValue(f.GetValue)) 941 return o 942 } 943 944 type callframeResult struct { 945 vm *goja.Runtime 946 toBuf toBufFn 947 948 gasUsed uint 949 output []byte 950 err error 951 } 952 953 func (r *callframeResult) GetGasUsed() uint { 954 return r.gasUsed 955 } 956 957 func (r *callframeResult) GetOutput() goja.Value { 958 res, err := r.toBuf(r.vm, r.output) 959 if err != nil { 960 r.vm.Interrupt(err) 961 return nil 962 } 963 return res 964 } 965 966 func (r *callframeResult) GetError() goja.Value { 967 if r.err != nil { 968 return r.vm.ToValue(r.err.Error()) 969 } 970 return goja.Undefined() 971 } 972 973 func (r *callframeResult) setupObject() *goja.Object { 974 o := r.vm.NewObject() 975 o.Set("getGasUsed", r.vm.ToValue(r.GetGasUsed)) 976 o.Set("getOutput", r.vm.ToValue(r.GetOutput)) 977 o.Set("getError", r.vm.ToValue(r.GetError)) 978 return o 979 } 980 981 type steplog struct { 982 vm *goja.Runtime 983 984 op *opObj 985 memory *memoryObj 986 stack *stackObj 987 contract *contractObj 988 989 pc uint64 990 gas uint64 991 cost uint64 992 depth int 993 refund uint64 994 err error 995 } 996 997 func (l *steplog) GetPC() uint64 { return l.pc } 998 func (l *steplog) GetGas() uint64 { return l.gas } 999 func (l *steplog) GetCost() uint64 { return l.cost } 1000 func (l *steplog) GetDepth() int { return l.depth } 1001 func (l *steplog) GetRefund() uint64 { return l.refund } 1002 1003 func (l *steplog) GetError() goja.Value { 1004 if l.err != nil { 1005 return l.vm.ToValue(l.err.Error()) 1006 } 1007 return goja.Undefined() 1008 } 1009 1010 func (l *steplog) setupObject() *goja.Object { 1011 o := l.vm.NewObject() 1012 // Setup basic fields. 1013 o.Set("getPC", l.vm.ToValue(l.GetPC)) 1014 o.Set("getGas", l.vm.ToValue(l.GetGas)) 1015 o.Set("getCost", l.vm.ToValue(l.GetCost)) 1016 o.Set("getDepth", l.vm.ToValue(l.GetDepth)) 1017 o.Set("getRefund", l.vm.ToValue(l.GetRefund)) 1018 o.Set("getError", l.vm.ToValue(l.GetError)) 1019 // Setup nested objects. 1020 o.Set("op", l.op.setupObject()) 1021 o.Set("stack", l.stack.setupObject()) 1022 o.Set("memory", l.memory.setupObject()) 1023 o.Set("contract", l.contract.setupObject()) 1024 return o 1025 }