From 61b484c13b50b0a2f256a1b0f2447511f1568968 Mon Sep 17 00:00:00 2001 From: Aaron Gable Date: Mon, 12 Aug 2024 09:17:09 -0700 Subject: [PATCH] Update to math/rand/v2 (#7657) Replace all of Boulder's usage of the Go stdlib "math/rand" package with the newer "math/rand/v2" package which first became available in go1.22. This package has an improved API and faster performance across the board. See https://go.dev/blog/randv2 and https://go.dev/blog/chacha8rand for details. --- bdns/servers.go | 7 ++++--- ca/ca.go | 4 ++-- cmd/cert-checker/main_test.go | 4 ++-- cmd/rocsp-tool/client.go | 14 +++++++------- core/util.go | 2 +- crl/updater/continuous.go | 4 ++-- ctpolicy/loglist/loglist.go | 4 ++-- grpc/internal/grpcrand/grpcrand.go | 9 ++++----- mocks/sa.go | 4 ++-- ocsp/responder/responder.go | 4 ++-- policy/pa.go | 4 ++-- ra/ra_test.go | 6 +++--- ratelimits/limiter_test.go | 4 ++-- sa/sa_test.go | 4 ++-- semaphore/semaphore_test.go | 7 ++++--- test/ct-test-srv/main.go | 4 ++-- test/load-generator/acme/challenge.go | 4 ++-- test/load-generator/boulder-calls.go | 12 ++++++------ va/caa.go | 2 +- va/http_test.go | 4 ++-- va/va.go | 2 +- 21 files changed, 55 insertions(+), 54 deletions(-) diff --git a/bdns/servers.go b/bdns/servers.go index dd8edee98..bfb9d7e0f 100644 --- a/bdns/servers.go +++ b/bdns/servers.go @@ -4,15 +4,16 @@ import ( "context" "errors" "fmt" - "math/rand" + "math/rand/v2" "net" "strconv" "sync" "time" - "github.com/letsencrypt/boulder/cmd" "github.com/miekg/dns" "github.com/prometheus/client_golang/prometheus" + + "github.com/letsencrypt/boulder/cmd" ) // ServerProvider represents a type which can provide a list of addresses for @@ -306,7 +307,7 @@ func (dp *dynamicProvider) Addrs() ([]string, error) { var r []string dp.mu.RLock() for ip, ports := range dp.addrs { - port := fmt.Sprint(ports[rand.Intn(len(ports))]) + port := fmt.Sprint(ports[rand.IntN(len(ports))]) addr := net.JoinHostPort(ip, port) r = append(r, addr) } diff --git a/ca/ca.go b/ca/ca.go index 5eb28eaac..e55b2d665 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -14,7 +14,7 @@ import ( "errors" "fmt" "math/big" - mrand "math/rand" + mrand "math/rand/v2" "strings" "time" @@ -535,7 +535,7 @@ func (ca *certificateAuthorityImpl) issuePrecertificateInner(ctx context.Context if !ok || len(issuerPool) == 0 { return nil, nil, berrors.InternalServerError("no issuers found for public key algorithm %s", csr.PublicKeyAlgorithm) } - issuer := issuerPool[mrand.Intn(len(issuerPool))] + issuer := issuerPool[mrand.IntN(len(issuerPool))] if issuer.Cert.NotAfter.Before(notAfter) { err = berrors.InternalServerError("cannot issue a certificate that expires after the issuer certificate") diff --git a/cmd/cert-checker/main_test.go b/cmd/cert-checker/main_test.go index eee0012b0..2a7f7419b 100644 --- a/cmd/cert-checker/main_test.go +++ b/cmd/cert-checker/main_test.go @@ -15,7 +15,7 @@ import ( "errors" "log" "math/big" - mrand "math/rand" + mrand "math/rand/v2" "os" "slices" "sort" @@ -355,7 +355,7 @@ func TestGetAndProcessCerts(t *testing.T) { reg := satest.CreateWorkingRegistration(t, isa.SA{Impl: sa}) test.AssertNotError(t, err, "Couldn't create registration") for range 5 { - rawCert.SerialNumber = big.NewInt(mrand.Int63()) + rawCert.SerialNumber = big.NewInt(mrand.Int64()) certDER, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") _, err = sa.AddCertificate(context.Background(), &sapb.AddCertificateRequest{ diff --git a/cmd/rocsp-tool/client.go b/cmd/rocsp-tool/client.go index c70fa30aa..f6e3cb856 100644 --- a/cmd/rocsp-tool/client.go +++ b/cmd/rocsp-tool/client.go @@ -3,7 +3,7 @@ package notmain import ( "context" "fmt" - "math/rand" + "math/rand/v2" "os" "sync/atomic" "time" @@ -104,9 +104,9 @@ func (cl *client) loadFromDB(ctx context.Context, speed ProcessingSpeed, startFr if result.err != nil { errorCount++ if errorCount < 10 || - (errorCount < 1000 && rand.Intn(1000) < 100) || - (errorCount < 100000 && rand.Intn(1000) < 10) || - (rand.Intn(1000) < 1) { + (errorCount < 1000 && rand.IntN(1000) < 100) || + (errorCount < 100000 && rand.IntN(1000) < 10) || + (rand.IntN(1000) < 1) { cl.logger.Errf("error: %s", result.err) } } else { @@ -115,9 +115,9 @@ func (cl *client) loadFromDB(ctx context.Context, speed ProcessingSpeed, startFr total := successCount + errorCount if total < 10 || - (total < 1000 && rand.Intn(1000) < 100) || - (total < 100000 && rand.Intn(1000) < 10) || - (rand.Intn(1000) < 1) { + (total < 1000 && rand.IntN(1000) < 100) || + (total < 100000 && rand.IntN(1000) < 10) || + (rand.IntN(1000) < 1) { cl.logger.Infof("stored %d responses, %d errors", successCount, errorCount) } } diff --git a/core/util.go b/core/util.go index 36a323530..607b3edbf 100644 --- a/core/util.go +++ b/core/util.go @@ -15,7 +15,7 @@ import ( "fmt" "io" "math/big" - mrand "math/rand" + mrand "math/rand/v2" "os" "path" "reflect" diff --git a/crl/updater/continuous.go b/crl/updater/continuous.go index e4552f68f..4597fd60a 100644 --- a/crl/updater/continuous.go +++ b/crl/updater/continuous.go @@ -2,7 +2,7 @@ package updater import ( "context" - "math/rand" + "math/rand/v2" "sync" "time" @@ -21,7 +21,7 @@ func (cu *crlUpdater) Run(ctx context.Context) error { // Wait for a random number of nanoseconds less than the updatePeriod, so // that process restarts do not skip or delay shards deterministically. - waitTimer := time.NewTimer(time.Duration(rand.Int63n(cu.updatePeriod.Nanoseconds()))) + waitTimer := time.NewTimer(time.Duration(rand.Int64N(cu.updatePeriod.Nanoseconds()))) defer waitTimer.Stop() select { case <-waitTimer.C: diff --git a/ctpolicy/loglist/loglist.go b/ctpolicy/loglist/loglist.go index 8722b65c8..95cc724a1 100644 --- a/ctpolicy/loglist/loglist.go +++ b/ctpolicy/loglist/loglist.go @@ -5,7 +5,7 @@ import ( "encoding/json" "errors" "fmt" - "math/rand" + "math/rand/v2" "os" "strings" "time" @@ -314,6 +314,6 @@ func (ll List) PickOne(operator string, expiry time.Time) (string, string, error return "", "", fmt.Errorf("no log found for group %q and expiry %s", operator, expiry) } - log := candidates[rand.Intn(len(candidates))] + log := candidates[rand.IntN(len(candidates))] return log.Url, log.Key, nil } diff --git a/grpc/internal/grpcrand/grpcrand.go b/grpc/internal/grpcrand/grpcrand.go index 740f83c2b..f4df37293 100644 --- a/grpc/internal/grpcrand/grpcrand.go +++ b/grpc/internal/grpcrand/grpcrand.go @@ -21,13 +21,12 @@ package grpcrand import ( - "math/rand" + "math/rand/v2" "sync" - "time" ) var ( - r = rand.New(rand.NewSource(time.Now().UnixNano())) + r = rand.New(rand.NewPCG(rand.Uint64(), rand.Uint64())) mu sync.Mutex ) @@ -42,14 +41,14 @@ func Int() int { func Int63n(n int64) int64 { mu.Lock() defer mu.Unlock() - return r.Int63n(n) + return r.Int64N(n) } // Intn implements rand.Intn on the grpcrand global source. func Intn(n int) int { mu.Lock() defer mu.Unlock() - return r.Intn(n) + return r.IntN(n) } // Float64 implements rand.Float64 on the grpcrand global source. diff --git a/mocks/sa.go b/mocks/sa.go index 511fcef7a..bbbb65cc9 100644 --- a/mocks/sa.go +++ b/mocks/sa.go @@ -6,7 +6,7 @@ import ( "crypto/x509" "errors" "fmt" - "math/rand" + "math/rand/v2" "net" "os" "time" @@ -353,7 +353,7 @@ func (sa *StorageAuthority) NewOrderAndAuthzs(_ context.Context, req *sapb.NewOr Names: req.NewOrder.Names, V2Authorizations: req.NewOrder.V2Authorizations, // Mock new fields generated by the database transaction. - Id: rand.Int63(), + Id: rand.Int64(), Created: timestamppb.Now(), // A new order is never processing because it can't have been finalized yet. BeganProcessing: false, diff --git a/ocsp/responder/responder.go b/ocsp/responder/responder.go index 5fc273644..d985e92ef 100644 --- a/ocsp/responder/responder.go +++ b/ocsp/responder/responder.go @@ -40,7 +40,7 @@ import ( "errors" "fmt" "io" - "math/rand" + "math/rand/v2" "net/http" "net/url" "time" @@ -153,7 +153,7 @@ var hashToString = map[crypto.Hash]string{ } func SampledError(log blog.Logger, sampleRate int, format string, a ...interface{}) { - if sampleRate > 0 && rand.Intn(sampleRate) == 0 { + if sampleRate > 0 && rand.IntN(sampleRate) == 0 { log.Errf(format, a...) } } diff --git a/policy/pa.go b/policy/pa.go index ce7857a7d..2e7981518 100644 --- a/policy/pa.go +++ b/policy/pa.go @@ -5,7 +5,7 @@ import ( "encoding/hex" "errors" "fmt" - "math/rand" + "math/rand/v2" "net" "net/mail" "os" @@ -46,7 +46,7 @@ func New(challengeTypes map[core.AcmeChallenge]bool, log blog.Logger) (*Authorit log: log, enabledChallenges: challengeTypes, // We don't need real randomness for this. - pseudoRNG: rand.New(rand.NewSource(99)), + pseudoRNG: rand.New(rand.NewPCG(rand.Uint64(), rand.Uint64())), } return &pa, nil diff --git a/ra/ra_test.go b/ra/ra_test.go index b09a76a86..f9d6782db 100644 --- a/ra/ra_test.go +++ b/ra/ra_test.go @@ -15,7 +15,7 @@ import ( "errors" "fmt" "math/big" - mrand "math/rand" + mrand "math/rand/v2" "net" "os" "regexp" @@ -2401,7 +2401,7 @@ func (msa *mockSAWithAuthzs) GetAuthorizations2(ctx context.Context, req *sapb.G func (msa *mockSAWithAuthzs) NewOrderAndAuthzs(ctx context.Context, req *sapb.NewOrderAndAuthzsRequest, _ ...grpc.CallOption) (*corepb.Order, error) { authzIDs := req.NewOrder.V2Authorizations for range req.NewAuthzs { - authzIDs = append(authzIDs, mrand.Int63()) + authzIDs = append(authzIDs, mrand.Int64()) } return &corepb.Order{ // Fields from the input new order request. @@ -2411,7 +2411,7 @@ func (msa *mockSAWithAuthzs) NewOrderAndAuthzs(ctx context.Context, req *sapb.Ne V2Authorizations: authzIDs, CertificateProfileName: req.NewOrder.CertificateProfileName, // Mock new fields generated by the database transaction. - Id: mrand.Int63(), + Id: mrand.Int64(), Created: timestamppb.Now(), // A new order is never processing because it can't have been finalized yet. BeganProcessing: false, diff --git a/ratelimits/limiter_test.go b/ratelimits/limiter_test.go index efec45432..62f927bdd 100644 --- a/ratelimits/limiter_test.go +++ b/ratelimits/limiter_test.go @@ -2,7 +2,7 @@ package ratelimits import ( "context" - "math/rand" + "math/rand/v2" "net" "testing" "time" @@ -43,7 +43,7 @@ func setup(t *testing.T) (context.Context, map[string]*Limiter, *TransactionBuil // runs. randIP := make(net.IP, 4) for i := range 4 { - randIP[i] = byte(rand.Intn(256)) + randIP[i] = byte(rand.IntN(256)) } // Construct a limiter for each source. diff --git a/sa/sa_test.go b/sa/sa_test.go index 10991d412..626a27d74 100644 --- a/sa/sa_test.go +++ b/sa/sa_test.go @@ -14,7 +14,7 @@ import ( "fmt" "math/big" "math/bits" - mrand "math/rand" + mrand "math/rand/v2" "net" "os" "reflect" @@ -3358,7 +3358,7 @@ func TestSerialsForIncident(t *testing.T) { "1335": true, "1336": true, "1337": true, "1338": true, } for i := range expectedSerials { - randInt := func() int64 { return mrand.Int63() } + randInt := func() int64 { return mrand.Int64() } _, err := testIncidentsDbMap.ExecContext(ctx, fmt.Sprintf("INSERT INTO incident_foo (%s) VALUES ('%s', %d, %d, '%s')", "serial, registrationID, orderID, lastNoticeSent", diff --git a/semaphore/semaphore_test.go b/semaphore/semaphore_test.go index 71a5d2340..976491b73 100644 --- a/semaphore/semaphore_test.go +++ b/semaphore/semaphore_test.go @@ -6,14 +6,15 @@ package semaphore_test import ( "context" - "math/rand" + "math/rand/v2" "runtime" "sync" "testing" "time" - "github.com/letsencrypt/boulder/semaphore" "golang.org/x/sync/errgroup" + + "github.com/letsencrypt/boulder/semaphore" ) const maxSleep = 1 * time.Millisecond @@ -21,7 +22,7 @@ const maxSleep = 1 * time.Millisecond func HammerWeighted(sem *semaphore.Weighted, n int64, loops int) { for i := 0; i < loops; i++ { _ = sem.Acquire(context.Background(), n) - time.Sleep(time.Duration(rand.Int63n(int64(maxSleep/time.Nanosecond))) * time.Nanosecond) + time.Sleep(time.Duration(rand.Int64N(int64(maxSleep/time.Nanosecond))) * time.Nanosecond) sem.Release(n) } } diff --git a/test/ct-test-srv/main.go b/test/ct-test-srv/main.go index 564ad85f7..eb7c4bc81 100644 --- a/test/ct-test-srv/main.go +++ b/test/ct-test-srv/main.go @@ -13,7 +13,7 @@ import ( "fmt" "io" "log" - "math/rand" + "math/rand/v2" "net/http" "os" "strings" @@ -161,7 +161,7 @@ func (is *integrationSrv) addChainOrPre(w http.ResponseWriter, r *http.Request, is.submissions[hostnames]++ is.Unlock() - if is.flakinessRate != 0 && rand.Intn(100) < is.flakinessRate { + if is.flakinessRate != 0 && rand.IntN(100) < is.flakinessRate { time.Sleep(10 * time.Second) } diff --git a/test/load-generator/acme/challenge.go b/test/load-generator/acme/challenge.go index 47e8d861d..12aeb9aa2 100644 --- a/test/load-generator/acme/challenge.go +++ b/test/load-generator/acme/challenge.go @@ -3,7 +3,7 @@ package acme import ( "errors" "fmt" - mrand "math/rand" + mrand "math/rand/v2" "strings" "github.com/letsencrypt/boulder/core" @@ -67,7 +67,7 @@ func (strategy randomChallengeStrategy) PickChallenge(authz *core.Authorization) if len(authz.Challenges) == 0 { return nil, ErrPickChallengeAuthzMissingChallenges } - return &authz.Challenges[mrand.Intn(len(authz.Challenges))], nil + return &authz.Challenges[mrand.IntN(len(authz.Challenges))], nil } // preferredTypeChallengeStrategy is a ChallengeStrategy implementation that diff --git a/test/load-generator/boulder-calls.go b/test/load-generator/boulder-calls.go index 8f98cade3..351c551f0 100644 --- a/test/load-generator/boulder-calls.go +++ b/test/load-generator/boulder-calls.go @@ -16,7 +16,7 @@ import ( "errors" "fmt" "io" - mrand "math/rand" + mrand "math/rand/v2" "net/http" "time" @@ -72,7 +72,7 @@ func getAccount(s *State, c *acmeCache) error { } // Select a random account from the state and put it into the context - c.acct = s.accts[mrand.Intn(len(s.accts))] + c.acct = s.accts[mrand.IntN(len(s.accts))] c.ns = &nonceSource{s: s} return nil } @@ -164,7 +164,7 @@ func randDomain(base string) string { func newOrder(s *State, c *acmeCache) error { // Pick a random number of names within the constraints of the maxNamesPerCert // parameter - orderSize := 1 + mrand.Intn(s.maxNamesPerCert-1) + orderSize := 1 + mrand.IntN(s.maxNamesPerCert-1) // Generate that many random domain names. There may be some duplicates, we // don't care. The ACME server will collapse those down for us, how handy! dnsNames := []identifier.ACMEIdentifier{} @@ -231,7 +231,7 @@ func newOrder(s *State, c *acmeCache) error { // popPendingOrder *removes* a random pendingOrder from the context, returning // it. func popPendingOrder(c *acmeCache) *OrderJSON { - orderIndex := mrand.Intn(len(c.pendingOrders)) + orderIndex := mrand.IntN(len(c.pendingOrders)) order := c.pendingOrders[orderIndex] c.pendingOrders = append(c.pendingOrders[:orderIndex], c.pendingOrders[orderIndex+1:]...) return order @@ -465,7 +465,7 @@ func pollOrderForCert(order *OrderJSON, s *State, c *acmeCache) (*OrderJSON, err // popFulfilledOrder **removes** a fulfilled order from the context, returning // it. Fulfilled orders have all of their authorizations satisfied. func popFulfilledOrder(c *acmeCache) string { - orderIndex := mrand.Intn(len(c.fulfilledOrders)) + orderIndex := mrand.IntN(len(c.fulfilledOrders)) order := c.fulfilledOrders[orderIndex] c.fulfilledOrders = append(c.fulfilledOrders[:orderIndex], c.fulfilledOrders[orderIndex+1:]...) return order @@ -580,7 +580,7 @@ func postAsGet(s *State, c *acmeCache, url string, latencyTag string) (*http.Res } func popCertificate(c *acmeCache) string { - certIndex := mrand.Intn(len(c.certs)) + certIndex := mrand.IntN(len(c.certs)) certURL := c.certs[certIndex] c.certs = append(c.certs[:certIndex], c.certs[certIndex+1:]...) return certURL diff --git a/va/caa.go b/va/caa.go index b4703b6af..5c235bef4 100644 --- a/va/caa.go +++ b/va/caa.go @@ -3,7 +3,7 @@ package va import ( "context" "fmt" - "math/rand" + "math/rand/v2" "net/url" "regexp" "strings" diff --git a/va/http_test.go b/va/http_test.go index 038803539..92e17db66 100644 --- a/va/http_test.go +++ b/va/http_test.go @@ -6,7 +6,7 @@ import ( "encoding/base64" "errors" "fmt" - mrand "math/rand" + mrand "math/rand/v2" "net" "net/http" "net/http/httptest" @@ -1213,7 +1213,7 @@ func TestHTTPBadPort(t *testing.T) { // Pick a random port between 40000 and 65000 - with great certainty we won't // have an HTTP server listening on this port and the test will fail as // intended - badPort := 40000 + mrand.Intn(25000) + badPort := 40000 + mrand.IntN(25000) va.httpPort = badPort _, err := va.validateHTTP01(ctx, dnsi("localhost"), expectedToken, expectedKeyAuthorization) diff --git a/va/va.go b/va/va.go index c935f8d25..097c39d1c 100644 --- a/va/va.go +++ b/va/va.go @@ -7,7 +7,7 @@ import ( "encoding/json" "errors" "fmt" - "math/rand" + "math/rand/v2" "net" "net/url" "os"