OSDN Git Service

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