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