aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2016-02-03 16:34:52 +0800
committerPéter Szilágyi <peterke@gmail.com>2016-02-03 16:34:52 +0800
commita8fd0de0d344ce3828901fa47204a68971184684 (patch)
tree9e684d86355e0418f3b5c055bbf3c2e22437a2c4
parent2e2f093ec2315da670f3300954975d9136fe76af (diff)
parent15780ead07e650e829a5756857fdbb4e2871356a (diff)
downloaddexon-a8fd0de0d344ce3828901fa47204a68971184684.tar.gz
dexon-a8fd0de0d344ce3828901fa47204a68971184684.tar.zst
dexon-a8fd0de0d344ce3828901fa47204a68971184684.zip
Merge pull request #2156 from ppratscher/add_replay_tx
core/vm, rpc/api: added debug_replayTransaction RPC call
-rw-r--r--core/vm/common.go2
-rw-r--r--core/vm/vm.go2
-rw-r--r--eth/api.go139
-rw-r--r--rpc/javascript.go5
4 files changed, 147 insertions, 1 deletions
diff --git a/core/vm/common.go b/core/vm/common.go
index 2d1aa9332..395ed0471 100644
--- a/core/vm/common.go
+++ b/core/vm/common.go
@@ -28,6 +28,8 @@ import (
// Global Debug flag indicating Debug VM (full logging)
var Debug bool
+var GenerateStructLogs bool = false
+
// Type is the VM type accepted by **NewVm**
type Type byte
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 8e07aaa89..0c6bbcd42 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -367,7 +367,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, input []byte, contract *Co
// log emits a log event to the environment for each opcode encountered. This is not to be confused with the
// LOG* opcode.
func (self *Vm) log(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, err error) {
- if Debug {
+ if Debug || GenerateStructLogs {
mem := make([]byte, len(memory.Data()))
copy(mem, memory.Data())
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
diff --git a/rpc/javascript.go b/rpc/javascript.go
index c145163e5..4c0ac5354 100644
--- a/rpc/javascript.go
+++ b/rpc/javascript.go
@@ -407,6 +407,11 @@ web3._extend({
call: 'debug_writeMemProfile',
params: 1
}),
+ new web3._extend.Method({
+ name: 'replayTransaction',
+ call: 'debug_replayTransaction',
+ params: 4
+ })
],
properties:
[