github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/validation/seal_validator.go (about) 1 package validation 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/crypto/hash" 7 8 "github.com/onflow/flow-go/engine" 9 "github.com/onflow/flow-go/model/flow" 10 "github.com/onflow/flow-go/module" 11 "github.com/onflow/flow-go/module/signature" 12 "github.com/onflow/flow-go/state/fork" 13 "github.com/onflow/flow-go/state/protocol" 14 "github.com/onflow/flow-go/storage" 15 ) 16 17 // sealValidator holds all needed context for checking seal 18 // validity against current protocol state. 19 type sealValidator struct { 20 state protocol.State 21 assigner module.ChunkAssigner 22 signatureHasher hash.Hasher 23 seals storage.Seals 24 headers storage.Headers 25 index storage.Index 26 results storage.ExecutionResults 27 sealingConfigsGetter module.SealingConfigsGetter // number of required approvals per chunk to construct a seal 28 metrics module.ConsensusMetrics 29 } 30 31 func NewSealValidator( 32 state protocol.State, 33 headers storage.Headers, 34 index storage.Index, 35 results storage.ExecutionResults, 36 seals storage.Seals, 37 assigner module.ChunkAssigner, 38 sealingConfigsGetter module.SealingConfigsGetter, 39 metrics module.ConsensusMetrics, 40 ) *sealValidator { 41 return &sealValidator{ 42 state: state, 43 assigner: assigner, 44 signatureHasher: signature.NewBLSHasher(signature.ResultApprovalTag), 45 headers: headers, 46 results: results, 47 seals: seals, 48 index: index, 49 sealingConfigsGetter: sealingConfigsGetter, 50 metrics: metrics, 51 } 52 } 53 54 func (s *sealValidator) verifySealSignature(aggregatedSignatures *flow.AggregatedSignature, 55 chunk *flow.Chunk, executionResultID flow.Identifier) error { 56 // TODO: replace implementation once proper aggregation is used for Verifiers' attestation signatures. 57 58 atst := flow.Attestation{ 59 BlockID: chunk.BlockID, 60 ExecutionResultID: executionResultID, 61 ChunkIndex: chunk.Index, 62 } 63 atstID := atst.ID() 64 65 for i, signature := range aggregatedSignatures.VerifierSignatures { 66 signerId := aggregatedSignatures.SignerIDs[i] 67 68 nodeIdentity, err := identityForNode(s.state, chunk.BlockID, signerId) 69 if err != nil { 70 return err 71 } 72 73 valid, err := nodeIdentity.StakingPubKey.Verify(signature, atstID[:], s.signatureHasher) 74 if err != nil { 75 return fmt.Errorf("failed to verify signature: %w", err) 76 } 77 78 if !valid { 79 return engine.NewInvalidInputErrorf("Invalid signature for (%x)", nodeIdentity.NodeID) 80 } 81 } 82 83 return nil 84 } 85 86 // Validate checks the compliance of the payload seals and returns the last 87 // valid seal on the fork up to and including `candidate`. To be valid, we 88 // require that seals 89 // 1) form a valid chain on top of the last seal as of the parent of `candidate` and 90 // 2) correspond to blocks and execution results incorporated on the current fork. 91 // 3) has valid signatures for all of its chunks. 92 // 93 // Note that we don't explicitly check that sealed results satisfy the sub-graph 94 // check. Nevertheless, correctness in this regard is guaranteed because: 95 // - We only allow seals that correspond to ExecutionReceipts that were 96 // incorporated in this fork. 97 // - We only include ExecutionReceipts whose results pass the sub-graph check 98 // (as part of ReceiptValidator). 99 // 100 // => Therefore, only seals whose results pass the sub-graph check will be 101 // allowed. 102 func (s *sealValidator) Validate(candidate *flow.Block) (*flow.Seal, error) { 103 header := candidate.Header 104 payload := candidate.Payload 105 parentID := header.ParentID 106 107 // Get the latest seal in the fork that ends with the candidate's parent. 108 // The protocol state saves this information for each block that has been 109 // successfully added to the chain tree (even when the added block does not 110 // itself contain a seal). Per prerequisite of this method, the candidate block's parent must 111 // be part of the main chain (without any missing ancestors). For every block B that is 112 // attached to the main chain, we store the latest seal in the fork that ends with B. 113 // Therefore, _not_ finding the latest sealed block of the parent constitutes 114 // a fatal internal error. 115 lastSealUpToParent, err := s.seals.HighestInFork(parentID) 116 if err != nil { 117 return nil, fmt.Errorf("could not retrieve parent seal (%x): %w", parentID, err) 118 } 119 120 // if there is no seal in the block payload, use the last sealed block of 121 // the parent block as the last sealed block of the given block. 122 if len(payload.Seals) == 0 { 123 return lastSealUpToParent, nil 124 } 125 126 // map each seal to the block it is sealing for easy lookup; we will need to 127 // successfully connect _all_ of these seals to the last sealed block for 128 // the payload to be valid 129 byBlock := make(map[flow.Identifier]*flow.Seal) 130 for _, seal := range payload.Seals { 131 byBlock[seal.BlockID] = seal 132 } 133 if len(payload.Seals) != len(byBlock) { 134 return nil, engine.NewInvalidInputError("multiple seals for the same block") 135 } 136 137 // incorporatedResults collects execution results that are incorporated in unsealed 138 // blocks; CAUTION: some of these incorporated results might already be sealed. 139 incorporatedResults := make(map[flow.Identifier]*flow.IncorporatedResult) 140 141 // IDs of unsealed blocks on the fork 142 var unsealedBlockIDs []flow.Identifier 143 144 // Traverse fork starting from the lowest unsealed block (included) up to the parent block (included). 145 // For each visited block collect: IncorporatedResults and block ID 146 forkCollector := func(header *flow.Header) error { 147 blockID := header.ID() 148 if blockID == parentID { 149 // Important protocol edge case: There must be at least one block in between the block incorporating 150 // a result and the block sealing the result. This is because we need the Source of Randomness for 151 // the block that _incorporates_ the result, to compute the verifier assignment. Therefore, we require 152 // that the block _incorporating_ the result has at least one child in the fork, _before_ we include 153 // the seal. Thereby, we guarantee that a verifier assignment can be computed without needing 154 // information from the block that we are just constructing. Hence, we don't allow results to be 155 // sealed that were incorporated in the immediate parent which is being extended. 156 return nil 157 } 158 159 // keep track of blocks on the fork 160 unsealedBlockIDs = append(unsealedBlockIDs, blockID) 161 162 // Collect incorporated results 163 payloadIndex, err := s.index.ByBlockID(blockID) 164 if err != nil { 165 return fmt.Errorf("could not get block payload %x: %w", blockID, err) 166 } 167 for _, resultID := range payloadIndex.ResultIDs { 168 result, err := s.results.ByID(resultID) 169 if err != nil { 170 return fmt.Errorf("internal error fetching result %v incorporated in block %v: %w", resultID, blockID, err) 171 } 172 incorporatedResults[resultID] = flow.NewIncorporatedResult(blockID, result) 173 } 174 return nil 175 } 176 err = fork.TraverseForward(s.headers, parentID, forkCollector, fork.ExcludingBlock(lastSealUpToParent.BlockID)) 177 if err != nil { 178 return nil, fmt.Errorf("internal error collecting incorporated results from unsealed fork: %w", err) 179 } 180 181 // We do _not_ add the results from the candidate block's own payload to incorporatedResults. 182 // That's because a result requires to be added to a block first in order to determine 183 // its chunk assignment for verification. Therefore a seal can only be added in the 184 // next block or after. In other words, a receipt and its seal can't be the same block. 185 186 // Iterate through the unsealed blocks, starting at the one with lowest 187 // height and try to create a chain of valid seals. 188 latestSeal := lastSealUpToParent 189 for _, blockID := range unsealedBlockIDs { 190 // if there are no more seals left, we can exit earlier 191 if len(byBlock) == 0 { 192 return latestSeal, nil 193 } 194 195 // the chain of seals should not skip blocks 196 seal, found := byBlock[blockID] 197 if !found { 198 return nil, engine.NewInvalidInputErrorf("chain of seals broken (missing seal for block %x)", blockID) 199 } 200 delete(byBlock, blockID) 201 202 // the sealed result must be previously incorporated in the fork: 203 incorporatedResult, ok := incorporatedResults[seal.ResultID] 204 if !ok { 205 return nil, engine.NewInvalidInputErrorf("seal %x does not correspond to a result on this fork", seal.ID()) 206 } 207 208 // check the integrity of the seal (by itself) 209 err := s.validateSeal(seal, incorporatedResult) 210 if err != nil { 211 if !engine.IsInvalidInputError(err) { 212 return nil, fmt.Errorf("unexpected internal error while validating seal %x for result %x for block %x: %w", 213 seal.ID(), seal.ResultID, seal.BlockID, err) 214 } 215 return nil, fmt.Errorf("invalid seal %x for result %x for block %x: %w", seal.ID(), seal.ResultID, seal.BlockID, err) 216 } 217 218 // check that the sealed execution results form a chain 219 if incorporatedResult.Result.PreviousResultID != latestSeal.ResultID { 220 return nil, engine.NewInvalidInputErrorf("sealed execution results for block %x does not connect to previously sealed result", blockID) 221 } 222 223 latestSeal = seal 224 } 225 226 // it is illegal to include more seals than there are unsealed blocks in the fork 227 if len(byBlock) > 0 { 228 return nil, engine.NewInvalidInputErrorf("more seals then unsealed blocks in fork (left: %d)", len(byBlock)) 229 } 230 231 return latestSeal, nil 232 } 233 234 // validateSeal performs integrity checks of single seal. To be valid, we 235 // require that seal: 236 // 1) Contains correct number of approval signatures, one aggregated sig for each chunk. 237 // 2) Every aggregated signature contains valid signer ids. module.ChunkAssigner is used to perform this check. 238 // 3) Every aggregated signature contains valid signatures. 239 // Returns: 240 // * nil - in case of success 241 // * engine.InvalidInputError - in case of malformed seal 242 // * exception - in case of unexpected error 243 func (s *sealValidator) validateSeal(seal *flow.Seal, incorporatedResult *flow.IncorporatedResult) error { 244 executionResult := incorporatedResult.Result 245 246 // check that each chunk has an AggregatedSignature 247 if len(seal.AggregatedApprovalSigs) != executionResult.Chunks.Len() { 248 return engine.NewInvalidInputErrorf("mismatching signatures, expected: %d, got: %d", 249 executionResult.Chunks.Len(), 250 len(seal.AggregatedApprovalSigs)) 251 } 252 253 assignments, err := s.assigner.Assign(executionResult, incorporatedResult.IncorporatedBlockID) 254 if err != nil { 255 return fmt.Errorf("failed to retrieve verifier assignment for result %x incorporated in block %x: %w", 256 executionResult.ID(), incorporatedResult.IncorporatedBlockID, err) 257 } 258 259 // Check that each AggregatedSignature has enough valid signatures from 260 // verifiers that were assigned to the corresponding chunk. 261 executionResultID := executionResult.ID() 262 emergencySealed := false 263 for _, chunk := range executionResult.Chunks { 264 chunkSigs := &seal.AggregatedApprovalSigs[chunk.Index] 265 266 // for each approving Verification Node (SignerID), we expect exactly one signature 267 numberApprovers := chunkSigs.CardinalitySignerSet() 268 if len(chunkSigs.SignerIDs) != numberApprovers { 269 return engine.NewInvalidInputErrorf("chunk %d contains repeated approvals from the same verifier", chunk.Index) 270 } 271 if len(chunkSigs.VerifierSignatures) != numberApprovers { 272 return engine.NewInvalidInputErrorf("expecting signatures from %d approvers but got %d", numberApprovers, len(chunkSigs.VerifierSignatures)) 273 } 274 275 // the chunk must have been approved by at least the minimally 276 // required number of Verification Nodes 277 requireApprovalsForSealConstruction := s.sealingConfigsGetter.RequireApprovalsForSealConstructionDynamicValue() 278 requireApprovalsForSealVerification := s.sealingConfigsGetter.RequireApprovalsForSealVerificationConst() 279 if uint(numberApprovers) < requireApprovalsForSealConstruction { 280 if uint(numberApprovers) >= requireApprovalsForSealVerification { 281 // Emergency sealing is a _temporary_ fallback to reduce the probability of 282 // sealing halts due to bugs in the verification nodes, where they don't 283 // approve a chunk even though they should (false-negative). 284 // TODO: remove this fallback for BFT 285 emergencySealed = true 286 } else { 287 return engine.NewInvalidInputErrorf("chunk %d has %d approvals but require at least %d", 288 chunk.Index, numberApprovers, requireApprovalsForSealVerification) 289 } 290 } 291 292 // only Verification Nodes that were assigned to the chunk are allowed to approve it 293 for _, signerId := range chunkSigs.SignerIDs { 294 if !assignments.HasVerifier(chunk, signerId) { 295 return engine.NewInvalidInputErrorf("invalid signer id at chunk: %d", chunk.Index) 296 } 297 } 298 299 // Verification Nodes' approval signatures must be valid 300 err := s.verifySealSignature(chunkSigs, chunk, executionResultID) 301 if err != nil { 302 return fmt.Errorf("invalid seal signature: %w", err) 303 } 304 } 305 306 // TODO: remove this metric after emergency-sealing development 307 if emergencySealed { 308 s.metrics.EmergencySeal() 309 } 310 311 return nil 312 }