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