OSDN Git Service

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