aboutsummaryrefslogtreecommitdiffstats
path: root/ethminer/miner.go
blob: 8ea6c51e5394f78f4c7b877433fa42e0cea92cd1 (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
package ethminer

import (
    "bytes"
    "github.com/ethereum/eth-go/ethchain"
    "github.com/ethereum/eth-go/ethutil"
    "github.com/ethereum/eth-go/ethwire"
    "sort"
)

type Miner struct {
    pow         ethchain.PoW
    ethereum    ethchain.EthManager
    coinbase    []byte
    reactChan   chan ethutil.React
    txs         ethchain.Transactions
    uncles      []*ethchain.Block
    block       *ethchain.Block
    powChan     chan []byte
    powQuitChan chan ethutil.React
    quitChan    chan bool
}

func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) Miner {
    reactChan := make(chan ethutil.React, 1)   // This is the channel that receives 'updates' when ever a new transaction or block comes in
    powChan := make(chan []byte, 1)            // This is the channel that receives valid sha hases for a given block
    powQuitChan := make(chan ethutil.React, 1) // This is the channel that can exit the miner thread
    quitChan := make(chan bool, 1)

    ethereum.Reactor().Subscribe("newBlock", reactChan)
    ethereum.Reactor().Subscribe("newTx:pre", 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
    ethereum.Reactor().Subscribe("newBlock", powQuitChan)
    ethereum.Reactor().Subscribe("newTx:pre", powQuitChan)

    miner := Miner{
        pow:         &ethchain.EasyPow{},
        ethereum:    ethereum,
        coinbase:    coinbase,
        reactChan:   reactChan,
        powChan:     powChan,
        powQuitChan: powQuitChan,
        quitChan:    quitChan,
    }

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

    return miner
}
func (miner *Miner) Start() {
    // Prepare inital block
    //miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
    go miner.listener()
}
func (miner *Miner) listener() {
out:
    for {
        select {
        case <-miner.quitChan:
            break out
        case chanMessage := <-miner.reactChan:
            if block, ok := chanMessage.Resource.(*ethchain.Block); ok {
                //ethutil.Config.Log.Infoln("[MINER] 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
                    //ethutil.Config.Log.Infoln("[MINER] 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 {
                        ethutil.Config.Log.Infoln("[MINER] 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 (self *Miner) Stop() {
    self.powQuitChan <- ethutil.React{}
    self.quitChan <- true
}

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 transaction and apply them to the new state
    // Error may be ignored. It's not important during mining
    coinbase := self.block.State().GetOrNewStateObject(self.block.Coinbase)
    receipts, txs, unhandledTxs, err := stateManager.ProcessTransactions(coinbase, self.block.State(), self.block, self.block, self.txs)
    if err != nil {
        ethutil.Config.Log.Debugln("[MINER]", err)
    }
    self.txs = append(txs, unhandledTxs...)

    // 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)

    ethutil.Config.Log.Infoln("[MINER] Mining on block. Includes", len(self.txs), "transactions")

    // 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 {
            ethutil.Config.Log.Infoln(err)
        } else {
            self.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{self.block.Value().Val})
            ethutil.Config.Log.Infof("[MINER] 🔨  Mined block %x\n", self.block.Hash())
            ethutil.Config.Log.Infoln(self.block)
            // Gather the new batch of transactions currently in the tx pool
            self.txs = self.ethereum.TxPool().CurrentTransactions()
        }
    }
}