OSDN Git Service

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