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

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

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

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
    quitChan := make(chan ethutil.React, 1)  // This is the channel that can exit the miner thread

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

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

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

    return miner
}
func (miner *Miner) Start() {
    // Prepare inital block
    miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
    go func() { miner.listener() }()
}
func (miner *Miner) listener() {
    for {
        select {
        case chanMessage := <-miner.reactChan:
            if block, ok := chanMessage.Resource.(*ethchain.Block); ok {
                log.Println("[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
                    log.Println("[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 {
                        log.Println("[MINER] Adding uncle block")
                        miner.uncles = append(miner.uncles, block)
                        miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
                    }
                }
            }

            if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok {
                //log.Println("[MINER] Got new transaction from Reactor", tx)
                found := false
                for _, ctx := range miner.txs {
                    if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found {
                        break
                    }

                }
                if found == false {
                    //log.Println("[MINER] We did not know about this transaction, adding")
                    miner.txs = append(miner.txs, tx)
                    miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
                    miner.block.SetTransactions(miner.txs)
                } else {
                    //log.Println("[MINER] We already had this transaction, ignoring")
                }
            }
        default:
            log.Println("[MINER] Mining on block. Includes", len(miner.txs), "transactions")

            // Apply uncles
            if len(miner.uncles) > 0 {
                miner.block.SetUncles(miner.uncles)
            }

            // FIXME @ maranh, first block doesn't need this. Everything after the first block does.
            // Please check and fix
            miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State())
            // Apply all transactions to the block
            miner.ethereum.StateManager().ApplyTransactions(miner.block, miner.block.Transactions())
            miner.ethereum.StateManager().AccumelateRewards(miner.block)

            // Search the nonce
            //log.Println("[MINER] Initialision complete, starting mining")
            miner.block.Nonce = miner.pow.Search(miner.block, miner.quitChan)
            if miner.block.Nonce != nil {
                miner.ethereum.StateManager().PrepareDefault(miner.block)
                err := miner.ethereum.StateManager().ProcessBlock(miner.block, true)
                if err != nil {
                    log.Println(err)
                    miner.txs = []*ethchain.Transaction{} // Move this somewhere neat
                    miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
                } else {

                    /*
                        // XXX @maranh This is already done in the state manager, why a 2nd time?
                        if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) {
                            log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce))
                        }
                    */
                    miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val})
                    log.Printf("[MINER] 🔨  Mined block %x\n", miner.block.Hash())

                    miner.txs = []*ethchain.Transaction{} // Move this somewhere neat
                    miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs)
                }
            }
        }
    }
}