218 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			218 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|   | package miner | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"bytes" | ||
|  | 	"sort" | ||
|  | 
 | ||
|  | 	"github.com/ethereum/go-ethereum/chain" | ||
|  | 	"github.com/ethereum/go-ethereum/event" | ||
|  | 	"github.com/ethereum/go-ethereum/logger" | ||
|  | 	"github.com/ethereum/go-ethereum/wire" | ||
|  | ) | ||
|  | 
 | ||
|  | 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 | ||
|  | 
 | ||
|  | 	events      event.Subscription | ||
|  | 	powQuitChan chan struct{} | ||
|  | 	powDone     chan struct{} | ||
|  | 
 | ||
|  | 	turbo bool | ||
|  | } | ||
|  | 
 | ||
|  | const ( | ||
|  | 	Started = iota | ||
|  | 	Stopped | ||
|  | ) | ||
|  | 
 | ||
|  | type Event struct { | ||
|  | 	Type  int // Started || Stopped | ||
|  | 	Miner *Miner | ||
|  | } | ||
|  | 
 | ||
|  | 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, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return &miner | ||
|  | } | ||
|  | 
 | ||
|  | func (self *Miner) ToggleTurbo() { | ||
|  | 	self.turbo = !self.turbo | ||
|  | 
 | ||
|  | 	self.pow.Turbo(self.turbo) | ||
|  | } | ||
|  | 
 | ||
|  | 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.StateManager().Prepare(miner.block.State(), miner.block.State()) | ||
|  | 	go miner.listener() | ||
|  | 
 | ||
|  | 	minerlogger.Infoln("Started") | ||
|  | 	mux.Post(Event{Started, miner}) | ||
|  | } | ||
|  | 
 | ||
|  | func (miner *Miner) Stop() { | ||
|  | 	minerlogger.Infoln("Stopping...") | ||
|  | 	miner.events.Unsubscribe() | ||
|  | 	miner.ethereum.EventMux().Post(Event{Stopped, miner}) | ||
|  | } | ||
|  | 
 | ||
|  | func (miner *Miner) listener() { | ||
|  | 	miner.startMining() | ||
|  | 
 | ||
|  | 	for { | ||
|  | 		select { | ||
|  | 		case event := <-miner.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) | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 		case <-miner.powDone: | ||
|  | 			miner.startMining() | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (miner *Miner) startMining() { | ||
|  | 	if miner.powDone == nil { | ||
|  | 		miner.powDone = make(chan struct{}) | ||
|  | 	} | ||
|  | 	miner.powQuitChan = make(chan struct{}) | ||
|  | 	go miner.mineNewBlock() | ||
|  | } | ||
|  | 
 | ||
|  | func (miner *Miner) stopMining() { | ||
|  | 	println("stop mining") | ||
|  | 	_, isopen := <-miner.powQuitChan | ||
|  | 	if isopen { | ||
|  | 		close(miner.powQuitChan) | ||
|  | 	} | ||
|  | 	//<-miner.powDone | ||
|  | } | ||
|  | 
 | ||
|  | func (self *Miner) mineNewBlock() { | ||
|  | 	stateManager := self.ethereum.StateManager() | ||
|  | 
 | ||
|  | 	self.block = self.ethereum.ChainManager().NewBlock(self.coinbase) | ||
|  | 
 | ||
|  | 	// Apply uncles | ||
|  | 	if len(self.uncles) > 0 { | ||
|  | 		self.block.SetUncles(self.uncles) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Sort the transactions by nonce in case of odd network propagation | ||
|  | 	sort.Sort(chain.TxByNonce{self.txs}) | ||
|  | 
 | ||
|  | 	// 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 := stateManager.ProcessTransactions(coinbase, self.block.State(), self.block, self.block, self.txs) | ||
|  | 	if err != nil { | ||
|  | 		minerlogger.Debugln(err) | ||
|  | 	} | ||
|  | 	self.ethereum.TxPool().RemoveSet(erroneous) | ||
|  | 	self.txs = append(txs, unhandledTxs...) | ||
|  | 
 | ||
|  | 	self.block.SetTransactions(txs) | ||
|  | 	self.block.SetReceipts(receipts) | ||
|  | 
 | ||
|  | 	// Accumulate the rewards included for this block | ||
|  | 	stateManager.AccumelateRewards(self.block.State(), self.block, parent) | ||
|  | 
 | ||
|  | 	self.block.State().Update() | ||
|  | 
 | ||
|  | 	minerlogger.Infof("Mining on block. Includes %v transactions", len(self.txs)) | ||
|  | 
 | ||
|  | 	// Find a valid nonce | ||
|  | 	nonce := self.pow.Search(self.block, self.powQuitChan) | ||
|  | 	if nonce != nil { | ||
|  | 		self.block.Nonce = nonce | ||
|  | 		err := self.ethereum.StateManager().Process(self.block) | ||
|  | 		if err != nil { | ||
|  | 			minerlogger.Infoln(err) | ||
|  | 		} else { | ||
|  | 			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}) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Continue mining on the next block | ||
|  | 		self.startMining() | ||
|  | 	} | ||
|  | } |