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