Skip to content

Remove max sample value #1374

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 18, 2023
2 changes: 1 addition & 1 deletion genesis/beacons.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func SampleBeacons(networkID uint32, count int) ([]string, []string) {
sampledIDs := make([]string, 0, count)

s := sampler.NewUniform()
_ = s.Initialize(uint64(len(ips)))
s.Initialize(uint64(len(ips)))
indices, _ := s.Sample(count)
for _, index := range indices {
sampledIPs = append(sampledIPs, ips[int(index)])
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ require (
github.com/stretchr/testify v1.8.1
github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a
github.com/thepudds/fzgen v0.4.2
go.opentelemetry.io/otel v1.11.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0
Expand Down Expand Up @@ -107,6 +108,7 @@ require (
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rjeczalik/notify v0.9.3 // indirect
github.com/sanity-io/litter v1.5.1 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
Expand Down
9 changes: 8 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down Expand Up @@ -373,6 +374,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
Expand Down Expand Up @@ -404,9 +406,11 @@ github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZe
github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/sanity-io/litter v1.5.1 h1:dwnrSypP6q56o3lFxTU+t2fwQ9A+U5qrXVO4Qg9KwVU=
github.com/sanity-io/litter v1.5.1/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
Expand All @@ -432,6 +436,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
Expand All @@ -449,6 +454,8 @@ github.com/supranational/blst v0.3.11-0.20220920110316-f72618070295/go.mod h1:jZ
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI=
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/thepudds/fzgen v0.4.2 h1:HlEHl5hk2/cqEomf2uK5SA/FeJc12s/vIHmOG+FbACw=
github.com/thepudds/fzgen v0.4.2/go.mod h1:kHCWdsv5tdnt32NIHYDdgq083m6bMtaY0M+ipiO9xWE=
github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4=
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
Expand Down
4 changes: 1 addition & 3 deletions network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,9 +683,7 @@ func (n *network) Peers(peerID ids.NodeID) ([]ips.ClaimedIPPort, error) {
// We select a random sample of validators to gossip to avoid starving out a
// validator from being gossiped for an extended period of time.
s := sampler.NewUniform()
if err := s.Initialize(uint64(len(unknownValidators))); err != nil {
return nil, err
}
s.Initialize(uint64(len(unknownValidators)))

// Calculate the unknown information we need to send to this peer.
validatorIPs := make([]ips.ClaimedIPPort, 0, int(n.config.PeerListNumValidatorIPs))
Expand Down
4 changes: 1 addition & 3 deletions network/peer/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,7 @@ func (s *peerSet) Sample(n int, precondition func(Peer) bool) []Peer {
}

sampler := sampler.NewUniform()
// It is impossible for the sampler to report an error here. Since
// [len(s.peersSlice)] <= MaxInt64.
_ = sampler.Initialize(uint64(len(s.peersSlice)))
sampler.Initialize(uint64(len(s.peersSlice)))

peers := make([]Peer, 0, n)
for len(peers) < n {
Expand Down
4 changes: 2 additions & 2 deletions snow/consensus/snowball/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (n *Network) Initialize(params Parameters, numColors int) {

func (n *Network) AddNode(sb Consensus) {
s := sampler.NewUniform()
_ = s.Initialize(uint64(len(n.colors)))
s.Initialize(uint64(len(n.colors)))
indices, _ := s.Sample(len(n.colors))
sb.Initialize(n.params, n.colors[int(indices[0])])
for _, index := range indices[1:] {
Expand Down Expand Up @@ -70,7 +70,7 @@ func (n *Network) Round() {
running := n.running[runningInd]

s := sampler.NewUniform()
_ = s.Initialize(uint64(len(n.nodes)))
s.Initialize(uint64(len(n.nodes)))
count := len(n.nodes)
if count > n.params.K {
count = n.params.K
Expand Down
4 changes: 2 additions & 2 deletions snow/consensus/snowman/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Network struct {

func (n *Network) shuffleColors() {
s := sampler.NewUniform()
_ = s.Initialize(uint64(len(n.colors)))
s.Initialize(uint64(len(n.colors)))
indices, _ := s.Sample(len(n.colors))
colors := []*TestBlock(nil)
for _, index := range indices {
Expand Down Expand Up @@ -105,7 +105,7 @@ func (n *Network) Round() error {
running := n.running[runningInd]

s := sampler.NewUniform()
_ = s.Initialize(uint64(len(n.nodes)))
s.Initialize(uint64(len(n.nodes)))
indices, _ := s.Sample(n.params.K)
sampledColors := bag.Bag[ids.ID]{}
for _, index := range indices {
Expand Down
8 changes: 4 additions & 4 deletions snow/consensus/snowstorm/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Network struct {

func (n *Network) shuffleConsumers() {
s := sampler.NewUniform()
_ = s.Initialize(uint64(len(n.consumers)))
s.Initialize(uint64(len(n.consumers)))
indices, _ := s.Sample(len(n.consumers))
consumers := []*TestTx(nil)
for _, index := range indices {
Expand Down Expand Up @@ -56,7 +56,7 @@ func (n *Network) Initialize(
for len(colors) > 0 {
selected := []ids.ID{}
s := sampler.NewUniform()
_ = s.Initialize(uint64(len(colors)))
s.Initialize(uint64(len(colors)))
size := len(colors)
if size > colorsPerConsumer {
size = colorsPerConsumer
Expand Down Expand Up @@ -134,12 +134,12 @@ func (n *Network) Round() error {
}

s := sampler.NewUniform()
_ = s.Initialize(uint64(len(n.running)))
s.Initialize(uint64(len(n.running)))
runningInd, _ := s.Next()

running := n.running[runningInd]

_ = s.Initialize(uint64(len(n.nodes)))
s.Initialize(uint64(len(n.nodes)))
indices, _ := s.Sample(n.params.K)
sampledColors := bag.Bag[ids.ID]{}
sampledColors.SetThreshold(n.params.Alpha)
Expand Down
8 changes: 2 additions & 6 deletions snow/engine/avalanche/transitive.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,7 @@ func (t *Transitive) Gossip(ctx context.Context) error {
return nil
}

if err := t.uniformSampler.Initialize(uint64(len(edge))); err != nil {
return err // Should never happen
}
t.uniformSampler.Initialize(uint64(len(edge)))
indices, err := t.uniformSampler.Sample(1)
if err != nil {
return err // Also should never really happen because the edge has positive length
Expand Down Expand Up @@ -734,9 +732,7 @@ func (t *Transitive) issueBatch(ctx context.Context, txs []snowstorm.Tx) error {
// Randomly select parents of this vertex from among the virtuous set
virtuousIDs := t.Consensus.Virtuous().CappedList(t.Params.Parents)
numVirtuousIDs := len(virtuousIDs)
if err := t.uniformSampler.Initialize(uint64(numVirtuousIDs)); err != nil {
return err
}
t.uniformSampler.Initialize(uint64(numVirtuousIDs))

indices, err := t.uniformSampler.Sample(numVirtuousIDs)
if err != nil {
Expand Down
94 changes: 65 additions & 29 deletions utils/sampler/rand.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,94 @@
package sampler

import (
"math/rand"
"math"
"sync"
"time"

"gonum.org/v1/gonum/mathext/prng"
)

var (
int63Mask uint64 = 1<<63 - 1
globalRNG = newRNG()
)
var globalRNG = newRNG()

func newRNG() rng {
source := prng.NewMT19937()
source.Seed(uint64(time.Now().UnixNano()))
func newRNG() *rng {
// We don't use a cryptographically secure source of randomness here, as
// there's no need to ensure a truly random sampling.
return rand.New(&syncSource{rng: source}) // #nosec G404
source := prng.NewMT19937()
source.Seed(uint64(time.Now().UnixNano()))
return &rng{rng: source}
}

func Seed(seed int64) {
globalRNG.Seed(seed)
}

type rng interface {
// Seed uses the provided seed value to initialize the generator to a
// deterministic state.
Seed(seed int64)

// Int63n returns, as an int64, a non-negative pseudo-random number in
// [0,n). It panics if n <= 0.
Int63n(n int64) int64
type source interface {
Seed(uint64)
Uint64() uint64
}

type syncSource struct {
type rng struct {
lock sync.Mutex
rng *prng.MT19937
rng source
}

// Seed uses the provided seed value to initialize the generator to a
// deterministic state.
func (r *rng) Seed(seed int64) {
r.lock.Lock()
r.rng.Seed(uint64(seed))
r.lock.Unlock()
}

func (s *syncSource) Seed(seed int64) {
s.lock.Lock()
s.rng.Seed(uint64(seed))
s.lock.Unlock()
// Uint64Inclusive returns a pseudo-random number in [0,n].
//
// Invariant: The result of this function is stored in chain state, so any
// modifications are considered breaking.
func (r *rng) Uint64Inclusive(n uint64) uint64 {
switch {
// n+1 is power of two, so we can just mask
//
// Note: This does work for MaxUint64 as overflow is explicitly part of the
// compiler specification: https://go.dev/ref/spec#Integer_overflow
case n&(n+1) == 0:
return r.uint64() & n

// n is greater than MaxUint64/2 so we need to just iterate until we get a
// number in the requested range.
case n > math.MaxInt64:
v := r.uint64()
for v > n {
v = r.uint64()
}
return v

// n is less than MaxUint64/2 so we generate a number in the range
// [0, k*(n+1)) where k is the largest integer such that k*(n+1) is less
// than or equal to MaxUint64/2. We can't easily find k such that k*(n+1) is
// less than or equal to MaxUint64 because the calculation would overflow.
//
// ref: https://github.com/golang/go/blob/ce10e9d84574112b224eae88dc4e0f43710808de/src/math/rand/rand.go#L127-L132
default:
max := (1 << 63) - 1 - (1<<63)%(n+1)
v := r.uint63()
for v > max {
v = r.uint63()
}
return v % (n + 1)
}
}

func (s *syncSource) Int63() int64 {
return int64(s.Uint64() & int63Mask)
// uint63 returns a random number in [0, MaxInt64]
func (r *rng) uint63() uint64 {
return r.uint64() & math.MaxInt64
}

func (s *syncSource) Uint64() uint64 {
s.lock.Lock()
n := s.rng.Uint64()
s.lock.Unlock()
// uint64 returns a random number in [0, MaxUint64]
func (r *rng) uint64() uint64 {
// Note: We must grab a write lock here because rng.Uint64 internally
// modifies state.
r.lock.Lock()
n := r.rng.Uint64()
r.lock.Unlock()
return n
}
Loading