aboutsummaryrefslogblamecommitdiffstats
path: root/ethminer/miner.go
blob: f4f697abaa14795a3e44ebdc27f85a98f3e7bd0e (plain) (tree)




















































































































































                                                                                                                                                                
package ethminer

import (
    "bytes"
    "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.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)
            }

            // 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("Error result from process block:", err)
                    log.Println(miner.block)
                } else {

                    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())
                    log.Println(miner.block)

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