gui.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. package ethui
  2. import (
  3. "bytes"
  4. "fmt"
  5. "github.com/ethereum/eth-go"
  6. "github.com/ethereum/eth-go/ethchain"
  7. "github.com/ethereum/eth-go/ethdb"
  8. "github.com/ethereum/eth-go/ethpub"
  9. "github.com/ethereum/eth-go/ethutil"
  10. "github.com/go-qml/qml"
  11. "github.com/obscuren/mutan"
  12. "math/big"
  13. "strings"
  14. )
  15. type Gui struct {
  16. // The main application window
  17. win *qml.Window
  18. // QML Engine
  19. engine *qml.Engine
  20. component *qml.Common
  21. // The ethereum interface
  22. eth *eth.Ethereum
  23. // The public Ethereum library
  24. lib *EthLib
  25. uiLib *UiLib
  26. txDb *ethdb.LDBDatabase
  27. addr []byte
  28. pub *ethpub.PEthereum
  29. }
  30. // Create GUI, but doesn't start it
  31. func New(ethereum *eth.Ethereum) *Gui {
  32. lib := &EthLib{stateManager: ethereum.StateManager(), blockChain: ethereum.BlockChain(), txPool: ethereum.TxPool()}
  33. db, err := ethdb.NewLDBDatabase("tx_database")
  34. if err != nil {
  35. panic(err)
  36. }
  37. // On first run we won't have any keys yet, so this would crash.
  38. // Therefor we check if we are ready to actually start this process
  39. var addr []byte
  40. if ethutil.GetKeyRing().Len() != 0 {
  41. addr = ethutil.GetKeyRing().Get(0).Address()
  42. }
  43. pub := ethpub.NewPEthereum(ethereum)
  44. return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr, pub: pub}
  45. }
  46. func (gui *Gui) Start(assetPath string) {
  47. const version = "0.5.0 RC7"
  48. defer gui.txDb.Close()
  49. // Register ethereum functions
  50. qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
  51. Init: func(p *ethpub.PBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
  52. }, {
  53. Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
  54. }})
  55. ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", version))
  56. ethutil.Config.Log.Infoln("[GUI] Starting GUI")
  57. // Create a new QML engine
  58. gui.engine = qml.NewEngine()
  59. context := gui.engine.Context()
  60. // Expose the eth library and the ui library to QML
  61. context.SetVar("eth", gui)
  62. gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
  63. context.SetVar("ui", gui.uiLib)
  64. // Load the main QML interface
  65. data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
  66. var win *qml.Window
  67. var err error
  68. if len(data) == 0 {
  69. win, err = gui.showKeyImport(context)
  70. } else {
  71. win, err = gui.showWallet(context)
  72. }
  73. if err != nil {
  74. ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'")
  75. panic(err)
  76. }
  77. win.Show()
  78. win.Wait()
  79. gui.eth.Stop()
  80. }
  81. func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
  82. component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/wallet.qml"))
  83. if err != nil {
  84. return nil, err
  85. }
  86. win := gui.createWindow(component)
  87. go gui.setInitialBlockChain()
  88. go gui.readPreviousTransactions()
  89. go gui.update()
  90. return win, nil
  91. }
  92. func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) {
  93. context.SetVar("lib", gui.lib)
  94. component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml"))
  95. if err != nil {
  96. return nil, err
  97. }
  98. return gui.createWindow(component), nil
  99. }
  100. func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
  101. win := comp.CreateWindow(nil)
  102. gui.win = win
  103. gui.uiLib.win = win
  104. db := &Debugger{gui.win, make(chan bool)}
  105. gui.lib.Db = db
  106. gui.uiLib.Db = db
  107. return gui.win
  108. }
  109. func (gui *Gui) setInitialBlockChain() {
  110. // Load previous 10 blocks
  111. chain := gui.eth.BlockChain().GetChain(gui.eth.BlockChain().CurrentBlock.Hash(), 10)
  112. for _, block := range chain {
  113. gui.processBlock(block)
  114. }
  115. }
  116. func (gui *Gui) readPreviousTransactions() {
  117. it := gui.txDb.Db().NewIterator(nil, nil)
  118. for it.Next() {
  119. tx := ethchain.NewTransactionFromBytes(it.Value())
  120. gui.win.Root().Call("addTx", ethpub.NewPTx(tx))
  121. }
  122. it.Release()
  123. }
  124. func (gui *Gui) processBlock(block *ethchain.Block) {
  125. gui.win.Root().Call("addBlock", ethpub.NewPBlock(block))
  126. }
  127. func (gui *Gui) setWalletValue(amount, unconfirmedFunds *big.Int) {
  128. var str string
  129. if unconfirmedFunds != nil {
  130. pos := "+"
  131. if unconfirmedFunds.Cmp(big.NewInt(0)) >= 0 {
  132. pos = "-"
  133. }
  134. val := ethutil.CurrencyToString(new(big.Int).Abs(ethutil.BigCopy(unconfirmedFunds)))
  135. str = fmt.Sprintf("%v (%s %v)", ethutil.CurrencyToString(amount), pos, val)
  136. } else {
  137. str = fmt.Sprintf("%v", ethutil.CurrencyToString(amount))
  138. }
  139. gui.win.Root().Call("setWalletValue", str)
  140. }
  141. // Simple go routine function that updates the list of peers in the GUI
  142. func (gui *Gui) update() {
  143. reactor := gui.eth.Reactor()
  144. blockChan := make(chan ethutil.React, 1)
  145. txChan := make(chan ethutil.React, 1)
  146. reactor.Subscribe("newBlock", blockChan)
  147. reactor.Subscribe("newTx:pre", txChan)
  148. reactor.Subscribe("newTx:post", txChan)
  149. state := gui.eth.StateManager().TransState()
  150. unconfirmedFunds := new(big.Int)
  151. gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.addr).Amount)))
  152. for {
  153. select {
  154. case b := <-blockChan:
  155. block := b.Resource.(*ethchain.Block)
  156. if bytes.Compare(block.Coinbase, gui.addr) == 0 {
  157. gui.setWalletValue(gui.eth.StateManager().CurrentState().GetAccount(gui.addr).Amount, nil)
  158. }
  159. case txMsg := <-txChan:
  160. tx := txMsg.Resource.(*ethchain.Transaction)
  161. if txMsg.Event == "newTx:pre" {
  162. object := state.GetAccount(gui.addr)
  163. if bytes.Compare(tx.Sender(), gui.addr) == 0 && object.Nonce <= tx.Nonce {
  164. gui.win.Root().Call("addTx", ethpub.NewPTx(tx))
  165. gui.txDb.Put(tx.Hash(), tx.RlpEncode())
  166. /*
  167. object.Nonce += 1
  168. state.SetStateObject(object)
  169. */
  170. unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
  171. } else if bytes.Compare(tx.Recipient, gui.addr) == 0 {
  172. gui.win.Root().Call("addTx", ethpub.NewPTx(tx))
  173. gui.txDb.Put(tx.Hash(), tx.RlpEncode())
  174. unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
  175. }
  176. gui.setWalletValue(object.Amount, unconfirmedFunds)
  177. } else {
  178. object := state.GetAccount(gui.addr)
  179. if bytes.Compare(tx.Sender(), gui.addr) == 0 {
  180. object.SubAmount(tx.Value)
  181. } else if bytes.Compare(tx.Recipient, gui.addr) == 0 {
  182. object.AddAmount(tx.Value)
  183. }
  184. gui.setWalletValue(object.Amount, nil)
  185. state.UpdateStateObject(object)
  186. }
  187. }
  188. }
  189. }
  190. // Logging functions that log directly to the GUI interface
  191. func (gui *Gui) Println(v ...interface{}) {
  192. str := strings.TrimRight(fmt.Sprintln(v...), "\n")
  193. lines := strings.Split(str, "\n")
  194. for _, line := range lines {
  195. gui.win.Root().Call("addLog", line)
  196. }
  197. }
  198. func (gui *Gui) Printf(format string, v ...interface{}) {
  199. str := strings.TrimRight(fmt.Sprintf(format, v...), "\n")
  200. lines := strings.Split(str, "\n")
  201. for _, line := range lines {
  202. gui.win.Root().Call("addLog", line)
  203. }
  204. }
  205. func (gui *Gui) Transact(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) {
  206. keyPair := ethutil.GetKeyRing().Get(0)
  207. return gui.pub.Transact(ethutil.Hex(keyPair.PrivateKey), recipient, value, gas, gasPrice, data)
  208. }
  209. func (gui *Gui) Create(recipient, value, gas, gasPrice, data string) (*ethpub.PReceipt, error) {
  210. keyPair := ethutil.GetKeyRing().Get(0)
  211. mainInput, initInput := mutan.PreParse(data)
  212. return gui.pub.Create(ethutil.Hex(keyPair.PrivateKey), value, gas, gasPrice, initInput, mainInput)
  213. }