github.com/koko1123/flow-go-1@v0.29.6/module/chunks/chunk_assigner.go (about) 1 package chunks 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/flow-go/crypto/hash" 7 "github.com/onflow/flow-go/crypto/random" 8 chunkmodels "github.com/koko1123/flow-go-1/model/chunks" 9 "github.com/koko1123/flow-go-1/model/encoding/json" 10 "github.com/koko1123/flow-go-1/model/flow" 11 "github.com/koko1123/flow-go-1/model/flow/filter" 12 "github.com/koko1123/flow-go-1/module/mempool" 13 "github.com/koko1123/flow-go-1/module/mempool/stdmap" 14 "github.com/koko1123/flow-go-1/state/protocol" 15 "github.com/koko1123/flow-go-1/state/protocol/seed" 16 ) 17 18 // ChunkAssigner implements an instance of the Public Chunk Assignment 19 // algorithm for assigning chunks to verifier nodes in a deterministic but 20 // unpredictable manner. It implements the ChunkAssigner interface. 21 type ChunkAssigner struct { 22 alpha int // used to indicate the number of verifiers that should be assigned to each chunk 23 assignments mempool.Assignments 24 25 protocolState protocol.State 26 } 27 28 // NewChunkAssigner generates and returns an instance of the Public Chunk 29 // Assignment algorithm. Parameter alpha is the number of verifiers that should 30 // be assigned to each chunk. 31 func NewChunkAssigner(alpha uint, protocolState protocol.State) (*ChunkAssigner, error) { 32 // TODO to have limit of assignment mempool as a parameter (2703) 33 assignment, err := stdmap.NewAssignments(1000) 34 if err != nil { 35 return nil, fmt.Errorf("could not create an assignment mempool: %w", err) 36 } 37 return &ChunkAssigner{ 38 alpha: int(alpha), 39 assignments: assignment, 40 protocolState: protocolState, 41 }, nil 42 } 43 44 // Size returns number of assignments 45 func (p *ChunkAssigner) Size() uint { 46 return p.assignments.Size() 47 } 48 49 // Assign generates the assignment 50 // error returns: 51 // - NoValidChildBlockError indicates that no valid child block is known 52 // (which contains the block's source of randomness) 53 // - unexpected errors should be considered symptoms of internal bugs 54 func (p *ChunkAssigner) Assign(result *flow.ExecutionResult, blockID flow.Identifier) (*chunkmodels.Assignment, error) { 55 // computes a fingerprint for blockID||resultID||alpha 56 hash, err := fingerPrint(blockID, result.ID(), p.alpha) 57 if err != nil { 58 return nil, fmt.Errorf("could not compute hash of identifiers: %w", err) 59 } 60 61 // checks cache against this assignment 62 assignmentFingerprint := flow.HashToID(hash) 63 a, exists := p.assignments.ByID(assignmentFingerprint) 64 if exists { 65 return a, nil 66 } 67 68 // Get a list of verifiers at block that is being sealed 69 verifiers, err := p.protocolState.AtBlockID(result.BlockID).Identities(filter.And(filter.HasRole(flow.RoleVerification), 70 filter.HasWeight(true), 71 filter.Not(filter.Ejected))) 72 if err != nil { 73 return nil, fmt.Errorf("could not get verifiers: %w", err) 74 } 75 76 // create RNG for assignment 77 rng, err := p.rngByBlockID(p.protocolState.AtBlockID(blockID)) 78 if err != nil { 79 return nil, err 80 } 81 82 // otherwise, it computes the assignment and caches it for future calls 83 a, err = chunkAssignment(verifiers.NodeIDs(), result.Chunks, rng, p.alpha) 84 if err != nil { 85 return nil, fmt.Errorf("could not complete chunk assignment: %w", err) 86 } 87 88 // adds assignment to mempool 89 _ = p.assignments.Add(assignmentFingerprint, a) 90 91 return a, nil 92 } 93 94 func (p *ChunkAssigner) rngByBlockID(stateSnapshot protocol.Snapshot) (random.Rand, error) { 95 // TODO: seed could be cached to optimize performance 96 randomSource, err := stateSnapshot.RandomSource() // potentially returns NoValidChildBlockError 97 if err != nil { 98 return nil, fmt.Errorf("failed to retrieve source of randomness: %w", err) 99 } 100 101 rng, err := seed.PRGFromRandomSource(randomSource, seed.ProtocolVerificationChunkAssignment) 102 if err != nil { 103 return nil, fmt.Errorf("failed to instantiate random number generator: %w", err) 104 } 105 106 return rng, nil 107 } 108 109 // ChunkAssignment implements the business logic of the Public Chunk Assignment algorithm and returns an 110 // assignment object for the chunks where each chunk is assigned to alpha-many verifier node from ids list 111 func chunkAssignment(ids flow.IdentifierList, chunks flow.ChunkList, rng random.Rand, alpha int) (*chunkmodels.Assignment, error) { 112 if len(ids) < alpha { 113 return nil, fmt.Errorf("not enough verification nodes for chunk assignment: %d, minumum should be %d", len(ids), alpha) 114 } 115 116 // creates an assignment 117 assignment := chunkmodels.NewAssignment() 118 119 // permutes the entire slice 120 err := rng.Shuffle(len(ids), ids.Swap) 121 if err != nil { 122 return nil, fmt.Errorf("shuffling verifiers failed: %w", err) 123 } 124 t := ids 125 126 for i := 0; i < chunks.Len(); i++ { 127 assignees := make([]flow.Identifier, 0, alpha) 128 if len(t) >= alpha { // More verifiers than required for this chunk 129 assignees = append(assignees, t[:alpha]...) 130 t = t[alpha:] 131 } else { // Less verifiers than required for this chunk 132 assignees = append(assignees, t...) // take all remaining elements from t 133 134 // now, we need `still` elements from a new shuffling round: 135 still := alpha - len(assignees) 136 t = ids[:ids.Len()-len(assignees)] // but we exclude the elements we already picked from the population 137 err := rng.Samples(len(t), still, t.Swap) 138 if err != nil { 139 return nil, fmt.Errorf("sampling verifiers failed: %w", err) 140 } 141 142 // by adding `still` elements from new shuffling round: we have alpha assignees for the current chunk 143 assignees = append(assignees, t[:still]...) 144 145 // we have already assigned the first `still` elements in `ids` 146 // note that remaining elements ids[still:] still need shuffling 147 t = ids[still:] 148 err = rng.Shuffle(len(t), t.Swap) 149 if err != nil { 150 return nil, fmt.Errorf("shuffling verifiers failed: %w", err) 151 } 152 } 153 // extracts chunk by index 154 chunk, ok := chunks.ByIndex(uint64(i)) 155 if !ok { 156 return nil, fmt.Errorf("chunk out of range requested: %v", i) 157 } 158 assignment.Add(chunk, assignees) 159 } 160 return assignment, nil 161 } 162 163 func fingerPrint(blockID flow.Identifier, resultID flow.Identifier, alpha int) (hash.Hash, error) { 164 hasher := hash.NewSHA3_256() 165 166 // encodes alpha parameter 167 encAlpha, err := json.NewMarshaler().Marshal(alpha) 168 if err != nil { 169 return nil, fmt.Errorf("could not encode alpha: %w", err) 170 } 171 172 _, err = hasher.Write(blockID[:]) 173 if err != nil { 174 return nil, fmt.Errorf("could not hash blockID: %w", err) 175 } 176 _, err = hasher.Write(resultID[:]) 177 if err != nil { 178 return nil, fmt.Errorf("could not hash result: %w", err) 179 } 180 _, err = hasher.Write(encAlpha) 181 if err != nil { 182 return nil, fmt.Errorf("could not hash alpha: %w", err) 183 } 184 185 return hasher.SumHash(), nil 186 }