1 // Copyright (c) 2016 The btcsuite developers
2 // Use of this source code is governed by an ISC
3 // license that can be found in the LICENSE file.
15 // Halflife defines the time (in seconds) by which the transient part
16 // of the ban score decays to one half of it's original value.
19 // lambda is the decaying constant.
20 lambda = math.Ln2 / Halflife
22 // Lifetime defines the maximum age of the transient part of the ban
23 // score to be considered a non-zero score (in seconds).
26 // precomputedLen defines the amount of decay factors (one per second) that
27 // should be precomputed at initialization.
31 // precomputedFactor stores precomputed exponential decay factors for the first
32 // 'precomputedLen' seconds starting from t == 0.
33 var precomputedFactor [precomputedLen]float64
35 // init precomputes decay factors.
37 for i := range precomputedFactor {
38 precomputedFactor[i] = math.Exp(-1.0 * float64(i) * lambda)
42 // decayFactor returns the decay factor at t seconds, using precalculated values
43 // if available, or calculating the factor if needed.
44 func decayFactor(t int64) float64 {
45 if t < precomputedLen {
46 return precomputedFactor[t]
48 return math.Exp(-1.0 * float64(t) * lambda)
51 // DynamicBanScore provides dynamic ban scores consisting of a persistent and a
52 // decaying component. The persistent score could be utilized to create simple
53 // additive banning policies similar to those found in other bitcoin node
56 // The decaying score enables the creation of evasive logic which handles
57 // misbehaving peers (especially application layer DoS attacks) gracefully
58 // by disconnecting and banning peers attempting various kinds of flooding.
59 // DynamicBanScore allows these two approaches to be used in tandem.
61 // Zero value: Values of type DynamicBanScore are immediately ready for use upon
63 type DynamicBanScore struct {
70 // String returns the ban score as a human-readable string.
71 func (s *DynamicBanScore) String() string {
73 r := fmt.Sprintf("persistent %v + transient %v at %v = %v as of now",
74 s.persistent, s.transient, s.lastUnix, s.Int())
79 // Int returns the current ban score, the sum of the persistent and decaying
82 // This function is safe for concurrent access.
83 func (s *DynamicBanScore) Int() uint32 {
85 r := s.int(time.Now())
90 // Increase increases both the persistent and decaying scores by the values
91 // passed as parameters. The resulting score is returned.
93 // This function is safe for concurrent access.
94 func (s *DynamicBanScore) Increase(persistent, transient uint32) uint32 {
96 r := s.increase(persistent, transient, time.Now())
101 // Reset set both persistent and decaying scores to zero.
103 // This function is safe for concurrent access.
104 func (s *DynamicBanScore) Reset() {
112 // int returns the ban score, the sum of the persistent and decaying scores at a
113 // given point in time.
115 // This function is not safe for concurrent access. It is intended to be used
116 // internally and during testing.
117 func (s *DynamicBanScore) int(t time.Time) uint32 {
118 dt := t.Unix() - s.lastUnix
119 if s.transient < 1 || dt < 0 || Lifetime < dt {
122 return s.persistent + uint32(s.transient*decayFactor(dt))
125 // increase increases the persistent, the decaying or both scores by the values
126 // passed as parameters. The resulting score is calculated as if the action was
127 // carried out at the point time represented by the third parameter. The
128 // resulting score is returned.
130 // This function is not safe for concurrent access.
131 func (s *DynamicBanScore) increase(persistent, transient uint32, t time.Time) uint32 {
132 s.persistent += persistent
134 dt := tu - s.lastUnix
139 } else if s.transient > 1 && dt > 0 {
140 s.transient *= decayFactor(dt)
142 s.transient += float64(transient)
145 return s.persistent + uint32(s.transient)