aboutsummaryrefslogtreecommitdiffstats
path: root/internal/ethapi
diff options
context:
space:
mode:
Diffstat (limited to 'internal/ethapi')
-rw-r--r--internal/ethapi/api.go61
-rw-r--r--internal/ethapi/tracer.go105
2 files changed, 86 insertions, 80 deletions
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 59a29d722..025f42617 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -151,9 +151,9 @@ func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string]string {
// Define a formatter to flatten a transaction into a string
var format = func(tx *types.Transaction) string {
if to := tx.To(); to != nil {
- return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice())
+ return fmt.Sprintf("%s: %v wei + %v gas × %v wei", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice())
}
- return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice())
+ return fmt.Sprintf("contract creation: %v wei + %v gas × %v wei", tx.Value(), tx.Gas(), tx.GasPrice())
}
// Flatten the pending transactions
for account, txs := range pending {
@@ -710,45 +710,52 @@ type ExecutionResult struct {
// StructLogRes stores a structured log emitted by the EVM while replaying a
// transaction in debug mode
type StructLogRes struct {
- Pc uint64 `json:"pc"`
- Op string `json:"op"`
- Gas uint64 `json:"gas"`
- GasCost uint64 `json:"gasCost"`
- Depth int `json:"depth"`
- Error error `json:"error"`
- Stack []string `json:"stack"`
- Memory []string `json:"memory"`
- Storage map[string]string `json:"storage"`
+ Pc uint64 `json:"pc"`
+ Op string `json:"op"`
+ Gas uint64 `json:"gas"`
+ GasCost uint64 `json:"gasCost"`
+ Depth int `json:"depth"`
+ Error error `json:"error,omitempty"`
+ Stack *[]string `json:"stack,omitempty"`
+ Memory *[]string `json:"memory,omitempty"`
+ Storage *map[string]string `json:"storage,omitempty"`
}
// formatLogs formats EVM returned structured logs for json output
-func FormatLogs(structLogs []vm.StructLog) []StructLogRes {
- formattedStructLogs := make([]StructLogRes, len(structLogs))
- for index, trace := range structLogs {
- formattedStructLogs[index] = StructLogRes{
+func FormatLogs(logs []vm.StructLog) []StructLogRes {
+ formatted := make([]StructLogRes, len(logs))
+ for index, trace := range logs {
+ formatted[index] = StructLogRes{
Pc: trace.Pc,
Op: trace.Op.String(),
Gas: trace.Gas,
GasCost: trace.GasCost,
Depth: trace.Depth,
Error: trace.Err,
- Stack: make([]string, len(trace.Stack)),
- Storage: make(map[string]string),
}
-
- for i, stackValue := range trace.Stack {
- formattedStructLogs[index].Stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32))
+ if trace.Stack != nil {
+ stack := make([]string, len(trace.Stack))
+ for i, stackValue := range trace.Stack {
+ stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32))
+ }
+ formatted[index].Stack = &stack
}
-
- for i := 0; i+32 <= len(trace.Memory); i += 32 {
- formattedStructLogs[index].Memory = append(formattedStructLogs[index].Memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
+ if trace.Memory != nil {
+ memory := make([]string, 0, (len(trace.Memory)+31)/32)
+ for i := 0; i+32 <= len(trace.Memory); i += 32 {
+ memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
+ }
+ formatted[index].Memory = &memory
}
-
- for i, storageValue := range trace.Storage {
- formattedStructLogs[index].Storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
+ if trace.Storage != nil {
+ storage := make(map[string]string)
+ for i, storageValue := range trace.Storage {
+ storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
+ }
+ formatted[index].Storage = &storage
}
}
- return formattedStructLogs
+ return formatted
}
// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
diff --git a/internal/ethapi/tracer.go b/internal/ethapi/tracer.go
index 051626527..71cafc6e9 100644
--- a/internal/ethapi/tracer.go
+++ b/internal/ethapi/tracer.go
@@ -130,28 +130,28 @@ type dbWrapper struct {
}
// getBalance retrieves an account's balance
-func (dw *dbWrapper) getBalance(addr common.Address) *big.Int {
- return dw.db.GetBalance(addr)
+func (dw *dbWrapper) getBalance(addr []byte) *big.Int {
+ return dw.db.GetBalance(common.BytesToAddress(addr))
}
// getNonce retrieves an account's nonce
-func (dw *dbWrapper) getNonce(addr common.Address) uint64 {
- return dw.db.GetNonce(addr)
+func (dw *dbWrapper) getNonce(addr []byte) uint64 {
+ return dw.db.GetNonce(common.BytesToAddress(addr))
}
// getCode retrieves an account's code
-func (dw *dbWrapper) getCode(addr common.Address) []byte {
- return dw.db.GetCode(addr)
+func (dw *dbWrapper) getCode(addr []byte) []byte {
+ return dw.db.GetCode(common.BytesToAddress(addr))
}
// getState retrieves an account's state data for the given hash
-func (dw *dbWrapper) getState(addr common.Address, hash common.Hash) common.Hash {
- return dw.db.GetState(addr, hash)
+func (dw *dbWrapper) getState(addr []byte, hash common.Hash) common.Hash {
+ return dw.db.GetState(common.BytesToAddress(addr), hash)
}
// exists returns true iff the account exists
-func (dw *dbWrapper) exists(addr common.Address) bool {
- return dw.db.Exist(addr)
+func (dw *dbWrapper) exists(addr []byte) bool {
+ return dw.db.Exist(common.BytesToAddress(addr))
}
// toValue returns an otto.Value for the dbWrapper
@@ -200,19 +200,18 @@ func (c *contractWrapper) toValue(vm *otto.Otto) otto.Value {
// JavascriptTracer provides an implementation of Tracer that evaluates a
// Javascript function for each VM execution step.
type JavascriptTracer struct {
- vm *otto.Otto // Javascript VM instance
- traceobj *otto.Object // User-supplied object to call
- log map[string]interface{} // (Reusable) map for the `log` arg to `step`
- logvalue otto.Value // JS view of `log`
- memory *memoryWrapper // Wrapper around the VM memory
- memvalue otto.Value // JS view of `memory`
- stack *stackWrapper // Wrapper around the VM stack
- stackvalue otto.Value // JS view of `stack`
- db *dbWrapper // Wrapper around the VM environment
- dbvalue otto.Value // JS view of `db`
- contract *contractWrapper // Wrapper around the contract object
- contractvalue otto.Value // JS view of `contract`
- err error // Error, if one has occurred
+ vm *otto.Otto // Javascript VM instance
+ traceobj *otto.Object // User-supplied object to call
+ op *opCodeWrapper // Wrapper around the VM opcode
+ log map[string]interface{} // (Reusable) map for the `log` arg to `step`
+ logvalue otto.Value // JS view of `log`
+ memory *memoryWrapper // Wrapper around the VM memory
+ stack *stackWrapper // Wrapper around the VM stack
+ db *dbWrapper // Wrapper around the VM environment
+ dbvalue otto.Value // JS view of `db`
+ contract *contractWrapper // Wrapper around the contract object
+ err error // Error, if one has occurred
+ result interface{} // Final result to return to the user
}
// NewJavascriptTracer instantiates a new JavascriptTracer instance.
@@ -230,7 +229,6 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
if err != nil {
return nil, err
}
-
// Check the required functions exist
step, err := jstracer.Get("step")
if err != nil {
@@ -247,31 +245,34 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
if !result.IsFunction() {
return nil, fmt.Errorf("Trace object must expose a function result()")
}
-
// Create the persistent log object
- log := make(map[string]interface{})
+ var (
+ op = new(opCodeWrapper)
+ mem = new(memoryWrapper)
+ stack = new(stackWrapper)
+ db = new(dbWrapper)
+ contract = new(contractWrapper)
+ )
+ log := map[string]interface{}{
+ "op": op.toValue(vm),
+ "memory": mem.toValue(vm),
+ "stack": stack.toValue(vm),
+ "contract": contract.toValue(vm),
+ }
logvalue, _ := vm.ToValue(log)
- // Create persistent wrappers for memory and stack
- mem := &memoryWrapper{}
- stack := &stackWrapper{}
- db := &dbWrapper{}
- contract := &contractWrapper{}
-
return &JavascriptTracer{
- vm: vm,
- traceobj: jstracer,
- log: log,
- logvalue: logvalue,
- memory: mem,
- memvalue: mem.toValue(vm),
- stack: stack,
- stackvalue: stack.toValue(vm),
- db: db,
- dbvalue: db.toValue(vm),
- contract: contract,
- contractvalue: contract.toValue(vm),
- err: nil,
+ vm: vm,
+ traceobj: jstracer,
+ op: op,
+ log: log,
+ logvalue: logvalue,
+ memory: mem,
+ stack: stack,
+ db: db,
+ dbvalue: db.toValue(vm),
+ contract: contract,
+ err: nil,
}, nil
}
@@ -319,24 +320,22 @@ func wrapError(context string, err error) error {
// CaptureState implements the Tracer interface to trace a single step of VM execution
func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
if jst.err == nil {
+ jst.op.op = op
jst.memory.memory = memory
jst.stack.stack = stack
jst.db.db = env.StateDB
jst.contract.contract = contract
- ocw := &opCodeWrapper{op}
-
jst.log["pc"] = pc
- jst.log["op"] = ocw.toValue(jst.vm)
jst.log["gas"] = gas
- jst.log["gasPrice"] = cost
- jst.log["memory"] = jst.memvalue
- jst.log["stack"] = jst.stackvalue
- jst.log["contract"] = jst.contractvalue
+ jst.log["cost"] = cost
jst.log["depth"] = depth
jst.log["account"] = contract.Address()
- jst.log["err"] = err
+ delete(jst.log, "error")
+ if err != nil {
+ jst.log["error"] = err
+ }
_, err := jst.callSafely("step", jst.logvalue, jst.dbvalue)
if err != nil {
jst.err = wrapError("step", err)