github.com/pure-x-eth/consensus_tm@v0.0.0-20230502163723-e3c2ff987250/p2p/fuzz.go (about) 1 package p2p 2 3 import ( 4 "net" 5 "time" 6 7 "github.com/pure-x-eth/consensus_tm/config" 8 tmrand "github.com/pure-x-eth/consensus_tm/libs/rand" 9 tmsync "github.com/pure-x-eth/consensus_tm/libs/sync" 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 tmsync.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() 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 }