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