mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 14:52:17 +00:00
As both configs are concerned with the p2p packaage and PeerConfig is only used inside of the package there is no good reason to keep the couple of fields separate, therefore it is collapsed into the more general P2PConifg. This is a stepping stone towards a setup where the components inside of p2p do not have any knowledge about the config. follow-up to #1325
153 lines
3.8 KiB
Go
153 lines
3.8 KiB
Go
package p2p
|
|
|
|
import (
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/tendermint/tendermint/config"
|
|
cmn "github.com/tendermint/tmlibs/common"
|
|
)
|
|
|
|
// FuzzedConnection wraps any net.Conn and depending on the mode either delays
|
|
// reads/writes or randomly drops reads/writes/connections.
|
|
type FuzzedConnection struct {
|
|
conn net.Conn
|
|
|
|
mtx sync.Mutex
|
|
start <-chan time.Time
|
|
active bool
|
|
|
|
config *config.FuzzConnConfig
|
|
}
|
|
|
|
// FuzzConn creates a new FuzzedConnection. Fuzzing starts immediately.
|
|
func FuzzConn(conn net.Conn) net.Conn {
|
|
return FuzzConnFromConfig(conn, config.DefaultFuzzConnConfig())
|
|
}
|
|
|
|
// FuzzConnFromConfig creates a new FuzzedConnection from a config. Fuzzing
|
|
// starts immediately.
|
|
func FuzzConnFromConfig(conn net.Conn, config *config.FuzzConnConfig) net.Conn {
|
|
return &FuzzedConnection{
|
|
conn: conn,
|
|
start: make(<-chan time.Time),
|
|
active: true,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
// FuzzConnAfter creates a new FuzzedConnection. Fuzzing starts when the
|
|
// duration elapses.
|
|
func FuzzConnAfter(conn net.Conn, d time.Duration) net.Conn {
|
|
return FuzzConnAfterFromConfig(conn, d, config.DefaultFuzzConnConfig())
|
|
}
|
|
|
|
// FuzzConnAfterFromConfig creates a new FuzzedConnection from a config.
|
|
// Fuzzing starts when the duration elapses.
|
|
func FuzzConnAfterFromConfig(
|
|
conn net.Conn,
|
|
d time.Duration,
|
|
config *config.FuzzConnConfig,
|
|
) net.Conn {
|
|
return &FuzzedConnection{
|
|
conn: conn,
|
|
start: time.After(d),
|
|
active: false,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
// Config returns the connection's config.
|
|
func (fc *FuzzedConnection) Config() *config.FuzzConnConfig {
|
|
return fc.config
|
|
}
|
|
|
|
// Read implements net.Conn.
|
|
func (fc *FuzzedConnection) Read(data []byte) (n int, err error) {
|
|
if fc.fuzz() {
|
|
return 0, nil
|
|
}
|
|
return fc.conn.Read(data)
|
|
}
|
|
|
|
// Write implements net.Conn.
|
|
func (fc *FuzzedConnection) Write(data []byte) (n int, err error) {
|
|
if fc.fuzz() {
|
|
return 0, nil
|
|
}
|
|
return fc.conn.Write(data)
|
|
}
|
|
|
|
// Close implements net.Conn.
|
|
func (fc *FuzzedConnection) Close() error { return fc.conn.Close() }
|
|
|
|
// LocalAddr implements net.Conn.
|
|
func (fc *FuzzedConnection) LocalAddr() net.Addr { return fc.conn.LocalAddr() }
|
|
|
|
// RemoteAddr implements net.Conn.
|
|
func (fc *FuzzedConnection) RemoteAddr() net.Addr { return fc.conn.RemoteAddr() }
|
|
|
|
// SetDeadline implements net.Conn.
|
|
func (fc *FuzzedConnection) SetDeadline(t time.Time) error { return fc.conn.SetDeadline(t) }
|
|
|
|
// SetReadDeadline implements net.Conn.
|
|
func (fc *FuzzedConnection) SetReadDeadline(t time.Time) error {
|
|
return fc.conn.SetReadDeadline(t)
|
|
}
|
|
|
|
// SetWriteDeadline implements net.Conn.
|
|
func (fc *FuzzedConnection) SetWriteDeadline(t time.Time) error {
|
|
return fc.conn.SetWriteDeadline(t)
|
|
}
|
|
|
|
func (fc *FuzzedConnection) randomDuration() time.Duration {
|
|
maxDelayMillis := int(fc.config.MaxDelay.Nanoseconds() / 1000)
|
|
return time.Millisecond * time.Duration(cmn.RandInt()%maxDelayMillis) // nolint: gas
|
|
}
|
|
|
|
// implements the fuzz (delay, kill conn)
|
|
// and returns whether or not the read/write should be ignored
|
|
func (fc *FuzzedConnection) fuzz() bool {
|
|
if !fc.shouldFuzz() {
|
|
return false
|
|
}
|
|
|
|
switch fc.config.Mode {
|
|
case config.FuzzModeDrop:
|
|
// randomly drop the r/w, drop the conn, or sleep
|
|
r := cmn.RandFloat64()
|
|
if r <= fc.config.ProbDropRW {
|
|
return true
|
|
} else if r < fc.config.ProbDropRW+fc.config.ProbDropConn {
|
|
// XXX: can't this fail because machine precision?
|
|
// XXX: do we need an error?
|
|
fc.Close() // nolint: errcheck, gas
|
|
return true
|
|
} else if r < fc.config.ProbDropRW+fc.config.ProbDropConn+fc.config.ProbSleep {
|
|
time.Sleep(fc.randomDuration())
|
|
}
|
|
case config.FuzzModeDelay:
|
|
// sleep a bit
|
|
time.Sleep(fc.randomDuration())
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (fc *FuzzedConnection) shouldFuzz() bool {
|
|
if fc.active {
|
|
return true
|
|
}
|
|
|
|
fc.mtx.Lock()
|
|
defer fc.mtx.Unlock()
|
|
|
|
select {
|
|
case <-fc.start:
|
|
fc.active = true
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|