ui_lib.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
  2. //
  3. // This library is free software; you can redistribute it and/or
  4. // modify it under the terms of the GNU General Public
  5. // License as published by the Free Software Foundation; either
  6. // version 2.1 of the License, or (at your option) any later version.
  7. //
  8. // This library is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. // General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this library; if not, write to the Free Software
  15. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  16. // MA 02110-1301 USA
  17. package main
  18. import (
  19. "bytes"
  20. "fmt"
  21. "path"
  22. "strconv"
  23. "strings"
  24. "github.com/ethereum/go-ethereum/core"
  25. "github.com/ethereum/go-ethereum/core/types"
  26. "github.com/ethereum/go-ethereum/crypto"
  27. "github.com/ethereum/go-ethereum/eth"
  28. "github.com/ethereum/go-ethereum/ethutil"
  29. "github.com/ethereum/go-ethereum/event/filter"
  30. "github.com/ethereum/go-ethereum/javascript"
  31. "github.com/ethereum/go-ethereum/miner"
  32. "github.com/ethereum/go-ethereum/state"
  33. "github.com/ethereum/go-ethereum/ui/qt"
  34. "github.com/ethereum/go-ethereum/xeth"
  35. "gopkg.in/qml.v1"
  36. )
  37. type memAddr struct {
  38. Num string
  39. Value string
  40. }
  41. // UI Library that has some basic functionality exposed
  42. type UiLib struct {
  43. *xeth.JSXEth
  44. engine *qml.Engine
  45. eth *eth.Ethereum
  46. connected bool
  47. assetPath string
  48. // The main application window
  49. win *qml.Window
  50. Db *Debugger
  51. DbWindow *DebuggerWindow
  52. jsEngine *javascript.JSRE
  53. filterCallbacks map[int][]int
  54. filterManager *filter.FilterManager
  55. miner *miner.Miner
  56. }
  57. func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
  58. lib := &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)}
  59. lib.miner = miner.New(eth.KeyManager().Address(), eth)
  60. lib.filterManager = filter.NewFilterManager(eth.EventMux())
  61. return lib
  62. }
  63. func (self *UiLib) Notef(args []interface{}) {
  64. guilogger.Infoln(args...)
  65. }
  66. func (self *UiLib) LookupDomain(domain string) string {
  67. world := self.World()
  68. if len(domain) > 32 {
  69. domain = string(crypto.Sha3([]byte(domain)))
  70. }
  71. data := world.Config().Get("DnsReg").StorageString(domain).Bytes()
  72. // Left padded = A record, Right padded = CNAME
  73. if len(data) > 0 && data[0] == 0 {
  74. data = bytes.TrimLeft(data, "\x00")
  75. var ipSlice []string
  76. for _, d := range data {
  77. ipSlice = append(ipSlice, strconv.Itoa(int(d)))
  78. }
  79. return strings.Join(ipSlice, ".")
  80. } else {
  81. data = bytes.TrimRight(data, "\x00")
  82. return string(data)
  83. }
  84. }
  85. func (self *UiLib) LookupName(addr string) string {
  86. var (
  87. nameReg = self.World().Config().Get("NameReg")
  88. lookup = nameReg.Storage(ethutil.Hex2Bytes(addr))
  89. )
  90. if lookup.Len() != 0 {
  91. return strings.Trim(lookup.Str(), "\x00")
  92. }
  93. return addr
  94. }
  95. func (self *UiLib) LookupAddress(name string) string {
  96. var (
  97. nameReg = self.World().Config().Get("NameReg")
  98. lookup = nameReg.Storage(ethutil.RightPadBytes([]byte(name), 32))
  99. )
  100. if lookup.Len() != 0 {
  101. return ethutil.Bytes2Hex(lookup.Bytes())
  102. }
  103. return ""
  104. }
  105. func (self *UiLib) PastPeers() *ethutil.List {
  106. return ethutil.NewList([]string{})
  107. //return ethutil.NewList(eth.PastPeers())
  108. }
  109. func (self *UiLib) ImportTx(rlpTx string) {
  110. tx := types.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx))
  111. err := self.eth.TxPool().Add(tx)
  112. if err != nil {
  113. guilogger.Infoln("import tx failed ", err)
  114. }
  115. }
  116. func (self *UiLib) EvalJavascriptFile(path string) {
  117. self.jsEngine.LoadExtFile(path[7:])
  118. }
  119. func (self *UiLib) EvalJavascriptString(str string) string {
  120. value, err := self.jsEngine.Run(str)
  121. if err != nil {
  122. return err.Error()
  123. }
  124. return fmt.Sprintf("%v", value)
  125. }
  126. func (ui *UiLib) OpenQml(path string) {
  127. container := NewQmlApplication(path[7:], ui)
  128. app := NewExtApplication(container, ui)
  129. go app.run()
  130. }
  131. func (ui *UiLib) OpenHtml(path string) {
  132. container := NewHtmlApplication(path, ui)
  133. app := NewExtApplication(container, ui)
  134. go app.run()
  135. }
  136. func (ui *UiLib) OpenBrowser() {
  137. ui.OpenHtml("file://" + ui.AssetPath("ext/home.html"))
  138. }
  139. func (ui *UiLib) Muted(content string) {
  140. component, err := ui.engine.LoadFile(ui.AssetPath("qml/muted.qml"))
  141. if err != nil {
  142. guilogger.Debugln(err)
  143. return
  144. }
  145. win := component.CreateWindow(nil)
  146. go func() {
  147. path := "file://" + ui.AssetPath("muted/index.html")
  148. win.Set("url", path)
  149. win.Show()
  150. win.Wait()
  151. }()
  152. }
  153. func (ui *UiLib) Connect(button qml.Object) {
  154. if !ui.connected {
  155. ui.eth.Start(true)
  156. ui.connected = true
  157. button.Set("enabled", false)
  158. }
  159. }
  160. func (ui *UiLib) ConnectToPeer(addr string) {
  161. ui.eth.SuggestPeer(addr)
  162. }
  163. func (ui *UiLib) AssetPath(p string) string {
  164. return path.Join(ui.assetPath, p)
  165. }
  166. func (self *UiLib) StartDbWithContractAndData(contractHash, data string) {
  167. dbWindow := NewDebuggerWindow(self)
  168. object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash))
  169. if len(object.Code) > 0 {
  170. dbWindow.SetCode("0x" + ethutil.Bytes2Hex(object.Code))
  171. }
  172. dbWindow.SetData("0x" + data)
  173. dbWindow.Show()
  174. }
  175. func (self *UiLib) StartDbWithCode(code string) {
  176. dbWindow := NewDebuggerWindow(self)
  177. dbWindow.SetCode("0x" + code)
  178. dbWindow.Show()
  179. }
  180. func (self *UiLib) StartDebugger() {
  181. dbWindow := NewDebuggerWindow(self)
  182. dbWindow.Show()
  183. }
  184. func (self *UiLib) NewFilter(object map[string]interface{}) (id int) {
  185. filter := qt.NewFilterFromMap(object, self.eth)
  186. filter.MessageCallback = func(messages state.Messages) {
  187. self.win.Root().Call("invokeFilterCallback", xeth.ToJSMessages(messages), id)
  188. }
  189. id = self.filterManager.InstallFilter(filter)
  190. return id
  191. }
  192. func (self *UiLib) NewFilterString(typ string) (id int) {
  193. filter := core.NewFilter(self.eth)
  194. filter.BlockCallback = func(block *types.Block) {
  195. if self.win != nil && self.win.Root() != nil {
  196. self.win.Root().Call("invokeFilterCallback", "{}", id)
  197. } else {
  198. fmt.Println("QML is lagging")
  199. }
  200. }
  201. id = self.filterManager.InstallFilter(filter)
  202. return id
  203. }
  204. func (self *UiLib) Messages(id int) *ethutil.List {
  205. filter := self.filterManager.GetFilter(id)
  206. if filter != nil {
  207. messages := xeth.ToJSMessages(filter.Find())
  208. return messages
  209. }
  210. return ethutil.EmptyList()
  211. }
  212. func (self *UiLib) UninstallFilter(id int) {
  213. self.filterManager.UninstallFilter(id)
  214. }
  215. func mapToTxParams(object map[string]interface{}) map[string]string {
  216. // Default values
  217. if object["from"] == nil {
  218. object["from"] = ""
  219. }
  220. if object["to"] == nil {
  221. object["to"] = ""
  222. }
  223. if object["value"] == nil {
  224. object["value"] = ""
  225. }
  226. if object["gas"] == nil {
  227. object["gas"] = ""
  228. }
  229. if object["gasPrice"] == nil {
  230. object["gasPrice"] = ""
  231. }
  232. var dataStr string
  233. var data []string
  234. if list, ok := object["data"].(*qml.List); ok {
  235. list.Convert(&data)
  236. } else if str, ok := object["data"].(string); ok {
  237. data = []string{str}
  238. }
  239. for _, str := range data {
  240. if ethutil.IsHex(str) {
  241. str = str[2:]
  242. if len(str) != 64 {
  243. str = ethutil.LeftPadString(str, 64)
  244. }
  245. } else {
  246. str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
  247. }
  248. dataStr += str
  249. }
  250. object["data"] = dataStr
  251. conv := make(map[string]string)
  252. for key, value := range object {
  253. if v, ok := value.(string); ok {
  254. conv[key] = v
  255. }
  256. }
  257. return conv
  258. }
  259. func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
  260. object := mapToTxParams(params)
  261. return self.JSXEth.Transact(
  262. object["from"],
  263. object["to"],
  264. object["value"],
  265. object["gas"],
  266. object["gasPrice"],
  267. object["data"],
  268. )
  269. }
  270. func (self *UiLib) Compile(code string) (string, error) {
  271. bcode, err := ethutil.Compile(code, false)
  272. if err != nil {
  273. return err.Error(), err
  274. }
  275. return ethutil.Bytes2Hex(bcode), err
  276. }
  277. func (self *UiLib) Call(params map[string]interface{}) (string, error) {
  278. object := mapToTxParams(params)
  279. return self.JSXEth.Execute(
  280. object["to"],
  281. object["value"],
  282. object["gas"],
  283. object["gasPrice"],
  284. object["data"],
  285. )
  286. }
  287. func (self *UiLib) AddLocalTransaction(to, data, gas, gasPrice, value string) int {
  288. return self.miner.AddLocalTx(&miner.LocalTx{
  289. To: ethutil.Hex2Bytes(to),
  290. Data: ethutil.Hex2Bytes(data),
  291. Gas: gas,
  292. GasPrice: gasPrice,
  293. Value: value,
  294. }) - 1
  295. }
  296. func (self *UiLib) RemoveLocalTransaction(id int) {
  297. self.miner.RemoveLocalTx(id)
  298. }
  299. func (self *UiLib) SetGasPrice(price string) {
  300. self.miner.MinAcceptedGasPrice = ethutil.Big(price)
  301. }
  302. func (self *UiLib) ToggleMining() bool {
  303. if !self.miner.Mining() {
  304. self.miner.Start()
  305. return true
  306. } else {
  307. self.miner.Stop()
  308. return false
  309. }
  310. }
  311. /*
  312. // XXX Refactor me & MOVE
  313. func (self *Ethereum) InstallFilter(filter *core.Filter) (id int) {
  314. return self.filterManager.InstallFilter(filter)
  315. }
  316. func (self *Ethereum) UninstallFilter(id int) { self.filterManager.UninstallFilter(id) }
  317. func (self *Ethereum) GetFilter(id int) *core.Filter { return self.filterManager.GetFilter(id) }
  318. */