mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-26 05:17:22 +00:00
73 lines
2.6 KiB
Go
73 lines
2.6 KiB
Go
// Package utils defines utility functions for the beacon-chain.
|
|
package utils
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
|
)
|
|
|
|
// ShuffleIndices returns a list of pseudorandomly sampled
|
|
// 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,
|
|
// we have a bias at 2**24, this check defines our max list size and is used to remove the bias.
|
|
// more info on modulo bias: https://stackoverflow.com/questions/10984974/why-do-people-say-there-is-modulo-bias-when-using-a-random-number-generator.
|
|
if len(indicesList) >= upperBound {
|
|
return nil, errors.New("input list exceeded upper bound and reached modulo bias")
|
|
}
|
|
|
|
// Rehash the seed to obtain a new pattern of bytes.
|
|
hashSeed := hashutil.Hash(seed[:])
|
|
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++
|
|
}
|
|
}
|
|
}
|
|
return indicesList, nil
|
|
}
|
|
|
|
// SplitIndices splits a list into n pieces.
|
|
func SplitIndices(l []uint32, n uint64) [][]uint32 {
|
|
var divided [][]uint32
|
|
var lSize = uint64(len(l))
|
|
for i := uint64(0); i < n; i++ {
|
|
start := lSize * i / n
|
|
end := lSize * (i + 1) / n
|
|
divided = append(divided, l[start:end])
|
|
}
|
|
return divided
|
|
}
|