OSDN Git Service

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