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 }