gui.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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. "math/big"
  12. "strings"
  13. )
  14. type Gui struct {
  15. // The main application window
  16. win *qml.Window
  17. // QML Engine
  18. engine *qml.Engine
  19. component *qml.Common
  20. // The ethereum interface
  21. eth *eth.Ethereum
  22. // The public Ethereum library
  23. lib *EthLib
  24. txDb *ethdb.LDBDatabase
  25. addr []byte
  26. }
  27. // Create GUI, but doesn't start it
  28. func New(ethereum *eth.Ethereum) *Gui {
  29. lib := &EthLib{stateManager: ethereum.StateManager(), blockChain: ethereum.BlockChain(), txPool: ethereum.TxPool()}
  30. db, err := ethdb.NewLDBDatabase("tx_database")
  31. if err != nil {
  32. panic(err)
  33. }
  34. data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
  35. // On first run we won't have any keys yet, so this would crash.
  36. // Therefor we check if we are ready to actually start this process
  37. var addr []byte
  38. if len(data) > 0 {
  39. key := ethutil.Config.Db.GetKeys()[0]
  40. addr = key.Address()
  41. ethereum.StateManager().WatchAddr(addr)
  42. }
  43. return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr}
  44. }
  45. func (ui *Gui) Start(assetPath string) {
  46. defer ui.txDb.Close()
  47. // Register ethereum functions
  48. qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
  49. Init: func(p *ethpub.PBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
  50. }, {
  51. Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
  52. }})
  53. ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", "0.2"))
  54. ethutil.Config.Log.Infoln("[GUI] Starting GUI")
  55. // Create a new QML engine
  56. ui.engine = qml.NewEngine()
  57. context := ui.engine.Context()
  58. // Expose the eth library and the ui library to QML
  59. context.SetVar("eth", ui.lib)
  60. uiLib := NewUiLib(ui.engine, ui.eth, assetPath)
  61. context.SetVar("ui", uiLib)
  62. // Load the main QML interface
  63. data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
  64. var err error
  65. var component qml.Object
  66. firstRun := len(data) == 0
  67. if firstRun {
  68. component, err = ui.engine.LoadFile(uiLib.AssetPath("qml/first_run.qml"))
  69. } else {
  70. component, err = ui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml"))
  71. }
  72. if err != nil {
  73. ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'")
  74. panic(err)
  75. }
  76. ui.win = component.CreateWindow(nil)
  77. uiLib.win = ui.win
  78. db := &Debugger{ui.win, make(chan bool)}
  79. ui.lib.Db = db
  80. uiLib.Db = db
  81. // Register the ui as a block processor
  82. //ui.eth.BlockManager.SecondaryBlockProcessor = ui
  83. //ui.eth.TxPool.SecondaryProcessor = ui
  84. // Add the ui as a log system so we can log directly to the UGI
  85. ethutil.Config.Log.AddLogSystem(ui)
  86. // Loads previous blocks
  87. if firstRun == false {
  88. go ui.setInitialBlockChain()
  89. go ui.readPreviousTransactions()
  90. go ui.update()
  91. }
  92. ui.win.Show()
  93. ui.win.Wait()
  94. ui.eth.Stop()
  95. }
  96. func (ui *Gui) setInitialBlockChain() {
  97. // Load previous 10 blocks
  98. chain := ui.eth.BlockChain().GetChain(ui.eth.BlockChain().CurrentBlock.Hash(), 10)
  99. for _, block := range chain {
  100. ui.ProcessBlock(block)
  101. }
  102. }
  103. func (ui *Gui) readPreviousTransactions() {
  104. it := ui.txDb.Db().NewIterator(nil, nil)
  105. for it.Next() {
  106. tx := ethchain.NewTransactionFromBytes(it.Value())
  107. ui.win.Root().Call("addTx", ethpub.NewPTx(tx))
  108. }
  109. it.Release()
  110. }
  111. func (ui *Gui) ProcessBlock(block *ethchain.Block) {
  112. ui.win.Root().Call("addBlock", ethpub.NewPBlock(block))
  113. }
  114. // Simple go routine function that updates the list of peers in the GUI
  115. func (ui *Gui) update() {
  116. txChan := make(chan ethchain.TxMsg, 1)
  117. ui.eth.TxPool().Subscribe(txChan)
  118. account := ui.eth.StateManager().GetAddrState(ui.addr).Object
  119. unconfirmedFunds := new(big.Int)
  120. ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(account.Amount)))
  121. addrState := ui.eth.StateManager().GetAddrState(ui.addr)
  122. for {
  123. select {
  124. case txMsg := <-txChan:
  125. tx := txMsg.Tx
  126. if txMsg.Type == ethchain.TxPre {
  127. if bytes.Compare(tx.Sender(), ui.addr) == 0 && addrState.Nonce <= tx.Nonce {
  128. ui.win.Root().Call("addTx", ethpub.NewPTx(tx))
  129. ui.txDb.Put(tx.Hash(), tx.RlpEncode())
  130. addrState.Nonce += 1
  131. unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
  132. } else if bytes.Compare(tx.Recipient, ui.addr) == 0 {
  133. ui.win.Root().Call("addTx", ethpub.NewPTx(tx))
  134. ui.txDb.Put(tx.Hash(), tx.RlpEncode())
  135. unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
  136. }
  137. pos := "+"
  138. if unconfirmedFunds.Cmp(big.NewInt(0)) >= 0 {
  139. pos = "-"
  140. }
  141. val := ethutil.CurrencyToString(new(big.Int).Abs(ethutil.BigCopy(unconfirmedFunds)))
  142. str := fmt.Sprintf("%v (%s %v)", ethutil.CurrencyToString(account.Amount), pos, val)
  143. ui.win.Root().Call("setWalletValue", str)
  144. } else {
  145. amount := account.Amount
  146. if bytes.Compare(tx.Sender(), ui.addr) == 0 {
  147. amount.Sub(account.Amount, tx.Value)
  148. } else if bytes.Compare(tx.Recipient, ui.addr) == 0 {
  149. amount.Add(account.Amount, tx.Value)
  150. }
  151. ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(amount)))
  152. }
  153. }
  154. /*
  155. accountAmount := ui.eth.BlockManager.GetAddrState(ui.addr).Account.Amount
  156. ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", accountAmount))
  157. ui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", ui.eth.Peers().Len(), ui.eth.MaxPeers))
  158. time.Sleep(1 * time.Second)
  159. */
  160. }
  161. }
  162. // Logging functions that log directly to the GUI interface
  163. func (ui *Gui) Println(v ...interface{}) {
  164. str := strings.TrimRight(fmt.Sprintln(v...), "\n")
  165. lines := strings.Split(str, "\n")
  166. for _, line := range lines {
  167. ui.win.Root().Call("addLog", line)
  168. }
  169. }
  170. func (ui *Gui) Printf(format string, v ...interface{}) {
  171. str := strings.TrimRight(fmt.Sprintf(format, v...), "\n")
  172. lines := strings.Split(str, "\n")
  173. for _, line := range lines {
  174. ui.win.Root().Call("addLog", line)
  175. }
  176. }