diff options
Diffstat (limited to 'miner/miner.go')
-rw-r--r-- | miner/miner.go | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/miner/miner.go b/miner/miner.go new file mode 100644 index 000000000..f63096b63 --- /dev/null +++ b/miner/miner.go @@ -0,0 +1,261 @@ +/* + This file is part of go-ethereum + + go-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + go-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @authors + * Jeffrey Wilcke <i@jev.io> + * @date 2014 + * + */ + +package miner + +import ( + "math/big" + "sort" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/pow" + "github.com/ethereum/go-ethereum/pow/ezp" + + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "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) *types.Transaction { + return nil +} + +var minerlogger = logger.NewLogger("MINER") + +type Miner struct { + eth *eth.Ethereum + events event.Subscription + + uncles types.Blocks + localTxs map[int]*LocalTx + localTxId int + + pow pow.PoW + quitCh chan struct{} + powQuitCh chan struct{} + + Coinbase []byte + + mining bool + + MinAcceptedGasPrice *big.Int +} + +func New(coinbase []byte, eth *eth.Ethereum) *Miner { + return &Miner{ + eth: eth, + powQuitCh: make(chan struct{}), + pow: ezp.New(), + mining: false, + localTxs: make(map[int]*LocalTx), + MinAcceptedGasPrice: big.NewInt(10000000000000), + Coinbase: coinbase, + } +} + +func (self *Miner) GetPow() pow.PoW { + return self.pow +} + +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 + } + + 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(core.NewBlockEvent{}, core.TxPreEvent{}, &LocalTx{}) + + go self.update() + go self.mine() +} + +func (self *Miner) Stop() { + if !self.mining { + return + } + + self.mining = false + + minerlogger.Infoln("Stopping mining operations") + + self.events.Unsubscribe() + + close(self.quitCh) + close(self.powQuitCh) +} + +func (self *Miner) Mining() bool { + return self.mining +} + +func (self *Miner) update() { +out: + for { + select { + case event := <-self.events.Chan(): + switch event := event.(type) { + case core.NewBlockEvent: + block := event.Block + 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 core.TxPreEvent, *LocalTx: + self.reset() + go self.mine() + } + case <-self.quitCh: + break out + } + } +} + +func (self *Miner) reset() { + close(self.powQuitCh) + self.powQuitCh = make(chan struct{}) +} + +func (self *Miner) mine() { + var ( + blockManager = self.eth.BlockManager() + chainMan = self.eth.ChainManager() + block = chainMan.NewBlock(self.Coinbase) + ) + + // Apply uncles + if len(self.uncles) > 0 { + block.SetUncles(self.uncles) + } + + 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 + receipts, txs, _, erroneous, err := blockManager.ApplyTransactions(coinbase, block.State(), block, transactions, true) + if err != nil { + minerlogger.Debugln(err) + } + self.eth.TxPool().RemoveSet(erroneous) + + block.SetTransactions(txs) + block.SetReceipts(receipts) + + // Accumulate the rewards included for this block + blockManager.AccumelateRewards(block.State(), block, parent) + + block.State().Update(ethutil.Big0) + + minerlogger.Infof("Mining on block. Includes %v transactions", len(transactions)) + + // Find a valid nonce + nonce := self.pow.Search(block, self.powQuitCh) + if nonce != nil { + block.Nonce = nonce + err := chainMan.InsertChain(types.Blocks{block}) + if err != nil { + minerlogger.Infoln(err) + } else { + self.eth.Broadcast(wire.MsgBlockTy, []interface{}{block.Value().Val}) + + minerlogger.Infof("🔨 Mined block %x\n", block.Hash()) + minerlogger.Infoln(block) + } + + go self.mine() + } +} + +func (self *Miner) finiliseTxs() types.Transactions { + // Sort the transactions by nonce in case of odd network propagation + actualSize := len(self.localTxs) // See copy below + txs := make(types.Transactions, actualSize+self.eth.TxPool().Size()) + + state := self.eth.ChainManager().TransState() + // XXX This has to change. Coinbase is, for new, same as key. + key := self.eth.KeyManager() + for i, ltx := range self.localTxs { + tx := types.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data) + tx.SetNonce(state.GetNonce(self.Coinbase)) + state.SetNonce(self.Coinbase, tx.Nonce()+1) + + tx.Sign(key.PrivateKey()) + + txs[i] = tx + } + + // Faster than append + for _, tx := range self.eth.TxPool().CurrentTransactions() { + if tx.GasPrice().Cmp(self.MinAcceptedGasPrice) >= 0 { + txs[actualSize] = tx + actualSize++ + } + } + + newTransactions := make(types.Transactions, actualSize) + copy(newTransactions, txs[:actualSize]) + sort.Sort(types.TxByNonce{newTransactions}) + + return newTransactions +} |