aboutsummaryrefslogtreecommitdiffstats
path: root/ethminer/miner.go
blob: 299a5204a59f46fd4eb49d318090524bea976d33 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
package ethminer

import (
    "bytes"
    "sort"

    "github.com/ethereum/eth-go/ethchain"
    "github.com/ethereum/eth-go/ethlog"
    "github.com/ethereum/eth-go/ethreact"
    "github.com/ethereum/eth-go/ethwire"
)

var logger = ethlog.NewLogger("MINER")

type Miner struct {
    pow         ethchain.PoW
    ethereum    ethchain.EthManager
    coinbase    []byte
    reactChan   chan ethreact.Event
    txs         ethchain.Transactions
    uncles      []*ethchain.Block
    block       *ethchain.Block
    powChan     chan []byte
    powQuitChan chan ethreact.Event
    quitChan    chan chan error

    turbo bool
}

func (self *Miner) GetPow() ethchain.PoW {
    return self.pow
}

func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) *Miner {
    miner := Miner{
        pow:      &ethchain.EasyPow{},
        ethereum: ethereum,
        coinbase: coinbase,
    }

    return &miner
}

func (self *Miner) ToggleTurbo() {
    self.turbo = !self.turbo

    self.pow.Turbo(self.turbo)
}

func (miner *Miner) Start() {
    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
    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)

    // Prepare inital block
    //miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
    go miner.listener()

    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)

    logger.Infoln("Started")

    reactor.Post("miner:start", miner)
}

func (miner *Miner) listener() {
    for {
        select {
        case status := <-miner.quitChan:
            logger.Infoln("Stopped")
            status <- nil
            return
        case chanMessage := <-miner.reactChan:

            if block, ok := chanMessage.Resource.(*ethchain.Block); ok {
                //logger.Infoln("Got new block via Reactor")
                if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 {
                    // TODO: Perhaps continue mining to get some uncle rewards
                    //logger.Infoln("New top block found resetting state")

                    // 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
                    //miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)

                } else {
                    if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 {
                        logger.Infoln("Adding uncle block")
                        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 {
                    // Undo all previous commits
                    miner.block.Undo()
                    // Apply new transactions
                    miner.txs = append(miner.txs, tx)
                }
            }
        default:
            miner.mineNewBlock()
        }
    }
}

func (miner *Miner) Stop() {
    logger.Infoln("Stopping...")

    miner.powQuitChan <- ethreact.Event{}

    status := make(chan error)
    miner.quitChan <- status
    <-status

    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)

    reactor.Post("miner:stop", miner)
}

func (self *Miner) mineNewBlock() {

    stateManager := self.ethereum.StateManager()

    self.block = self.ethereum.BlockChain().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(ethchain.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.BlockChain().GetBlock(self.block.PrevHash)
    coinbase := self.block.State().GetOrNewStateObject(self.block.Coinbase)
    coinbase.SetGasPool(self.block.CalcGasLimit(parent))
    receipts, txs, unhandledTxs, err := stateManager.ProcessTransactions(coinbase, self.block.State(), self.block, self.block, self.txs)
    if err != nil {
        logger.Debugln(err)
    }
    self.txs = append(txs, unhandledTxs...)
    self.block.SetTxHash(receipts)

    // Set the transactions to the block so the new SHA3 can be calculated
    self.block.SetReceipts(receipts, txs)

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

    self.block.State().Update()

    logger.Infof("Mining on block. Includes %v transactions", len(self.txs))

    // Find a valid nonce
    self.block.Nonce = self.pow.Search(self.block, self.powQuitChan)
    if self.block.Nonce != nil {
        err := self.ethereum.StateManager().Process(self.block, false)
        if err != nil {
            logger.Infoln(err)
        } else {
            self.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{self.block.Value().Val})
            logger.Infof("🔨  Mined block %x\n", self.block.Hash())
            logger.Infoln(self.block)
            // Gather the new batch of transactions currently in the tx pool
            self.txs = self.ethereum.TxPool().CurrentTransactions()
        }
    }
}