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