github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/p2p/trust/banscore.go (about) 1 package trust 2 3 import ( 4 "fmt" 5 "math" 6 "sync" 7 "time" 8 ) 9 10 const ( 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. 13 Halflife = 60 14 15 // lambda is the decaying constant. 16 lambda = math.Ln2 / Halflife 17 18 // Lifetime defines the maximum age of the transient part of the ban 19 // score to be considered a non-zero score (in seconds). 20 Lifetime = 1800 21 22 // precomputedLen defines the amount of decay factors (one per second) that 23 // should be precomputed at initialization. 24 precomputedLen = 64 25 ) 26 27 // precomputedFactor stores precomputed exponential decay factors for the first 28 // 'precomputedLen' seconds starting from t == 0. 29 var precomputedFactor [precomputedLen]float64 30 31 // init precomputes decay factors. 32 func Init() { 33 for i := range precomputedFactor { 34 precomputedFactor[i] = math.Exp(-1.0 * float64(i) * lambda) 35 } 36 } 37 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] 43 } 44 return math.Exp(-1.0 * float64(t) * lambda) 45 } 46 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 50 // implementations. 51 // 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. 56 // 57 // Zero value: Values of type DynamicBanScore are immediately ready for use upon 58 // declaration. 59 type DynamicBanScore struct { 60 lastUnix int64 61 transient float64 62 persistent uint32 63 mtx sync.Mutex 64 } 65 66 // String returns the ban score as a human-readable string. 67 func (s *DynamicBanScore) String() string { 68 s.mtx.Lock() 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())) 71 s.mtx.Unlock() 72 return r 73 } 74 75 // Int returns the current ban score, the sum of the persistent and decaying 76 // scores. 77 // 78 // This function is safe for concurrent access. 79 func (s *DynamicBanScore) Int() uint32 { 80 s.mtx.Lock() 81 r := s.int(time.Now()) 82 s.mtx.Unlock() 83 return r 84 } 85 86 // Increase increases both the persistent and decaying scores by the values 87 // passed as parameters. The resulting score is returned. 88 // 89 // This function is safe for concurrent access. 90 func (s *DynamicBanScore) Increase(persistent, transient uint32) uint32 { 91 s.mtx.Lock() 92 r := s.increase(persistent, transient, time.Now()) 93 s.mtx.Unlock() 94 return r 95 } 96 97 // Reset set both persistent and decaying scores to zero. 98 // 99 // This function is safe for concurrent access. 100 func (s *DynamicBanScore) Reset() { 101 s.mtx.Lock() 102 s.persistent = 0 103 s.transient = 0 104 s.lastUnix = 0 105 s.mtx.Unlock() 106 } 107 108 // int returns the ban score, the sum of the persistent and decaying scores at a 109 // given point in time. 110 // 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 { 116 return s.persistent 117 } 118 return s.persistent + uint32(s.transient*decayFactor(dt)) 119 } 120 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. 125 // 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 129 tu := t.Unix() 130 dt := tu - s.lastUnix 131 132 if transient > 0 { 133 if Lifetime < dt { 134 s.transient = 0 135 } else if s.transient > 1 && dt > 0 { 136 s.transient *= decayFactor(dt) 137 } 138 s.transient += float64(transient) 139 s.lastUnix = tu 140 } 141 return s.persistent + uint32(s.transient) 142 }