OSDN Git Service

new repo
[bytom/vapor.git] / mining / miningpool / miningpool.go
1 package miningpool
2
3 import (
4         "errors"
5         "sync"
6         "time"
7
8         log "github.com/sirupsen/logrus"
9
10         "github.com/vapor/account"
11         "github.com/vapor/mining"
12         "github.com/vapor/protocol"
13         "github.com/vapor/protocol/bc"
14         "github.com/vapor/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         newBlockCh     chan *bc.Hash
36 }
37
38 // NewMiningPool will create a new MiningPool
39 func NewMiningPool(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool, newBlockCh chan *bc.Hash) *MiningPool {
40         m := &MiningPool{
41                 submitCh:       make(chan *submitBlockMsg, maxSubmitChSize),
42                 chain:          c,
43                 accountManager: accountManager,
44                 txPool:         txPool,
45                 newBlockCh:     newBlockCh,
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         reply := make(chan error, 1)
98         m.submitCh <- &submitBlockMsg{blockHeader: bh, reply: reply}
99         err := <-reply
100         if err != nil {
101                 log.WithFields(log.Fields{"err": err, "height": bh.Height}).Warning("submitWork failed")
102         }
103         return err
104 }
105
106 func (m *MiningPool) submitWork(bh *types.BlockHeader) error {
107         m.mutex.Lock()
108         defer m.mutex.Unlock()
109
110         if m.block == nil || bh.PreviousBlockHash != m.block.PreviousBlockHash {
111                 return errors.New("pending mining block has been changed")
112         }
113
114         //m.block.Nonce = bh.Nonce
115         m.block.Timestamp = bh.Timestamp
116         isOrphan, err := m.chain.ProcessBlock(m.block)
117         if err != nil {
118                 return err
119         }
120         if isOrphan {
121                 return errors.New("submit result is orphan")
122         }
123
124         blockHash := bh.Hash()
125         m.newBlockCh <- &blockHash
126         return nil
127 }