call.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // Copyright 2021 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. package native
  17. import (
  18. "encoding/json"
  19. "errors"
  20. "math/big"
  21. "strconv"
  22. "strings"
  23. "sync/atomic"
  24. "time"
  25. "github.com/ethereum/go-ethereum/common"
  26. "github.com/ethereum/go-ethereum/core/vm"
  27. "github.com/ethereum/go-ethereum/eth/tracers"
  28. )
  29. func init() {
  30. register("callTracer", newCallTracer)
  31. }
  32. type callFrame struct {
  33. Type string `json:"type"`
  34. From string `json:"from"`
  35. To string `json:"to,omitempty"`
  36. Value string `json:"value,omitempty"`
  37. Gas string `json:"gas"`
  38. GasUsed string `json:"gasUsed"`
  39. Input string `json:"input"`
  40. Output string `json:"output,omitempty"`
  41. Error string `json:"error,omitempty"`
  42. Calls []callFrame `json:"calls,omitempty"`
  43. }
  44. type callTracer struct {
  45. env *vm.EVM
  46. callstack []callFrame
  47. interrupt uint32 // Atomic flag to signal execution interruption
  48. reason error // Textual reason for the interruption
  49. }
  50. // newCallTracer returns a native go tracer which tracks
  51. // call frames of a tx, and implements vm.EVMLogger.
  52. func newCallTracer() tracers.Tracer {
  53. // First callframe contains tx context info
  54. // and is populated on start and end.
  55. t := &callTracer{callstack: make([]callFrame, 1)}
  56. return t
  57. }
  58. // CaptureStart implements the EVMLogger interface to initialize the tracing operation.
  59. func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
  60. t.env = env
  61. t.callstack[0] = callFrame{
  62. Type: "CALL",
  63. From: addrToHex(from),
  64. To: addrToHex(to),
  65. Input: bytesToHex(input),
  66. Gas: uintToHex(gas),
  67. Value: bigToHex(value),
  68. }
  69. if create {
  70. t.callstack[0].Type = "CREATE"
  71. }
  72. }
  73. // CaptureEnd is called after the call finishes to finalize the tracing.
  74. func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
  75. t.callstack[0].GasUsed = uintToHex(gasUsed)
  76. if err != nil {
  77. t.callstack[0].Error = err.Error()
  78. if err.Error() == "execution reverted" && len(output) > 0 {
  79. t.callstack[0].Output = bytesToHex(output)
  80. }
  81. } else {
  82. t.callstack[0].Output = bytesToHex(output)
  83. }
  84. }
  85. // CaptureState implements the EVMLogger interface to trace a single step of VM execution.
  86. func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
  87. }
  88. // CaptureFault implements the EVMLogger interface to trace an execution fault.
  89. func (t *callTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
  90. }
  91. // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
  92. func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
  93. // Skip if tracing was interrupted
  94. if atomic.LoadUint32(&t.interrupt) > 0 {
  95. t.env.Cancel()
  96. return
  97. }
  98. call := callFrame{
  99. Type: typ.String(),
  100. From: addrToHex(from),
  101. To: addrToHex(to),
  102. Input: bytesToHex(input),
  103. Gas: uintToHex(gas),
  104. Value: bigToHex(value),
  105. }
  106. t.callstack = append(t.callstack, call)
  107. }
  108. // CaptureExit is called when EVM exits a scope, even if the scope didn't
  109. // execute any code.
  110. func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
  111. size := len(t.callstack)
  112. if size <= 1 {
  113. return
  114. }
  115. // pop call
  116. call := t.callstack[size-1]
  117. t.callstack = t.callstack[:size-1]
  118. size -= 1
  119. call.GasUsed = uintToHex(gasUsed)
  120. if err == nil {
  121. call.Output = bytesToHex(output)
  122. } else {
  123. call.Error = err.Error()
  124. if call.Type == "CREATE" || call.Type == "CREATE2" {
  125. call.To = ""
  126. }
  127. }
  128. t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
  129. }
  130. // GetResult returns the json-encoded nested list of call traces, and any
  131. // error arising from the encoding or forceful termination (via `Stop`).
  132. func (t *callTracer) GetResult() (json.RawMessage, error) {
  133. if len(t.callstack) != 1 {
  134. return nil, errors.New("incorrect number of top-level calls")
  135. }
  136. res, err := json.Marshal(t.callstack[0])
  137. if err != nil {
  138. return nil, err
  139. }
  140. return json.RawMessage(res), t.reason
  141. }
  142. // Stop terminates execution of the tracer at the first opportune moment.
  143. func (t *callTracer) Stop(err error) {
  144. t.reason = err
  145. atomic.StoreUint32(&t.interrupt, 1)
  146. }
  147. func bytesToHex(s []byte) string {
  148. return "0x" + common.Bytes2Hex(s)
  149. }
  150. func bigToHex(n *big.Int) string {
  151. if n == nil {
  152. return ""
  153. }
  154. return "0x" + n.Text(16)
  155. }
  156. func uintToHex(n uint64) string {
  157. return "0x" + strconv.FormatUint(n, 16)
  158. }
  159. func addrToHex(a common.Address) string {
  160. return strings.ToLower(a.Hex())
  161. }