OSDN Git Service

Edit consensus (#116)
[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                 if isBlocker, err := b.chain.GetBBFT().IsBlocker(&bestBlockHash, xpubStr, nextBlockTime); !isBlocker {
61                         log.WithFields(log.Fields{"module": logModule, "error": err, "pubKey": xpubStr}).Debug("fail on check is next blocker")
62                         continue
63                 }
64
65                 block, err := proposal.NewBlockTemplate(b.chain, b.txPool, b.accountManager, nextBlockTime)
66                 if err != nil {
67                         log.WithFields(log.Fields{"module": logModule, "error": err}).Error("failed on create NewBlockTemplate")
68                         continue
69                 }
70
71                 isOrphan, err := b.chain.ProcessBlock(block)
72                 if err != nil {
73                         log.WithFields(log.Fields{"module": logModule, "height": block.BlockHeader.Height, "error": err}).Error("proposer fail on ProcessBlock")
74                         continue
75                 }
76
77                 log.WithFields(log.Fields{"module": logModule, "height": block.BlockHeader.Height, "isOrphan": isOrphan, "tx": len(block.Transactions)}).Info("proposer processed block")
78                 // Broadcast the block and announce chain insertion event
79                 if err = b.eventDispatcher.Post(event.NewProposedBlockEvent{Block: *block}); err != nil {
80                         log.WithFields(log.Fields{"module": logModule, "height": block.BlockHeader.Height, "error": err}).Error("proposer fail on post block")
81                 }
82         }
83 }
84
85 // Start begins the block propose process as well as the speed monitor used to
86 // track hashing metrics.  Calling this function when the block proposer has
87 // already been started will have no effect.
88 //
89 // This function is safe for concurrent access.
90 func (b *BlockProposer) Start() {
91         b.Lock()
92         defer b.Unlock()
93
94         // Nothing to do if the miner is already running
95         if b.started {
96                 return
97         }
98
99         b.quit = make(chan struct{})
100         go b.generateBlocks()
101
102         b.started = true
103         log.Infof("block proposer started")
104 }
105
106 // Stop gracefully stops the proposal process by signalling all workers, and the
107 // speed monitor to quit.  Calling this function when the block proposer has not
108 // already been started will have no effect.
109 //
110 // This function is safe for concurrent access.
111 func (b *BlockProposer) Stop() {
112         b.Lock()
113         defer b.Unlock()
114
115         // Nothing to do if the miner is not currently running
116         if !b.started {
117                 return
118         }
119
120         close(b.quit)
121         b.started = false
122         log.Info("block proposer stopped")
123 }
124
125 // IsProposing returns whether the block proposer has been started.
126 //
127 // This function is safe for concurrent access.
128 func (b *BlockProposer) IsProposing() bool {
129         b.Lock()
130         defer b.Unlock()
131
132         return b.started
133 }
134
135 // NewBlockProposer returns a new instance of a block proposer for the provided configuration.
136 // Use Start to begin the proposal process.  See the documentation for BlockProposer
137 // type for more details.
138 func NewBlockProposer(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool, dispatcher *event.Dispatcher) *BlockProposer {
139         return &BlockProposer{
140                 chain:           c,
141                 accountManager:  accountManager,
142                 txPool:          txPool,
143                 eventDispatcher: dispatcher,
144         }
145 }