From 5828f43e52660737ffe48e0f9ef8e2f62fcc49dc Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 11 Jan 2022 17:07:59 +0300 Subject: [PATCH] [#851] util/rand: use single random source It is much more convenient to skip source creation. Also fix some bugs: 1. `cryptoSource.Int63()` now returns number in [0, 1<<63) as required by `rand.Source` interface. 2. Replace `cryptoSource.Uint63()` with `cryptoSource.Uint64` to allow generate uint64 numbers directly (see rand.Source64 docs). Signed-off-by: Evgenii Stratonikov --- .../internal/modules/morph/subnet.go | 3 +- cmd/neofs-node/morph.go | 6 +- pkg/innerring/processors/audit/process.go | 3 +- pkg/morph/client/notary.go | 5 +- pkg/services/audit/auditor/pdp.go | 22 +++--- pkg/services/audit/auditor/por.go | 3 +- pkg/services/audit/auditor/util.go | 12 +--- pkg/util/rand/rand.go | 68 +++++++++---------- 8 files changed, 56 insertions(+), 66 deletions(-) diff --git a/cmd/neofs-adm/internal/modules/morph/subnet.go b/cmd/neofs-adm/internal/modules/morph/subnet.go index b3cc16908..961e2e1fd 100644 --- a/cmd/neofs-adm/internal/modules/morph/subnet.go +++ b/cmd/neofs-adm/internal/modules/morph/subnet.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "errors" "fmt" - "math" "github.com/nspcc-dev/neo-go/cli/flags" "github.com/nspcc-dev/neo-go/cli/input" @@ -206,7 +205,7 @@ var cmdSubnetCreate = &cobra.Command{ ) for { - num = uint32(rand.Uint64(rand.New(), math.MaxUint32)) + num = rand.Uint32() id.SetNumber(num) diff --git a/cmd/neofs-node/morph.go b/cmd/neofs-node/morph.go index e1584131f..136df420d 100644 --- a/cmd/neofs-node/morph.go +++ b/cmd/neofs-node/morph.go @@ -40,8 +40,7 @@ func initMorphComponents(c *cfg) { fatalOnErr(errors.New("missing Neo RPC endpoints")) } - crand := rand.New() // math/rand with cryptographic source - crand.Shuffle(len(addresses), func(i, j int) { + rand.Shuffle(len(addresses), func(i, j int) { addresses[i], addresses[j] = addresses[j], addresses[i] }) @@ -185,8 +184,7 @@ func listenMorphNotifications(c *cfg) { endpoints := morphconfig.NotificationEndpoint(c.appCfg) timeout := morphconfig.DialTimeout(c.appCfg) - crand := rand.New() // math/rand with cryptographic source - crand.Shuffle(len(endpoints), func(i, j int) { + rand.Shuffle(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] }) diff --git a/pkg/innerring/processors/audit/process.go b/pkg/innerring/processors/audit/process.go index 21849e077..8b6afd512 100644 --- a/pkg/innerring/processors/audit/process.go +++ b/pkg/innerring/processors/audit/process.go @@ -69,10 +69,9 @@ func (ap *Processor) processStartAudit(epoch uint64) { } n := nodes.Flatten() - crand := rand.New() // math/rand with cryptographic source // shuffle nodes to ask a random one - crand.Shuffle(len(n), func(i, j int) { + rand.Shuffle(len(n), func(i, j int) { n[i], n[j] = n[j], n[i] }) diff --git a/pkg/morph/client/notary.go b/pkg/morph/client/notary.go index af70a2107..b597fd3d1 100644 --- a/pkg/morph/client/notary.go +++ b/pkg/morph/client/notary.go @@ -361,9 +361,6 @@ func (c *Client) NotaryInvoke(contract util.Uint160, fee fixedn.Fixed8, nonce ui return c.notaryInvoke(false, true, contract, nonce, vub, method, args...) } -// randSource is a source of random numbers. -var randSource = rand.New() - // NotaryInvokeNotAlpha does the same as NotaryInvoke but does not use client's // private key in Invocation script. It means that main TX of notary request is // not expected to be signed by the current node. @@ -380,7 +377,7 @@ func (c *Client) NotaryInvokeNotAlpha(contract util.Uint160, fee fixedn.Fixed8, return c.Invoke(contract, fee, method, args...) } - return c.notaryInvoke(false, false, contract, randSource.Uint32(), nil, method, args...) + return c.notaryInvoke(false, false, contract, rand.Uint32(), nil, method, args...) } // NotarySignAndInvokeTX signs and sends notary request that was received from diff --git a/pkg/services/audit/auditor/pdp.go b/pkg/services/audit/auditor/pdp.go index d0d0e23a3..e4276526d 100644 --- a/pkg/services/audit/auditor/pdp.go +++ b/pkg/services/audit/auditor/pdp.go @@ -6,6 +6,7 @@ import ( "sync" "time" + "github.com/nspcc-dev/neofs-node/pkg/util/rand" "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/object" "github.com/nspcc-dev/tzhash/tz" @@ -88,16 +89,18 @@ func (c *Context) splitPayload(id *object.ID) []uint64 { ) for i := uint64(0); i < hashRangeNumber; i++ { - var nextLn uint64 if i < hashRangeNumber-1 { - nextLn = randUint64(size-prev-(hashRangeNumber-i)) + 1 + max := size - prev - (hashRangeNumber - i) + if max == 0 { + prev++ + } else { + prev += rand.Uint64()%max + 1 + } } else { - nextLn = size - prev + prev = size } - notches = append(notches, prev+nextLn) - - prev += nextLn + notches = append(notches, prev) } return notches @@ -107,13 +110,16 @@ func (c *Context) collectHashes(p *gamePair) { fn := func(n *netmap.Node, rngs []*object.Range, hashWriter func([]byte)) { // TODO: add order randomization for i := range rngs { - sleepDur := time.Duration(randUint64(c.maxPDPSleep)) + var sleepDur time.Duration + if c.maxPDPSleep > 0 { + sleepDur = time.Duration(rand.Uint64() % c.maxPDPSleep) + } c.log.Debug("sleep before get range hash", zap.Stringer("interval", sleepDur), ) - time.Sleep(time.Duration(sleepDur)) + time.Sleep(sleepDur) h, err := c.cnrCom.GetRangeHash(c.task, n, p.id, rngs[i]) if err != nil { diff --git a/pkg/services/audit/auditor/por.go b/pkg/services/audit/auditor/por.go index 61414ff16..e1e9d54b8 100644 --- a/pkg/services/audit/auditor/por.go +++ b/pkg/services/audit/auditor/por.go @@ -68,8 +68,7 @@ func (c *Context) checkStorageGroupPoR(ind int, sg *object.ID) { flat := placement.FlattenNodes(objectPlacement) - crand := rand.New() // math/rand with cryptographic source - crand.Shuffle(len(flat), func(i, j int) { + rand.Shuffle(len(flat), func(i, j int) { flat[i], flat[j] = flat[j], flat[i] }) diff --git a/pkg/services/audit/auditor/util.go b/pkg/services/audit/auditor/util.go index 0982fe7ab..71f7c180c 100644 --- a/pkg/services/audit/auditor/util.go +++ b/pkg/services/audit/auditor/util.go @@ -4,12 +4,11 @@ import ( "github.com/nspcc-dev/neofs-node/pkg/util/rand" ) -// returns random uint64 number [0; n) outside exclude map. -// exclude must contain no more than n-1 elements [0; n) +// nextRandUint64 returns random uint64 number [0; n) outside exclude map. +// Panics if len(exclude) >= n. func nextRandUint64(n uint64, exclude map[uint64]struct{}) uint64 { ln := uint64(len(exclude)) - - ind := randUint64(n - ln) + ind := rand.Uint64() % (n - ln) for i := ind; ; i++ { if _, ok := exclude[i]; !ok { @@ -17,8 +16,3 @@ func nextRandUint64(n uint64, exclude map[uint64]struct{}) uint64 { } } } - -// returns random uint64 number [0, n). -func randUint64(n uint64) uint64 { - return rand.Uint64(rand.New(), int64(n)) -} diff --git a/pkg/util/rand/rand.go b/pkg/util/rand/rand.go index b42b58e42..97508f82a 100644 --- a/pkg/util/rand/rand.go +++ b/pkg/util/rand/rand.go @@ -6,41 +6,39 @@ import ( mrand "math/rand" ) -type cryptoSource struct{} - -// Read is alias for crypto/rand.Read. -var Read = crand.Read - -// New constructs the source of random numbers. -func New() *mrand.Rand { - return mrand.New(&cryptoSource{}) -} - -func (s *cryptoSource) Seed(int64) {} - -func (s *cryptoSource) Int63() int64 { - return int64(s.Uint63()) -} - -func (s *cryptoSource) Uint63() uint64 { - buf := make([]byte, 8) - if _, err := crand.Read(buf); err != nil { - return 0 - } - - return binary.BigEndian.Uint64(buf) -} +var source = mrand.New(&cryptoSource{}) // Uint64 returns a random uint64 value. -func Uint64(r *mrand.Rand, max int64) uint64 { - if max <= 0 { - return 0 - } - - var i int64 = -1 - for i < 0 { - i = r.Int63n(max) - } - - return uint64(i) +func Uint64() uint64 { + return source.Uint64() +} + +// Uint64 returns a random uint32 value. +func Uint32() uint32 { + return source.Uint32() +} + +// Shuffle randomizes the order of elements. +// n is the number of elements. Shuffle panics if n < 0. +// swap swaps the elements with indexes i and j. +func Shuffle(n int, swap func(i, j int)) { + source.Shuffle(n, swap) +} + +// cryptoSource is math/rand.Source which takes entropy via crypto/rand. +type cryptoSource struct{} + +// Seed implements math/rand.Source. +func (s *cryptoSource) Seed(int64) {} + +// Int63 implements math/rand.Source. +func (s *cryptoSource) Int63() int64 { + return int64(s.Uint64() >> 1) +} + +// Uint64 implements math/rand.Source64. +func (s *cryptoSource) Uint64() uint64 { + var buf [8]byte + _, _ = crand.Read(buf[:]) // always returns nil + return binary.BigEndian.Uint64(buf[:]) }