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 }