github.com/koko1123/flow-go-1@v0.29.6/model/flow/sealing_segment.go (about) 1 package flow 2 3 import ( 4 "fmt" 5 ) 6 7 // SealingSegment is the chain segment such that the last block (greatest height) 8 // is this snapshot's reference block and the first (least height) is the most 9 // recently sealed block as of this snapshot (ie. the block referenced by LatestSeal). 10 // 11 // In other words, the most recently incorporated seal as of the highest block 12 // references the lowest block. The highest block does not need to contain this seal. 13 // 14 // Example 1 - E seals A: 15 // 16 // A <- B <- C <- D <- E(SA) 17 // 18 // The above sealing segment's last block (E) has a seal for block A, which is 19 // the first block of the sealing segment. 20 // 21 // Example 2 - E contains no seals, but latest seal prior to E seals A: 22 // 23 // A <- B <- C <- D(SA) <- E 24 // 25 // Example 3 - E contains multiple seals 26 // 27 // B <- C <- D <- E(SA, SB) 28 // 29 // MINIMALITY REQUIREMENT: 30 // Note that block B is the highest sealed block as of E. Therefore, the 31 // sealing segment's lowest block must be B. Essentially, this is a minimality 32 // requirement for the history: it shouldn't be longer than necessary. So 33 // extending the chain segment above to A <- B <- C <- D <- E(SA, SB) would 34 // _not_ yield a valid SealingSegment. 35 // 36 // ROOT SEALING SEGMENTS: 37 // Root sealing segments are sealing segments which contain the root block: 38 // * the root block is a self-sealing block with an empty payload 39 // * the root block must be the first block (least height) in the segment 40 // * no blocks in the segment may contain any seals (by the minimality requirement) 41 // * it is possible (but not necessary) for root sealing segments to contain only the root block 42 // 43 // Example 1 - one self-sealing root block 44 // 45 // ROOT 46 // 47 // The above sealing segment is the form of sealing segments within root snapshots, 48 // for example those snapshots used to bootstrap a new network, or spork. 49 // 50 // Example 2 - one self-sealing root block followed by any number of seal-less blocks 51 // 52 // ROOT <- A <- B 53 // 54 // All non-root sealing segments contain more than one block. 55 // Sealing segments are in ascending height order. 56 // 57 // In addition to storing the blocks within the sealing segment, as defined above, 58 // the SealingSegment structure also stores any resources which are referenced 59 // by blocks in the segment, but not included in the payloads of blocks within 60 // the segment. In particular: 61 // * results referenced by receipts within segment payloads 62 // * results referenced by seals within segment payloads 63 // * seals which represent the latest state commitment as of a segment block 64 type SealingSegment struct { 65 // Blocks contain the chain segment blocks in ascending height order. 66 Blocks []*Block 67 68 // ExecutionResults contain any results which are referenced by receipts 69 // or seals in the sealing segment, but not included in any segment block 70 // payloads. 71 // 72 // Due to decoupling of execution receipts from execution results, 73 // it's possible that blocks from the sealing segment will be referring to 74 // execution results incorporated in blocks that aren't part of the segment. 75 ExecutionResults ExecutionResultList 76 77 // LatestSeals is a mapping from block ID to the ID of the latest seal 78 // incorporated as of that block. Note: we store the seals' IDs here 79 // (instead of the full seals), because the seals for all blocks, except for 80 // the lowest one, are contained in the blocks of the sealing segment. 81 LatestSeals map[Identifier]Identifier 82 83 // FirstSeal contains the latest seal as of the first block in the segment. 84 // Per convention, this field holds a seal that was included _prior_ to the 85 // first block of the sealing segment. If the first block in the segment 86 // contains a seal, then this field is `nil`. 87 // This information is needed for the `Commit` method of protocol snapshot 88 // to return the sealed state, when the first block contains no seal. 89 FirstSeal *Seal 90 } 91 92 func (segment *SealingSegment) Highest() *Block { 93 return segment.Blocks[len(segment.Blocks)-1] 94 } 95 96 func (segment *SealingSegment) Lowest() *Block { 97 return segment.Blocks[0] 98 } 99 100 // FinalizedSeal returns the seal that seals the lowest block. 101 // Per specification, this seal must be included in a SealingSegment. 102 // The SealingSegment must be validated. 103 // No errors are expected during normal operation. 104 func (segment *SealingSegment) FinalizedSeal() (*Seal, error) { 105 if isRootSegment(segment.LatestSeals) { 106 return segment.FirstSeal, nil 107 } 108 109 seal, err := findLatestSealForLowestBlock(segment.Blocks, segment.LatestSeals) 110 if err != nil { 111 return nil, err 112 } 113 114 // sanity check 115 if seal.BlockID != segment.Lowest().ID() { 116 return nil, fmt.Errorf("finalized seal should seal the lowest block %v, but actually is to seal %v", 117 segment.Lowest().ID(), seal.BlockID) 118 } 119 return seal, nil 120 } 121 122 // Validate validates the sealing segment structure and returns an error if 123 // the segment isn't valid. This is done by re-building the segment from scratch, 124 // re-using the validation logic already present in the SealingSegmentBuilder. 125 // The node logic requires a valid sealing segment to bootstrap. 126 // No errors are expected during normal operation. 127 func (segment *SealingSegment) Validate() error { 128 129 // populate lookup of seals and results in the segment to satisfy builder 130 seals := make(map[Identifier]*Seal) 131 results := segment.ExecutionResults.Lookup() 132 133 if segment.FirstSeal != nil { 134 seals[segment.FirstSeal.ID()] = segment.FirstSeal 135 } 136 for _, block := range segment.Blocks { 137 for _, result := range block.Payload.Results { 138 results[result.ID()] = result 139 } 140 for _, seal := range block.Payload.Seals { 141 seals[seal.ID()] = seal 142 } 143 } 144 145 getResult := func(resultID Identifier) (*ExecutionResult, error) { 146 result, ok := results[resultID] 147 if !ok { 148 return nil, fmt.Errorf("result (id=%x) not found in segment", resultID) 149 } 150 return result, nil 151 } 152 getSeal := func(blockID Identifier) (*Seal, error) { 153 sealID, ok := segment.LatestSeals[blockID] 154 if !ok { 155 return nil, fmt.Errorf("seal for block (id=%x) not found in segment", blockID) 156 } 157 seal, ok := seals[sealID] 158 if !ok { 159 return nil, fmt.Errorf("seal (id=%x) not found in segment at block %x", sealID, blockID) 160 } 161 return seal, nil 162 } 163 164 builder := NewSealingSegmentBuilder(getResult, getSeal) 165 for _, block := range segment.Blocks { 166 err := builder.AddBlock(block) 167 if err != nil { 168 return fmt.Errorf("invalid segment: %w", err) 169 } 170 } 171 _, err := builder.SealingSegment() 172 if err != nil { 173 return fmt.Errorf("invalid segment: %w", err) 174 } 175 return nil 176 } 177 178 var ( 179 ErrSegmentMissingSeal = fmt.Errorf("sealing segment failed sanity check: missing seal referenced by segment") 180 ErrSegmentBlocksWrongLen = fmt.Errorf("sealing segment failed sanity check: non-root sealing segment must have at least 2 blocks") 181 ErrSegmentInvalidBlockHeight = fmt.Errorf("sealing segment failed sanity check: blocks must be in ascending order") 182 ErrSegmentResultLookup = fmt.Errorf("failed to lookup execution result") 183 ErrSegmentSealLookup = fmt.Errorf("failed to lookup seal") 184 ) 185 186 // GetResultFunc is a getter function for results by ID. 187 // No errors are expected during normal operation. 188 type GetResultFunc func(resultID Identifier) (*ExecutionResult, error) 189 190 // GetSealByBlockIDFunc is a getter function for seals by block ID, returning 191 // the latest seals incorporated as of the given block. 192 // No errors are expected during normal operation. 193 type GetSealByBlockIDFunc func(blockID Identifier) (*Seal, error) 194 195 // SealingSegmentBuilder is a utility for incrementally building a sealing segment. 196 type SealingSegmentBuilder struct { 197 // access to storage to read referenced by not included resources 198 resultLookup GetResultFunc 199 sealByBlockIDLookup GetSealByBlockIDFunc 200 // keep track of resources included in payloads 201 includedResults map[Identifier]struct{} 202 // resources to include in the sealing segment 203 blocks []*Block 204 results []*ExecutionResult 205 latestSeals map[Identifier]Identifier 206 firstSeal *Seal 207 } 208 209 // AddBlock appends a block to the sealing segment under construction. 210 // No errors are expected during normal operation. 211 func (builder *SealingSegmentBuilder) AddBlock(block *Block) error { 212 // sanity check: block should be 1 height higher than current highest 213 if !builder.isValidHeight(block) { 214 return fmt.Errorf("invalid block height (%d): %w", block.Header.Height, ErrSegmentInvalidBlockHeight) 215 } 216 blockID := block.ID() 217 218 // a block might contain receipts or seals that refer to results that are included in blocks 219 // whose height is below the first block of the segment. 220 // In order to include those missing results into the segment, we construct a list of those 221 // missing result IDs referenced by this block 222 missingResultIDs := make(map[Identifier]struct{}) 223 224 // for the first (lowest) block, if it contains no seal, store the latest 225 // seal incorporated prior to the first block 226 if len(builder.blocks) == 0 { 227 if len(block.Payload.Seals) == 0 { 228 seal, err := builder.sealByBlockIDLookup(blockID) 229 if err != nil { 230 return fmt.Errorf("%w: %v", ErrSegmentSealLookup, err) 231 } 232 builder.firstSeal = seal 233 // add first seal result ID here, since it isn't in payload 234 missingResultIDs[seal.ResultID] = struct{}{} 235 } 236 } 237 238 // index the latest seal for this block 239 latestSeal, err := builder.sealByBlockIDLookup(blockID) 240 if err != nil { 241 return fmt.Errorf("%w: %v", ErrSegmentSealLookup, err) 242 } 243 builder.latestSeals[blockID] = latestSeal.ID() 244 245 // cache included results and seals 246 // they could be referenced in a future block in the segment 247 for _, result := range block.Payload.Results { 248 builder.includedResults[result.ID()] = struct{}{} 249 } 250 251 for _, receipt := range block.Payload.Receipts { 252 if _, ok := builder.includedResults[receipt.ResultID]; !ok { 253 missingResultIDs[receipt.ResultID] = struct{}{} 254 } 255 } 256 for _, seal := range block.Payload.Seals { 257 if _, ok := builder.includedResults[seal.ResultID]; !ok { 258 missingResultIDs[seal.ResultID] = struct{}{} 259 } 260 } 261 262 // add the missing results 263 for resultID := range missingResultIDs { 264 result, err := builder.resultLookup(resultID) 265 266 if err != nil { 267 return fmt.Errorf("%w: (%x) %v", ErrSegmentResultLookup, resultID, err) 268 } 269 builder.addExecutionResult(result) 270 builder.includedResults[resultID] = struct{}{} 271 } 272 273 builder.blocks = append(builder.blocks, block) 274 return nil 275 } 276 277 // AddExecutionResult adds result to executionResults 278 func (builder *SealingSegmentBuilder) addExecutionResult(result *ExecutionResult) { 279 builder.results = append(builder.results, result) 280 } 281 282 // SealingSegment completes building the sealing segment, validating the segment 283 // constructed so far, and returning it as a SealingSegment if it is valid. 284 // 285 // All errors indicate the SealingSegmentBuilder internal state does not represent 286 // a valid sealing segment. 287 // No errors are expected during normal operation. 288 func (builder *SealingSegmentBuilder) SealingSegment() (*SealingSegment, error) { 289 if err := builder.validateSegment(); err != nil { 290 return nil, fmt.Errorf("failed to validate sealing segment: %w", err) 291 } 292 293 return &SealingSegment{ 294 Blocks: builder.blocks, 295 ExecutionResults: builder.results, 296 LatestSeals: builder.latestSeals, 297 FirstSeal: builder.firstSeal, 298 }, nil 299 } 300 301 // isValidHeight returns true iff block is exactly 1 height higher than the current highest block in the segment. 302 func (builder *SealingSegmentBuilder) isValidHeight(block *Block) bool { 303 if builder.highest() == nil { 304 return true 305 } 306 307 return block.Header.Height == builder.highest().Header.Height+1 308 } 309 310 // validateRootSegment will check that the current builder state represents a valid 311 // root sealing segment. In particular: 312 // * the root block must be the first block (least height) in the segment 313 // * no blocks in the segment may contain any seals (by the minimality requirement) 314 // 315 // All errors indicate an invalid root segment, and either a bug in SealingSegmentBuilder 316 // or a corrupted underlying protocol state. 317 // No errors are expected during normal operation. 318 func (builder *SealingSegmentBuilder) validateRootSegment() error { 319 if len(builder.blocks) == 0 { 320 return fmt.Errorf("root segment must have at least 1 block") 321 } 322 if builder.lowest().Header.View != 0 { 323 return fmt.Errorf("root block has unexpected view (%d != 0)", builder.lowest().Header.View) 324 } 325 if len(builder.results) != 1 { 326 return fmt.Errorf("expected %d results, got %d", 1, len(builder.results)) 327 } 328 if builder.firstSeal == nil { 329 return fmt.Errorf("firstSeal must not be nil for root segment") 330 } 331 if builder.results[0].BlockID != builder.lowest().ID() { 332 return fmt.Errorf("result (block_id=%x) is not for root block (id=%x)", builder.results[0].BlockID, builder.lowest().ID()) 333 } 334 if builder.results[0].ID() != builder.firstSeal.ResultID { 335 return fmt.Errorf("firstSeal (result_id=%x) is not for root result (id=%x)", builder.firstSeal.ResultID, builder.results[0].ID()) 336 } 337 if builder.results[0].BlockID != builder.firstSeal.BlockID { 338 return fmt.Errorf("root seal (block_id=%x) references different block than root result (block_id=%x)", builder.firstSeal.BlockID, builder.results[0].BlockID) 339 } 340 for _, block := range builder.blocks { 341 if len(block.Payload.Seals) > 0 { 342 return fmt.Errorf("root segment cannot contain blocks with seals (minimality requirement) - block (height=%d,id=%x) has %d seals", 343 block.Header.Height, block.ID(), len(block.Payload.Seals)) 344 } 345 } 346 return nil 347 } 348 349 // validateSegment will validate if builder satisfies conditions for a valid sealing segment. 350 // No errors are expected during normal operation. 351 func (builder *SealingSegmentBuilder) validateSegment() error { 352 // sealing cannot be empty 353 if len(builder.blocks) == 0 { 354 return fmt.Errorf("expect at least 2 blocks in a sealing segment or 1 block in the case of root segments, but got an empty sealing segment: %w", ErrSegmentBlocksWrongLen) 355 } 356 357 // if root sealing segment, use different validation 358 if isRootSegment(builder.latestSeals) { 359 err := builder.validateRootSegment() 360 if err != nil { 361 return fmt.Errorf("invalid root segment: %w", err) 362 } 363 return nil 364 } 365 366 // validate the latest seal is for the lowest block 367 _, err := findLatestSealForLowestBlock(builder.blocks, builder.latestSeals) 368 if err != nil { 369 return fmt.Errorf("sealing segment missing seal (lowest block id: %x) (highest block id: %x) %v: %w", builder.lowest().ID(), builder.highest().ID(), err, ErrSegmentMissingSeal) 370 } 371 372 return nil 373 } 374 375 // highest returns the highest block in segment. 376 func (builder *SealingSegmentBuilder) highest() *Block { 377 if len(builder.blocks) == 0 { 378 return nil 379 } 380 381 return builder.blocks[len(builder.blocks)-1] 382 } 383 384 // lowest returns the lowest block in segment. 385 func (builder *SealingSegmentBuilder) lowest() *Block { 386 return builder.blocks[0] 387 } 388 389 // NewSealingSegmentBuilder returns *SealingSegmentBuilder 390 func NewSealingSegmentBuilder(resultLookup GetResultFunc, sealLookup GetSealByBlockIDFunc) *SealingSegmentBuilder { 391 return &SealingSegmentBuilder{ 392 resultLookup: resultLookup, 393 sealByBlockIDLookup: sealLookup, 394 includedResults: make(map[Identifier]struct{}), 395 latestSeals: make(map[Identifier]Identifier), 396 blocks: make([]*Block, 0), 397 results: make(ExecutionResultList, 0), 398 } 399 } 400 401 // findLatestSealForLowestBlock finds the seal for the lowest block. 402 // As a sanity check, the method confirms that this seal is the latest seal as of the highest block. 403 // In other words, this function checks that the sealing segment's history is minimal. 404 // Inputs: 405 // - `blocks` is the continuous sequence of blocks that form the sealing segment 406 // - `latestSeals` holds for each block the identifier of the latest seal included in the fork as of this block 407 // 408 // CAUTION: this method is only applicable for non-root sealing segments, where at least one block 409 // was sealed after the root block. 410 // Examples: 411 // 412 // A <- B <- C <- D(seal_A) ==> valid 413 // A <- B <- C <- D(seal_A) <- E() ==> valid 414 // A <- B <- C <- D(seal_A,seal_B) ==> invalid, because latest seal is B, but lowest block is A 415 // A <- B <- C <- D(seal_X,seal_A) ==> valid, because it's OK for block X to be unknown 416 // A <- B <- C <- D(seal_A) <- E(seal_B) ==> invalid, because latest seal is B, but lowest block is A 417 // A(seal_A) ==> invalid, because this is impossible for non-root sealing segments 418 // 419 // The node logic requires a valid sealing segment to bootstrap. There are no 420 // errors expected during normal operations. 421 func findLatestSealForLowestBlock(blocks []*Block, latestSeals map[Identifier]Identifier) (*Seal, error) { 422 lowestBlockID := blocks[0].ID() 423 highestBlockID := blocks[len(blocks)-1].ID() 424 425 // get the ID of the latest seal for highest block 426 latestSealID := latestSeals[highestBlockID] 427 428 // find the seal within the block payloads 429 for i := len(blocks) - 1; i >= 0; i-- { 430 block := blocks[i] 431 // look for latestSealID in the payload 432 for _, seal := range block.Payload.Seals { 433 // if we found the latest seal, confirm it seals lowest 434 if seal.ID() == latestSealID { 435 if seal.BlockID == lowestBlockID { 436 return seal, nil 437 } 438 return nil, fmt.Errorf("invalid segment: segment contain seal for block %v, but doesn't match lowest block %v", 439 seal.BlockID, lowestBlockID) 440 } 441 } 442 443 // the latest seal must be found in a block that has a seal when traversing blocks 444 // backwards from higher height to lower height. 445 // otherwise, the sealing segment is invalid 446 if len(block.Payload.Seals) > 0 { 447 return nil, fmt.Errorf("invalid segment: segment's last block contain seal %v, but doesn't match latestSealID: %v", 448 block.Payload.Seals[0].ID(), latestSealID) 449 } 450 } 451 452 return nil, fmt.Errorf("invalid segment: seal %v not found", latestSealID) 453 } 454 455 // isRootSegment returns true if the input latestSeals map represents a root segment. 456 // The implementation makes use of the fact that root sealing segments uniquely 457 // have the same latest seal, for all blocks in the segment. 458 func isRootSegment(latestSeals map[Identifier]Identifier) bool { 459 var rootSealID Identifier 460 // set root seal ID to the latest seal value for any block in the segment 461 for _, sealID := range latestSeals { 462 rootSealID = sealID 463 break 464 } 465 // then, verify all other blocks have the same latest seal 466 for _, sealID := range latestSeals { 467 if sealID != rootSealID { 468 return false 469 } 470 } 471 return true 472 }