github.com/onflow/flow-go@v0.33.17/consensus/hotstuff/committees/cluster_committee.go (about)

     1  package committees
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/consensus/hotstuff"
     7  	"github.com/onflow/flow-go/consensus/hotstuff/committees/leader"
     8  	"github.com/onflow/flow-go/consensus/hotstuff/model"
     9  	"github.com/onflow/flow-go/model/flow"
    10  	"github.com/onflow/flow-go/model/flow/filter"
    11  	"github.com/onflow/flow-go/state/protocol"
    12  	"github.com/onflow/flow-go/storage"
    13  )
    14  
    15  // Cluster represents the committee for a cluster of collection nodes. Cluster
    16  // committees are epoch-scoped.
    17  //
    18  // Clusters build blocks on a cluster chain but must obtain identity table
    19  // information from the main chain. Thus, block ID parameters in this DynamicCommittee
    20  // implementation reference blocks on the cluster chain, which in turn reference
    21  // blocks on the main chain - this implementation manages that translation.
    22  type Cluster struct {
    23  	state    protocol.State
    24  	payloads storage.ClusterPayloads
    25  	me       flow.Identifier
    26  	// pre-computed leader selection for the full lifecycle of the cluster
    27  	selection *leader.LeaderSelection
    28  	// a filter that returns all members of the cluster committee allowed to vote
    29  	clusterMemberFilter flow.IdentityFilter
    30  	// initial set of cluster members, WITHOUT dynamic weight changes
    31  	// TODO: should use identity skeleton https://github.com/dapperlabs/flow-go/issues/6232
    32  	initialClusterMembers flow.IdentityList
    33  	weightThresholdForQC  uint64 // computed based on initial cluster committee weights
    34  	weightThresholdForTO  uint64 // computed based on initial cluster committee weights
    35  }
    36  
    37  var _ hotstuff.Replicas = (*Cluster)(nil)
    38  var _ hotstuff.DynamicCommittee = (*Cluster)(nil)
    39  
    40  func NewClusterCommittee(
    41  	state protocol.State,
    42  	payloads storage.ClusterPayloads,
    43  	cluster protocol.Cluster,
    44  	epoch protocol.Epoch,
    45  	me flow.Identifier,
    46  ) (*Cluster, error) {
    47  
    48  	selection, err := leader.SelectionForCluster(cluster, epoch)
    49  	if err != nil {
    50  		return nil, fmt.Errorf("could not compute leader selection for cluster: %w", err)
    51  	}
    52  
    53  	totalWeight := cluster.Members().TotalWeight()
    54  	com := &Cluster{
    55  		state:     state,
    56  		payloads:  payloads,
    57  		me:        me,
    58  		selection: selection,
    59  		clusterMemberFilter: filter.And(
    60  			cluster.Members().Selector(),
    61  			filter.Not(filter.Ejected),
    62  			filter.HasWeight(true),
    63  		),
    64  		initialClusterMembers: cluster.Members(),
    65  		weightThresholdForQC:  WeightThresholdToBuildQC(totalWeight),
    66  		weightThresholdForTO:  WeightThresholdToTimeout(totalWeight),
    67  	}
    68  	return com, nil
    69  }
    70  
    71  // IdentitiesByBlock returns the identities of all cluster members that are authorized to
    72  // participate at the given block. The order of the identities is the canonical order.
    73  func (c *Cluster) IdentitiesByBlock(blockID flow.Identifier) (flow.IdentityList, error) {
    74  	// blockID is a collection block not a block produced by consensus,
    75  	// to query the identities from protocol state, we need to use the reference block id from the payload
    76  	//
    77  	// first retrieve the cluster block payload
    78  	payload, err := c.payloads.ByBlockID(blockID)
    79  	if err != nil {
    80  		return nil, fmt.Errorf("could not get cluster payload: %w", err)
    81  	}
    82  
    83  	// an empty reference block ID indicates a root block
    84  	isRootBlock := payload.ReferenceBlockID == flow.ZeroID
    85  
    86  	// use the initial cluster members for root block
    87  	if isRootBlock {
    88  		return c.initialClusterMembers, nil
    89  	}
    90  
    91  	// otherwise use the snapshot given by the reference block
    92  	identities, err := c.state.AtBlockID(payload.ReferenceBlockID).Identities(c.clusterMemberFilter)
    93  	return identities, err
    94  }
    95  
    96  func (c *Cluster) IdentityByBlock(blockID flow.Identifier, nodeID flow.Identifier) (*flow.Identity, error) {
    97  
    98  	// first retrieve the cluster block payload
    99  	payload, err := c.payloads.ByBlockID(blockID)
   100  	if err != nil {
   101  		return nil, fmt.Errorf("could not get cluster payload: %w", err)
   102  	}
   103  
   104  	// an empty reference block ID indicates a root block
   105  	isRootBlock := payload.ReferenceBlockID == flow.ZeroID
   106  
   107  	// use the initial cluster members for root block
   108  	if isRootBlock {
   109  		identity, ok := c.initialClusterMembers.ByNodeID(nodeID)
   110  		if !ok {
   111  			return nil, model.NewInvalidSignerErrorf("node %v is not an authorized hotstuff participant", nodeID)
   112  		}
   113  		return identity, nil
   114  	}
   115  
   116  	// otherwise use the snapshot given by the reference block
   117  	identity, err := c.state.AtBlockID(payload.ReferenceBlockID).Identity(nodeID)
   118  	if protocol.IsIdentityNotFound(err) {
   119  		return nil, model.NewInvalidSignerErrorf("%v is not a valid node id at block %v: %w", nodeID, payload.ReferenceBlockID, err)
   120  	}
   121  	if err != nil {
   122  		return nil, fmt.Errorf("could not get identity for node (id=%x): %w", nodeID, err)
   123  	}
   124  	if !c.clusterMemberFilter(identity) {
   125  		return nil, model.NewInvalidSignerErrorf("node %v is not an authorized hotstuff cluster member", nodeID)
   126  	}
   127  	return identity, nil
   128  }
   129  
   130  // IdentitiesByEpoch returns the initial cluster members for this epoch. The view
   131  // parameter is the view in the cluster consensus. Since clusters only exist for
   132  // one epoch, we don't need to check the view.
   133  func (c *Cluster) IdentitiesByEpoch(_ uint64) (flow.IdentityList, error) {
   134  	return c.initialClusterMembers, nil
   135  }
   136  
   137  // IdentityByEpoch returns the node from the initial cluster members for this epoch.
   138  // The view parameter is the view in the cluster consensus. Since clusters only exist
   139  // for one epoch, we don't need to check the view.
   140  //
   141  // Returns:
   142  //   - model.InvalidSignerError if nodeID was not listed by the Epoch Setup event as an
   143  //     authorized participant in this cluster
   144  func (c *Cluster) IdentityByEpoch(_ uint64, nodeID flow.Identifier) (*flow.Identity, error) {
   145  	identity, ok := c.initialClusterMembers.ByNodeID(nodeID)
   146  	if !ok {
   147  		return nil, model.NewInvalidSignerErrorf("node %v is not an authorized hotstuff participant", nodeID)
   148  	}
   149  	return identity, nil
   150  }
   151  
   152  func (c *Cluster) LeaderForView(view uint64) (flow.Identifier, error) {
   153  	return c.selection.LeaderForView(view)
   154  }
   155  
   156  // QuorumThresholdForView returns the weight threshold required to build a QC
   157  // for the given view. The view parameter is the view in the cluster consensus.
   158  // Since clusters only exist for one epoch, and the weight threshold is static
   159  // over the course of an epoch, we don't need to check the view.
   160  //
   161  // No errors are expected during normal operation.
   162  func (c *Cluster) QuorumThresholdForView(_ uint64) (uint64, error) {
   163  	return c.weightThresholdForQC, nil
   164  }
   165  
   166  // TimeoutThresholdForView returns the minimum weight of observed timeout objects to
   167  // safely immediately timeout for the current view. The view parameter is the view
   168  // in the cluster consensus. Since clusters only exist for one epoch, and the weight
   169  // threshold is static over the course of an epoch, we don't need to check the view.
   170  //
   171  // No errors are expected during normal operation.
   172  func (c *Cluster) TimeoutThresholdForView(_ uint64) (uint64, error) {
   173  	return c.weightThresholdForTO, nil
   174  }
   175  
   176  func (c *Cluster) Self() flow.Identifier {
   177  	return c.me
   178  }
   179  
   180  func (c *Cluster) DKG(_ uint64) (hotstuff.DKG, error) {
   181  	panic("queried DKG of cluster committee")
   182  }