11 // Halflife defines the time (in seconds) by which the transient part
12 // of the ban score decays to one half of it's original value.
15 // lambda is the decaying constant.
16 lambda = math.Ln2 / Halflife
18 // Lifetime defines the maximum age of the transient part of the ban
19 // score to be considered a non-zero score (in seconds).
22 // precomputedLen defines the amount of decay factors (one per second) that
23 // should be precomputed at initialization.
27 // precomputedFactor stores precomputed exponential decay factors for the first
28 // 'precomputedLen' seconds starting from t == 0.
29 var precomputedFactor [precomputedLen]float64
31 // init precomputes decay factors.
33 for i := range precomputedFactor {
34 precomputedFactor[i] = math.Exp(-1.0 * float64(i) * lambda)
38 // decayFactor returns the decay factor at t seconds, using precalculated values
39 // if available, or calculating the factor if needed.
40 func decayFactor(t int64) float64 {
41 if t < precomputedLen {
42 return precomputedFactor[t]
44 return math.Exp(-1.0 * float64(t) * lambda)
47 // DynamicBanScore provides dynamic ban scores consisting of a persistent and a
48 // decaying component. The persistent score could be utilized to create simple
49 // additive banning policies similar to those found in other bitcoin node
52 // The decaying score enables the creation of evasive logic which handles
53 // misbehaving peers (especially application layer DoS attacks) gracefully
54 // by disconnecting and banning peers attempting various kinds of flooding.
55 // DynamicBanScore allows these two approaches to be used in tandem.
57 // Zero value: Values of type DynamicBanScore are immediately ready for use upon
59 type DynamicBanScore struct {
66 // String returns the ban score as a human-readable string.
67 func (s *DynamicBanScore) String() string {
69 r := fmt.Sprintf("persistent %v + transient %v at %v = %v as of now",
70 s.persistent, s.transient, s.lastUnix, s.int(time.Now()))
75 // Int returns the current ban score, the sum of the persistent and decaying
78 // This function is safe for concurrent access.
79 func (s *DynamicBanScore) Int() uint32 {
81 r := s.int(time.Now())
86 // Increase increases both the persistent and decaying scores by the values
87 // passed as parameters. The resulting score is returned.
89 // This function is safe for concurrent access.
90 func (s *DynamicBanScore) Increase(persistent, transient uint32) uint32 {
92 r := s.increase(persistent, transient, time.Now())
97 // Reset set both persistent and decaying scores to zero.
99 // This function is safe for concurrent access.
100 func (s *DynamicBanScore) Reset() {
108 // int returns the ban score, the sum of the persistent and decaying scores at a
109 // given point in time.
111 // This function is not safe for concurrent access. It is intended to be used
112 // internally and during testing.
113 func (s *DynamicBanScore) int(t time.Time) uint32 {
114 dt := t.Unix() - s.lastUnix
115 if s.transient < 1 || dt < 0 || Lifetime < dt {
118 return s.persistent + uint32(s.transient*decayFactor(dt))
121 // increase increases the persistent, the decaying or both scores by the values
122 // passed as parameters. The resulting score is calculated as if the action was
123 // carried out at the point time represented by the third parameter. The
124 // resulting score is returned.
126 // This function is not safe for concurrent access.
127 func (s *DynamicBanScore) increase(persistent, transient uint32, t time.Time) uint32 {
128 s.persistent += persistent
130 dt := tu - s.lastUnix
135 } else if s.transient > 1 && dt > 0 {
136 s.transient *= decayFactor(dt)
138 s.transient += float64(transient)
141 return s.persistent + uint32(s.transient)