55 "errors"
66 "fmt"
77
8+ "github.com/onflow/crypto/random"
89 "github.com/rs/zerolog"
910
1011 "github.com/onflow/cadence"
@@ -21,8 +22,9 @@ import (
2122// The number of nodes in each cluster is deterministic and only depends on the number of clusters
2223// and the number of nodes. The repartition of internal and partner nodes is also deterministic
2324// and only depends on the number of clusters and nodes.
24- // The identity of internal and partner nodes in each cluster is the non-deterministic and is randomized
25- // using the system entropy.
25+ // The identity of internal and partner nodes in each cluster is deterministically randomized
26+ // using the provided entropy `randomSource`. Ideally this entropy should be derived from the random beacon of the
27+ // previous epoch, or some other verifiable random source.
2628// The function guarantees a specific constraint when partitioning the nodes into clusters:
2729// Each cluster must contain strictly more than 2/3 of internal nodes. If the constraint can't be
2830// satisfied, an exception is returned.
@@ -33,11 +35,12 @@ import (
3335// - partnerNodes: identity list of partner nodes.
3436// - internalNodes: identity list of internal nodes.
3537// - numCollectionClusters: the number of clusters to generate
38+ // - randomSource: entropy used to randomize the assignment.
3639// Returns:
3740// - flow.AssignmentList: the generated assignment list.
3841// - flow.ClusterList: the generate collection cluster list.
3942// - error: if any error occurs. Any error returned from this function is irrecoverable.
40- func ConstructClusterAssignment (log zerolog.Logger , partnerNodes , internalNodes flow.IdentityList , numCollectionClusters int ) (flow.AssignmentList , flow.ClusterList , error ) {
43+ func ConstructClusterAssignment (log zerolog.Logger , partnerNodes , internalNodes flow.IdentityList , numCollectionClusters int , randomSource random. Rand ) (flow.AssignmentList , flow.ClusterList , error ) {
4144
4245 partnerCollectors := partnerNodes .Filter (filter.HasRole [flow.Identity ](flow .RoleCollection ))
4346 internalCollectors := internalNodes .Filter (filter.HasRole [flow.Identity ](flow .RoleCollection ))
@@ -49,12 +52,17 @@ func ConstructClusterAssignment(log zerolog.Logger, partnerNodes, internalNodes
4952 nCollectors , numCollectionClusters )
5053 }
5154
52- // shuffle both collector lists based on a non-deterministic algorithm
53- partnerCollectors , err := partnerCollectors .Shuffle ()
55+ // shuffle partner nodes in-place using the provided randomness
56+ err := randomSource .Shuffle (len (partnerCollectors ), func (i , j int ) {
57+ partnerCollectors [i ], partnerCollectors [j ] = partnerCollectors [j ], partnerCollectors [i ]
58+ })
5459 if err != nil {
5560 log .Fatal ().Err (err ).Msg ("could not shuffle partners" )
5661 }
57- internalCollectors , err = internalCollectors .Shuffle ()
62+ // shuffle internal nodes in-place using the provided randomness
63+ err = randomSource .Shuffle (len (internalCollectors ), func (i , j int ) {
64+ internalCollectors [i ], internalCollectors [j ] = internalCollectors [j ], internalCollectors [i ]
65+ })
5866 if err != nil {
5967 log .Fatal ().Err (err ).Msg ("could not shuffle internals" )
6068 }
0 commit comments