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