-
Notifications
You must be signed in to change notification settings - Fork 672
/
windower.go
118 lines (100 loc) · 2.84 KB
/
windower.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package proposer
import (
"context"
"time"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow/validators"
"github.com/ava-labs/avalanchego/utils"
"github.com/ava-labs/avalanchego/utils/math"
"github.com/ava-labs/avalanchego/utils/sampler"
"github.com/ava-labs/avalanchego/utils/wrappers"
)
// Proposer list constants
const (
MaxWindows = 6
WindowDuration = 5 * time.Second
MaxDelay = MaxWindows * WindowDuration
)
var _ Windower = (*windower)(nil)
type Windower interface {
Delay(
ctx context.Context,
chainHeight,
pChainHeight uint64,
validatorID ids.NodeID,
) (time.Duration, error)
}
// windower interfaces with P-Chain and it is responsible for calculating the
// delay for the block submission window of a given validator
type windower struct {
state validators.State
subnetID ids.ID
chainSource uint64
sampler sampler.WeightedWithoutReplacement
}
func New(state validators.State, subnetID, chainID ids.ID) Windower {
w := wrappers.Packer{Bytes: chainID[:]}
return &windower{
state: state,
subnetID: subnetID,
chainSource: w.UnpackLong(),
sampler: sampler.NewDeterministicWeightedWithoutReplacement(),
}
}
func (w *windower) Delay(ctx context.Context, chainHeight, pChainHeight uint64, validatorID ids.NodeID) (time.Duration, error) {
if validatorID == ids.EmptyNodeID {
return MaxDelay, nil
}
// get the validator set by the p-chain height
validatorsMap, err := w.state.GetValidatorSet(ctx, pChainHeight, w.subnetID)
if err != nil {
return 0, err
}
// convert the map of validators to a slice
validators := make([]validatorData, 0, len(validatorsMap))
weight := uint64(0)
for k, v := range validatorsMap {
validators = append(validators, validatorData{
id: k,
weight: v.Weight,
})
newWeight, err := math.Add64(weight, v.Weight)
if err != nil {
return 0, err
}
weight = newWeight
}
// canonically sort validators
// Note: validators are sorted by ID, sorting by weight would not create a
// canonically sorted list
utils.Sort(validators)
// convert the slice of validators to a slice of weights
validatorWeights := make([]uint64, len(validators))
for i, v := range validators {
validatorWeights[i] = v.weight
}
if err := w.sampler.Initialize(validatorWeights); err != nil {
return 0, err
}
numToSample := MaxWindows
if weight < uint64(numToSample) {
numToSample = int(weight)
}
seed := chainHeight ^ w.chainSource
w.sampler.Seed(int64(seed))
indices, err := w.sampler.Sample(numToSample)
if err != nil {
return 0, err
}
delay := time.Duration(0)
for _, index := range indices {
nodeID := validators[index].id
if nodeID == validatorID {
return delay, nil
}
delay += WindowDuration
}
return delay, nil
}