From 14eb119790bbb98880a829c9256f7f161fdf374a Mon Sep 17 00:00:00 2001 From: Dan Laine Date: Fri, 21 Aug 2020 16:17:29 -0400 Subject: [PATCH 01/10] handle unhandled errors. Comment in places we use math/rand so future auditors know we're aware. --- api/server.go | 3 ++- network/network.go | 29 ++++++++++++++++------------- node/node.go | 4 +++- utils/sampler/uniform_replacer.go | 1 + utils/sampler/weighted_best.go | 1 + vms/platformvm/common_blocks.go | 9 ++++++--- 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/api/server.go b/api/server.go index dda480b4cf48..4527f3a23fe0 100644 --- a/api/server.go +++ b/api/server.go @@ -156,7 +156,8 @@ func rejectMiddleware(handler http.Handler, ctx *snow.Context) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // If chain isn't done bootstrapping, ignore API calls if !ctx.IsBootstrapped() { w.WriteHeader(http.StatusServiceUnavailable) - w.Write([]byte("API call rejected because chain is not done bootstrapping")) + // Doesn't matter if there's an error while writing. They'll get the StatusServiceUnavailable code. + _, _ = w.Write([]byte("API call rejected because chain is not done bootstrapping")) } else { handler.ServeHTTP(w, r) } diff --git a/network/network.go b/network/network.go index 4de789f1b5a5..2137faa2d85f 100644 --- a/network/network.go +++ b/network/network.go @@ -225,19 +225,21 @@ func NewNetwork( pingFrequency time.Duration, ) Network { netw := &network{ - log: log, - id: id, - ip: ip, - networkID: networkID, - version: version, - parser: parser, - listener: listener, - dialer: dialer, - serverUpgrader: serverUpgrader, - clientUpgrader: clientUpgrader, - vdrs: vdrs, - beacons: beacons, - router: router, + log: log, + id: id, + ip: ip, + networkID: networkID, + version: version, + parser: parser, + listener: listener, + dialer: dialer, + serverUpgrader: serverUpgrader, + clientUpgrader: clientUpgrader, + vdrs: vdrs, + beacons: beacons, + router: router, + // math/rand is OK to use here. This field just makes sure we don't connect + // to ourselves when TLS is disabled. nodeID: rand.Uint32(), initialReconnectDelay: initialReconnectDelay, maxReconnectDelay: maxReconnectDelay, @@ -888,6 +890,7 @@ func (n *network) connectTo(ip utils.IPDesc) { delay = time.Duration(float64(delay) * (1 + rand.Float64())) if delay > n.maxReconnectDelay { // set the timeout to [.75, 1) * maxReconnectDelay + // math/rand is OK to use here delay = time.Duration(float64(n.maxReconnectDelay) * (3 + rand.Float64()) / 4) } diff --git a/node/node.go b/node/node.go index d3047a53f3b3..97f5655d93be 100644 --- a/node/node.go +++ b/node/node.go @@ -443,7 +443,9 @@ func (n *Node) initChainManager(avaxAssetID ids.ID) error { // to its own local validator manager (which isn't used for sampling) if !n.Config.EnableStaking { defaultSubnetValidators := validators.NewSet() - defaultSubnetValidators.Add(validators.NewValidator(n.ID, 1)) + if err := defaultSubnetValidators.Add(validators.NewValidator(n.ID, 1)); err != nil { + return fmt.Errorf("couldn't add validator to Default Subnet: %w", err) + } vdrs = validators.NewManager() vdrs.PutValidatorSet(constants.DefaultSubnetID, defaultSubnetValidators) } diff --git a/utils/sampler/uniform_replacer.go b/utils/sampler/uniform_replacer.go index ddbf5924a0ae..039d38551b06 100644 --- a/utils/sampler/uniform_replacer.go +++ b/utils/sampler/uniform_replacer.go @@ -54,6 +54,7 @@ func (s *uniformReplacer) Sample(count int) ([]uint64, error) { drawn := make(defaultMap, count) results := make([]uint64, count) for i := 0; i < count; i++ { + // math/rand is OK to use here. draw := uint64(rand.Int63n(int64(s.length-uint64(i)))) + uint64(i) ret := drawn.get(draw, draw) diff --git a/utils/sampler/weighted_best.go b/utils/sampler/weighted_best.go index bdeb0019c4b8..683d5bfd6ae0 100644 --- a/utils/sampler/weighted_best.go +++ b/utils/sampler/weighted_best.go @@ -50,6 +50,7 @@ func (s *weightedBest) Initialize(weights []uint64) error { samples := make([]uint64, s.benchmarkIterations) for i := range samples { + // math/rand is OK to use here samples[i] = uint64(rand.Int63n(int64(totalWeight))) } diff --git a/vms/platformvm/common_blocks.go b/vms/platformvm/common_blocks.go index 67d53f847965..b62f92d57552 100644 --- a/vms/platformvm/common_blocks.go +++ b/vms/platformvm/common_blocks.go @@ -270,11 +270,11 @@ func (ddb *DoubleDecisionBlock) Accept() error { // Update the state of the chain in the database if err := ddb.onAcceptDB.Commit(); err != nil { - ddb.vm.Ctx.Log.Warn("unable to commit onAcceptDB") + ddb.vm.Ctx.Log.Warn("unable to commit onAcceptDB: %s", err) return err } if err := ddb.vm.DB.Commit(); err != nil { - ddb.vm.Ctx.Log.Warn("unable to commit vm's DB") + ddb.vm.Ctx.Log.Warn("unable to commit vm's DB: %s", err) return err } @@ -282,7 +282,10 @@ func (ddb *DoubleDecisionBlock) Accept() error { child.setBaseDatabase(ddb.vm.DB) } if ddb.onAcceptFunc != nil { - ddb.onAcceptFunc() + if err := ddb.onAcceptFunc(); err != nil { + ddb.vm.Ctx.Log.Warn("error executing OnAcceptFunc(): %s", err) + return err + } } // remove this block and its parent from memory From 1ba580a3fc2c77dd37dc2c85313dc368386022e4 Mon Sep 17 00:00:00 2001 From: StephenButtolph Date: Sun, 23 Aug 2020 01:48:16 -0400 Subject: [PATCH 02/10] added new sampling algos and optimized initializations --- snow/validators/set.go | 8 ++ utils/sampler/uniform.go | 3 + utils/sampler/uniform_benchmark_test.go | 15 ++-- utils/sampler/uniform_best.go | 81 +++++++++++++++++++ utils/sampler/uniform_replacer.go | 3 - utils/sampler/uniform_resample.go | 54 +++++++++++++ utils/sampler/uniform_test.go | 8 ++ utils/sampler/weighted.go | 14 ++++ utils/sampler/weighted_benchmark_test.go | 78 ++++++++++++++++-- utils/sampler/weighted_best.go | 13 +-- utils/sampler/weighted_heap.go | 4 +- utils/sampler/weighted_without_replacement.go | 18 +++++ .../weighted_without_replacement_generic.go | 8 -- .../weighted_without_replacement_test.go | 13 ++- 14 files changed, 285 insertions(+), 35 deletions(-) create mode 100644 utils/sampler/uniform_best.go create mode 100644 utils/sampler/uniform_resample.go diff --git a/snow/validators/set.go b/snow/validators/set.go index 9a3c8c5602fb..ef967ae55e26 100644 --- a/snow/validators/set.go +++ b/snow/validators/set.go @@ -71,6 +71,14 @@ func NewSet() Set { } } +// NewBestSet returns a new, empty set of validators. +func NewBestSet(expectedSampleSize int) Set { + return &set{ + vdrMap: make(map[[20]byte]int), + sampler: sampler.NewBestWeightedWithoutReplacement(expectedSampleSize), + } +} + // set of validators. Validator function results are cached. Therefore, to // update a validators weight, one should ensure to call add with the updated // validator. diff --git a/utils/sampler/uniform.go b/utils/sampler/uniform.go index faa89d5e5a4f..f7c84588477d 100644 --- a/utils/sampler/uniform.go +++ b/utils/sampler/uniform.go @@ -8,3 +8,6 @@ type Uniform interface { Initialize(sampleRange uint64) error Sample(length int) ([]uint64, error) } + +// NewUniform returns a new sampler +func NewUniform() Uniform { return &uniformReplacer{} } diff --git a/utils/sampler/uniform_benchmark_test.go b/utils/sampler/uniform_benchmark_test.go index 96731766a199..ed51d94aa2b7 100644 --- a/utils/sampler/uniform_benchmark_test.go +++ b/utils/sampler/uniform_benchmark_test.go @@ -10,18 +10,17 @@ import ( // BenchmarkAllUniform func BenchmarkAllUniform(b *testing.B) { - sizes := []int{ - 1, - 5, - 25, - 50, - 75, - 100, + sizes := []uint64{ + 30, + 35, + 500, + 10000, + 100000, } for _, s := range uniformSamplers { for _, size := range sizes { b.Run(fmt.Sprintf("sampler %s with %d elements uniformly", s.name, size), func(b *testing.B) { - UniformBenchmark(b, s.sampler, 1000000, size) + UniformBenchmark(b, s.sampler, size, 30) }) } } diff --git a/utils/sampler/uniform_best.go b/utils/sampler/uniform_best.go new file mode 100644 index 000000000000..965475bd4065 --- /dev/null +++ b/utils/sampler/uniform_best.go @@ -0,0 +1,81 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package sampler + +import ( + "errors" + "math" + "math/rand" + "time" + + "github.com/ava-labs/gecko/utils/timer" +) + +var ( + errNoValidUniformSamplers = errors.New("no valid uniform samplers found") +) + +func init() { rand.Seed(time.Now().UnixNano()) } + +// uniformBest implements the Uniform interface. +// +// Sampling is performed by using another implementation of the Uniform +// interface. +// +// Initialization attempts to find the best sampling algorithm given the dataset +// by performing a benchmark of the provided implementations. +type uniformBest struct { + Uniform + samplers []Uniform + maxSampleSize int + benchmarkIterations int + clock timer.Clock +} + +// NewBestUniform returns a new sampler +func NewBestUniform(expectedSampleSize int) Uniform { + return &uniformBest{ + samplers: []Uniform{ + &uniformReplacer{}, + &uniformResample{}, + }, + maxSampleSize: expectedSampleSize, + benchmarkIterations: 100, + } +} + +func (s *uniformBest) Initialize(length uint64) error { + s.Uniform = nil + bestDuration := time.Duration(math.MaxInt64) + + sampleSize := s.maxSampleSize + if length < uint64(sampleSize) { + sampleSize = int(length) + } + +samplerLoop: + for _, sampler := range s.samplers { + if err := sampler.Initialize(length); err != nil { + continue + } + + start := s.clock.Time() + for i := 0; i < s.benchmarkIterations; i++ { + if _, err := sampler.Sample(sampleSize); err != nil { + continue samplerLoop + } + } + end := s.clock.Time() + duration := end.Sub(start) + if duration < bestDuration { + bestDuration = duration + s.Uniform = sampler + } + } + + if s.Uniform == nil { + return errNoValidUniformSamplers + } + return nil +} diff --git a/utils/sampler/uniform_replacer.go b/utils/sampler/uniform_replacer.go index ddbf5924a0ae..33414e28e43a 100644 --- a/utils/sampler/uniform_replacer.go +++ b/utils/sampler/uniform_replacer.go @@ -35,9 +35,6 @@ type uniformReplacer struct { length uint64 } -// NewUniform returns a new sampler -func NewUniform() Uniform { return &uniformReplacer{} } - func (s *uniformReplacer) Initialize(length uint64) error { if length > math.MaxInt64 { return errOutOfRange diff --git a/utils/sampler/uniform_resample.go b/utils/sampler/uniform_resample.go new file mode 100644 index 000000000000..8912c0383c5c --- /dev/null +++ b/utils/sampler/uniform_resample.go @@ -0,0 +1,54 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package sampler + +import ( + "math" + "math/rand" + "time" +) + +func init() { rand.Seed(time.Now().UnixNano()) } + +// uniformResample allows for sampling over a uniform distribution without +// replacement. +// +// Sampling is performed by sampling with replacement and resampling if a +// duplicate is sampled. +// +// Initialization takes O(1) time. +// +// Sampling is performed in O(count) time and O(count) space. +type uniformResample struct { + length uint64 +} + +func (s *uniformResample) Initialize(length uint64) error { + if length > math.MaxInt64 { + return errOutOfRange + } + s.length = length + return nil +} + +func (s *uniformResample) Sample(count int) ([]uint64, error) { + if count < 0 || s.length < uint64(count) { + return nil, errOutOfRange + } + + drawn := make(map[uint64]struct{}, count) + results := make([]uint64, count) + for i := 0; i < count; i++ { + draw := uint64(rand.Int63n(int64(s.length))) + if _, ok := drawn[draw]; ok { + i-- + continue + } + drawn[draw] = struct{}{} + + results[i] = draw + } + + return results, nil +} diff --git a/utils/sampler/uniform_test.go b/utils/sampler/uniform_test.go index 30b333e064f7..4dce04b5fbcf 100644 --- a/utils/sampler/uniform_test.go +++ b/utils/sampler/uniform_test.go @@ -22,6 +22,14 @@ var ( name: "replacer", sampler: &uniformReplacer{}, }, + { + name: "resampler", + sampler: &uniformResample{}, + }, + { + name: "best", + sampler: NewBestUniform(30), + }, } uniformTests = []struct { name string diff --git a/utils/sampler/weighted.go b/utils/sampler/weighted.go index 3c1b2f9b4a3b..f9e56e8099fb 100644 --- a/utils/sampler/weighted.go +++ b/utils/sampler/weighted.go @@ -15,3 +15,17 @@ type Weighted interface { Initialize(weights []uint64) error Sample(sampleValue uint64) (int, error) } + +// NewWeighted returns a new sampler +func NewWeighted() Weighted { + return &weightedBest{ + samplers: []Weighted{ + &weightedArray{}, + &weightedHeap{}, + &weightedUniform{ + maxWeight: 1 << 10, + }, + }, + benchmarkIterations: 100, + } +} diff --git a/utils/sampler/weighted_benchmark_test.go b/utils/sampler/weighted_benchmark_test.go index aa6b85202b36..acbf49802632 100644 --- a/utils/sampler/weighted_benchmark_test.go +++ b/utils/sampler/weighted_benchmark_test.go @@ -13,8 +13,8 @@ import ( safemath "github.com/ava-labs/gecko/utils/math" ) -// BenchmarkAllWeighted -func BenchmarkAllWeighted(b *testing.B) { +// BenchmarkAllWeightedSampling +func BenchmarkAllWeightedSampling(b *testing.B) { pows := []float64{ 0, 1, @@ -31,17 +31,52 @@ func BenchmarkAllWeighted(b *testing.B) { for _, s := range weightedSamplers { for _, pow := range pows { for _, size := range sizes { - if WeightedPowBenchmark(b, s.sampler, pow, size) { + if WeightedPowBenchmarkSampler(b, s.sampler, pow, size) { b.Run(fmt.Sprintf("sampler %s with %d elements at x^%.1f", s.name, size, pow), func(b *testing.B) { - WeightedPowBenchmark(b, s.sampler, pow, size) + WeightedPowBenchmarkSampler(b, s.sampler, pow, size) }) } } } for _, size := range sizes { - if WeightedSingletonBenchmark(b, s.sampler, size) { + if WeightedSingletonBenchmarkSampler(b, s.sampler, size) { b.Run(fmt.Sprintf("sampler %s with %d singleton elements", s.name, size), func(b *testing.B) { - WeightedSingletonBenchmark(b, s.sampler, size) + WeightedSingletonBenchmarkSampler(b, s.sampler, size) + }) + } + } + } +} + +// BenchmarkAllWeightedInitializer +func BenchmarkAllWeightedInitializer(b *testing.B) { + pows := []float64{ + 0, + 1, + 2, + 3, + } + sizes := []int{ + 10, + 500, + 1000, + 50000, + 100000, + } + for _, s := range weightedSamplers { + for _, pow := range pows { + for _, size := range sizes { + if WeightedPowBenchmarkSampler(b, s.sampler, pow, size) { + b.Run(fmt.Sprintf("initializer %s with %d elements at x^%.1f", s.name, size, pow), func(b *testing.B) { + WeightedPowBenchmarkInitializer(b, s.sampler, pow, size) + }) + } + } + } + for _, size := range sizes { + if WeightedSingletonBenchmarkSampler(b, s.sampler, size) { + b.Run(fmt.Sprintf("initializer %s with %d singleton elements", s.name, size), func(b *testing.B) { + WeightedSingletonBenchmarkInitializer(b, s.sampler, size) }) } } @@ -67,7 +102,7 @@ func CalcWeightedPoW(exponent float64, size int) (uint64, []uint64, error) { return totalWeight, weights, nil } -func WeightedPowBenchmark( +func WeightedPowBenchmarkSampler( b *testing.B, s Weighted, exponent float64, @@ -88,7 +123,7 @@ func WeightedPowBenchmark( return true } -func WeightedSingletonBenchmark(b *testing.B, s Weighted, size int) bool { +func WeightedSingletonBenchmarkSampler(b *testing.B, s Weighted, size int) bool { weights := make([]uint64, size) weights[0] = uint64(math.MaxInt64 - size + 1) for i := 1; i < len(weights); i++ { @@ -106,3 +141,30 @@ func WeightedSingletonBenchmark(b *testing.B, s Weighted, size int) bool { } return true } + +func WeightedPowBenchmarkInitializer( + b *testing.B, + s Weighted, + exponent float64, + size int, +) { + _, weights, _ := CalcWeightedPoW(exponent, size) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = s.Initialize(weights) + } +} + +func WeightedSingletonBenchmarkInitializer(b *testing.B, s Weighted, size int) { + weights := make([]uint64, size) + weights[0] = uint64(math.MaxInt64 - size + 1) + for i := 1; i < len(weights); i++ { + weights[i] = 1 + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = s.Initialize(weights) + } +} diff --git a/utils/sampler/weighted_best.go b/utils/sampler/weighted_best.go index bdeb0019c4b8..47851d519ba9 100644 --- a/utils/sampler/weighted_best.go +++ b/utils/sampler/weighted_best.go @@ -15,7 +15,7 @@ import ( ) var ( - errNoValidSamplers = errors.New("no valid samplers found") + errNoValidWeightedSamplers = errors.New("no valid weighted samplers found") ) func init() { rand.Seed(time.Now().UnixNano()) } @@ -48,9 +48,12 @@ func (s *weightedBest) Initialize(weights []uint64) error { return errWeightsTooLarge } - samples := make([]uint64, s.benchmarkIterations) - for i := range samples { - samples[i] = uint64(rand.Int63n(int64(totalWeight))) + samples := []uint64(nil) + if totalWeight > 0 { + samples = make([]uint64, s.benchmarkIterations) + for i := range samples { + samples[i] = uint64(rand.Int63n(int64(totalWeight))) + } } s.Weighted = nil @@ -75,7 +78,7 @@ func (s *weightedBest) Initialize(weights []uint64) error { } if s.Weighted == nil { - return errNoValidSamplers + return errNoValidWeightedSamplers } return nil } diff --git a/utils/sampler/weighted_heap.go b/utils/sampler/weighted_heap.go index 9125a7ad15b5..2383222ab337 100644 --- a/utils/sampler/weighted_heap.go +++ b/utils/sampler/weighted_heap.go @@ -47,7 +47,9 @@ func (s *weightedHeap) Initialize(weights []uint64) error { // Initialize the heap for i := len(s.heap) - 1; i > 0; i-- { - parentIndex := (i - 1) / 2 + // Explicitly performing a shift here allows the compiler to avoid + // checking for negative numbers, which saves a couple cycles + parentIndex := (i - 1) >> 1 newWeight, err := safemath.Add64( s.heap[parentIndex].cumulativeWeight, s.heap[i].cumulativeWeight, diff --git a/utils/sampler/weighted_without_replacement.go b/utils/sampler/weighted_without_replacement.go index 1b3f3569a6ea..5990a5bca6c3 100644 --- a/utils/sampler/weighted_without_replacement.go +++ b/utils/sampler/weighted_without_replacement.go @@ -10,3 +10,21 @@ type WeightedWithoutReplacement interface { Initialize(weights []uint64) error Sample(count int) ([]int, error) } + +// NewWeightedWithoutReplacement returns a new sampler +func NewWeightedWithoutReplacement() WeightedWithoutReplacement { + return &weightedWithoutReplacementGeneric{ + u: NewUniform(), + w: NewWeighted(), + } +} + +// NewBestWeightedWithoutReplacement returns a new sampler +func NewBestWeightedWithoutReplacement( + expectedSampleSize int, +) WeightedWithoutReplacement { + return &weightedWithoutReplacementGeneric{ + u: NewBestUniform(expectedSampleSize), + w: NewWeighted(), + } +} diff --git a/utils/sampler/weighted_without_replacement_generic.go b/utils/sampler/weighted_without_replacement_generic.go index e402e8942cb4..609af36e9754 100644 --- a/utils/sampler/weighted_without_replacement_generic.go +++ b/utils/sampler/weighted_without_replacement_generic.go @@ -12,14 +12,6 @@ type weightedWithoutReplacementGeneric struct { w Weighted } -// NewWeightedWithoutReplacement returns a new sampler -func NewWeightedWithoutReplacement() WeightedWithoutReplacement { - return &weightedWithoutReplacementGeneric{ - u: &uniformReplacer{}, - w: &weightedHeap{}, - } -} - func (s *weightedWithoutReplacementGeneric) Initialize(weights []uint64) error { totalWeight := uint64(0) for _, weight := range weights { diff --git a/utils/sampler/weighted_without_replacement_test.go b/utils/sampler/weighted_without_replacement_test.go index 4c963ddc6098..3a5fd04cab49 100644 --- a/utils/sampler/weighted_without_replacement_test.go +++ b/utils/sampler/weighted_without_replacement_test.go @@ -18,10 +18,19 @@ var ( sampler WeightedWithoutReplacement }{ { - name: "generic with replacer and heap", + name: "generic with replacer and best", sampler: &weightedWithoutReplacementGeneric{ u: &uniformReplacer{}, - w: &weightedHeap{}, + w: &weightedBest{ + samplers: []Weighted{ + &weightedArray{}, + &weightedHeap{}, + &weightedUniform{ + maxWeight: 1 << 10, + }, + }, + benchmarkIterations: 30, + }, }, }, } From f0cb44cabe4d635555bcbba1edaa1038ae6fb49d Mon Sep 17 00:00:00 2001 From: StephenButtolph Date: Sun, 23 Aug 2020 11:27:47 -0400 Subject: [PATCH 03/10] add a maximum timeout value to prevent extremely long timeouts --- snow/networking/timeout/manager.go | 3 ++- utils/timer/adaptive_timeout_manager.go | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/snow/networking/timeout/manager.go b/snow/networking/timeout/manager.go index 8c6ae26fe768..74724f1f1a5d 100644 --- a/snow/networking/timeout/manager.go +++ b/snow/networking/timeout/manager.go @@ -24,7 +24,8 @@ func (m *Manager) Initialize( return m.tm.Initialize( time.Second, 500*time.Millisecond, - 2, + 10*time.Second, + 1.1, time.Millisecond, namespace, registerer, diff --git a/utils/timer/adaptive_timeout_manager.go b/utils/timer/adaptive_timeout_manager.go index 2ded8412e4f4..da68c6727a18 100644 --- a/utils/timer/adaptive_timeout_manager.go +++ b/utils/timer/adaptive_timeout_manager.go @@ -52,6 +52,7 @@ type AdaptiveTimeoutManager struct { currentDurationMetric prometheus.Gauge minimumDuration time.Duration + maximumDuration time.Duration increaseRatio float64 decreaseValue time.Duration @@ -66,6 +67,7 @@ type AdaptiveTimeoutManager struct { func (tm *AdaptiveTimeoutManager) Initialize( initialDuration time.Duration, minimumDuration time.Duration, + maximumDuration time.Duration, increaseRatio float64, decreaseValue time.Duration, namespace string, @@ -77,6 +79,7 @@ func (tm *AdaptiveTimeoutManager) Initialize( Help: "Duration of current network timeouts in nanoseconds", }) tm.minimumDuration = minimumDuration + tm.maximumDuration = maximumDuration tm.increaseRatio = increaseRatio tm.decreaseValue = decreaseValue tm.currentDuration = initialDuration @@ -164,6 +167,11 @@ func (tm *AdaptiveTimeoutManager) remove(id ids.ID, currentTime time.Time) { // If the current timeout duration is less than or equal to the // timeout that was triggered, double the duration. tm.currentDuration = time.Duration(float64(tm.currentDuration) * tm.increaseRatio) + + if tm.currentDuration > tm.maximumDuration { + // Make sure that we never get stuck in a bad situation + tm.currentDuration = tm.maximumDuration + } } } else { // This request is being removed because it finished successfully. From a15dd9b57493603499936c728efdda96fc631035 Mon Sep 17 00:00:00 2001 From: StephenButtolph Date: Sun, 23 Aug 2020 11:32:34 -0400 Subject: [PATCH 04/10] fixed timeout tests --- utils/timer/adaptive_timeout_manager_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/timer/adaptive_timeout_manager_test.go b/utils/timer/adaptive_timeout_manager_test.go index 091c3866626f..24607a961b38 100644 --- a/utils/timer/adaptive_timeout_manager_test.go +++ b/utils/timer/adaptive_timeout_manager_test.go @@ -18,6 +18,7 @@ func TestAdaptiveTimeoutManager(t *testing.T) { tm.Initialize( time.Millisecond, // initialDuration time.Millisecond, // minimumDuration + time.Hour, // maximumDuration 2, // increaseRatio time.Microsecond, // decreaseValue "gecko", // namespace From 16b657910129541cc3f6c0d82bde95d5af906fb5 Mon Sep 17 00:00:00 2001 From: StephenButtolph Date: Sun, 23 Aug 2020 13:49:47 -0400 Subject: [PATCH 05/10] bumped version --- node/node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/node.go b/node/node.go index d3047a53f3b3..a5a2647ea83b 100644 --- a/node/node.go +++ b/node/node.go @@ -63,7 +63,7 @@ var ( genesisHashKey = []byte("genesisID") // Version is the version of this code - Version = version.NewDefaultVersion("avalanche", 0, 6, 2) + Version = version.NewDefaultVersion("avalanche", 0, 6, 3) versionParser = version.NewDefaultParser() ) From 5d67d92705a0cdc235ea9ec21c8deb8cc183fcd6 Mon Sep 17 00:00:00 2001 From: StephenButtolph Date: Mon, 24 Aug 2020 12:15:08 -0400 Subject: [PATCH 06/10] removed usage of sh --- vms/rpcchainvm/factory.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vms/rpcchainvm/factory.go b/vms/rpcchainvm/factory.go index 9621c3e6bd6e..da725ec5d0a2 100644 --- a/vms/rpcchainvm/factory.go +++ b/vms/rpcchainvm/factory.go @@ -29,7 +29,7 @@ func (f *Factory) New(ctx *snow.Context) (interface{}, error) { config := &plugin.ClientConfig{ HandshakeConfig: Handshake, Plugins: PluginMap, - Cmd: exec.Command("sh", "-c", f.Path), + Cmd: exec.Command(f.Path), AllowedProtocols: []plugin.Protocol{ plugin.ProtocolNetRPC, plugin.ProtocolGRPC, From eddd39ff7d48ece35257c872ed542131d9204219 Mon Sep 17 00:00:00 2001 From: StephenButtolph Date: Mon, 24 Aug 2020 15:06:45 -0400 Subject: [PATCH 07/10] standardized usage of path and filepath --- ipcs/chainipc.go | 4 ++-- main/main.go | 4 ++-- node/node.go | 4 ++-- utils/logging/factory.go | 8 +++----- utils/logging/log.go | 4 ++-- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/ipcs/chainipc.go b/ipcs/chainipc.go index 07162aebfe51..8ad168b6249f 100644 --- a/ipcs/chainipc.go +++ b/ipcs/chainipc.go @@ -5,7 +5,7 @@ package ipcs import ( "fmt" - "path" + "path/filepath" "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/snow/triggers" @@ -87,5 +87,5 @@ func (cipcs *ChainIPCs) Unpublish(chainID ids.ID) (bool, error) { } func ipcURL(ctx context, chainID ids.ID, eventType string) string { - return path.Join(ctx.path, fmt.Sprintf("%d-%s-%s", ctx.networkID, chainID.String(), eventType)) + return filepath.Join(ctx.path, fmt.Sprintf("%d-%s-%s", ctx.networkID, chainID.String(), eventType)) } diff --git a/main/main.go b/main/main.go index 022bef5f592b..b65cbba04c96 100644 --- a/main/main.go +++ b/main/main.go @@ -5,7 +5,7 @@ package main import ( "fmt" - "path" + "path/filepath" "github.com/ava-labs/gecko/nat" "github.com/ava-labs/gecko/node" @@ -22,7 +22,7 @@ func main() { } config := Config.LoggingConfig - config.Directory = path.Join(config.Directory, "node") + config.Directory = filepath.Join(config.Directory, "node") factory := logging.NewFactory(config) defer factory.Close() diff --git a/node/node.go b/node/node.go index d3047a53f3b3..f55437467baa 100644 --- a/node/node.go +++ b/node/node.go @@ -12,7 +12,7 @@ import ( "io/ioutil" "net" "os" - "path" + "path/filepath" "sync" "time" @@ -461,7 +461,7 @@ func (n *Node) initChainManager(avaxAssetID ids.ID) error { Fee: n.Config.TxFee, }), n.vmManager.RegisterVMFactory(genesis.EVMID, &rpcchainvm.Factory{ - Path: path.Join(n.Config.PluginDir, "evm"), + Path: filepath.Join(n.Config.PluginDir, "evm"), }), n.vmManager.RegisterVMFactory(spdagvm.ID, &spdagvm.Factory{ TxFee: n.Config.TxFee, diff --git a/utils/logging/factory.go b/utils/logging/factory.go index d9abe06722b2..8f988aae2101 100644 --- a/utils/logging/factory.go +++ b/utils/logging/factory.go @@ -3,9 +3,7 @@ package logging -import ( - "path" -) +import "path/filepath" // Factory ... type Factory interface { @@ -42,7 +40,7 @@ func (f *factory) Make() (Logger, error) { func (f *factory) MakeChain(chainID string, subdir string) (Logger, error) { config := f.config config.MsgPrefix = chainID + " Chain" - config.Directory = path.Join(config.Directory, "chain", chainID, subdir) + config.Directory = filepath.Join(config.Directory, "chain", chainID, subdir) log, err := New(config) if err == nil { @@ -54,7 +52,7 @@ func (f *factory) MakeChain(chainID string, subdir string) (Logger, error) { // MakeSubdir ... func (f *factory) MakeSubdir(subdir string) (Logger, error) { config := f.config - config.Directory = path.Join(config.Directory, subdir) + config.Directory = filepath.Join(config.Directory, subdir) log, err := New(config) if err == nil { diff --git a/utils/logging/log.go b/utils/logging/log.go index eb3b8a8ffdf5..33d42a150032 100644 --- a/utils/logging/log.go +++ b/utils/logging/log.go @@ -7,7 +7,7 @@ import ( "bufio" "fmt" "os" - "path" + "path/filepath" "runtime" "strings" "sync" @@ -351,7 +351,7 @@ func (fw *fileWriter) Rotate() error { } func (fw *fileWriter) create(fileIndex int) (*bufio.Writer, *os.File, error) { - filename := path.Join(fw.config.Directory, fmt.Sprintf("%d.log", fw.fileIndex)) + filename := filepath.Join(fw.config.Directory, fmt.Sprintf("%d.log", fw.fileIndex)) file, err := os.Create(filename) if err != nil { return nil, nil, err From 74e667a2cbaaa8b16659eeadc6b2238f84aee134 Mon Sep 17 00:00:00 2001 From: StephenButtolph Date: Mon, 24 Aug 2020 16:39:33 -0400 Subject: [PATCH 08/10] Added more detailed comments for non-crypto secure random sampling --- network/network.go | 2 ++ utils/sampler/uniform_replacer.go | 3 ++- utils/sampler/uniform_resample.go | 6 ++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/network/network.go b/network/network.go index 2137faa2d85f..909f46e29d2b 100644 --- a/network/network.go +++ b/network/network.go @@ -47,6 +47,8 @@ const ( defaultPingFrequency = 3 * defaultPingPongTimeout / 4 ) +func init() { rand.Seed(time.Now().UnixNano()) } + // Network defines the functionality of the networking library. type Network interface { // All consensus messages can be sent through this interface. Thread safety diff --git a/utils/sampler/uniform_replacer.go b/utils/sampler/uniform_replacer.go index 9a93b59d4fa1..816181ba83c4 100644 --- a/utils/sampler/uniform_replacer.go +++ b/utils/sampler/uniform_replacer.go @@ -51,7 +51,8 @@ func (s *uniformReplacer) Sample(count int) ([]uint64, error) { drawn := make(defaultMap, count) results := make([]uint64, count) for i := 0; i < count; i++ { - // math/rand is OK to use here. + // We don't use a cryptographically secure source of randomness here, as + // there's no need to ensure a truly random sampling. draw := uint64(rand.Int63n(int64(s.length-uint64(i)))) + uint64(i) ret := drawn.get(draw, draw) diff --git a/utils/sampler/uniform_resample.go b/utils/sampler/uniform_resample.go index 8912c0383c5c..751d577718d3 100644 --- a/utils/sampler/uniform_resample.go +++ b/utils/sampler/uniform_resample.go @@ -39,15 +39,17 @@ func (s *uniformResample) Sample(count int) ([]uint64, error) { drawn := make(map[uint64]struct{}, count) results := make([]uint64, count) - for i := 0; i < count; i++ { + for i := 0; i < count; { + // We don't use a cryptographically secure source of randomness here, as + // there's no need to ensure a truly random sampling. draw := uint64(rand.Int63n(int64(s.length))) if _, ok := drawn[draw]; ok { - i-- continue } drawn[draw] = struct{}{} results[i] = draw + i++ } return results, nil From 989dc53eb8a5b056218a506d5838e176242a19c2 Mon Sep 17 00:00:00 2001 From: StephenButtolph Date: Mon, 24 Aug 2020 16:45:48 -0400 Subject: [PATCH 09/10] Added more detailed comments in network.go around random number generation --- network/network.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/network/network.go b/network/network.go index 909f46e29d2b..ea99eec0013c 100644 --- a/network/network.go +++ b/network/network.go @@ -240,8 +240,9 @@ func NewNetwork( vdrs: vdrs, beacons: beacons, router: router, - // math/rand is OK to use here. This field just makes sure we don't connect - // to ourselves when TLS is disabled. + // This field just makes sure we don't connect to ourselves when TLS is + // disabled. So, cryptographically secure random number generation isn't + // used here. nodeID: rand.Uint32(), initialReconnectDelay: initialReconnectDelay, maxReconnectDelay: maxReconnectDelay, @@ -887,12 +888,12 @@ func (n *network) connectTo(ip utils.IPDesc) { delay = n.initialReconnectDelay } - // Ignore weak randomness warnings in calculating timeouts because true - // randomness is unnecessary here + // Randomization is only performed here to distribute reconnection + // attempts to a node that previously shut down. This doesn't require + // cryptographically secure random number generation. delay = time.Duration(float64(delay) * (1 + rand.Float64())) if delay > n.maxReconnectDelay { // set the timeout to [.75, 1) * maxReconnectDelay - // math/rand is OK to use here delay = time.Duration(float64(n.maxReconnectDelay) * (3 + rand.Float64()) / 4) } From f0facbaa2fdd7aea7ff7e9951a236cf6289d01e1 Mon Sep 17 00:00:00 2001 From: StephenButtolph Date: Mon, 24 Aug 2020 16:53:54 -0400 Subject: [PATCH 10/10] v0.6.4 version bump --- node/node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/node.go b/node/node.go index 9230a566f4e4..95acaa0cc108 100644 --- a/node/node.go +++ b/node/node.go @@ -63,7 +63,7 @@ var ( genesisHashKey = []byte("genesisID") // Version is the version of this code - Version = version.NewDefaultVersion("avalanche", 0, 6, 3) + Version = version.NewDefaultVersion("avalanche", 0, 6, 4) versionParser = version.NewDefaultParser() )