diff options
Diffstat (limited to 'ethereal')
-rw-r--r-- | ethereal/assets/debugger/debugger.qml | 215 | ||||
-rw-r--r-- | ethereal/assets/qml/wallet.qml | 13 | ||||
-rw-r--r-- | ethereal/ui/debugger.go | 151 | ||||
-rw-r--r-- | ethereal/ui/gui.go | 2 | ||||
-rw-r--r-- | ethereal/ui/ui_lib.go | 46 |
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() } |