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  }