From 429dd2a100f3b9e2b612b59bcb48f79a805cd6f9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 7 Nov 2014 12:18:48 +0100 Subject: [PATCH] Implemented new miner w/ ui interface for merged mining. Closes #177 * Miner has been rewritten * Added new miner pane * Added option for local txs * Added option to read from MergeMining contract and list them for merged mining --- block_pool.go | 7 +- chain/block_manager.go | 16 +- chain/chain_manager.go | 34 +++- chain/dagger.go | 3 +- cmd/mist/assets/miner.png | Bin 0 -> 2100 bytes cmd/mist/assets/qml/main.qml | 42 ++-- cmd/mist/assets/qml/views/miner.qml | 254 +++++++++++++++++++++++ cmd/mist/bindings.go | 21 -- cmd/mist/gui.go | 51 +++-- cmd/mist/ui_lib.go | 38 +++- cmd/utils/cmd.go | 2 +- miner/miner.go | 306 ++++++++++++++-------------- p2p/connection.go | 2 +- p2p/message.go | 2 +- p2p/messenger_test.go | 3 +- peer.go | 4 +- xeth/config.go | 2 + xeth/hexface.go | 4 + 18 files changed, 558 insertions(+), 233 deletions(-) create mode 100644 cmd/mist/assets/miner.png create mode 100644 cmd/mist/assets/qml/views/miner.qml diff --git a/block_pool.go b/block_pool.go index ff0675c504..ec945fa6e5 100644 --- a/block_pool.go +++ b/block_pool.go @@ -315,9 +315,12 @@ out: // otherwise process and don't emit anything if len(blocks) > 0 { chainManager := self.eth.ChainManager() + // Test and import chain := chain.NewChain(blocks) - _, err := chainManager.TestChain(chain) + _, err := chainManager.TestChain(chain, true) if err != nil { + poollogger.Debugln(err) + self.Reset() poollogger.Debugf("Punishing peer for supplying bad chain (%v)\n", self.peer.conn.RemoteAddr()) @@ -327,7 +330,7 @@ out: self.td = ethutil.Big0 self.peer = nil } else { - chainManager.InsertChain(chain) + //chainManager.InsertChain(chain) for _, block := range blocks { self.Remove(block.Hash()) } diff --git a/chain/block_manager.go b/chain/block_manager.go index ed2fbfe8c1..79c18fbe37 100644 --- a/chain/block_manager.go +++ b/chain/block_manager.go @@ -228,7 +228,7 @@ func (sm *BlockManager) Process(block *Block) (td *big.Int, err error) { func (sm *BlockManager) ProcessWithParent(block, parent *Block) (td *big.Int, err error) { sm.lastAttemptedBlock = block - state := parent.State() + state := parent.State().Copy() // Defer the Undo on the Trie. If the block processing happened // we don't want to undo but since undo only happens on dirty @@ -240,20 +240,22 @@ func (sm *BlockManager) ProcessWithParent(block, parent *Block) (td *big.Int, er fmt.Printf("## %x %x ##\n", block.Hash(), block.Number) } + _, err = sm.ApplyDiff(state, parent, block) + if err != nil { + return nil, err + } + + /* Go and C++ don't have consensus here. FIXME txSha := DeriveSha(block.transactions) if bytes.Compare(txSha, block.TxSha) != 0 { return nil, fmt.Errorf("Error validating transaction sha. Received %x, got %x", block.TxSha, txSha) } - receipts, err := sm.ApplyDiff(state, parent, block) - if err != nil { - return nil, err - } - receiptSha := DeriveSha(receipts) if bytes.Compare(receiptSha, block.ReceiptSha) != 0 { return nil, fmt.Errorf("Error validating receipt sha. Received %x, got %x", block.ReceiptSha, receiptSha) } + */ // Block validation if err = sm.ValidateBlock(block, parent); err != nil { @@ -374,7 +376,7 @@ func (sm *BlockManager) AccumelateRewards(state *state.State, block, parent *Blo uncleParent := sm.bc.GetBlock(uncle.PrevHash) if uncleParent == nil { - return UncleError("Uncle's parent unknown") + return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4])) } if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 { diff --git a/chain/chain_manager.go b/chain/chain_manager.go index 31f5f75434..5e62e67715 100644 --- a/chain/chain_manager.go +++ b/chain/chain_manager.go @@ -23,6 +23,8 @@ type ChainManager struct { CurrentBlock *Block LastBlockHash []byte + + workingChain *BlockChain } func NewChainManager(ethereum EthManager) *ChainManager { @@ -225,9 +227,18 @@ func (self *ChainManager) CalcTotalDiff(block *Block) (*big.Int, error) { return td, nil } -func (bc *ChainManager) GetBlock(hash []byte) *Block { +func (self *ChainManager) GetBlock(hash []byte) *Block { data, _ := ethutil.Config.Db.Get(hash) if len(data) == 0 { + if self.workingChain != nil { + // Check the temp chain + for e := self.workingChain.Front(); e != nil; e = e.Next() { + if bytes.Compare(e.Value.(*link).block.Hash(), hash) == 0 { + return e.Value.(*link).block + } + } + } + return nil } @@ -310,6 +321,7 @@ func NewChain(blocks Blocks) *BlockChain { } // This function assumes you've done your checking. No checking is done at this stage anymore +/* func (self *ChainManager) InsertChain(chain *BlockChain) { for e := chain.Front(); e != nil; e = e.Next() { link := e.Value.(*link) @@ -318,8 +330,11 @@ func (self *ChainManager) InsertChain(chain *BlockChain) { self.add(link.block) } } +*/ + +func (self *ChainManager) TestChain(chain *BlockChain, imp bool) (td *big.Int, err error) { + self.workingChain = chain -func (self *ChainManager) TestChain(chain *BlockChain) (td *big.Int, err error) { for e := chain.Front(); e != nil; e = e.Next() { var ( l = e.Value.(*link) @@ -348,12 +363,21 @@ func (self *ChainManager) TestChain(chain *BlockChain) (td *big.Int, err error) return } l.td = td + + if imp { + self.SetTotalDifficulty(td) + self.add(block) + } } - if td.Cmp(self.TD) <= 0 { - err = &TDError{td, self.TD} - return + if !imp { + if td.Cmp(self.TD) <= 0 { + err = &TDError{td, self.TD} + return + } } + self.workingChain = nil + return } diff --git a/chain/dagger.go b/chain/dagger.go index 3333e002d1..2cf70e0915 100644 --- a/chain/dagger.go +++ b/chain/dagger.go @@ -47,6 +47,7 @@ func (pow *EasyPow) Search(block *Block, stop <-chan struct{}) []byte { select { case <-stop: powlogger.Infoln("Breaking from mining") + pow.HashRate = 0 return nil default: i++ @@ -55,7 +56,7 @@ func (pow *EasyPow) Search(block *Block, stop <-chan struct{}) []byte { elapsed := time.Now().UnixNano() - start hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000 pow.HashRate = int64(hashes) - powlogger.Infoln("Hashing @", int64(pow.HashRate), "khash") + powlogger.Infoln("Hashing @", pow.HashRate, "khash") t = time.Now() } diff --git a/cmd/mist/assets/miner.png b/cmd/mist/assets/miner.png new file mode 100644 index 0000000000000000000000000000000000000000..58e3f4dfee4a6cf043c9a2b0bfce9b0a5f0f7424 GIT binary patch literal 2100 zcmV-42+Q}0P)>!a@hQcN$T8hLcYBU>r-lPBRS+7U_$_K zT>Q43)Sod3>QazugkDV)_kO~z<|oK@m6lIl!WvK7RFgkXInD8}Xm#Lw0s2hp5Lp~|sk5Dg2``VZK%8r4i29)9&U zCIIHk0wm7r%hG}4Cy^*^hn7tsfDIrlfePCQl^mt+PE1fe35Cu}T_+$8s9f#AY6uI5 zGLhz|44g2D!M$0kUOfYt1ydO92^S2%YAS4h4zX5u5}U`O`n&^$Ulk8y=sXTbpr{FA z1H^`~<5`AJz5)*}P%psL2JkK$%0mOrX}~ETMM_phRU}5?3smwL4W-64Fv<(^P^sa@ zC#M`Kg*c|bfZ+|O7vNFzRwnBVV+UL`7TNJCVu28hDFRaS9t0qjNIQo_q6z?zsHWz1 z5R~K7@vG2a)>bvQzk+%k*`FD!oUrVVR&< zz=UN)jR0i^MGfFW5NLq-@%cz-LA9(P?JYEZXEN%gp0y|75)TOg2QUBz1dNoxQBe;( z4`WLh@p0n-7+d9390-H>#SIZl)jW)KLu!W`2Y_oYq``3jJw_XoQDYFLi2*0Xi2y!H z0ZbN59Kh6(v=)LKCIlM+tGfo`r>Aqr0{J|NB-Fly=8YX>T?aOAE8qdf5~uzGDom&u z>sWK>YUZvP&uuSEQZV<$@mkY0P;QT2g^A#z9(jtPL+xiM0G@%hJ1-&(bhAJpNFcGf zr~t>hdogdzJW9i+Gwr%5%((Sh1=FTY(He6$&QT7=f{A-og9wg=umUgdypY~Xs2Seo zd)Sa&f=s__zAU_ds>m~lU5r$s-|};aJdboO^JMzXcgXq;8EI*4lb%Cs>)*^MN4g`f zG*^2ok!~32Nc8*6Ls!Z5cg`2}Qcu7G==0Bch`fp1asO}RrWp^&WB1-D^667iZJaiW zEJqgIH$k3U_^cdl?-bwnrI61{KA%(QTx+_f9C#6l>h^o{R5de@mr+plbk%6*9Gj)H4yqINhIpX z=5lnjcaksWDHMtTq|+&SrFxM{r%6T}TgmZW+feFL4oOe2#=|kf4XIbu1JtYLE3h2} zHXl7~z=NhCuxQLnOh4m&#I2_oIE3O-^2-F3kRS+X@9gBWoqK3)J)+RL)^trdluAAp zhjM7hm0c#LcCqr@XF30@EO^isd8sF00r~)!Y`YIBt}!C-M0gvK-$&T{vTVJ#QGR#p zB{F&3W%BrMpOyoMTIIFZH^_p8i{!zFA5rLBYr3W!ne^>T<>xnym34QXEhj&GUhL!g zG7@Q5J=9A*V{bfQ@YQ3u{=)~6?q(zM79wxNO{*d-MmF7roHP79+5Gp9q~&m%{Q1qd zq1 zucmKVVWg>PxcuU=r)2-3WAb@-UZHcX>6&sx{t|Csebrbt8PyckRuA=3&z|>#`gmdD zy4%_EL6r1pl-RB}Qdw>!Y(``?((y;+&MO-gD*%Eu6Pf#ZPZ3XTUjF#v28Hh>^B*bv~#pVSd{JK}O24K{tyLqj#KW7gbZD<$_=>b48&finXhr)3|Xg1 zFK*n;PM|eD_R8r7h(pi|=o_C%pLqYyDfkxffgI2k?>Y|z|JUsp@M7Zx;GVh<0Yac0 e2MNCFcKlz4b~d&P^jOOP0000fzY{ literal 0 HcmV?d00001 diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml index cfd227b496..d2a8d1d632 100644 --- a/cmd/mist/assets/qml/main.qml +++ b/cmd/mist/assets/qml/main.qml @@ -12,7 +12,6 @@ import "../ext/http.js" as Http ApplicationWindow { id: root - property alias miningButtonText: miningButton.text property var ethx : Eth.ethx property var browser @@ -47,6 +46,7 @@ ApplicationWindow { Component.onCompleted: { var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true}); var browser = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true}); + var browser = addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true}); root.browser = browser; addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"}); @@ -252,29 +252,18 @@ ApplicationWindow { } statusBar: StatusBar { - height: 32 + //height: 32 id: statusBar - RowLayout { - Button { - id: miningButton - text: "Start Mining" - onClicked: { - gui.toggleMining() - } - } + Label { + //y: 6 + id: walletValueLabel - RowLayout { - Label { - id: walletValueLabel - - font.pixelSize: 10 - styleColor: "#797979" - } - } + font.pixelSize: 10 + styleColor: "#797979" } Label { - y: 6 + //y: 6 objectName: "miningLabel" visible: true font.pixelSize: 10 @@ -283,7 +272,7 @@ ApplicationWindow { } Label { - y: 6 + //y: 6 id: lastBlockLabel objectName: "lastBlockLabel" visible: true @@ -297,14 +286,14 @@ ApplicationWindow { id: downloadIndicator value: 0 objectName: "downloadIndicator" - y: 3 + y: -4 x: statusBar.width / 2 - this.width / 2 width: 160 } Label { objectName: "downloadLabel" - y: 7 + //y: 7 anchors.left: downloadIndicator.right anchors.leftMargin: 5 font.pixelSize: 10 @@ -314,7 +303,7 @@ ApplicationWindow { RowLayout { id: peerGroup - y: 7 + //y: 7 anchors.right: parent.right MouseArea { onDoubleClicked: peerWindow.visible = true @@ -323,14 +312,9 @@ ApplicationWindow { Label { id: peerLabel - font.pixelSize: 8 + font.pixelSize: 10 text: "0 / 0" } - Image { - id: peerImage - width: 10; height: 10 - source: "../network.png" - } } } diff --git a/cmd/mist/assets/qml/views/miner.qml b/cmd/mist/assets/qml/views/miner.qml new file mode 100644 index 0000000000..e162d60a4f --- /dev/null +++ b/cmd/mist/assets/qml/views/miner.qml @@ -0,0 +1,254 @@ +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 + +Rectangle { + id: root + property var title: "Miner" + property var iconSource: "../miner.png" + property var menuItem + + color: "#00000000" + + ColumnLayout { + spacing: 10 + anchors.fill: parent + + Rectangle { + id: mainPane + color: "#00000000" + anchors { + top: parent.top + bottom: localTxPane.top + left: parent.left + right: parent.right + } + + Rectangle { + id: menu + height: 25 + anchors { + left: parent.left + } + + RowLayout { + id: tools + anchors { + left: parent.left + right: parent.right + } + + Button { + text: "Start" + onClicked: { + eth.setGasPrice(minGasPrice.text || "10000000000000"); + if (eth.toggleMining()) { + this.text = "Stop"; + } else { + this.text = "Start"; + } + } + } + + Rectangle { + anchors.top: parent.top + anchors.topMargin: 2 + width: 200 + TextField { + id: minGasPrice + placeholderText: "Min Gas: 10000000000000" + width: 200 + validator: RegExpValidator { regExp: /\d*/ } + } + } + } + } + + Column { + anchors { + left: parent.left + right: parent.right + top: menu.bottom + topMargin: 5 + } + + Text { + text: "Merged mining options" + } + + TableView { + id: mergedMiningTable + height: 300 + anchors { + left: parent.left + right: parent.right + } + Component { + id: checkBoxDelegate + + Item { + id: test + CheckBox { + anchors.fill: parent + checked: styleData.value + + onClicked: { + var model = mergedMiningModel.get(styleData.row) + + if (this.checked) { + model.id = txModel.createLocalTx(model.address, "0", "5000", "0", "") + } else { + txModel.removeWithId(model.id); + model.id = 0; + } + } + } + } + } + TableViewColumn{ role: "checked" ; title: "" ; width: 40 ; delegate: checkBoxDelegate } + TableViewColumn{ role: "name" ; title: "Name" ; width: 480 } + model: ListModel { + objectName: "mergedMiningModel" + id: mergedMiningModel + function addMergedMiningOption(model) { + this.append(model); + } + } + Component.onCompleted: { + /* interface test stuff + // XXX Temp. replace with above eventually + var tmpItems = ["JEVCoin", "Some coin", "Other coin", "Etc coin"]; + var address = "e6716f9544a56c530d868e4bfbacb172315bdead"; + for (var i = 0; i < tmpItems.length; i++) { + mergedMiningModel.append({checked: false, name: tmpItems[i], address: address, id: 0, itemId: i}); + } + */ + } + } + } + } + + Rectangle { + id: localTxPane + color: "#ececec" + border.color: "#cccccc" + border.width: 1 + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 300 + + ColumnLayout { + spacing: 10 + anchors.fill: parent + RowLayout { + id: newLocalTx + anchors { + left: parent.left + leftMargin: 5 + top: parent.top + topMargin: 5 + bottomMargin: 5 + } + + Text { + text: "Local tx" + } + + Rectangle { + width: 250 + color: "#00000000" + anchors.top: parent.top + anchors.topMargin: 2 + + TextField { + id: to + placeholderText: "To" + width: 250 + validator: RegExpValidator { regExp: /[abcdefABCDEF1234567890]*/ } + } + } + TextField { + property var defaultGas: "5000" + id: gas + placeholderText: "Gas" + text: defaultGas + validator: RegExpValidator { regExp: /\d*/ } + } + TextField { + id: gasPrice + placeholderText: "Price" + validator: RegExpValidator { regExp: /\d*/ } + } + TextField { + id: value + placeholderText: "Amount" + text: "0" + validator: RegExpValidator { regExp: /\d*/ } + } + TextField { + id: data + placeholderText: "Data" + validator: RegExpValidator { regExp: /[abcdefABCDEF1234567890]*/ } + } + Button { + text: "Create" + onClicked: { + if (to.text.length == 40 && gasPrice.text.length != 0 && value.text.length != 0 && gas.text.length != 0) { + txModel.createLocalTx(to.text, gasPrice.text, gas.text, value.text, data.text); + + to.text = ""; gasPrice.text = ""; + gas.text = gas.defaultGas; + value.text = "0" + } + } + } + } + + TableView { + id: txTableView + anchors { + top: newLocalTx.bottom + topMargin: 5 + left: parent.left + right: parent.right + bottom: parent.bottom + } + TableViewColumn{ role: "to" ; title: "To" ; width: 480 } + TableViewColumn{ role: "gas" ; title: "Gas" ; width: 100 } + TableViewColumn{ role: "gasPrice" ; title: "Gas Price" ; width: 100 } + TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 } + TableViewColumn{ role: "data" ; title: "Data" ; width: 100 } + + model: ListModel { + id: txModel + Component.onCompleted: { + } + function removeWithId(id) { + for (var i = 0; i < this.count; i++) { + if (txModel.get(i).id == id) { + this.remove(i); + eth.removeLocalTransaction(id) + break; + } + } + } + + function createLocalTx(to, gasPrice, gas, value, data) { + var id = eth.addLocalTransaction(to, data, gas, gasPrice, value) + txModel.insert(0, {to: to, gas: gas, gasPrice: gasPrice, value: value, data: data, id: id}); + + return id + } + } + } + } + } + } +} diff --git a/cmd/mist/bindings.go b/cmd/mist/bindings.go index ebdd8ec734..480c38b2ef 100644 --- a/cmd/mist/bindings.go +++ b/cmd/mist/bindings.go @@ -70,10 +70,6 @@ func (gui *Gui) GetCustomIdentifier() string { return gui.clientIdentity.GetCustomIdentifier() } -func (gui *Gui) ToggleTurboMining() { - gui.miner.ToggleTurbo() -} - // functions that allow Gui to implement interface guilogger.LogSystem func (gui *Gui) SetLogLevel(level logger.LogLevel) { gui.logLevel = level @@ -137,20 +133,3 @@ func (self *Gui) DumpState(hash, path string) { file.Write(stateDump) } -func (gui *Gui) ToggleMining() { - var txt string - if gui.eth.Mining { - utils.StopMining(gui.eth) - txt = "Start mining" - - gui.getObjectByName("miningLabel").Set("visible", false) - } else { - utils.StartMining(gui.eth) - gui.miner = utils.GetMiner() - txt = "Stop mining" - - gui.getObjectByName("miningLabel").Set("visible", true) - } - - gui.win.Root().Set("miningButtonText", txt) -} diff --git a/cmd/mist/gui.go b/cmd/mist/gui.go index 5af1b5c7b1..2950112447 100644 --- a/cmd/mist/gui.go +++ b/cmd/mist/gui.go @@ -272,8 +272,6 @@ type address struct { func (gui *Gui) loadAddressBook() { view := gui.getObjectByName("infoView") - view.Call("clearAddress") - nameReg := gui.pipe.World().Config().Get("NameReg") if nameReg != nil { nameReg.EachStorage(func(name string, value *ethutil.Value) { @@ -286,6 +284,28 @@ func (gui *Gui) loadAddressBook() { } } +func (self *Gui) loadMergedMiningOptions() { + view := self.getObjectByName("mergedMiningModel") + + nameReg := self.pipe.World().Config().Get("MergeMining") + if nameReg != nil { + i := 0 + nameReg.EachStorage(func(name string, value *ethutil.Value) { + if name[0] != 0 { + value.Decode() + + view.Call("addMergedMiningOption", struct { + Checked bool + Name, Address string + Id, ItemId int + }{false, name, ethutil.Bytes2Hex(value.Bytes()), 0, i}) + + i++ + } + }) + } +} + func (gui *Gui) insertTransaction(window string, tx *chain.Transaction) { pipe := xeth.New(gui.eth) nameReg := pipe.World().Config().Get("NameReg") @@ -382,6 +402,7 @@ func (gui *Gui) update() { go func() { go gui.setInitialChainManager() gui.loadAddressBook() + gui.loadMergedMiningOptions() gui.setPeerInfo() gui.readPreviousTransactions() }() @@ -410,7 +431,6 @@ func (gui *Gui) update() { chain.NewBlockEvent{}, chain.TxPreEvent{}, chain.TxPostEvent{}, - miner.Event{}, ) // nameReg := gui.pipe.World().Config().Get("NameReg") @@ -469,12 +489,14 @@ func (gui *Gui) update() { case eth.PeerListEvent: gui.setPeerInfo() - case miner.Event: - if ev.Type == miner.Started { - gui.miner = ev.Miner - } else { - gui.miner = nil - } + /* + case miner.Event: + if ev.Type == miner.Started { + gui.miner = ev.Miner + } else { + gui.miner = nil + } + */ } case <-peerUpdateTicker.C: @@ -483,10 +505,13 @@ func (gui *Gui) update() { statusText := "#" + gui.eth.ChainManager().CurrentBlock.Number.String() lastBlockLabel.Set("text", statusText) - if gui.miner != nil { - pow := gui.miner.GetPow() - miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash") - } + miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash") + /* + if gui.miner != nil { + pow := gui.miner.GetPow() + miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash") + } + */ blockLength := gui.eth.BlockPool().BlocksProcessed chainLength := gui.eth.BlockPool().ChainLength diff --git a/cmd/mist/ui_lib.go b/cmd/mist/ui_lib.go index bb978707d1..bdf5513255 100644 --- a/cmd/mist/ui_lib.go +++ b/cmd/mist/ui_lib.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethutil" "github.com/ethereum/go-ethereum/javascript" + "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/ui/qt" "github.com/ethereum/go-ethereum/xeth" @@ -55,10 +56,15 @@ type UiLib struct { jsEngine *javascript.JSRE filterCallbacks map[int][]int + + miner *miner.Miner } func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib { - return &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)} + 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)} + lib.miner = miner.New(eth.KeyManager().Address(), eth) + + return lib } func (self *UiLib) Notef(args []interface{}) { @@ -328,3 +334,33 @@ func (self *UiLib) Call(params map[string]interface{}) (string, error) { object["data"], ) } + +func (self *UiLib) AddLocalTransaction(to, data, gas, gasPrice, value string) int { + return self.miner.AddLocalTx(&miner.LocalTx{ + To: ethutil.Hex2Bytes(to), + Data: ethutil.Hex2Bytes(data), + Gas: gas, + GasPrice: gasPrice, + Value: value, + }) - 1 +} + +func (self *UiLib) RemoveLocalTransaction(id int) { + self.miner.RemoveLocalTx(id) +} + +func (self *UiLib) SetGasPrice(price string) { + self.miner.MinAcceptedGasPrice = ethutil.Big(price) +} + +func (self *UiLib) ToggleMining() bool { + if !self.miner.Mining() { + self.miner.Start() + + return true + } else { + self.miner.Stop() + + return false + } +} diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index e396554030..96590b20f4 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -266,7 +266,7 @@ func StartMining(ethereum *eth.Ethereum) bool { go func() { clilogger.Infoln("Start mining") if gminer == nil { - gminer = miner.NewDefaultMiner(addr, ethereum) + gminer = miner.New(addr, ethereum) } // Give it some time to connect with peers time.Sleep(3 * time.Second) diff --git a/miner/miner.go b/miner/miner.go index 7491ab373f..2ab74e516a 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -1,220 +1,230 @@ package miner import ( - "bytes" + "math/big" "sort" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/chain" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/wire" ) +type LocalTx struct { + To []byte `json:"to"` + Data []byte `json:"data"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Value string `json:"value"` +} + +func (self *LocalTx) Sign(key []byte) *chain.Transaction { + return nil +} + var minerlogger = logger.NewLogger("MINER") type Miner struct { - pow chain.PoW - ethereum chain.EthManager - coinbase []byte - txs chain.Transactions - uncles []*chain.Block - block *chain.Block + eth *eth.Ethereum + events event.Subscription - events event.Subscription - powQuitChan chan struct{} - powDone chan struct{} + uncles chain.Blocks + localTxs map[int]*LocalTx + localTxId int - turbo bool + pow chain.PoW + quitCh chan struct{} + powQuitCh chan struct{} + + Coinbase []byte + + mining bool + + MinAcceptedGasPrice *big.Int } -const ( - Started = iota - Stopped -) - -type Event struct { - Type int // Started || Stopped - Miner *Miner +func New(coinbase []byte, eth *eth.Ethereum) *Miner { + return &Miner{ + eth: eth, + powQuitCh: make(chan struct{}), + pow: &chain.EasyPow{}, + mining: false, + localTxs: make(map[int]*LocalTx), + MinAcceptedGasPrice: big.NewInt(10000000000000), + Coinbase: coinbase, + } } func (self *Miner) GetPow() chain.PoW { return self.pow } -func NewDefaultMiner(coinbase []byte, ethereum chain.EthManager) *Miner { - miner := Miner{ - pow: &chain.EasyPow{}, - ethereum: ethereum, - coinbase: coinbase, +func (self *Miner) AddLocalTx(tx *LocalTx) int { + minerlogger.Infof("Added local tx (%x %v / %v)\n", tx.To[0:4], tx.GasPrice, tx.Value) + + self.localTxId++ + self.localTxs[self.localTxId] = tx + self.eth.EventMux().Post(tx) + + return self.localTxId +} + +func (self *Miner) RemoveLocalTx(id int) { + if tx := self.localTxs[id]; tx != nil { + minerlogger.Infof("Removed local tx (%x %v / %v)\n", tx.To[0:4], tx.GasPrice, tx.Value) + } + self.eth.EventMux().Post(&LocalTx{}) + + delete(self.localTxs, id) +} + +func (self *Miner) Start() { + if self.mining { + return } - return &miner + minerlogger.Infoln("Starting mining operations") + self.mining = true + self.quitCh = make(chan struct{}) + self.powQuitCh = make(chan struct{}) + + mux := self.eth.EventMux() + self.events = mux.Subscribe(chain.NewBlockEvent{}, chain.TxPreEvent{}, &LocalTx{}) + + go self.update() + go self.mine() } -func (self *Miner) ToggleTurbo() { - self.turbo = !self.turbo +func (self *Miner) Stop() { + if !self.mining { + return + } - self.pow.Turbo(self.turbo) + self.mining = false + + minerlogger.Infoln("Stopping mining operations") + + self.events.Unsubscribe() + + close(self.quitCh) + close(self.powQuitCh) } -func (miner *Miner) Start() { - - // Insert initial TXs in our little miner 'pool' - miner.txs = miner.ethereum.TxPool().Flush() - miner.block = miner.ethereum.ChainManager().NewBlock(miner.coinbase) - - mux := miner.ethereum.EventMux() - miner.events = mux.Subscribe(chain.NewBlockEvent{}, chain.TxPreEvent{}) - - // Prepare inital block - //miner.ethereum.BlockManager().Prepare(miner.block.State(), miner.block.State()) - go miner.listener() - - minerlogger.Infoln("Started") - mux.Post(Event{Started, miner}) +func (self *Miner) Mining() bool { + return self.mining } -func (miner *Miner) Stop() { - minerlogger.Infoln("Stopping...") - miner.events.Unsubscribe() - miner.ethereum.EventMux().Post(Event{Stopped, miner}) -} - -func (miner *Miner) listener() { - miner.startMining() - +func (self *Miner) update() { +out: for { select { - case event := <-miner.events.Chan(): + case event := <-self.events.Chan(): switch event := event.(type) { case chain.NewBlockEvent: - miner.stopMining() - block := event.Block - //minerlogger.Infoln("Got new block via Reactor") - if bytes.Compare(miner.ethereum.ChainManager().CurrentBlock.Hash(), block.Hash()) == 0 { - // TODO: Perhaps continue mining to get some uncle rewards - //minerlogger.Infoln("New top block found resetting state") - - // Filter out which Transactions we have that were not in this block - var newtxs []*chain.Transaction - for _, tx := range miner.txs { - found := false - for _, othertx := range block.Transactions() { - if bytes.Compare(tx.Hash(), othertx.Hash()) == 0 { - found = true - } - } - if found == false { - newtxs = append(newtxs, tx) - } - } - miner.txs = newtxs - } else { - if bytes.Compare(block.PrevHash, miner.ethereum.ChainManager().CurrentBlock.PrevHash) == 0 { - minerlogger.Infoln("Adding uncle block") - miner.uncles = append(miner.uncles, block) - } - } - miner.startMining() - - case chain.TxPreEvent: - miner.stopMining() - - found := false - for _, ctx := range miner.txs { - if found = bytes.Compare(ctx.Hash(), event.Tx.Hash()) == 0; found { - break - } - - miner.startMining() - } - if found == false { - // Undo all previous commits - miner.block.Undo() - // Apply new transactions - miner.txs = append(miner.txs, event.Tx) + if self.eth.ChainManager().HasBlock(block.Hash()) { + self.reset() + self.eth.TxPool().RemoveSet(block.Transactions()) + go self.mine() + } else if true { + // do uncle stuff } + case chain.TxPreEvent, *LocalTx: + self.reset() + go self.mine() } - - case <-miner.powDone: - miner.startMining() + case <-self.quitCh: + break out } } } -func (miner *Miner) startMining() { - if miner.powDone == nil { - miner.powDone = make(chan struct{}) - } - miner.powQuitChan = make(chan struct{}) - go miner.mineNewBlock() +func (self *Miner) reset() { + println("reset") + close(self.powQuitCh) + self.powQuitCh = make(chan struct{}) } -func (miner *Miner) stopMining() { - println("stop mining") - _, isopen := <-miner.powQuitChan - if isopen { - close(miner.powQuitChan) - } - //<-miner.powDone -} - -func (self *Miner) mineNewBlock() { - blockManager := self.ethereum.BlockManager() - chainMan := self.ethereum.ChainManager() - - self.block = chainMan.NewBlock(self.coinbase) +func (self *Miner) mine() { + var ( + blockManager = self.eth.BlockManager() + chainMan = self.eth.ChainManager() + block = chainMan.NewBlock(self.Coinbase) + ) + block.MinGasPrice = self.MinAcceptedGasPrice // Apply uncles if len(self.uncles) > 0 { - self.block.SetUncles(self.uncles) + block.SetUncles(self.uncles) } - // Sort the transactions by nonce in case of odd network propagation - sort.Sort(chain.TxByNonce{self.txs}) + parent := chainMan.GetBlock(block.PrevHash) + coinbase := block.State().GetOrNewStateObject(block.Coinbase) + coinbase.SetGasPool(block.CalcGasLimit(parent)) + + transactions := self.finiliseTxs() // Accumulate all valid transactions and apply them to the new state // Error may be ignored. It's not important during mining - parent := self.ethereum.ChainManager().GetBlock(self.block.PrevHash) - coinbase := self.block.State().GetOrNewStateObject(self.block.Coinbase) - coinbase.SetGasPool(self.block.CalcGasLimit(parent)) - receipts, txs, unhandledTxs, erroneous, err := blockManager.ProcessTransactions(coinbase, self.block.State(), self.block, self.block, self.txs) + receipts, txs, _, erroneous, err := blockManager.ProcessTransactions(coinbase, block.State(), block, block, transactions) if err != nil { minerlogger.Debugln(err) } - self.ethereum.TxPool().RemoveSet(erroneous) - self.txs = append(txs, unhandledTxs...) + self.eth.TxPool().RemoveSet(erroneous) - self.block.SetTransactions(txs) - self.block.SetReceipts(receipts) + block.SetTransactions(txs) + block.SetReceipts(receipts) // Accumulate the rewards included for this block - blockManager.AccumelateRewards(self.block.State(), self.block, parent) + blockManager.AccumelateRewards(block.State(), block, parent) - self.block.State().Update() + block.State().Update() - minerlogger.Infof("Mining on block. Includes %v transactions", len(self.txs)) + minerlogger.Infof("Mining on block. Includes %v transactions", len(transactions)) // Find a valid nonce - nonce := self.pow.Search(self.block, self.powQuitChan) + nonce := self.pow.Search(block, self.powQuitCh) if nonce != nil { - self.block.Nonce = nonce - lchain := chain.NewChain(chain.Blocks{self.block}) - _, err := chainMan.TestChain(lchain) + block.Nonce = nonce + lchain := chain.NewChain(chain.Blocks{block}) + _, err := chainMan.TestChain(lchain, true) if err != nil { minerlogger.Infoln(err) } else { - self.ethereum.ChainManager().InsertChain(lchain) - self.ethereum.Broadcast(wire.MsgBlockTy, []interface{}{self.block.Value().Val}) - minerlogger.Infof("🔨 Mined block %x\n", self.block.Hash()) - minerlogger.Infoln(self.block) - // Gather the new batch of transactions currently in the tx pool - self.txs = self.ethereum.TxPool().CurrentTransactions() - self.ethereum.EventMux().Post(chain.NewBlockEvent{self.block}) + //chainMan.InsertChain(lchain) + self.eth.EventMux().Post(chain.NewBlockEvent{block}) + self.eth.Broadcast(wire.MsgBlockTy, []interface{}{block.Value().Val}) + + minerlogger.Infof("🔨 Mined block %x\n", block.Hash()) + minerlogger.Infoln(block) } - // Continue mining on the next block - self.startMining() + go self.mine() } } + +func (self *Miner) finiliseTxs() chain.Transactions { + // Sort the transactions by nonce in case of odd network propagation + var txs chain.Transactions + + state := self.eth.BlockManager().TransState() + // XXX This has to change. Coinbase is, for new, same as key. + key := self.eth.KeyManager() + for _, ltx := range self.localTxs { + tx := chain.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data) + tx.Nonce = state.GetNonce(self.Coinbase) + state.SetNonce(self.Coinbase, tx.Nonce+1) + + tx.Sign(key.PrivateKey()) + + txs = append(txs, tx) + } + + txs = append(txs, self.eth.TxPool().CurrentTransactions()...) + sort.Sort(chain.TxByNonce{txs}) + + return txs +} diff --git a/p2p/connection.go b/p2p/connection.go index e999cbe55f..be366235d4 100644 --- a/p2p/connection.go +++ b/p2p/connection.go @@ -6,7 +6,7 @@ import ( "net" "time" - "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/ethutil" ) type Connection struct { diff --git a/p2p/message.go b/p2p/message.go index 4886eaa1f2..446e74dff7 100644 --- a/p2p/message.go +++ b/p2p/message.go @@ -2,7 +2,7 @@ package p2p import ( // "fmt" - "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/go-ethereum/ethutil" ) type MsgCode uint8 diff --git a/p2p/messenger_test.go b/p2p/messenger_test.go index bc21d34ba6..472d74515e 100644 --- a/p2p/messenger_test.go +++ b/p2p/messenger_test.go @@ -3,9 +3,10 @@ package p2p import ( // "fmt" "bytes" - "github.com/ethereum/eth-go/ethutil" "testing" "time" + + "github.com/ethereum/go-ethereum/ethutil" ) func setupMessenger(handlers Handlers) (*TestNetworkConnection, chan *PeerError, *Messenger) { diff --git a/peer.go b/peer.go index f5afb45953..e0b2f73550 100644 --- a/peer.go +++ b/peer.go @@ -24,7 +24,7 @@ const ( // The size of the output buffer for writing messages outputBufferSize = 50 // Current protocol version - ProtocolVersion = 37 + ProtocolVersion = 39 // Current P2P version P2PVersion = 2 // Ethereum network version @@ -863,7 +863,7 @@ func (p *Peer) String() string { strConnectType = "disconnected" } - return fmt.Sprintf("[%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.version, p.caps) + return fmt.Sprintf("[%s] (%s) %v %s", strConnectType, strBoundType, p.conn.RemoteAddr(), p.version) } diff --git a/xeth/config.go b/xeth/config.go index 34aa9e32d9..ad0660d759 100644 --- a/xeth/config.go +++ b/xeth/config.go @@ -19,6 +19,8 @@ func (self *Config) Get(name string) *Object { objectAddr := configCtrl.GetStorage(ethutil.BigD([]byte{0})) domainAddr := (&Object{self.pipe.World().safeGet(objectAddr.Bytes())}).StorageString("DnsReg").Bytes() return &Object{self.pipe.World().safeGet(domainAddr)} + case "MergeMining": + addr = []byte{4} default: addr = ethutil.RightPadBytes([]byte(name), 32) } diff --git a/xeth/hexface.go b/xeth/hexface.go index 21e82e37d9..5ef3eaf1a3 100644 --- a/xeth/hexface.go +++ b/xeth/hexface.go @@ -254,6 +254,10 @@ func (self *JSXEth) CompileMutan(code string) string { return ethutil.Bytes2Hex(data) } +func (self *JSXEth) FindInConfig(str string) string { + return ethutil.Bytes2Hex(self.World().Config().Get(str).Address()) +} + func ToJSMessages(messages state.Messages) *ethutil.List { var msgs []JSMessage for _, m := range messages {