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  }