OSDN Git Service

Mov (#518)
[bytom/vapor.git] / proposal / blockproposer / blockproposer.go
1 package blockproposer
2
3 import (
4         "encoding/hex"
5         "sync"
6         "time"
7
8         log "github.com/sirupsen/logrus"
9
10         "github.com/bytom/vapor/account"
11         "github.com/bytom/vapor/config"
12         "github.com/bytom/vapor/consensus"
13         "github.com/bytom/vapor/event"
14         "github.com/bytom/vapor/proposal"
15         "github.com/bytom/vapor/protocol"
16 )
17
18 const (
19         logModule         = "blockproposer"
20         warnTimeNum       = 2
21         warnTimeDenom     = 5
22         criticalTimeNum   = 4
23         criticalTimeDenom = 5
24 )
25
26 // BlockProposer propose several block in specified time range
27 type BlockProposer struct {
28         sync.Mutex
29         chain           *protocol.Chain
30         accountManager  *account.Manager
31         txPool          *protocol.TxPool
32         started         bool
33         quit            chan struct{}
34         eventDispatcher *event.Dispatcher
35 }
36
37 // generateBlocks is a worker that is controlled by the proposeWorkerController.
38 // It is self contained in that it creates block templates and attempts to solve
39 // them while detecting when it is performing stale work and reacting
40 // accordingly by generating a new block template.  When a block is verified, it
41 // is submitted.
42 //
43 // It must be run as a goroutine.
44 func (b *BlockProposer) generateBlocks() {
45         xpub := config.CommonConfig.PrivateKey().XPub()
46         xpubStr := hex.EncodeToString(xpub[:])
47         ticker := time.NewTicker(time.Duration(consensus.ActiveNetParams.BlockTimeInterval) * time.Millisecond)
48         defer ticker.Stop()
49
50         for {
51                 select {
52                 case <-b.quit:
53                         return
54                 case <-ticker.C:
55                 }
56
57                 bestBlockHeader := b.chain.BestBlockHeader()
58                 bestBlockHash := bestBlockHeader.Hash()
59
60                 now := uint64(time.Now().UnixNano() / 1e6)
61                 base := now
62                 if now < bestBlockHeader.Timestamp {
63                         base = bestBlockHeader.Timestamp
64                 }
65                 minTimeToNextBlock := consensus.ActiveNetParams.BlockTimeInterval - base%consensus.ActiveNetParams.BlockTimeInterval
66                 nextBlockTime := base + minTimeToNextBlock
67                 if (nextBlockTime - now) < consensus.ActiveNetParams.BlockTimeInterval/10 {
68                         nextBlockTime += consensus.ActiveNetParams.BlockTimeInterval
69                 }
70
71                 blocker, err := b.chain.GetBlocker(&bestBlockHash, nextBlockTime)
72                 if err != nil {
73                         log.WithFields(log.Fields{"module": logModule, "error": err, "pubKey": xpubStr}).Error("fail on check is next blocker")
74                         continue
75                 }
76
77                 if xpubStr != blocker {
78                         continue
79                 }
80
81                 warnDuration := time.Duration(consensus.ActiveNetParams.BlockTimeInterval*warnTimeNum/warnTimeDenom) * time.Millisecond
82                 criticalDuration := time.Duration(consensus.ActiveNetParams.BlockTimeInterval*criticalTimeNum/criticalTimeDenom) * time.Millisecond
83                 block, err := proposal.NewBlockTemplate(b.chain, b.accountManager, nextBlockTime, warnDuration, criticalDuration)
84                 if err != nil {
85                         log.WithFields(log.Fields{"module": logModule, "error": err}).Error("failed on create NewBlockTemplate")
86                         continue
87                 }
88
89                 isOrphan, err := b.chain.ProcessBlock(block)
90                 if err != nil {
91                         log.WithFields(log.Fields{"module": logModule, "height": block.BlockHeader.Height, "error": err}).Error("proposer fail on ProcessBlock")
92                         continue
93                 }
94
95                 log.WithFields(log.Fields{"module": logModule, "height": block.BlockHeader.Height, "isOrphan": isOrphan, "tx": len(block.Transactions)}).Info("proposer processed block")
96                 // Broadcast the block and announce chain insertion event
97                 if err = b.eventDispatcher.Post(event.NewProposedBlockEvent{Block: *block}); err != nil {
98                         log.WithFields(log.Fields{"module": logModule, "height": block.BlockHeader.Height, "error": err}).Error("proposer fail on post block")
99                 }
100         }
101 }
102
103 // Start begins the block propose process as well as the speed monitor used to
104 // track hashing metrics.  Calling this function when the block proposer has
105 // already been started will have no effect.
106 //
107 // This function is safe for concurrent access.
108 func (b *BlockProposer) Start() {
109         b.Lock()
110         defer b.Unlock()
111
112         // Nothing to do if the miner is already running
113         if b.started {
114                 return
115         }
116
117         b.quit = make(chan struct{})
118         go b.generateBlocks()
119
120         b.started = true
121         log.Infof("block proposer started")
122 }
123
124 // Stop gracefully stops the proposal process by signalling all workers, and the
125 // speed monitor to quit.  Calling this function when the block proposer has not
126 // already been started will have no effect.
127 //
128 // This function is safe for concurrent access.
129 func (b *BlockProposer) Stop() {
130         b.Lock()
131         defer b.Unlock()
132
133         // Nothing to do if the miner is not currently running
134         if !b.started {
135                 return
136         }
137
138         close(b.quit)
139         b.started = false
140         log.Info("block proposer stopped")
141 }
142
143 // IsProposing returns whether the block proposer has been started.
144 //
145 // This function is safe for concurrent access.
146 func (b *BlockProposer) IsProposing() bool {
147         b.Lock()
148         defer b.Unlock()
149
150         return b.started
151 }
152
153 // NewBlockProposer returns a new instance of a block proposer for the provided configuration.
154 // Use Start to begin the proposal process.  See the documentation for BlockProposer
155 // type for more details.
156 func NewBlockProposer(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool, dispatcher *event.Dispatcher) *BlockProposer {
157         return &BlockProposer{
158                 chain:           c,
159                 accountManager:  accountManager,
160                 txPool:          txPool,
161                 eventDispatcher: dispatcher,
162         }
163 }