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