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,15 +52,15 @@ 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 ()
54- if err != nil {
55- log . Fatal (). Err ( err ). Msg ( "could not shuffle partners" )
56- }
57- internalCollectors , err = internalCollectors . Shuffle ()
58- if err != nil {
59- log . Fatal (). Err ( err ). Msg ( "could not shuffle internals" )
60- }
55+ //random := rand.New(randomSource)
56+ // shuffle partner nodes in-place using the provided randomness
57+ _ = randomSource . Shuffle ( len ( partnerCollectors ), func ( i , j int ) {
58+ partnerCollectors [ i ], partnerCollectors [ j ] = partnerCollectors [ j ], partnerCollectors [ i ]
59+ })
60+ // shuffle internal nodes in-place using the provided randomness
61+ _ = randomSource . Shuffle ( len ( internalCollectors ), func ( i , j int ) {
62+ internalCollectors [ i ], internalCollectors [ j ] = internalCollectors [ j ], internalCollectors [ i ]
63+ })
6164
6265 // capture first reference weight to validate that all collectors have equal weight
6366 refWeight := internalCollectors [0 ].InitialWeight
0 commit comments