github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/mining/miningpool/miningpool.go (about)

     1  package miningpool
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  	"time"
     7  
     8  	log "github.com/sirupsen/logrus"
     9  
    10  	"github.com/bytom/bytom/account"
    11  	"github.com/bytom/bytom/event"
    12  	"github.com/bytom/bytom/mining"
    13  	"github.com/bytom/bytom/protocol"
    14  	"github.com/bytom/bytom/protocol/bc/types"
    15  )
    16  
    17  const (
    18  	maxSubmitChSize = 50
    19  )
    20  
    21  type submitBlockMsg struct {
    22  	blockHeader *types.BlockHeader
    23  	reply       chan error
    24  }
    25  
    26  // MiningPool is the support struct for p2p mine pool
    27  type MiningPool struct {
    28  	mutex    sync.RWMutex
    29  	block    *types.Block
    30  	submitCh chan *submitBlockMsg
    31  
    32  	chain           *protocol.Chain
    33  	accountManager  *account.Manager
    34  	txPool          *protocol.TxPool
    35  	eventDispatcher *event.Dispatcher
    36  }
    37  
    38  // NewMiningPool will create a new MiningPool
    39  func NewMiningPool(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool, dispatcher *event.Dispatcher) *MiningPool {
    40  	m := &MiningPool{
    41  		submitCh:        make(chan *submitBlockMsg, maxSubmitChSize),
    42  		chain:           c,
    43  		accountManager:  accountManager,
    44  		txPool:          txPool,
    45  		eventDispatcher: dispatcher,
    46  	}
    47  	m.generateBlock()
    48  	go m.blockUpdater()
    49  	return m
    50  }
    51  
    52  // blockUpdater is the goroutine for keep update mining block
    53  func (m *MiningPool) blockUpdater() {
    54  	for {
    55  		select {
    56  		case <-m.chain.BlockWaiter(m.chain.BestBlockHeight() + 1):
    57  			m.generateBlock()
    58  
    59  		case submitMsg := <-m.submitCh:
    60  			err := m.submitWork(submitMsg.blockHeader)
    61  			if err == nil {
    62  				m.generateBlock()
    63  			}
    64  			submitMsg.reply <- err
    65  		}
    66  	}
    67  }
    68  
    69  // generateBlock generates a block template to mine
    70  func (m *MiningPool) generateBlock() {
    71  	m.mutex.Lock()
    72  	defer m.mutex.Unlock()
    73  
    74  	block, err := mining.NewBlockTemplate(m.chain, m.txPool, m.accountManager)
    75  	if err != nil {
    76  		log.Errorf("miningpool: failed on create NewBlockTemplate: %v", err)
    77  		return
    78  	}
    79  	m.block = block
    80  }
    81  
    82  // GetWork will return a block header for p2p mining
    83  func (m *MiningPool) GetWork() (*types.BlockHeader, error) {
    84  	if m.block != nil {
    85  		m.mutex.RLock()
    86  		defer m.mutex.RUnlock()
    87  
    88  		m.block.BlockHeader.Timestamp = uint64(time.Now().Unix())
    89  		bh := m.block.BlockHeader
    90  		return &bh, nil
    91  	}
    92  	return nil, errors.New("no block is ready for mining")
    93  }
    94  
    95  // SubmitWork will try to submit the result to the blockchain
    96  func (m *MiningPool) SubmitWork(bh *types.BlockHeader) error {
    97  	if bh == nil {
    98  		return errors.New("can't submit empty block")
    99  	}
   100  
   101  	reply := make(chan error, 1)
   102  	m.submitCh <- &submitBlockMsg{blockHeader: bh, reply: reply}
   103  	err := <-reply
   104  	if err != nil {
   105  		log.WithFields(log.Fields{"err": err, "height": bh.Height}).Warning("submitWork failed")
   106  	}
   107  	return err
   108  }
   109  
   110  func (m *MiningPool) submitWork(bh *types.BlockHeader) error {
   111  	m.mutex.Lock()
   112  	defer m.mutex.Unlock()
   113  
   114  	if m.block == nil || bh.PreviousBlockHash != m.block.PreviousBlockHash {
   115  		return errors.New("pending mining block has been changed")
   116  	}
   117  
   118  	m.block.Nonce = bh.Nonce
   119  	m.block.Timestamp = bh.Timestamp
   120  	isOrphan, err := m.chain.ProcessBlock(m.block)
   121  	if err != nil {
   122  		return err
   123  	}
   124  	if isOrphan {
   125  		return errors.New("submit result is orphan")
   126  	}
   127  
   128  	if err := m.eventDispatcher.Post(event.NewMinedBlockEvent{Block: *m.block}); err != nil {
   129  		return err
   130  	}
   131  
   132  	return nil
   133  }