github.com/onflow/flow-go@v0.33.17/model/verification/chunkDataPackRequest.go (about)

     1  package verification
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/model/chunks"
     7  	"github.com/onflow/flow-go/model/flow"
     8  	"github.com/onflow/flow-go/model/flow/filter"
     9  )
    10  
    11  // ChunkDataPackRequest is an internal data structure in fetcher engine that is passed between the engine
    12  // and requester module. It conveys required information for requesting a chunk data pack.
    13  type ChunkDataPackRequest struct {
    14  	chunks.Locator // uniquely identifies chunk
    15  	ChunkDataPackRequestInfo
    16  }
    17  
    18  type ChunkDataPackRequestInfo struct {
    19  	ChunkID   flow.Identifier
    20  	Height    uint64              // block height of execution result of the chunk, used to drop chunk requests of sealed heights.
    21  	Agrees    flow.IdentifierList // execution node ids that generated the result of chunk.
    22  	Disagrees flow.IdentifierList // execution node ids that generated a conflicting result with result of chunk.
    23  	Targets   flow.IdentityList   // list of all execution nodes identity at the block height of this chunk (including non-responders).
    24  }
    25  
    26  // SampleTargets returns identifier of execution nodes that can be asked for the chunk data pack, based on
    27  // the agreeing and disagreeing execution nodes of the chunk data pack request.
    28  func (c ChunkDataPackRequestInfo) SampleTargets(count int) (flow.IdentifierList, error) {
    29  	// if there are enough receipts produced the same result (agrees), we sample from them.
    30  	if len(c.Agrees) >= count {
    31  		sample, err := c.Targets.Filter(filter.HasNodeID(c.Agrees...)).Sample(uint(count))
    32  		if err != nil {
    33  			return nil, fmt.Errorf("sampling target failed: %w", err)
    34  		}
    35  		return sample.NodeIDs(), nil
    36  	}
    37  
    38  	// since there is at least one agree, then usually, we just need `count - 1` extra nodes as backup.
    39  	// We pick these extra nodes randomly from the rest nodes who we haven't received its receipt.
    40  	// In the case where all other execution nodes has produced different results, then we will only
    41  	// fetch from the one produced the same result (the only agree)
    42  	need := uint(count - len(c.Agrees))
    43  
    44  	nonResponders, err := c.Targets.Filter(filter.Not(filter.HasNodeID(c.Disagrees...))).Sample(need)
    45  	if err != nil {
    46  		return nil, fmt.Errorf("sampling target failed: %w", err)
    47  	}
    48  	return append(c.Agrees, nonResponders.NodeIDs()...), nil
    49  }
    50  
    51  type ChunkDataPackRequestInfoList []*ChunkDataPackRequestInfo
    52  type ChunkDataPackRequestList []*ChunkDataPackRequest
    53  
    54  // ContainsChunkID returns true if list contains a request for chunk ID.
    55  func (c ChunkDataPackRequestList) ContainsChunkID(chunkID flow.Identifier) bool {
    56  	for _, request := range c {
    57  		if request.ChunkID == chunkID {
    58  			return true
    59  		}
    60  	}
    61  
    62  	return false
    63  }
    64  
    65  // ContainsLocator returns true if list contains a request for the given resultID and chunkIndex.
    66  func (c ChunkDataPackRequestList) ContainsLocator(resultID flow.Identifier, chunkIndex uint64) bool {
    67  	for _, request := range c {
    68  		if request.ResultID == resultID && request.Index == chunkIndex {
    69  			return true
    70  		}
    71  	}
    72  
    73  	return false
    74  }
    75  
    76  // UniqueRequestInfo extracts and returns request info based on chunk IDs. Note that a ChunkDataPackRequestList
    77  // may have duplicate requests for the same chunk ID that belongs to distinct execution results.
    78  func (c ChunkDataPackRequestList) UniqueRequestInfo() ChunkDataPackRequestInfoList {
    79  	added := make(map[flow.Identifier]*ChunkDataPackRequestInfo)
    80  
    81  	requestInfoList := ChunkDataPackRequestInfoList{}
    82  
    83  	for _, request := range c {
    84  		var info *ChunkDataPackRequestInfo
    85  		if _, ok := added[request.ChunkID]; !ok {
    86  			info = &request.ChunkDataPackRequestInfo
    87  		} else {
    88  			info = added[request.ChunkID]
    89  			info.Agrees = append(info.Agrees, request.Agrees...)
    90  			info.Disagrees = append(info.Disagrees, request.Disagrees...)
    91  			info.Targets = append(info.Targets, request.Targets...)
    92  		}
    93  
    94  		added[request.ChunkID] = info
    95  	}
    96  
    97  	for chunkID := range added {
    98  		info := added[chunkID]
    99  		requestInfoList = append(requestInfoList, info)
   100  	}
   101  
   102  	return requestInfoList
   103  }