github.com/BlockABC/godash@v0.0.0-20191112120524-f4aa3a32c566/dynamicbanscore.go (about) 1 // Copyright (c) 2016 The btcsuite developers 2 // Copyright (c) 2016 The Dash developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package main 7 8 import ( 9 "fmt" 10 "math" 11 "sync" 12 "time" 13 ) 14 15 const ( 16 // Halflife defines the time (in seconds) by which the transient part 17 // of the ban score decays to one half of it's original value. 18 Halflife = 60 19 20 // lambda is the decaying constant. 21 lambda = math.Ln2 / Halflife 22 23 // Lifetime defines the maximum age of the transient part of the ban 24 // score to be considered a non-zero score (in seconds). 25 Lifetime = 1800 26 27 // precomputedLen defines the amount of decay factors (one per second) that 28 // should be precomputed at initialization. 29 precomputedLen = 64 30 ) 31 32 // precomputedFactor stores precomputed exponential decay factors for the first 33 // 'precomputedLen' seconds starting from t == 0. 34 var precomputedFactor [precomputedLen]float64 35 36 // init precomputes decay factors. 37 func init() { 38 for i := range precomputedFactor { 39 precomputedFactor[i] = math.Exp(-1.0 * float64(i) * lambda) 40 } 41 } 42 43 // decayFactor returns the decay factor at t seconds, using precalculated values 44 // if available, or calculating the factor if needed. 45 func decayFactor(t int64) float64 { 46 if t < precomputedLen { 47 return precomputedFactor[t] 48 } 49 return math.Exp(-1.0 * float64(t) * lambda) 50 } 51 52 // dynamicBanScore provides dynamic ban scores consisting of a persistent and a 53 // decaying component. The persistent score could be utilized to create simple 54 // additive banning policies similar to those found in other bitcoin node 55 // implementations. 56 // 57 // The decaying score enables the creation of evasive logic which handles 58 // misbehaving peers (especially application layer DoS attacks) gracefully 59 // by disconnecting and banning peers attempting various kinds of flooding. 60 // dynamicBanScore allows these two approaches to be used in tandem. 61 // 62 // Zero value: Values of type dynamicBanScore are immediately ready for use upon 63 // declaration. 64 type dynamicBanScore struct { 65 lastUnix int64 66 transient float64 67 persistent uint32 68 sync.Mutex 69 } 70 71 // String returns the ban score as a human-readable string. 72 func (s *dynamicBanScore) String() string { 73 s.Lock() 74 r := fmt.Sprintf("persistent %v + transient %v at %v = %v as of now", 75 s.persistent, s.transient, s.lastUnix, s.Int()) 76 s.Unlock() 77 return r 78 } 79 80 // Int returns the current ban score, the sum of the persistent and decaying 81 // scores. 82 // 83 // This function is safe for concurrent access. 84 func (s *dynamicBanScore) Int() uint32 { 85 s.Lock() 86 r := s.int(time.Now()) 87 s.Unlock() 88 return r 89 } 90 91 // Increase increases both the persistent and decaying scores by the values 92 // passed as parameters. The resulting score is returned. 93 // 94 // This function is safe for concurrent access. 95 func (s *dynamicBanScore) Increase(persistent, transient uint32) uint32 { 96 s.Lock() 97 r := s.increase(persistent, transient, time.Now()) 98 s.Unlock() 99 return r 100 } 101 102 // Reset set both persistent and decaying scores to zero. 103 // 104 // This function is safe for concurrent access. 105 func (s *dynamicBanScore) Reset() { 106 s.Lock() 107 s.persistent = 0 108 s.transient = 0 109 s.lastUnix = 0 110 s.Unlock() 111 } 112 113 // int returns the ban score, the sum of the persistent and decaying scores at a 114 // given point in time. 115 // 116 // This function is not safe for concurrent access. It is intended to be used 117 // internally and during testing. 118 func (s *dynamicBanScore) int(t time.Time) uint32 { 119 dt := t.Unix() - s.lastUnix 120 if s.transient < 1 || dt < 0 || Lifetime < dt { 121 return s.persistent 122 } 123 return s.persistent + uint32(s.transient*decayFactor(dt)) 124 } 125 126 // increase increases the persistent, the decaying or both scores by the values 127 // passed as parameters. The resulting score is calculated as if the action was 128 // carried out at the point time represented by the third parameter. The 129 // resulting score is returned. 130 // 131 // This function is not safe for concurrent access. 132 func (s *dynamicBanScore) increase(persistent, transient uint32, t time.Time) uint32 { 133 s.persistent += persistent 134 tu := t.Unix() 135 dt := tu - s.lastUnix 136 137 if transient > 0 { 138 if Lifetime < dt { 139 s.transient = 0 140 } else if s.transient > 1 && dt > 0 { 141 s.transient *= decayFactor(dt) 142 } 143 s.transient += float64(transient) 144 s.lastUnix = tu 145 } 146 return s.persistent + uint32(s.transient) 147 }