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 }