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