miner.go 5.94 KB
Newer Older
Maran's avatar
Maran committed
1 2 3 4 5
package ethminer

import (
	"bytes"
	"github.com/ethereum/eth-go/ethchain"
zelig's avatar
zelig committed
6
	"github.com/ethereum/eth-go/ethlog"
7
	"github.com/ethereum/eth-go/ethreact"
Maran's avatar
Maran committed
8
	"github.com/ethereum/eth-go/ethwire"
9
	"sort"
Maran's avatar
Maran committed
10 11
)

zelig's avatar
zelig committed
12 13
var logger = ethlog.NewLogger("MINER")

Maran's avatar
Maran committed
14
type Miner struct {
15 16 17
	pow         ethchain.PoW
	ethereum    ethchain.EthManager
	coinbase    []byte
18
	reactChan   chan ethreact.Event
19
	txs         ethchain.Transactions
20 21 22
	uncles      []*ethchain.Block
	block       *ethchain.Block
	powChan     chan []byte
23
	powQuitChan chan ethreact.Event
zelig's avatar
zelig committed
24
	quitChan    chan chan error
Maran's avatar
Maran committed
25 26
}

27 28
func (self *Miner) GetPow() ethchain.PoW {
	return self.pow
Maran's avatar
Maran committed
29 30
}

31
func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) *Miner {
Maran's avatar
Maran committed
32
	miner := Miner{
33 34 35
		pow:      &ethchain.EasyPow{},
		ethereum: ethereum,
		coinbase: coinbase,
Maran's avatar
Maran committed
36 37
	}

38
	return &miner
Maran's avatar
Maran committed
39
}
zelig's avatar
zelig committed
40

Maran's avatar
Maran committed
41
func (miner *Miner) Start() {
42 43 44
	miner.reactChan = make(chan ethreact.Event, 1)   // This is the channel that receives 'updates' when ever a new transaction or block comes in
	miner.powChan = make(chan []byte, 1)             // This is the channel that receives valid sha hashes for a given block
	miner.powQuitChan = make(chan ethreact.Event, 1) // This is the channel that can exit the miner thread
zelig's avatar
zelig committed
45 46 47 48 49
	miner.quitChan = make(chan chan error, 1)

	// Insert initial TXs in our little miner 'pool'
	miner.txs = miner.ethereum.TxPool().Flush()
	miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase)
50

Maran's avatar
Maran committed
51
	// Prepare inital block
52 53
	//miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
	go miner.listener()
54 55 56 57 58 59 60 61 62 63 64 65 66

	reactor := miner.ethereum.Reactor()
	reactor.Subscribe("newBlock", miner.reactChan)
	reactor.Subscribe("newTx:pre", miner.reactChan)

	// We need the quit chan to be a Reactor event.
	// The POW search method is actually blocking and if we don't
	// listen to the reactor events inside of the pow itself
	// The miner overseer will never get the reactor events themselves
	// Only after the miner will find the sha
	reactor.Subscribe("newBlock", miner.powQuitChan)
	reactor.Subscribe("newTx:pre", miner.powQuitChan)

zelig's avatar
zelig committed
67
	logger.Infoln("Started")
68

zelig's avatar
zelig committed
69
	reactor.Post("miner:start", miner)
Maran's avatar
Maran committed
70
}
zelig's avatar
zelig committed
71

Maran's avatar
Maran committed
72 73 74
func (miner *Miner) listener() {
	for {
		select {
zelig's avatar
zelig committed
75
		case status := <-miner.quitChan:
zelig's avatar
zelig committed
76
			logger.Infoln("Stopped")
zelig's avatar
zelig committed
77 78
			status <- nil
			return
Maran's avatar
Maran committed
79
		case chanMessage := <-miner.reactChan:
zelig's avatar
zelig committed
80

Maran's avatar
Maran committed
81
			if block, ok := chanMessage.Resource.(*ethchain.Block); ok {
zelig's avatar
zelig committed
82
				//logger.Infoln("Got new block via Reactor")
Maran's avatar
Maran committed
83 84
				if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 {
					// TODO: Perhaps continue mining to get some uncle rewards
zelig's avatar
zelig committed
85
					//logger.Infoln("New top block found resetting state")
Maran's avatar
Maran committed
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

					// Filter out which Transactions we have that were not in this block
					var newtxs []*ethchain.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

					// Setup a fresh state to mine on
obscuren's avatar
obscuren committed
103
					//miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
Maran's avatar
Maran committed
104 105 106

				} else {
					if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 {
zelig's avatar
zelig committed
107
						logger.Infoln("Adding uncle block")
Maran's avatar
Maran committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121
						miner.uncles = append(miner.uncles, block)
					}
				}
			}

			if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok {
				found := false
				for _, ctx := range miner.txs {
					if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found {
						break
					}

				}
				if found == false {
122
					// Undo all previous commits
obscuren's avatar
obscuren committed
123
					miner.block.Undo()
124
					// Apply new transactions
Maran's avatar
Maran committed
125 126 127 128
					miner.txs = append(miner.txs, tx)
				}
			}
		default:
129 130 131 132
			miner.mineNewBlock()
		}
	}
}
133

134
func (miner *Miner) Stop() {
zelig's avatar
zelig committed
135
	logger.Infoln("Stopping...")
136 137 138

	miner.powQuitChan <- ethreact.Event{}

zelig's avatar
zelig committed
139 140 141
	status := make(chan error)
	miner.quitChan <- status
	<-status
142 143 144 145 146 147

	reactor := miner.ethereum.Reactor()
	reactor.Unsubscribe("newBlock", miner.powQuitChan)
	reactor.Unsubscribe("newTx:pre", miner.powQuitChan)
	reactor.Unsubscribe("newBlock", miner.reactChan)
	reactor.Unsubscribe("newTx:pre", miner.reactChan)
obscuren's avatar
obscuren committed
148

zelig's avatar
zelig committed
149
	reactor.Post("miner:stop", miner)
150 151
}

152 153
func (self *Miner) mineNewBlock() {
	stateManager := self.ethereum.StateManager()
154

obscuren's avatar
obscuren committed
155
	self.block = self.ethereum.BlockChain().NewBlock(self.coinbase)
Maran's avatar
Maran committed
156

157 158 159 160
	// Apply uncles
	if len(self.uncles) > 0 {
		self.block.SetUncles(self.uncles)
	}
Maran's avatar
Maran committed
161

162 163
	// Sort the transactions by nonce in case of odd network propagation
	sort.Sort(ethchain.TxByNonce{self.txs})
obscuren's avatar
obscuren committed
164

zelig's avatar
zelig committed
165
	// Accumulate all valid transactions and apply them to the new state
obscuren's avatar
obscuren committed
166
	// Error may be ignored. It's not important during mining
167
	parent := self.ethereum.BlockChain().GetBlock(self.block.PrevHash)
168
	coinbase := self.block.State().GetOrNewStateObject(self.block.Coinbase)
169
	coinbase.SetGasPool(self.block.CalcGasLimit(parent))
170
	receipts, txs, unhandledTxs, err := stateManager.ProcessTransactions(coinbase, self.block.State(), self.block, self.block, self.txs)
obscuren's avatar
obscuren committed
171
	if err != nil {
zelig's avatar
zelig committed
172
		logger.Debugln(err)
obscuren's avatar
obscuren committed
173 174
	}
	self.txs = append(txs, unhandledTxs...)
obscuren's avatar
obscuren committed
175
	self.block.SetTxHash(receipts)
obscuren's avatar
obscuren committed
176

177
	// Set the transactions to the block so the new SHA3 can be calculated
obscuren's avatar
obscuren committed
178
	self.block.SetReceipts(receipts, txs)
obscuren's avatar
obscuren committed
179

180 181 182
	// Accumulate the rewards included for this block
	stateManager.AccumelateRewards(self.block.State(), self.block)

183 184
	self.block.State().Update()

zelig's avatar
zelig committed
185
	logger.Infof("Mining on block. Includes %v transactions", len(self.txs))
186 187

	// Find a valid nonce
188
	self.block.Nonce = self.pow.Search(self.block, self.powQuitChan)
189
	if self.block.Nonce != nil {
190
		err := self.ethereum.StateManager().Process(self.block, false)
191
		if err != nil {
zelig's avatar
zelig committed
192
			logger.Infoln(err)
193 194
		} else {
			self.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{self.block.Value().Val})
zelig's avatar
zelig committed
195 196
			logger.Infof("🔨  Mined block %x\n", self.block.Hash())
			logger.Infoln(self.block)
197 198
			// Gather the new batch of transactions currently in the tx pool
			self.txs = self.ethereum.TxPool().CurrentTransactions()
Maran's avatar
Maran committed
199 200 201
		}
	}
}