github.com/koko1123/flow-go-1@v0.29.6/cmd/bootstrap/run/qc.go (about)

     1  package run
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/rs/zerolog"
     7  
     8  	"github.com/koko1123/flow-go-1/consensus/hotstuff"
     9  	"github.com/koko1123/flow-go-1/consensus/hotstuff/committees"
    10  	"github.com/koko1123/flow-go-1/consensus/hotstuff/mocks"
    11  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
    12  	hotstuffSig "github.com/koko1123/flow-go-1/consensus/hotstuff/signature"
    13  	"github.com/koko1123/flow-go-1/consensus/hotstuff/validator"
    14  	"github.com/koko1123/flow-go-1/consensus/hotstuff/verification"
    15  	"github.com/koko1123/flow-go-1/consensus/hotstuff/votecollector"
    16  	"github.com/koko1123/flow-go-1/model/bootstrap"
    17  	"github.com/koko1123/flow-go-1/model/dkg"
    18  	"github.com/koko1123/flow-go-1/model/flow"
    19  	"github.com/koko1123/flow-go-1/module/local"
    20  	"github.com/onflow/flow-go/crypto"
    21  )
    22  
    23  type Participant struct {
    24  	bootstrap.NodeInfo
    25  	RandomBeaconPrivKey crypto.PrivateKey
    26  }
    27  
    28  type ParticipantData struct {
    29  	Participants []Participant
    30  	Lookup       map[flow.Identifier]flow.DKGParticipant
    31  	GroupKey     crypto.PublicKey
    32  }
    33  
    34  func (pd *ParticipantData) Identities() flow.IdentityList {
    35  	nodes := make([]bootstrap.NodeInfo, 0, len(pd.Participants))
    36  	for _, participant := range pd.Participants {
    37  		nodes = append(nodes, participant.NodeInfo)
    38  	}
    39  	return bootstrap.ToIdentityList(nodes)
    40  }
    41  
    42  // GenerateRootQC generates QC for root block, caller needs to provide votes for root QC and
    43  // participantData to build the QC.
    44  // NOTE: at the moment, we require private keys for one node because we we re-using the full business logic,
    45  // which assumes that only consensus participants construct QCs, which also have produce votes.
    46  //
    47  // TODO: modularize QC construction code (and code to verify QC) to be instantiated without needing private keys.
    48  func GenerateRootQC(block *flow.Block, votes []*model.Vote, participantData *ParticipantData, identities flow.IdentityList) (*flow.QuorumCertificate, error) {
    49  	// create consensus committee's state
    50  	committee, err := committees.NewStaticCommittee(identities, flow.Identifier{}, participantData.Lookup, participantData.GroupKey)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	// STEP 1: create VoteProcessor
    56  	var createdQC *flow.QuorumCertificate
    57  	hotBlock := model.GenesisBlockFromFlow(block.Header)
    58  	processor, err := votecollector.NewBootstrapCombinedVoteProcessor(zerolog.Logger{}, committee, hotBlock, func(qc *flow.QuorumCertificate) {
    59  		createdQC = qc
    60  	})
    61  	if err != nil {
    62  		return nil, fmt.Errorf("could not CombinedVoteProcessor processor: %w", err)
    63  	}
    64  
    65  	// STEP 2: feed the votes into the vote processor to create QC
    66  	for _, vote := range votes {
    67  		err := processor.Process(vote)
    68  		if err != nil {
    69  			return nil, fmt.Errorf("fail to process vote %v for block %v from signer %v: %w",
    70  				vote.ID(),
    71  				block.ID(),
    72  				vote.SignerID,
    73  				err)
    74  		}
    75  	}
    76  
    77  	if createdQC == nil {
    78  		return nil, fmt.Errorf("QC is not created, total number of votes %v, expect to have 2/3 votes of %v participants",
    79  			len(votes), len(identities))
    80  	}
    81  
    82  	// STEP 3: validate constructed QC
    83  	val, err := createValidator(committee)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	err = val.ValidateQC(createdQC, hotBlock)
    88  
    89  	return createdQC, err
    90  }
    91  
    92  // GenerateRootBlockVotes generates votes for root block based on participantData
    93  func GenerateRootBlockVotes(block *flow.Block, participantData *ParticipantData) ([]*model.Vote, error) {
    94  	hotBlock := model.GenesisBlockFromFlow(block.Header)
    95  	n := len(participantData.Participants)
    96  	fmt.Println("Number of staked consensus nodes: ", n)
    97  
    98  	votes := make([]*model.Vote, 0, n)
    99  	for _, p := range participantData.Participants {
   100  		fmt.Println("generating votes from consensus participants: ", p.NodeID, p.Address, p.StakingPubKey().String())
   101  
   102  		// create the participant's local identity
   103  		keys, err := p.PrivateKeys()
   104  		if err != nil {
   105  			return nil, fmt.Errorf("could not get private keys for participant: %w", err)
   106  		}
   107  		me, err := local.New(p.Identity(), keys.StakingKey)
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  
   112  		// create signer and use it to generate vote
   113  		beaconStore := hotstuffSig.NewStaticRandomBeaconSignerStore(p.RandomBeaconPrivKey)
   114  		vote, err := verification.NewCombinedSigner(me, beaconStore).CreateVote(hotBlock)
   115  		if err != nil {
   116  			return nil, err
   117  		}
   118  		votes = append(votes, vote)
   119  	}
   120  	return votes, nil
   121  }
   122  
   123  // createValidator creates validator that can validate votes and QC
   124  func createValidator(committee hotstuff.Committee) (hotstuff.Validator, error) {
   125  	packer := hotstuffSig.NewConsensusSigDataPacker(committee)
   126  	verifier := verification.NewCombinedVerifier(committee, packer)
   127  
   128  	forks := &mocks.ForksReader{}
   129  	hotstuffValidator := validator.New(committee, forks, verifier)
   130  	return hotstuffValidator, nil
   131  }
   132  
   133  // GenerateQCParticipantData generates QC participant data used to create the
   134  // random beacon and staking signatures on the QC.
   135  //
   136  // allNodes must be in the same order that was used when running the DKG.
   137  func GenerateQCParticipantData(allNodes, internalNodes []bootstrap.NodeInfo, dkgData dkg.DKGData) (*ParticipantData, error) {
   138  
   139  	// stakingNodes can include external validators, so it can be longer than internalNodes
   140  	if len(allNodes) < len(internalNodes) {
   141  		return nil, fmt.Errorf("need at least as many staking public keys as private keys (pub=%d, priv=%d)", len(allNodes), len(internalNodes))
   142  	}
   143  
   144  	// length of DKG participants needs to match stakingNodes, since we run DKG for external and internal validators
   145  	if len(allNodes) != len(dkgData.PrivKeyShares) {
   146  		return nil, fmt.Errorf("need exactly the same number of staking public keys as DKG private participants")
   147  	}
   148  
   149  	qcData := &ParticipantData{}
   150  
   151  	participantLookup := make(map[flow.Identifier]flow.DKGParticipant)
   152  
   153  	// the index here is important - we assume allNodes is in the same order as the DKG
   154  	for i := 0; i < len(allNodes); i++ {
   155  		// assign a node to a DGKdata entry, using the canonical ordering
   156  		node := allNodes[i]
   157  		participantLookup[node.NodeID] = flow.DKGParticipant{
   158  			KeyShare: dkgData.PubKeyShares[i],
   159  			Index:    uint(i),
   160  		}
   161  	}
   162  
   163  	// the QC will be signed by everyone in internalNodes
   164  	for _, node := range internalNodes {
   165  
   166  		if node.NodeID == flow.ZeroID {
   167  			return nil, fmt.Errorf("node id cannot be zero")
   168  		}
   169  
   170  		if node.Weight == 0 {
   171  			return nil, fmt.Errorf("node (id=%s) cannot have 0 weight", node.NodeID)
   172  		}
   173  
   174  		dkgParticipant, ok := participantLookup[node.NodeID]
   175  		if !ok {
   176  			return nil, fmt.Errorf("nonexistannt node id (%x) in participant lookup", node.NodeID)
   177  		}
   178  		dkgIndex := dkgParticipant.Index
   179  
   180  		qcData.Participants = append(qcData.Participants, Participant{
   181  			NodeInfo:            node,
   182  			RandomBeaconPrivKey: dkgData.PrivKeyShares[dkgIndex],
   183  		})
   184  	}
   185  
   186  	qcData.Lookup = participantLookup
   187  	qcData.GroupKey = dkgData.PubGroupKey
   188  
   189  	return qcData, nil
   190  }