github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/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.HasInitialWeight[flow.Identity](true),
    21  		filter.IsValidCurrentEpochParticipant,
    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.HasInitialWeight[flow.Identity](true),
    36  		filter.IsValidCurrentEpochParticipant,
    37  		filter.HasRole[flow.Identity](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[flow.Identity]) (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 := snapshot.Params().SporkRootBlockHeight()
    69  	head, err := snapshot.Head()
    70  	if err != nil {
    71  		return false, fmt.Errorf("could not get snapshot head: %w", err)
    72  	}
    73  	return head.Height == sporkRootBlockHeight, nil
    74  }
    75  
    76  // PreviousEpochExists returns whether the previous epoch exists w.r.t. the given
    77  // state snapshot.
    78  // No errors are expected during normal operation.
    79  func PreviousEpochExists(snap Snapshot) (bool, error) {
    80  	_, err := snap.Epochs().Previous().Counter()
    81  	if errors.Is(err, ErrNoPreviousEpoch) {
    82  		return false, nil
    83  	}
    84  	if err != nil {
    85  		return false, fmt.Errorf("unexpected error checking previous epoch exists: %w", err)
    86  	}
    87  	return true, nil
    88  }
    89  
    90  // FindGuarantors decodes the signer indices from the guarantee, and finds the guarantor identifiers from protocol state
    91  // Expected Error returns during normal operations:
    92  //   - signature.InvalidSignerIndicesError if `signerIndices` does not encode a valid set of collection guarantors
    93  //   - state.ErrUnknownSnapshotReference if guarantee references an unknown block
    94  //   - protocol.ErrNextEpochNotCommitted if epoch has not been committed yet
    95  //   - protocol.ErrClusterNotFound if cluster is not found by the given chainID
    96  func FindGuarantors(state State, guarantee *flow.CollectionGuarantee) ([]flow.Identifier, error) {
    97  	snapshot := state.AtBlockID(guarantee.ReferenceBlockID)
    98  	epochs := snapshot.Epochs()
    99  	epoch := epochs.Current()
   100  	cluster, err := epoch.ClusterByChainID(guarantee.ChainID)
   101  
   102  	if err != nil {
   103  		return nil, fmt.Errorf(
   104  			"fail to retrieve collector clusters for guarantee (ReferenceBlockID: %v, ChainID: %v): %w",
   105  			guarantee.ReferenceBlockID, guarantee.ChainID, err)
   106  	}
   107  
   108  	guarantorIDs, err := signature.DecodeSignerIndicesToIdentifiers(cluster.Members().NodeIDs(), guarantee.SignerIndices)
   109  	if err != nil {
   110  		return nil, fmt.Errorf("could not decode signer indices for guarantee %v: %w", guarantee.ID(), err)
   111  	}
   112  
   113  	return guarantorIDs, nil
   114  }
   115  
   116  // OrderedSeals returns the seals in the input payload in ascending height order.
   117  // The Flow protocol has a variety of validity rules for `payload`. While we do not verify
   118  // payload validity in this function, the implementation is optimized for valid payloads,
   119  // where the heights of the sealed blocks form a continuous integer sequence (no gaps).
   120  // Per convention ['Vacuous Truth'], an empty set of seals is considered to be
   121  // ordered. Hence, if `payload.Seals` is empty, we return (nil, nil).
   122  // Expected Error returns during normal operations:
   123  //   - ErrMultipleSealsForSameHeight in case there are seals repeatedly sealing block at the same height
   124  //   - ErrDiscontinuousSeals in case there are height-gaps in the sealed blocks
   125  //   - storage.ErrNotFound if any of the seals references an unknown block
   126  func OrderedSeals(blockSeals []*flow.Seal, headers storage.Headers) ([]*flow.Seal, error) {
   127  	numSeals := uint64(len(blockSeals))
   128  	if numSeals == 0 {
   129  		return nil, nil
   130  	}
   131  	heights := make([]uint64, numSeals)
   132  	minHeight := uint64(math.MaxUint64)
   133  	for i, seal := range blockSeals {
   134  		header, err := headers.ByBlockID(seal.BlockID)
   135  		if err != nil {
   136  			return nil, fmt.Errorf("could not get block (id=%x) for seal: %w", seal.BlockID, err) // storage.ErrNotFound or exception
   137  		}
   138  		heights[i] = header.Height
   139  		if header.Height < minHeight {
   140  			minHeight = header.Height
   141  		}
   142  	}
   143  	// As seals in a valid payload must have consecutive heights, we can populate
   144  	// the ordered output by shifting by minHeight.
   145  	seals := make([]*flow.Seal, numSeals)
   146  	for i, seal := range blockSeals {
   147  		idx := heights[i] - minHeight
   148  		// (0) Per construction, `minHeight` is the smallest value in the `heights` slice. Hence, `idx ≥ 0`
   149  		// (1) But if there are gaps in the heights of the sealed blocks (byzantine inputs),
   150  		//    `idx` may reach/exceed `numSeals`. In this case, we respond with a `ErrDiscontinuousSeals`.
   151  		//     Thereby this function can tolerate all inputs without producing an 'out of range' panic.
   152  		// (2) In case of duplicates _and_ gaps, we might overwrite elements in `seals` while leaving
   153  		//     other elements as `nil`. We avoid the edge case of leaving `nil` elements in our output,
   154  		//     and instead, reject the input with an `ErrMultipleSealsForSameHeight`
   155  		if idx >= numSeals {
   156  			return nil, fmt.Errorf("sealed blocks' heights (unordered) %v : %w", heights, ErrDiscontinuousSeals)
   157  		}
   158  		if seals[idx] != nil {
   159  			return nil, fmt.Errorf("duplicate seal for block height %d: %w", heights[i], ErrMultipleSealsForSameHeight)
   160  		}
   161  		seals[idx] = seal
   162  	}
   163  	return seals, nil
   164  }