package ethui import ( "fmt" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" "github.com/go-qml/qml" "math/big" "strings" ) func formatData(data string) []byte { if len(data) == 0 { return nil } // Simple stupid d := new(big.Int) if data[0:1] == "\"" && data[len(data)-1:] == "\"" { d.SetBytes([]byte(data[1 : len(data)-1])) } else if data[:2] == "0x" { d.SetBytes(ethutil.FromHex(data[2:])) } else { d.SetString(data, 0) } return ethutil.BigToBytes(d, 256) } 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) SetCode(code string) { self.win.Set("codeText", code) } func (self *DebuggerWindow) SetData(data string) { self.win.Set("dataText", data) } func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) { if !self.Db.done { self.Db.Q <- true } defer func() { if r := recover(); r != nil { fmt.Println(r) self.Db.done = true } }() data := ethutil.StringToByteFunc(dataStr, func(s string) (ret []byte) { slice := strings.Split(dataStr, "\n") for _, dataItem := range slice { d := formatData(dataItem) ret = append(ret, d...) } return }) var err error script := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) { ret, err = ethutil.Compile(s) return }) 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) state := self.lib.eth.BlockChain().CurrentBlock.State() account := self.lib.eth.StateManager().TransState().GetAccount(keyPair.Address()) contract := ethchain.MakeContract(callerTx, state) callerClosure := ethchain.NewClosure(account, contract, script, 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, data, 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 } }