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  }