|
12 | 12 | # See the License for the specific language governing permissions and
|
13 | 13 | # limitations under the License.
|
14 | 14 |
|
15 |
| -from math import inf |
| 15 | +from collections import deque |
16 | 16 | from typing import TYPE_CHECKING, Iterable, Optional
|
17 | 17 |
|
18 | 18 | from structlog import get_logger
|
@@ -53,14 +53,18 @@ def __init__(self, protocol: 'HathorProtocol') -> None:
|
53 | 53 | # Time we sent last PING message.
|
54 | 54 | self.ping_start_time: Optional[float] = None
|
55 | 55 |
|
| 56 | + # Salt used in the last PING message. |
| 57 | + self.ping_salt: Optional[str] = None |
| 58 | + |
| 59 | + # Salt size in bytes. |
| 60 | + self.ping_salt_size: int = 32 |
| 61 | + |
56 | 62 | # Time we got last PONG response to a PING message.
|
57 | 63 | self.ping_last_response: float = 0
|
58 | 64 |
|
59 | 65 | # Round-trip time of the last PING/PONG.
|
60 |
| - self.ping_rtt: float = inf |
61 |
| - |
62 |
| - # Minimum round-trip time among PING/PONG. |
63 |
| - self.ping_min_rtt: float = inf |
| 66 | + self.rtt_window: deque[float] = deque() |
| 67 | + self.MAX_RTT_WINDOW: int = 200 # Last 200 samples (~= 10 minutes) |
64 | 68 |
|
65 | 69 | # The last blocks from the best blockchain in the peer
|
66 | 70 | self.peer_best_blockchain: list[HeightInfo] = []
|
@@ -146,7 +150,7 @@ def handle_get_peers(self, payload: str) -> None:
|
146 | 150 | self.send_peers(self.protocol.connections.iter_ready_connections())
|
147 | 151 |
|
148 | 152 | def send_peers(self, connections: Iterable['HathorProtocol']) -> None:
|
149 |
| - """ Send a PEERS command with a list of all known peers. |
| 153 | + """ Send a PEERS command with a list of all connected peers. |
150 | 154 | """
|
151 | 155 | peers = []
|
152 | 156 | for conn in connections:
|
@@ -185,28 +189,41 @@ def send_ping(self) -> None:
|
185 | 189 | """ Send a PING command. Usually you would use `send_ping_if_necessary` to
|
186 | 190 | prevent wasting bandwidth.
|
187 | 191 | """
|
| 192 | + # Add a salt number to prevent peers from faking rtt. |
188 | 193 | self.ping_start_time = self.reactor.seconds()
|
189 |
| - self.send_message(ProtocolMessages.PING) |
| 194 | + rng = self.protocol.connections.rng |
| 195 | + self.ping_salt = rng.randbytes(self.ping_salt_size).hex() |
| 196 | + self.send_message(ProtocolMessages.PING, self.ping_salt) |
190 | 197 |
|
191 |
| - def send_pong(self) -> None: |
| 198 | + def send_pong(self, salt: str) -> None: |
192 | 199 | """ Send a PONG command as a response to a PING command.
|
193 | 200 | """
|
194 |
| - self.send_message(ProtocolMessages.PONG) |
| 201 | + self.send_message(ProtocolMessages.PONG, salt) |
195 | 202 |
|
196 | 203 | def handle_ping(self, payload: str) -> None:
|
197 | 204 | """Executed when a PING command is received. It responds with a PONG message."""
|
198 |
| - self.send_pong() |
| 205 | + self.send_pong(payload) |
199 | 206 |
|
200 | 207 | def handle_pong(self, payload: str) -> None:
|
201 | 208 | """Executed when a PONG message is received."""
|
202 | 209 | if self.ping_start_time is None:
|
203 | 210 | # This should never happen.
|
204 | 211 | return
|
| 212 | + if self.ping_salt != payload: |
| 213 | + # Ignore pong without salts. |
| 214 | + return |
205 | 215 | self.ping_last_response = self.reactor.seconds()
|
206 |
| - self.ping_rtt = self.ping_last_response - self.ping_start_time |
207 |
| - self.ping_min_rtt = min(self.ping_min_rtt, self.ping_rtt) |
| 216 | + rtt = self.ping_last_response - self.ping_start_time |
| 217 | + self.rtt_window.appendleft(rtt) |
| 218 | + if len(self.rtt_window) > self.MAX_RTT_WINDOW: |
| 219 | + self.rtt_window.pop() |
208 | 220 | self.ping_start_time = None
|
209 |
| - self.log.debug('rtt updated', rtt=self.ping_rtt, min_rtt=self.ping_min_rtt) |
| 221 | + self.ping_salt = None |
| 222 | + self.log.debug('rtt updated', |
| 223 | + latest=rtt, |
| 224 | + min=min(self.rtt_window), |
| 225 | + max=max(self.rtt_window), |
| 226 | + avg=sum(self.rtt_window) / len(self.rtt_window)) |
210 | 227 |
|
211 | 228 | def send_get_best_blockchain(self, n_blocks: Optional[int] = None) -> None:
|
212 | 229 | """ Send a GET-BEST-BLOCKCHAIN command, requesting a list of the latest
|
|
0 commit comments