2018-08-09 18:25:48 +00:00
|
|
|
// Package utils defines utility functions for the beacon-chain.
|
2018-07-22 16:58:14 +00:00
|
|
|
package utils
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2018-10-14 02:32:29 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
2018-11-18 16:39:35 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2018-07-22 16:58:14 +00:00
|
|
|
)
|
|
|
|
|
2018-07-23 15:43:41 +00:00
|
|
|
// ShuffleIndices returns a list of pseudorandomly sampled
|
2018-12-09 19:21:23 +00:00
|
|
|
// indices. This is used to shuffle validators on ETH2.0 beacon chain.
|
|
|
|
func ShuffleIndices(seed common.Hash, indicesList []uint32) ([]uint32, error) {
|
|
|
|
// Each entropy is consumed from the seed in randBytes chunks.
|
|
|
|
randBytes := params.BeaconConfig().RandBytes
|
|
|
|
|
|
|
|
maxValidatorsPerRandBytes := params.BeaconConfig().MaxNumLog2Validators / randBytes
|
|
|
|
upperBound := 1<<(randBytes*maxValidatorsPerRandBytes) - 1
|
|
|
|
// Since we are consuming randBytes of entropy at a time in the loop,
|
2018-10-06 15:30:15 +00:00
|
|
|
// we have a bias at 2**24, this check defines our max list size and is used to remove the bias.
|
2018-11-18 16:39:35 +00:00
|
|
|
// more info on modulo bias: https://stackoverflow.com/questions/10984974/why-do-people-say-there-is-modulo-bias-when-using-a-random-number-generator.
|
2018-12-09 19:21:23 +00:00
|
|
|
if len(indicesList) >= upperBound {
|
|
|
|
return nil, errors.New("input list exceeded upper bound and reached modulo bias")
|
2018-07-22 16:58:14 +00:00
|
|
|
}
|
|
|
|
|
2018-12-09 19:21:23 +00:00
|
|
|
// Rehash the seed to obtain a new pattern of bytes.
|
2018-10-14 02:32:29 +00:00
|
|
|
hashSeed := hashutil.Hash(seed[:])
|
2018-12-09 19:21:23 +00:00
|
|
|
totalCount := len(indicesList)
|
|
|
|
index := 0
|
|
|
|
for index < totalCount-1 {
|
|
|
|
// Iterate through the hashSeed bytes in chunks of size randBytes.
|
|
|
|
for i := 0; i < 32-(32%int(randBytes)); i += int(randBytes) {
|
|
|
|
// Determine the number of indices remaining and exit if last index reached.
|
|
|
|
remaining := totalCount - index
|
|
|
|
if remaining == 1 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Read randBytes of hashSeed as a maxValidatorsPerRandBytes x randBytes big-endian integer.
|
|
|
|
randChunk := hashSeed[i : i+int(randBytes)]
|
|
|
|
var randValue int
|
|
|
|
for j := 0; j < int(randBytes); j++ {
|
|
|
|
randValue |= int(randChunk[j])
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sample values greater than or equal to sampleMax will cause
|
|
|
|
// modulo bias when mapped into the remaining range.
|
|
|
|
randMax := upperBound - upperBound%remaining
|
|
|
|
|
|
|
|
// Perform swap if the consumed entropy will not cause modulo bias.
|
|
|
|
if randValue < randMax {
|
|
|
|
// Select replacement index from the current index.
|
|
|
|
replacementIndex := (randValue % remaining) + index
|
|
|
|
indicesList[index], indicesList[replacementIndex] = indicesList[replacementIndex], indicesList[index]
|
|
|
|
index++
|
|
|
|
}
|
2018-07-22 16:58:14 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-09 19:21:23 +00:00
|
|
|
return indicesList, nil
|
2018-07-22 16:58:14 +00:00
|
|
|
}
|
2018-08-02 21:20:54 +00:00
|
|
|
|
2018-08-14 00:58:37 +00:00
|
|
|
// SplitIndices splits a list into n pieces.
|
2018-10-23 16:37:17 +00:00
|
|
|
func SplitIndices(l []uint32, n uint64) [][]uint32 {
|
2018-08-25 18:59:46 +00:00
|
|
|
var divided [][]uint32
|
2018-10-23 16:37:17 +00:00
|
|
|
var lSize = uint64(len(l))
|
|
|
|
for i := uint64(0); i < n; i++ {
|
|
|
|
start := lSize * i / n
|
|
|
|
end := lSize * (i + 1) / n
|
2018-08-14 00:58:37 +00:00
|
|
|
divided = append(divided, l[start:end])
|
2018-08-02 21:20:54 +00:00
|
|
|
}
|
2018-08-14 00:58:37 +00:00
|
|
|
return divided
|
2018-08-02 21:20:54 +00:00
|
|
|
}
|