OSDN Git Service

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