aboutsummaryrefslogtreecommitdiffstats
path: root/eth/api.go
diff options
context:
space:
mode:
Diffstat (limited to 'eth/api.go')
-rw-r--r--eth/api.go139
1 files changed, 139 insertions, 0 deletions
diff --git a/eth/api.go b/eth/api.go
index 0f55c60a9..bc7d17534 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -1501,6 +1501,145 @@ func (api *PrivateDebugAPI) SetHead(number uint64) {
api.eth.BlockChain().SetHead(number)
}
+// 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 *big.Int `json:"gas"`
+ GasCost *big.Int `json:"gasCost"`
+ Error error `json:"error"`
+ Stack []string `json:"stack"`
+ Memory map[string]string `json:"memory"`
+ Storage map[string]string `json:"storage"`
+}
+
+// TransactionExecutionRes groups all structured logs emitted by the evm
+// while replaying a transaction in debug mode as well as the amount of
+// gas used and the return value
+type TransactionExecutionResult struct {
+ Gas *big.Int `json:"gas"`
+ ReturnValue string `json:"returnValue"`
+ StructLogs []structLogRes `json:"structLogs"`
+}
+
+func (s *PrivateDebugAPI) doReplayTransaction(txHash common.Hash) ([]vm.StructLog, []byte, *big.Int, error) {
+ // Retrieve the tx from the chain
+ tx, _, blockIndex, _ := core.GetTransaction(s.eth.ChainDb(), txHash)
+
+ if tx == nil {
+ return nil, nil, nil, fmt.Errorf("Transaction not found")
+ }
+
+ block := s.eth.BlockChain().GetBlockByNumber(blockIndex - 1)
+ if block == nil {
+ return nil, nil, nil, fmt.Errorf("Unable to retrieve prior block")
+ }
+
+ // Create the state database
+ stateDb, err := state.New(block.Root(), s.eth.ChainDb())
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ txFrom, err := tx.From()
+
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("Unable to create transaction sender")
+ }
+ from := stateDb.GetOrNewStateObject(txFrom)
+ msg := callmsg{
+ from: from,
+ to: tx.To(),
+ gas: tx.Gas(),
+ gasPrice: tx.GasPrice(),
+ value: tx.Value(),
+ data: tx.Data(),
+ }
+
+ vmenv := core.NewEnv(stateDb, s.eth.BlockChain(), msg, block.Header())
+ gp := new(core.GasPool).AddGas(block.GasLimit())
+ vm.GenerateStructLogs = true
+ defer func() { vm.GenerateStructLogs = false }()
+
+ ret, gas, err := core.ApplyMessage(vmenv, msg, gp)
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("Error executing transaction %v", err)
+ }
+
+ return vmenv.StructLogs(), ret, gas, nil
+}
+
+// Executes a transaction and returns the structured logs of the evm
+// gathered during the execution
+func (s *PrivateDebugAPI) ReplayTransaction(txHash common.Hash, stackDepth int, memorySize int, storageSize int) (*TransactionExecutionResult, error) {
+
+ structLogs, ret, gas, err := s.doReplayTransaction(txHash)
+
+ if err != nil {
+ return nil, err
+ }
+
+ res := TransactionExecutionResult{
+ Gas: gas,
+ ReturnValue: fmt.Sprintf("%x", ret),
+ StructLogs: make([]structLogRes, len(structLogs)),
+ }
+
+ for index, trace := range structLogs {
+
+ stackLength := len(trace.Stack)
+
+ // Return full stack by default
+ if stackDepth != -1 && stackDepth < stackLength {
+ stackLength = stackDepth
+ }
+
+ res.StructLogs[index] = structLogRes{
+ Pc: trace.Pc,
+ Op: trace.Op.String(),
+ Gas: trace.Gas,
+ GasCost: trace.GasCost,
+ Error: trace.Err,
+ Stack: make([]string, stackLength),
+ Memory: make(map[string]string),
+ Storage: make(map[string]string),
+ }
+
+ for i := 0; i < stackLength; i++ {
+ res.StructLogs[index].Stack[i] = fmt.Sprintf("%x", common.LeftPadBytes(trace.Stack[i].Bytes(), 32))
+ }
+
+ addr := 0
+ memorySizeLocal := memorySize
+
+ // Return full memory by default
+ if memorySize == -1 {
+ memorySizeLocal = len(trace.Memory)
+ }
+
+ for i := 0; i+16 <= len(trace.Memory) && addr < memorySizeLocal; i += 16 {
+ res.StructLogs[index].Memory[fmt.Sprintf("%04d", addr*16)] = fmt.Sprintf("%x", trace.Memory[i:i+16])
+ addr++
+ }
+
+ storageLength := len(trace.Stack)
+ if storageSize != -1 && storageSize < storageLength {
+ storageLength = storageSize
+ }
+
+ i := 0
+ for storageIndex, storageValue := range trace.Storage {
+ if i >= storageLength {
+ break
+ }
+ res.StructLogs[index].Storage[fmt.Sprintf("%x", storageIndex)] = fmt.Sprintf("%x", storageValue)
+ i++
+ }
+ }
+ return &res, nil
+}
+
// PublicNetAPI offers network related RPC methods
type PublicNetAPI struct {
net *p2p.Server