github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/p2p/fuzz.go (about)

     1  package p2p
     2  
     3  import (
     4  	"net"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/tendermint/tendermint/config"
     9  	tmrand "github.com/tendermint/tendermint/libs/rand"
    10  )
    11  
    12  // FuzzedConnection wraps any net.Conn and depending on the mode either delays
    13  // reads/writes or randomly drops reads/writes/connections.
    14  type FuzzedConnection struct {
    15  	conn net.Conn
    16  
    17  	mtx    sync.Mutex
    18  	start  <-chan time.Time
    19  	active bool
    20  
    21  	config *config.FuzzConnConfig
    22  }
    23  
    24  // FuzzConn creates a new FuzzedConnection. Fuzzing starts immediately.
    25  func FuzzConn(conn net.Conn) net.Conn {
    26  	return FuzzConnFromConfig(conn, config.DefaultFuzzConnConfig())
    27  }
    28  
    29  // FuzzConnFromConfig creates a new FuzzedConnection from a config. Fuzzing
    30  // starts immediately.
    31  func FuzzConnFromConfig(conn net.Conn, config *config.FuzzConnConfig) net.Conn {
    32  	return &FuzzedConnection{
    33  		conn:   conn,
    34  		start:  make(<-chan time.Time),
    35  		active: true,
    36  		config: config,
    37  	}
    38  }
    39  
    40  // FuzzConnAfter creates a new FuzzedConnection. Fuzzing starts when the
    41  // duration elapses.
    42  func FuzzConnAfter(conn net.Conn, d time.Duration) net.Conn {
    43  	return FuzzConnAfterFromConfig(conn, d, config.DefaultFuzzConnConfig())
    44  }
    45  
    46  // FuzzConnAfterFromConfig creates a new FuzzedConnection from a config.
    47  // Fuzzing starts when the duration elapses.
    48  func FuzzConnAfterFromConfig(
    49  	conn net.Conn,
    50  	d time.Duration,
    51  	config *config.FuzzConnConfig,
    52  ) net.Conn {
    53  	return &FuzzedConnection{
    54  		conn:   conn,
    55  		start:  time.After(d),
    56  		active: false,
    57  		config: config,
    58  	}
    59  }
    60  
    61  // Config returns the connection's config.
    62  func (fc *FuzzedConnection) Config() *config.FuzzConnConfig {
    63  	return fc.config
    64  }
    65  
    66  // Read implements net.Conn.
    67  func (fc *FuzzedConnection) Read(data []byte) (n int, err error) {
    68  	if fc.fuzz() {
    69  		return 0, nil
    70  	}
    71  	return fc.conn.Read(data)
    72  }
    73  
    74  // Write implements net.Conn.
    75  func (fc *FuzzedConnection) Write(data []byte) (n int, err error) {
    76  	if fc.fuzz() {
    77  		return 0, nil
    78  	}
    79  	return fc.conn.Write(data)
    80  }
    81  
    82  // Close implements net.Conn.
    83  func (fc *FuzzedConnection) Close() error { return fc.conn.Close() }
    84  
    85  // LocalAddr implements net.Conn.
    86  func (fc *FuzzedConnection) LocalAddr() net.Addr { return fc.conn.LocalAddr() }
    87  
    88  // RemoteAddr implements net.Conn.
    89  func (fc *FuzzedConnection) RemoteAddr() net.Addr { return fc.conn.RemoteAddr() }
    90  
    91  // SetDeadline implements net.Conn.
    92  func (fc *FuzzedConnection) SetDeadline(t time.Time) error { return fc.conn.SetDeadline(t) }
    93  
    94  // SetReadDeadline implements net.Conn.
    95  func (fc *FuzzedConnection) SetReadDeadline(t time.Time) error {
    96  	return fc.conn.SetReadDeadline(t)
    97  }
    98  
    99  // SetWriteDeadline implements net.Conn.
   100  func (fc *FuzzedConnection) SetWriteDeadline(t time.Time) error {
   101  	return fc.conn.SetWriteDeadline(t)
   102  }
   103  
   104  func (fc *FuzzedConnection) randomDuration() time.Duration {
   105  	maxDelayMillis := int(fc.config.MaxDelay.Nanoseconds() / 1000)
   106  	return time.Millisecond * time.Duration(tmrand.Int()%maxDelayMillis) // nolint: gas
   107  }
   108  
   109  // implements the fuzz (delay, kill conn)
   110  // and returns whether or not the read/write should be ignored
   111  func (fc *FuzzedConnection) fuzz() bool {
   112  	if !fc.shouldFuzz() {
   113  		return false
   114  	}
   115  
   116  	switch fc.config.Mode {
   117  	case config.FuzzModeDrop:
   118  		// randomly drop the r/w, drop the conn, or sleep
   119  		r := tmrand.Float64()
   120  		switch {
   121  		case r <= fc.config.ProbDropRW:
   122  			return true
   123  		case r < fc.config.ProbDropRW+fc.config.ProbDropConn:
   124  			// XXX: can't this fail because machine precision?
   125  			// XXX: do we need an error?
   126  			fc.Close() // nolint: errcheck, gas
   127  			return true
   128  		case r < fc.config.ProbDropRW+fc.config.ProbDropConn+fc.config.ProbSleep:
   129  			time.Sleep(fc.randomDuration())
   130  		}
   131  	case config.FuzzModeDelay:
   132  		// sleep a bit
   133  		time.Sleep(fc.randomDuration())
   134  	}
   135  	return false
   136  }
   137  
   138  func (fc *FuzzedConnection) shouldFuzz() bool {
   139  	if fc.active {
   140  		return true
   141  	}
   142  
   143  	fc.mtx.Lock()
   144  	defer fc.mtx.Unlock()
   145  
   146  	select {
   147  	case <-fc.start:
   148  		fc.active = true
   149  		return true
   150  	default:
   151  		return false
   152  	}
   153  }