github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/ping.go (about) 1 package network 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "fmt" 7 "os" 8 "sort" 9 "time" 10 11 "github.com/piotrnar/gocoin/client/common" 12 ) 13 14 const ( 15 PingHistoryLength = 20 16 PingAssumedIfUnsupported = 4999 // ms 17 PingAssumedIfUnknown = 9999 // ms 18 ) 19 20 func (c *OneConnection) HandlePong(pl []byte) { 21 if pl != nil { 22 if !bytes.Equal(pl, c.PingInProgress) { 23 common.CountSafe("PongMismatch") 24 return 25 } 26 common.CountSafe("PongOK") 27 c.ExpireHeadersAndGetData(nil, c.X.PingSentCnt) 28 } else { 29 common.CountSafe("PongTimeout") 30 } 31 ms := time.Now().Sub(c.LastPingSent) / time.Millisecond 32 if ms == 0 { 33 //println(c.ConnID, "Ping returned after 0ms") 34 ms = 1 35 } 36 c.Mutex.Lock() 37 c.X.PingHistory[c.X.PingHistoryIdx] = int(ms) 38 c.X.PingHistoryIdx = (c.X.PingHistoryIdx + 1) % PingHistoryLength 39 c.PingInProgress = nil 40 c.Mutex.Unlock() 41 } 42 43 // GetAveragePing returns the (median) average ping. 44 // Make sure to call it within c.Mutex.Lock(). 45 func (c *OneConnection) GetAveragePing() int { 46 if !c.X.VersionReceived { 47 return 0 48 } 49 if c.Node.Version > 60000 { 50 var pgs [PingHistoryLength]int 51 var act_len int 52 for _, p := range c.X.PingHistory { 53 if p != 0 { 54 pgs[act_len] = p 55 act_len++ 56 } 57 } 58 if act_len == 0 { 59 return PingAssumedIfUnknown 60 } 61 sort.Ints(pgs[:act_len]) 62 return pgs[act_len/2] 63 } else { 64 return PingAssumedIfUnsupported 65 } 66 } 67 68 type SortedConnections []struct { 69 Conn *OneConnection 70 Ping int 71 BlockCount int 72 TxsCount int 73 MinutesOnline int 74 Special bool 75 } 76 77 // GetSortedConnections returns the slowest peers first. 78 // Make sure to call it with locked Mutex_net. 79 func GetSortedConnections() (list SortedConnections, any_ping bool) { 80 var cnt int 81 var now time.Time 82 var tlist SortedConnections 83 now = time.Now() 84 tlist = make(SortedConnections, len(OpenCons)) 85 for _, v := range OpenCons { 86 v.Mutex.Lock() 87 tlist[cnt].Conn = v 88 tlist[cnt].Ping = v.GetAveragePing() 89 tlist[cnt].BlockCount = len(v.blocksreceived) 90 tlist[cnt].TxsCount = v.X.TxsReceived 91 tlist[cnt].Special = v.X.IsSpecial || v.X.Authorized 92 if v.X.VersionReceived == false || v.X.ConnectedAt.IsZero() { 93 tlist[cnt].MinutesOnline = 0 94 } else { 95 tlist[cnt].MinutesOnline = int(now.Sub(v.X.ConnectedAt) / time.Minute) 96 } 97 v.Mutex.Unlock() 98 99 if tlist[cnt].Ping > 0 { 100 any_ping = true 101 } 102 103 cnt++ 104 } 105 if cnt > 0 { 106 list = make(SortedConnections, len(tlist)) 107 var ignore_bcnt bool // otherwise count blocks 108 var idx, best_idx, bcnt, best_bcnt, best_tcnt, best_ping int 109 110 for idx = len(list) - 1; idx >= 0; idx-- { 111 best_idx = -1 112 for i, v := range tlist { 113 if v.Conn == nil { 114 continue 115 } 116 if best_idx < 0 { 117 best_idx = i 118 best_tcnt = v.TxsCount 119 best_bcnt = v.BlockCount 120 best_ping = v.Ping 121 } else { 122 if ignore_bcnt { 123 bcnt = best_bcnt 124 } else { 125 bcnt = v.BlockCount 126 } 127 if best_bcnt < bcnt || 128 best_bcnt == bcnt && best_tcnt < v.TxsCount || 129 best_bcnt == bcnt && best_tcnt == v.TxsCount && best_ping > v.Ping { 130 best_bcnt = v.BlockCount 131 best_tcnt = v.TxsCount 132 best_ping = v.Ping 133 best_idx = i 134 } 135 } 136 } 137 list[idx] = tlist[best_idx] 138 tlist[best_idx].Conn = nil 139 ignore_bcnt = !ignore_bcnt 140 } 141 } 142 return 143 } 144 145 // drop_worst_peer should be called only when OutConsActive >= MaxOutCons. 146 func drop_worst_peer() bool { 147 var list SortedConnections 148 var any_ping bool 149 150 Mutex_net.Lock() 151 defer Mutex_net.Unlock() 152 153 list, any_ping = GetSortedConnections() 154 if !any_ping { // if "list" is empty "any_ping" will also be false 155 return false 156 } 157 158 for _, v := range list { 159 if v.MinutesOnline < OnlineImmunityMinutes { 160 continue 161 } 162 if v.Special { 163 continue 164 } 165 if v.Conn.X.Incomming { 166 if InConsActive+2 > common.GetUint32(&common.CFG.Net.MaxInCons) { 167 common.CountSafe("PeerInDropped") 168 if common.FLAG.Log { 169 f, _ := os.OpenFile("drop_log.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660) 170 if f != nil { 171 fmt.Fprintf(f, "%s: Drop incoming id:%d blks:%d txs:%d ping:%d mins:%d\n", 172 time.Now().Format("2006-01-02 15:04:05"), 173 v.Conn.ConnID, v.BlockCount, v.TxsCount, v.Ping, v.MinutesOnline) 174 f.Close() 175 } 176 } 177 v.Conn.Disconnect(true, "PeerInDropped") 178 return true 179 } 180 } else { 181 if OutConsActive+2 > common.GetUint32(&common.CFG.Net.MaxOutCons) { 182 common.CountSafe("PeerOutDropped") 183 if common.FLAG.Log { 184 f, _ := os.OpenFile("drop_log.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660) 185 if f != nil { 186 fmt.Fprintf(f, "%s: Drop outgoing id:%d blks:%d txs:%d ping:%d mins:%d\n", 187 time.Now().Format("2006-01-02 15:04:05"), 188 v.Conn.ConnID, v.BlockCount, v.TxsCount, v.Ping, v.MinutesOnline) 189 f.Close() 190 } 191 } 192 v.Conn.Disconnect(true, "PeerOutDropped") 193 return true 194 } 195 } 196 } 197 return false 198 } 199 200 func (c *OneConnection) TryPing(now time.Time) bool { 201 if c.Node.Version <= 60000 { 202 return false // insufficient protocol version 203 } 204 205 pingdur := common.GetDuration(&common.PingPeerEvery) 206 if pingdur == 0 { 207 return false // pinging disabled in global config 208 } 209 210 if now.Sub(c.LastPingSent) < pingdur { 211 return false // not yet... 212 } 213 214 if c.PingInProgress != nil { 215 c.HandlePong(nil) // this will set PingInProgress to nil 216 } 217 218 c.Mutex.Lock() 219 bip := len(c.GetBlockInProgress) 220 c.Mutex.Unlock() 221 if bip > 0 { 222 common.CountSafe("PingHelpBIP") 223 c.cntLockInc("PingHelpBIP") 224 return false 225 } 226 227 c.X.PingSentCnt++ 228 c.PingInProgress = make([]byte, 8) 229 rand.Read(c.PingInProgress[:]) 230 c.SendRawMsg("ping", c.PingInProgress) 231 c.LastPingSent = time.Now() 232 //println(c.PeerAddr.Ip(), "ping...") 233 return true 234 }