github.com/onflow/flow-go@v0.33.17/state/protocol/util.go (about)

     1  package protocol
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math"
     7  
     8  	"github.com/onflow/flow-go/model/flow"
     9  	"github.com/onflow/flow-go/model/flow/filter"
    10  	"github.com/onflow/flow-go/module/signature"
    11  	"github.com/onflow/flow-go/storage"
    12  )
    13  
    14  // IsNodeAuthorizedAt returns whether the node with the given ID is a valid
    15  // un-ejected network participant as of the given state snapshot.
    16  func IsNodeAuthorizedAt(snapshot Snapshot, id flow.Identifier) (bool, error) {
    17  	return CheckNodeStatusAt(
    18  		snapshot,
    19  		id,
    20  		filter.HasWeight(true),
    21  		filter.Not(filter.Ejected),
    22  	)
    23  }
    24  
    25  // IsNodeAuthorizedWithRoleAt returns whether the node with the given ID is a valid
    26  // un-ejected network participant with the specified role as of the given state snapshot.
    27  // Expected errors during normal operations:
    28  //   - state.ErrUnknownSnapshotReference if snapshot references an unknown block
    29  //
    30  // All other errors are unexpected and potential symptoms of internal state corruption.
    31  func IsNodeAuthorizedWithRoleAt(snapshot Snapshot, id flow.Identifier, role flow.Role) (bool, error) {
    32  	return CheckNodeStatusAt(
    33  		snapshot,
    34  		id,
    35  		filter.HasWeight(true),
    36  		filter.Not(filter.Ejected),
    37  		filter.HasRole(role),
    38  	)
    39  }
    40  
    41  // CheckNodeStatusAt returns whether the node with the given ID is a valid identity at the given
    42  // state snapshot, and satisfies all checks.
    43  // Expected errors during normal operations:
    44  //   - state.ErrUnknownSnapshotReference if snapshot references an unknown block
    45  //
    46  // All other errors are unexpected and potential symptoms of internal state corruption.
    47  func CheckNodeStatusAt(snapshot Snapshot, id flow.Identifier, checks ...flow.IdentityFilter) (bool, error) {
    48  	identity, err := snapshot.Identity(id)
    49  	if IsIdentityNotFound(err) {
    50  		return false, nil
    51  	}
    52  	if err != nil {
    53  		return false, fmt.Errorf("could not retrieve node identity (id=%x): %w)", id, err)
    54  	}
    55  
    56  	for _, check := range checks {
    57  		if !check(identity) {
    58  			return false, nil
    59  		}
    60  	}
    61  
    62  	return true, nil
    63  }
    64  
    65  // IsSporkRootSnapshot returns whether the given snapshot is the state snapshot
    66  // representing the initial state for a spork.
    67  func IsSporkRootSnapshot(snapshot Snapshot) (bool, error) {
    68  	sporkRootBlockHeight, err := snapshot.Params().SporkRootBlockHeight()
    69  	if err != nil {
    70  		return false, fmt.Errorf("could not get snapshot root block height: %w", err)
    71  	}
    72  	head, err := snapshot.Head()
    73  	if err != nil {
    74  		return false, fmt.Errorf("could not get snapshot head: %w", err)
    75  	}
    76  	return head.Height == sporkRootBlockHeight, nil
    77  }
    78  
    79  // PreviousEpochExists returns whether the previous epoch exists w.r.t. the given
    80  // state snapshot.
    81  // No errors are expected during normal operation.
    82  func PreviousEpochExists(snap Snapshot) (bool, error) {
    83  	_, err := snap.Epochs().Previous().Counter()
    84  	if errors.Is(err, ErrNoPreviousEpoch) {
    85  		return false, nil
    86  	}
    87  	if err != nil {
    88  		return false, fmt.Errorf("unexpected error checking previous epoch exists: %w", err)
    89  	}
    90  	return true, nil
    91  }
    92  
    93  // FindGuarantors decodes the signer indices from the guarantee, and finds the guarantor identifiers from protocol state
    94  // Expected Error returns during normal operations:
    95  //   - signature.InvalidSignerIndicesError if `signerIndices` does not encode a valid set of collection guarantors
    96  //   - state.ErrUnknownSnapshotReference if guarantee references an unknown block
    97  //   - protocol.ErrNextEpochNotCommitted if epoch has not been committed yet
    98  //   - protocol.ErrClusterNotFound if cluster is not found by the given chainID
    99  func FindGuarantors(state State, guarantee *flow.CollectionGuarantee) ([]flow.Identifier, error) {
   100  	snapshot := state.AtBlockID(guarantee.ReferenceBlockID)
   101  	epochs := snapshot.Epochs()
   102  	epoch := epochs.Current()
   103  	cluster, err := epoch.ClusterByChainID(guarantee.ChainID)
   104  
   105  	if err != nil {
   106  		return nil, fmt.Errorf(
   107  			"fail to retrieve collector clusters for guarantee (ReferenceBlockID: %v, ChainID: %v): %w",
   108  			guarantee.ReferenceBlockID, guarantee.ChainID, err)
   109  	}
   110  
   111  	guarantorIDs, err := signature.DecodeSignerIndicesToIdentifiers(cluster.Members().NodeIDs(), guarantee.SignerIndices)
   112  	if err != nil {
   113  		return nil, fmt.Errorf("could not decode signer indices for guarantee %v: %w", guarantee.ID(), err)
   114  	}
   115  
   116  	return guarantorIDs, nil
   117  }
   118  
   119  // OrderedSeals returns the seals in the input payload in ascending height order.
   120  // The Flow protocol has a variety of validity rules for `payload`. While we do not verify
   121  // payload validity in this function, the implementation is optimized for valid payloads,
   122  // where the heights of the sealed blocks form a continuous integer sequence (no gaps).
   123  // Per convention ['Vacuous Truth'], an empty set of seals is considered to be
   124  // ordered. Hence, if `payload.Seals` is empty, we return (nil, nil).
   125  // Expected Error returns during normal operations:
   126  //   - ErrMultipleSealsForSameHeight in case there are seals repeatedly sealing block at the same height
   127  //   - ErrDiscontinuousSeals in case there are height-gaps in the sealed blocks
   128  //   - storage.ErrNotFound if any of the seals references an unknown block
   129  func OrderedSeals(payload *flow.Payload, headers storage.Headers) ([]*flow.Seal, error) {
   130  	numSeals := uint64(len(payload.Seals))
   131  	if numSeals == 0 {
   132  		return nil, nil
   133  	}
   134  	heights := make([]uint64, numSeals)
   135  	minHeight := uint64(math.MaxUint64)
   136  	for i, seal := range payload.Seals {
   137  		header, err := headers.ByBlockID(seal.BlockID)
   138  		if err != nil {
   139  			return nil, fmt.Errorf("could not get block (id=%x) for seal: %w", seal.BlockID, err) // storage.ErrNotFound or exception
   140  		}
   141  		heights[i] = header.Height
   142  		if header.Height < minHeight {
   143  			minHeight = header.Height
   144  		}
   145  	}
   146  	// As seals in a valid payload must have consecutive heights, we can populate
   147  	// the ordered output by shifting by minHeight.
   148  	seals := make([]*flow.Seal, numSeals)
   149  	for i, seal := range payload.Seals {
   150  		idx := heights[i] - minHeight
   151  		// (0) Per construction, `minHeight` is the smallest value in the `heights` slice. Hence, `idx ≥ 0`
   152  		// (1) But if there are gaps in the heights of the sealed blocks (byzantine inputs),
   153  		//    `idx` may reach/exceed `numSeals`. In this case, we respond with a `ErrDiscontinuousSeals`.
   154  		//     Thereby this function can tolerate all inputs without producing an 'out of range' panic.
   155  		// (2) In case of duplicates _and_ gaps, we might overwrite elements in `seals` while leaving
   156  		//     other elements as `nil`. We avoid the edge case of leaving `nil` elements in our output,
   157  		//     and instead, reject the input with an `ErrMultipleSealsForSameHeight`
   158  		if idx >= numSeals {
   159  			return nil, fmt.Errorf("sealed blocks' heights (unordered) %v : %w", heights, ErrDiscontinuousSeals)
   160  		}
   161  		if seals[idx] != nil {
   162  			return nil, fmt.Errorf("duplicate seal for block height %d: %w", heights[i], ErrMultipleSealsForSameHeight)
   163  		}
   164  		seals[idx] = seal
   165  	}
   166  	return seals, nil
   167  }