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