aboutsummaryrefslogtreecommitdiffstats
path: root/ethereal
diff options
context:
space:
mode:
Diffstat (limited to 'ethereal')
-rw-r--r--ethereal/assets/debugger/debugger.qml215
-rw-r--r--ethereal/assets/qml/wallet.qml13
-rw-r--r--ethereal/ui/debugger.go151
-rw-r--r--ethereal/ui/gui.go2
-rw-r--r--ethereal/ui/ui_lib.go46
5 files changed, 386 insertions, 41 deletions
diff --git a/ethereal/assets/debugger/debugger.qml b/ethereal/assets/debugger/debugger.qml
new file mode 100644
index 000000000..bf69b4254
--- /dev/null
+++ b/ethereal/assets/debugger/debugger.qml
@@ -0,0 +1,215 @@
+import QtQuick 2.0
+import QtQuick.Controls 1.0;
+import QtQuick.Layouts 1.0;
+import QtQuick.Dialogs 1.0;
+import QtQuick.Window 2.1;
+import QtQuick.Controls.Styles 1.1
+import Ethereum 1.0
+
+ApplicationWindow {
+ visible: false
+ title: "Debugger"
+ minimumWidth: 1280
+ minimumHeight: 900
+ width: 1290
+ height: 900
+
+ SplitView {
+ anchors.fill: parent
+ property var asmModel: ListModel {
+ id: asmModel
+ }
+ TableView {
+ id: asmTableView
+ width: 200
+ TableViewColumn{ role: "value" ; title: "" ; width: 100 }
+ model: asmModel
+ }
+
+ Rectangle {
+ color: "#00000000"
+ anchors.left: asmTableView.right
+ anchors.right: parent.right
+ SplitView {
+ orientation: Qt.Vertical
+ anchors.fill: parent
+
+ Rectangle {
+ color: "#00000000"
+ height: 500
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ TextArea {
+ id: codeEditor
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: settings.left
+ }
+
+ Column {
+ id: settings
+ spacing: 5
+ width: 300
+ height: parent.height
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+
+ Label {
+ text: "Data"
+ }
+ TextArea {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: 150
+ }
+
+ Label {
+ text: "Amount"
+ }
+ TextField {
+ id: txValue
+ width: 200
+ placeholderText: "Amount"
+ validator: RegExpValidator { regExp: /\d*/ }
+ }
+ Label {
+ text: "Amount of gas"
+ }
+ TextField {
+ id: txGas
+ width: 200
+ validator: RegExpValidator { regExp: /\d*/ }
+ text: "10000"
+ placeholderText: "Gas"
+ }
+ Label {
+ text: "Gas price"
+ }
+ TextField {
+ id: txGasPrice
+ width: 200
+ placeholderText: "Gas price"
+ text: "1000000000000"
+ validator: RegExpValidator { regExp: /\d*/ }
+ }
+ }
+ }
+
+ SplitView {
+ orientation: Qt.Vertical
+ id: inspectorPane
+ height: 500
+
+ SplitView {
+ orientation: Qt.Horizontal
+ height: 300
+
+ TableView {
+ id: stackTableView
+ property var stackModel: ListModel {
+ id: stackModel
+ }
+ height: parent.height
+ width: 300
+ TableViewColumn{ role: "value" ; title: "Stack" ; width: 200 }
+ model: stackModel
+ }
+
+ TableView {
+ id: memoryTableView
+ property var memModel: ListModel {
+ id: memModel
+ }
+ height: parent.height
+ width: parent.width - stackTableView.width
+ TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50}
+ TableViewColumn{ role: "value" ; title: "Memory" ; width: 750}
+ model: memModel
+ }
+ }
+
+ SplitView {
+ height: 300
+ TableView {
+ id: storageTableView
+ property var memModel: ListModel {
+ id: storageModel
+ }
+ height: parent.height
+ width: parent.width - stackTableView.width
+ TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2}
+ TableViewColumn{ role: "value" ; title: "value" ; width: storageTableView.width / 2}
+ model: storageModel
+ }
+ }
+ }
+ }
+ }
+ }
+ statusBar: StatusBar {
+ RowLayout {
+ spacing: 5
+ anchors.fill: parent
+
+ Button {
+ property var enabled: true
+ id: debugStart
+ onClicked: {
+ dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text)
+ }
+ text: "Debug"
+ }
+
+ Button {
+ property var enabled: true
+ id: debugNextButton
+ onClicked: {
+ dbg.next()
+ }
+ text: "Next"
+ }
+ }
+ }
+
+ function setAsm(asm) {
+ asmModel.append({asm: asm})
+ }
+
+ function clearAsm() {
+ asmModel.clear()
+ }
+
+ function setInstruction(num) {
+ asmTableView.selection.clear()
+ asmTableView.selection.select(num-1)
+ }
+
+ function setMem(mem) {
+ memModel.append({num: mem.num, value: mem.value})
+ }
+ function clearMem(){
+ memModel.clear()
+ }
+
+ function setStack(stack) {
+ stackModel.append({value: stack})
+ }
+ function addDebugMessage(message){
+ debuggerLog.append({value: message})
+ }
+
+ function clearStack() {
+ stackModel.clear()
+ }
+
+ function clearStorage() {
+ storageModel.clear()
+ }
+
+ function setStorage(storage) {
+ storageModel.append({key: storage.key, value: storage.value})
+ }
+}
diff --git a/ethereal/assets/qml/wallet.qml b/ethereal/assets/qml/wallet.qml
index 0c0c977c1..3063bb10d 100644
--- a/ethereal/assets/qml/wallet.qml
+++ b/ethereal/assets/qml/wallet.qml
@@ -293,6 +293,7 @@ ApplicationWindow {
statusBar: StatusBar {
RowLayout {
anchors.fill: parent
+ /*
Button {
property var enabled: true
id: connectButton
@@ -303,10 +304,20 @@ ApplicationWindow {
}
text: "Connect"
}
+ */
+
+ Button {
+ property var enabled: true
+ id: debuggerWindow
+ onClicked: {
+ ui.startDebugger()
+ }
+ text: "Debugger"
+ }
Button {
id: importAppButton
- anchors.left: connectButton.right
+ anchors.left: debuggerWindow.right
anchors.leftMargin: 5
onClicked: openAppDialog.open()
text: "Import App"
diff --git a/ethereal/ui/debugger.go b/ethereal/ui/debugger.go
new file mode 100644
index 000000000..817c0b08f
--- /dev/null
+++ b/ethereal/ui/debugger.go
@@ -0,0 +1,151 @@
+package ethui
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/go-qml/qml"
+)
+
+type DebuggerWindow struct {
+ win *qml.Window
+ engine *qml.Engine
+ lib *UiLib
+ Db *Debugger
+}
+
+func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
+ engine := qml.NewEngine()
+ component, err := engine.LoadFile(lib.AssetPath("debugger/debugger.qml"))
+ if err != nil {
+ fmt.Println(err)
+
+ return nil
+ }
+
+ win := component.CreateWindow(nil)
+ db := &Debugger{win, make(chan bool), make(chan bool), true}
+
+ return &DebuggerWindow{engine: engine, win: win, lib: lib, Db: db}
+}
+
+func (self *DebuggerWindow) Show() {
+ context := self.engine.Context()
+ context.SetVar("dbg", self)
+
+ go func() {
+ self.win.Show()
+ self.win.Wait()
+ }()
+}
+
+func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, data string) {
+ if !self.Db.done {
+ self.Db.Q <- true
+ }
+
+ state := self.lib.eth.BlockChain().CurrentBlock.State()
+
+ defer func() {
+ if r := recover(); r != nil {
+ fmt.Println(r)
+ }
+ }()
+
+ script, err := ethutil.Compile(data)
+ if err != nil {
+ ethutil.Config.Log.Debugln(err)
+
+ return
+ }
+
+ dis := ethchain.Disassemble(script)
+ self.win.Root().Call("clearAsm")
+
+ for _, str := range dis {
+ self.win.Root().Call("setAsm", str)
+ }
+ // Contract addr as test address
+ keyPair := ethutil.GetKeyRing().Get(0)
+ callerTx := ethchain.NewContractCreationTx(ethutil.Big(valueStr), ethutil.Big(gasStr), ethutil.Big(gasPriceStr), script)
+ callerTx.Sign(keyPair.PrivateKey)
+
+ account := self.lib.eth.StateManager().TransState().GetAccount(keyPair.Address())
+ contract := ethchain.MakeContract(callerTx, state)
+ callerClosure := ethchain.NewClosure(account, contract, contract.Init(), state, ethutil.Big(gasStr), ethutil.Big(gasPriceStr))
+
+ block := self.lib.eth.BlockChain().CurrentBlock
+ vm := ethchain.NewVm(state, self.lib.eth.StateManager(), ethchain.RuntimeVars{
+ Origin: account.Address(),
+ BlockNumber: block.BlockInfo().Number,
+ PrevHash: block.PrevHash,
+ Coinbase: block.Coinbase,
+ Time: block.Time,
+ Diff: block.Difficulty,
+ })
+
+ self.Db.done = false
+ go func() {
+ callerClosure.Call(vm, contract.Init(), self.Db.halting)
+
+ state.Reset()
+
+ self.Db.done = true
+ }()
+}
+
+func (self *DebuggerWindow) Next() {
+ self.Db.Next()
+}
+
+type Debugger struct {
+ win *qml.Window
+ N chan bool
+ Q chan bool
+ done bool
+}
+
+type storeVal struct {
+ Key, Value string
+}
+
+func (d *Debugger) halting(pc int, op ethchain.OpCode, mem *ethchain.Memory, stack *ethchain.Stack, stateObject *ethchain.StateObject) bool {
+ d.win.Root().Call("setInstruction", pc)
+ d.win.Root().Call("clearMem")
+ d.win.Root().Call("clearStack")
+ d.win.Root().Call("clearStorage")
+
+ addr := 0
+ for i := 0; i+32 <= mem.Len(); i += 32 {
+ d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("% x", mem.Data()[i:i+32])})
+ addr++
+ }
+
+ for _, val := range stack.Data() {
+ d.win.Root().Call("setStack", val.String())
+ }
+
+ stateObject.State().EachStorage(func(key string, node *ethutil.Value) {
+ d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())})
+ })
+
+out:
+ for {
+ select {
+ case <-d.N:
+ break out
+ case <-d.Q:
+ d.done = true
+
+ return false
+ }
+ }
+
+ return true
+}
+
+func (d *Debugger) Next() {
+ if !d.done {
+ d.N <- true
+ }
+}
diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go
index 18e13d985..b49fafac1 100644
--- a/ethereal/ui/gui.go
+++ b/ethereal/ui/gui.go
@@ -130,7 +130,7 @@ func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
gui.win = win
gui.uiLib.win = win
- db := &Debugger{gui.win, make(chan bool), true}
+ db := &Debugger{gui.win, make(chan bool), make(chan bool), true}
gui.lib.Db = db
gui.uiLib.Db = db
diff --git a/ethereal/ui/ui_lib.go b/ethereal/ui/ui_lib.go
index 51fa26a88..998392525 100644
--- a/ethereal/ui/ui_lib.go
+++ b/ethereal/ui/ui_lib.go
@@ -2,7 +2,6 @@ package ethui
import (
"bitbucket.org/kardianos/osext"
- "fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
@@ -90,6 +89,12 @@ func (ui *UiLib) AssetPath(p string) string {
return path.Join(ui.assetPath, p)
}
+func (self *UiLib) StartDebugger() {
+ dbWindow := NewDebuggerWindow(self)
+
+ dbWindow.Show()
+}
+
func DefaultAssetPath() string {
var base string
// If the current working directory is the go-ethereum dir
@@ -163,42 +168,5 @@ func (ui *UiLib) DebugTx(recipient, valueStr, gasStr, gasPriceStr, data string)
}
func (ui *UiLib) Next() {
- if !ui.Db.done {
- ui.Db.Next()
- }
-}
-
-type Debugger struct {
- win *qml.Window
- N chan bool
- done bool
-}
-
-func (d *Debugger) halting(pc int, op ethchain.OpCode, mem *ethchain.Memory, stack *ethchain.Stack) {
- d.win.Root().Call("setInstruction", pc)
- d.win.Root().Call("clearMem")
- d.win.Root().Call("clearStack")
-
- addr := 0
- for i := 0; i+32 <= mem.Len(); i += 32 {
- d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("% x", mem.Data()[i:i+32])})
- addr++
- }
-
- for _, val := range stack.Data() {
- d.win.Root().Call("setStack", val.String())
- }
-
-out:
- for {
- select {
- case <-d.N:
- break out
- default:
- }
- }
-}
-
-func (d *Debugger) Next() {
- d.N <- true
+ ui.Db.Next()
}