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 }