github.com/koko1123/flow-go-1@v0.29.6/state/protocol/badger/validity.go (about)

     1  package badger
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/koko1123/flow-go-1/consensus/hotstuff/committees"
     7  	"github.com/koko1123/flow-go-1/consensus/hotstuff/mocks"
     8  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
     9  	"github.com/koko1123/flow-go-1/consensus/hotstuff/signature"
    10  	"github.com/koko1123/flow-go-1/consensus/hotstuff/validator"
    11  	"github.com/koko1123/flow-go-1/consensus/hotstuff/verification"
    12  	"github.com/koko1123/flow-go-1/model/flow"
    13  	"github.com/koko1123/flow-go-1/model/flow/factory"
    14  	"github.com/koko1123/flow-go-1/model/flow/filter"
    15  	"github.com/koko1123/flow-go-1/model/flow/order"
    16  	"github.com/koko1123/flow-go-1/state"
    17  	"github.com/koko1123/flow-go-1/state/protocol"
    18  )
    19  
    20  // isValidExtendingEpochSetup checks whether an epoch setup service being
    21  // added to the state is valid. In addition to intrinsic validitym, we also
    22  // check that it is valid w.r.t. the previous epoch setup event, and the
    23  // current epoch status.
    24  func isValidExtendingEpochSetup(extendingSetup *flow.EpochSetup, activeSetup *flow.EpochSetup, status *flow.EpochStatus) error {
    25  
    26  	// We should only have a single epoch setup event per epoch.
    27  	if status.NextEpoch.SetupID != flow.ZeroID {
    28  		// true iff EpochSetup event for NEXT epoch was already included before
    29  		return protocol.NewInvalidServiceEventError("duplicate epoch setup service event: %x", status.NextEpoch.SetupID)
    30  	}
    31  
    32  	// The setup event should have the counter increased by one.
    33  	if extendingSetup.Counter != activeSetup.Counter+1 {
    34  		return protocol.NewInvalidServiceEventError("next epoch setup has invalid counter (%d => %d)", activeSetup.Counter, extendingSetup.Counter)
    35  	}
    36  
    37  	// The first view needs to be exactly one greater than the current epoch final view
    38  	if extendingSetup.FirstView != activeSetup.FinalView+1 {
    39  		return protocol.NewInvalidServiceEventError(
    40  			"next epoch first view must be exactly 1 more than current epoch final view (%d != %d+1)",
    41  			extendingSetup.FirstView,
    42  			activeSetup.FinalView,
    43  		)
    44  	}
    45  
    46  	// Finally, the epoch setup event must contain all necessary information.
    47  	err := isValidEpochSetup(extendingSetup)
    48  	if err != nil {
    49  		return protocol.NewInvalidServiceEventError("invalid epoch setup: %w", err)
    50  	}
    51  
    52  	return nil
    53  }
    54  
    55  // isValidEpochSetup checks whether an epoch setup service event is intrinsically valid
    56  func isValidEpochSetup(setup *flow.EpochSetup) error {
    57  	return verifyEpochSetup(setup, true)
    58  }
    59  
    60  func verifyEpochSetup(setup *flow.EpochSetup, verifyNetworkAddress bool) error {
    61  	// STEP 1: general sanity checks
    62  	// the seed needs to be at least minimum length
    63  	if len(setup.RandomSource) != flow.EpochSetupRandomSourceLength {
    64  		return fmt.Errorf("seed has incorrect length (%d != %d)", len(setup.RandomSource), flow.EpochSetupRandomSourceLength)
    65  	}
    66  
    67  	// STEP 2: sanity checks of all nodes listed as participants
    68  	// there should be no duplicate node IDs
    69  	identLookup := make(map[flow.Identifier]struct{})
    70  	for _, participant := range setup.Participants {
    71  		_, ok := identLookup[participant.NodeID]
    72  		if ok {
    73  			return fmt.Errorf("duplicate node identifier (%x)", participant.NodeID)
    74  		}
    75  		identLookup[participant.NodeID] = struct{}{}
    76  	}
    77  
    78  	if verifyNetworkAddress {
    79  		// there should be no duplicate node addresses
    80  		addrLookup := make(map[string]struct{})
    81  		for _, participant := range setup.Participants {
    82  			_, ok := addrLookup[participant.Address]
    83  			if ok {
    84  				return fmt.Errorf("duplicate node address (%x)", participant.Address)
    85  			}
    86  			addrLookup[participant.Address] = struct{}{}
    87  		}
    88  	}
    89  
    90  	// there should be no nodes with zero weight
    91  	// TODO: we might want to remove the following as we generally want to allow nodes with
    92  	// zero weight in the protocol state.
    93  	for _, participant := range setup.Participants {
    94  		if participant.Weight == 0 {
    95  			return fmt.Errorf("node with zero weight (%x)", participant.NodeID)
    96  		}
    97  	}
    98  
    99  	// the participants must be ordered by canonical order
   100  	if !setup.Participants.Sorted(order.Canonical) {
   101  		return fmt.Errorf("participants are not canonically ordered")
   102  	}
   103  
   104  	// STEP 3: sanity checks for individual roles
   105  	// IMPORTANT: here we remove all nodes with zero weight, as they are allowed to partake
   106  	// in communication but not in respective node functions
   107  	activeParticipants := setup.Participants.Filter(filter.HasWeight(true))
   108  
   109  	// we need at least one node of each role
   110  	roles := make(map[flow.Role]uint)
   111  	for _, participant := range activeParticipants {
   112  		roles[participant.Role]++
   113  	}
   114  	if roles[flow.RoleConsensus] < 1 {
   115  		return fmt.Errorf("need at least one consensus node")
   116  	}
   117  	if roles[flow.RoleCollection] < 1 {
   118  		return fmt.Errorf("need at least one collection node")
   119  	}
   120  	if roles[flow.RoleExecution] < 1 {
   121  		return fmt.Errorf("need at least one execution node")
   122  	}
   123  	if roles[flow.RoleVerification] < 1 {
   124  		return fmt.Errorf("need at least one verification node")
   125  	}
   126  
   127  	// first view must be before final view
   128  	if setup.FirstView >= setup.FinalView {
   129  		return fmt.Errorf("first view (%d) must be before final view (%d)", setup.FirstView, setup.FinalView)
   130  	}
   131  
   132  	// we need at least one collection cluster
   133  	if len(setup.Assignments) == 0 {
   134  		return fmt.Errorf("need at least one collection cluster")
   135  	}
   136  
   137  	// the collection cluster assignments need to be valid
   138  	_, err := factory.NewClusterList(setup.Assignments, activeParticipants.Filter(filter.HasRole(flow.RoleCollection)))
   139  	if err != nil {
   140  		return fmt.Errorf("invalid cluster assignments: %w", err)
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  // isValidExtendingEpochCommit checks whether an epoch commit service being
   147  // added to the state is valid. In addition to intrinsic validity, we also
   148  // check that it is valid w.r.t. the previous epoch setup event, and the
   149  // current epoch status.
   150  func isValidExtendingEpochCommit(extendingCommit *flow.EpochCommit, extendingSetup *flow.EpochSetup, activeSetup *flow.EpochSetup, status *flow.EpochStatus) error {
   151  
   152  	// We should only have a single epoch commit event per epoch.
   153  	if status.NextEpoch.CommitID != flow.ZeroID {
   154  		// true iff EpochCommit event for NEXT epoch was already included before
   155  		return protocol.NewInvalidServiceEventError("duplicate epoch commit service event: %x", status.NextEpoch.CommitID)
   156  	}
   157  
   158  	// The epoch setup event needs to happen before the commit.
   159  	if status.NextEpoch.SetupID == flow.ZeroID {
   160  		return protocol.NewInvalidServiceEventError("missing epoch setup for epoch commit")
   161  	}
   162  
   163  	// The commit event should have the counter increased by one.
   164  	if extendingCommit.Counter != activeSetup.Counter+1 {
   165  		return protocol.NewInvalidServiceEventError("next epoch commit has invalid counter (%d => %d)", activeSetup.Counter, extendingCommit.Counter)
   166  	}
   167  
   168  	err := isValidEpochCommit(extendingCommit, extendingSetup)
   169  	if err != nil {
   170  		return state.NewInvalidExtensionErrorf("invalid epoch commit: %s", err)
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  // isValidEpochCommit checks whether an epoch commit service event is intrinsically valid.
   177  func isValidEpochCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error {
   178  
   179  	if len(setup.Assignments) != len(commit.ClusterQCs) {
   180  		return fmt.Errorf("number of clusters (%d) does not number of QCs (%d)", len(setup.Assignments), len(commit.ClusterQCs))
   181  	}
   182  
   183  	if commit.Counter != setup.Counter {
   184  		return fmt.Errorf("inconsistent epoch counter between commit (%d) and setup (%d) events in same epoch", commit.Counter, setup.Counter)
   185  	}
   186  
   187  	// make sure we have a valid DKG public key
   188  	if commit.DKGGroupKey == nil {
   189  		return fmt.Errorf("missing DKG public group key")
   190  	}
   191  
   192  	participants := setup.Participants.Filter(filter.IsValidDKGParticipant)
   193  	if len(participants) != len(commit.DKGParticipantKeys) {
   194  		return fmt.Errorf("participant list (len=%d) does not match dkg key list (len=%d)", len(participants), len(commit.DKGParticipantKeys))
   195  	}
   196  
   197  	return nil
   198  }
   199  
   200  // IsValidRootSnapshot checks internal consistency of root state snapshot
   201  // if verifyResultID allows/disallows Result ID verification
   202  func IsValidRootSnapshot(snap protocol.Snapshot, verifyResultID bool) error {
   203  
   204  	segment, err := snap.SealingSegment()
   205  	if err != nil {
   206  		return fmt.Errorf("could not get sealing segment: %w", err)
   207  	}
   208  	result, seal, err := snap.SealedResult()
   209  	if err != nil {
   210  		return fmt.Errorf("could not latest sealed result: %w", err)
   211  	}
   212  
   213  	err = segment.Validate()
   214  	if err != nil {
   215  		return fmt.Errorf("invalid root sealing segment: %w", err)
   216  	}
   217  
   218  	highest := segment.Highest() // reference block of the snapshot
   219  	lowest := segment.Lowest()   // last sealed block
   220  	highestID := highest.ID()
   221  	lowestID := lowest.ID()
   222  
   223  	if result.BlockID != lowestID {
   224  		return fmt.Errorf("root execution result for wrong block (%x != %x)", result.BlockID, lowest.ID())
   225  	}
   226  
   227  	if seal.BlockID != lowestID {
   228  		return fmt.Errorf("root block seal for wrong block (%x != %x)", seal.BlockID, lowest.ID())
   229  	}
   230  
   231  	if verifyResultID {
   232  		if seal.ResultID != result.ID() {
   233  			return fmt.Errorf("root block seal for wrong execution result (%x != %x)", seal.ResultID, result.ID())
   234  		}
   235  	}
   236  
   237  	// identities must be canonically ordered
   238  	identities, err := snap.Identities(filter.Any)
   239  	if err != nil {
   240  		return fmt.Errorf("could not get identities for root snapshot: %w", err)
   241  	}
   242  	if !identities.Sorted(order.Canonical) {
   243  		return fmt.Errorf("identities are not canonically ordered")
   244  	}
   245  
   246  	// root qc must be for reference block of snapshot
   247  	qc, err := snap.QuorumCertificate()
   248  	if err != nil {
   249  		return fmt.Errorf("could not get qc for root snapshot: %w", err)
   250  	}
   251  	if qc.BlockID != highestID {
   252  		return fmt.Errorf("qc is for wrong block (got: %x, expected: %x)", qc.BlockID, highestID)
   253  	}
   254  
   255  	firstView, err := snap.Epochs().Current().FirstView()
   256  	if err != nil {
   257  		return fmt.Errorf("could not get first view: %w", err)
   258  	}
   259  	finalView, err := snap.Epochs().Current().FinalView()
   260  	if err != nil {
   261  		return fmt.Errorf("could not get final view: %w", err)
   262  	}
   263  
   264  	// the segment must be fully within the current epoch
   265  	if firstView > lowest.Header.View {
   266  		return fmt.Errorf("lowest block of sealing segment has lower view than first view of epoch")
   267  	}
   268  	if highest.Header.View >= finalView {
   269  		return fmt.Errorf("final view of epoch less than first block view")
   270  	}
   271  
   272  	return nil
   273  }
   274  
   275  // IsValidRootSnapshotQCs checks internal consistency of QCs that are included in the root state snapshot
   276  // It verifies QCs for main consensus and for each collection cluster.
   277  func IsValidRootSnapshotQCs(snap protocol.Snapshot) error {
   278  	// validate main consensus QC
   279  	err := validateRootQC(snap)
   280  	if err != nil {
   281  		return fmt.Errorf("invalid root QC: %w", err)
   282  	}
   283  
   284  	// validate each collection cluster separately
   285  	curEpoch := snap.Epochs().Current()
   286  	clusters, err := curEpoch.Clustering()
   287  	if err != nil {
   288  		return fmt.Errorf("could not get clustering for root snapshot: %w", err)
   289  	}
   290  	for clusterIndex := range clusters {
   291  		cluster, err := curEpoch.Cluster(uint(clusterIndex))
   292  		if err != nil {
   293  			return fmt.Errorf("could not get cluster %d for root snapshot: %w", clusterIndex, err)
   294  		}
   295  		err = validateClusterQC(cluster)
   296  		if err != nil {
   297  			return fmt.Errorf("invalid cluster qc %d: %W", clusterIndex, err)
   298  		}
   299  	}
   300  	return nil
   301  }
   302  
   303  // validateRootQC performs validation of root QC
   304  // Returns nil on success
   305  func validateRootQC(snap protocol.Snapshot) error {
   306  	rootBlock, err := snap.Head()
   307  	if err != nil {
   308  		return fmt.Errorf("could not get root block: %w", err)
   309  	}
   310  
   311  	identities, err := snap.Identities(filter.IsVotingConsensusCommitteeMember)
   312  	if err != nil {
   313  		return fmt.Errorf("could not get root snapshot identities: %w", err)
   314  	}
   315  
   316  	rootQC, err := snap.QuorumCertificate()
   317  	if err != nil {
   318  		return fmt.Errorf("could not get root QC: %w", err)
   319  	}
   320  
   321  	dkg, err := snap.Epochs().Current().DKG()
   322  	if err != nil {
   323  		return fmt.Errorf("could not get DKG for root snapshot: %w", err)
   324  	}
   325  
   326  	hotstuffRootBlock := model.GenesisBlockFromFlow(rootBlock)
   327  	committee, err := committees.NewStaticCommitteeWithDKG(identities, flow.Identifier{}, dkg)
   328  	if err != nil {
   329  		return fmt.Errorf("could not create static committee: %w", err)
   330  	}
   331  	verifier := verification.NewCombinedVerifier(committee, signature.NewConsensusSigDataPacker(committee))
   332  	forks := &mocks.ForksReader{}
   333  	hotstuffValidator := validator.New(committee, forks, verifier)
   334  	err = hotstuffValidator.ValidateQC(rootQC, hotstuffRootBlock)
   335  	if err != nil {
   336  		return fmt.Errorf("could not validate root qc: %w", err)
   337  	}
   338  	return nil
   339  }
   340  
   341  // validateClusterQC performs QC validation of single collection cluster
   342  // Returns nil on success
   343  func validateClusterQC(cluster protocol.Cluster) error {
   344  	clusterRootBlock := model.GenesisBlockFromFlow(cluster.RootBlock().Header)
   345  
   346  	committee, err := committees.NewStaticCommittee(cluster.Members(), flow.Identifier{}, nil, nil)
   347  	if err != nil {
   348  		return fmt.Errorf("could not create static committee: %w", err)
   349  	}
   350  	verifier := verification.NewStakingVerifier()
   351  	forks := &mocks.ForksReader{}
   352  	hotstuffValidator := validator.New(committee, forks, verifier)
   353  	err = hotstuffValidator.ValidateQC(cluster.RootQC(), clusterRootBlock)
   354  	if err != nil {
   355  		return fmt.Errorf("could not validate root qc: %w", err)
   356  	}
   357  	return nil
   358  }