aboutsummaryrefslogtreecommitdiffstats
path: root/ethminer/miner.go
blob: 24af7fbcb7011173f10fa7deff71c567824d82e0 (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
212
213
214
215
216
217
218
219
package ethminer

import (
    "bytes"
    "sort"

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

var logger = ethlog.NewLogger("MINER")

type Miner struct {
    pow      ethchain.PoW
    ethereum ethchain.EthManager
    coinbase []byte
    txs      ethchain.Transactions
    uncles   []*ethchain.Block
    block    *ethchain.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() 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() {

    // 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(ethchain.NewBlockEvent{}, ethchain.TxEvent{})

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

    logger.Infoln("Started")
    mux.Post(Event{Started, miner})
}

func (miner *Miner) Stop() {
    logger.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 ethchain.NewBlockEvent:
                miner.stopMining()

                block := event.Block
                //logger.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
                    //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
                } else {
                    if bytes.Compare(block.PrevHash, miner.ethereum.ChainManager().CurrentBlock.PrevHash) == 0 {
                        logger.Infoln("Adding uncle block")
                        miner.uncles = append(miner.uncles, block)
                    }
                }
                miner.startMining()

            case ethchain.TxEvent:
                if event.Type == ethchain.TxPre {
                    miner.stopMining()

                    found := false
                    for _, ctx := range miner.txs {
                        if found = bytes.Compare(ctx.Hash(), event.Tx.Hash()) == 0; found {
                            break
                        }
                    }
                    if found == false {
                        // Undo all previous commits
                        miner.block.Undo()
                        // Apply new transactions
                        miner.txs = append(miner.txs, event.Tx)
                    }

                    miner.startMining()
                }
            }

        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(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.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 {
        logger.Debugln(err)
    }
    self.ethereum.TxPool().RemoveSet(erroneous)
    self.txs = append(txs, unhandledTxs...)

    self.block.SetReceipts(receipts)
    self.block.SetTransactions(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
    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 {
            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()
            self.ethereum.EventMux().Post(ethchain.NewBlockEvent{self.block})
        }

        // Continue mining on the next block
        self.startMining()
    }
}