github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/blockproducer/block_producer.go (about)

     1  package blockproducer
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/consensus/hotstuff"
     7  	"github.com/onflow/flow-go/consensus/hotstuff/model"
     8  	"github.com/onflow/flow-go/model/flow"
     9  	"github.com/onflow/flow-go/module"
    10  )
    11  
    12  // BlockProducer is responsible for producing new block proposals
    13  type BlockProducer struct {
    14  	signer    hotstuff.Signer
    15  	committee hotstuff.Replicas
    16  	builder   module.Builder
    17  }
    18  
    19  var _ hotstuff.BlockProducer = (*BlockProducer)(nil)
    20  
    21  // New creates a new BlockProducer which wraps the chain compliance layer block builder
    22  // to provide hotstuff with block proposals.
    23  // No errors are expected during normal operation.
    24  func New(signer hotstuff.Signer, committee hotstuff.Replicas, builder module.Builder) (*BlockProducer, error) {
    25  	bp := &BlockProducer{
    26  		signer:    signer,
    27  		committee: committee,
    28  		builder:   builder,
    29  	}
    30  	return bp, nil
    31  }
    32  
    33  // MakeBlockProposal builds a new HotStuff block proposal using the given view,
    34  // the given quorum certificate for its parent and [optionally] a timeout certificate for last view(could be nil).
    35  // No errors are expected during normal operation.
    36  func (bp *BlockProducer) MakeBlockProposal(view uint64, qc *flow.QuorumCertificate, lastViewTC *flow.TimeoutCertificate) (*flow.Header, error) {
    37  	// the custom functions allows us to set some custom fields on the block;
    38  	// in hotstuff, we use this for view number and signature-related fields
    39  	setHotstuffFields := func(header *flow.Header) error {
    40  		header.View = view
    41  		header.ParentView = qc.View
    42  		header.ParentVoterIndices = qc.SignerIndices
    43  		header.ParentVoterSigData = qc.SigData
    44  		header.ProposerID = bp.committee.Self()
    45  		header.LastViewTC = lastViewTC
    46  		return nil
    47  	}
    48  
    49  	// TODO: We should utilize the `EventHandler`'s `SafetyRules` to generate the block signature instead of using an independent signing logic: https://github.com/dapperlabs/flow-go/issues/6892
    50  	signProposal := func(header *flow.Header) error {
    51  		// turn the header into a block header proposal as known by hotstuff
    52  		block := model.Block{
    53  			BlockID:     header.ID(),
    54  			View:        view,
    55  			ProposerID:  header.ProposerID,
    56  			QC:          qc,
    57  			PayloadHash: header.PayloadHash,
    58  			Timestamp:   header.Timestamp,
    59  		}
    60  
    61  		// then sign the proposal
    62  		proposal, err := bp.signer.CreateProposal(&block)
    63  		if err != nil {
    64  			return fmt.Errorf("could not sign block proposal: %w", err)
    65  		}
    66  
    67  		header.ProposerSigData = proposal.SigData
    68  		return nil
    69  	}
    70  
    71  	// retrieve a fully built block header from the builder
    72  	header, err := bp.builder.BuildOn(qc.BlockID, setHotstuffFields, signProposal)
    73  	if err != nil {
    74  		return nil, fmt.Errorf("could not build block proposal on top of %v: %w", qc.BlockID, err)
    75  	}
    76  
    77  	return header, nil
    78  }