diff --git a/core/vm/common.go b/core/vm/common.go
index 2d1aa93326627c21cce3e9a7826572cc0909edfd..395ed04710ba080eb1cd1e2ce2e0ffe2e35c0446 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 8e07aaa899eef71dc04162dc0ea2c86d5392cf93..0c6bbcd42ce04072addd6e92ddb81adc2e9c9ed2 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 08b103a4cf68bb569981397518050dcd859a1c07..f287b1b014c1684b6c6a3a865c95e641ece1e010 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -1427,6 +1427,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 9e3b2218bf2b2fa4326f791171750b8d9847b794..967c82ea624e993313f51c3a6fb42c5d7034a626 100644
--- a/rpc/javascript.go
+++ b/rpc/javascript.go
@@ -399,6 +399,11 @@ web3._extend({
 			call: 'debug_writeMemProfile',
 			params: 1
 		}),
+		new web3._extend.Method({
+			name: 'replayTransaction',
+			call: 'debug_replayTransaction',
+			params: 4
+		})
 	],
 	properties:
 	[