Coverage Summary for Class: P2pHandler (org.ethereum.net.p2p)
Class |
Method, %
|
Line, %
|
P2pHandler |
0%
(0/14)
|
0%
(0/54)
|
P2pHandler$1 |
0%
(0/2)
|
0%
(0/5)
|
P2pHandler$2 |
0%
(0/1)
|
0%
(0/1)
|
Total |
0%
(0/17)
|
0%
(0/60)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package org.ethereum.net.p2p;
21
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.SimpleChannelInboundHandler;
24 import org.ethereum.listener.EthereumListener;
25 import org.ethereum.net.MessageQueue;
26 import org.ethereum.net.message.ReasonCode;
27 import org.ethereum.net.server.Channel;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import java.util.concurrent.*;
32
33 import static org.ethereum.net.message.StaticMessages.PING_MESSAGE;
34 import static org.ethereum.net.message.StaticMessages.PONG_MESSAGE;
35
36 /**
37 * Process the basic protocol messages between every peer on the network.
38 *
39 * Peers can send/receive
40 * <ul>
41 * <li>HELLO : Announce themselves to the network</li>
42 * <li>DISCONNECT : Disconnect themselves from the network</li>
43 * <li>GET_PEERS : Request a list of other knows peers</li>
44 * <li>PEERS : Send a list of known peers</li>
45 * <li>PING : Check if another peer is still alive</li>
46 * <li>PONG : Confirm that they themselves are still alive</li>
47 * </ul>
48 */
49 public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
50
51 public static final byte VERSION = 4;
52
53 private static final byte[] SUPPORTED_VERSIONS = {4, 5};
54
55 private static final Logger logger = LoggerFactory.getLogger("net");
56
57 private static ScheduledExecutorService pingTimer =
58 Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "P2pPingTimer"));
59
60 private int ethInbound;
61 private int ethOutbound;
62
63 private final EthereumListener ethereumListener;
64 private final MessageQueue msgQueue;
65 private final int pingInterval;
66
67 public P2pHandler(
68 EthereumListener ethereumListener,
69 MessageQueue msgQueue,
70 int pingInterval) {
71 this.ethereumListener = ethereumListener;
72 this.msgQueue = msgQueue;
73 this.pingInterval = pingInterval;
74 }
75
76 private Channel channel;
77 private ScheduledFuture<?> pingTask;
78
79
80 @Override
81 public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
82 logger.info("P2P protocol activated");
83 msgQueue.activate(ctx);
84 ethereumListener.trace("P2P protocol activated");
85 startTimers();
86 }
87
88
89 @Override
90 public void channelRead0(final ChannelHandlerContext ctx, P2pMessage msg) throws InterruptedException {
91
92 if (P2pMessageCodes.inRange(msg.getCommand().asByte())) {
93 logger.trace("P2PHandler invoke: [{}]", msg.getCommand());
94 }
95
96 ethereumListener.trace(String.format("P2PHandler invoke: [%s]", msg.getCommand()));
97
98 switch (msg.getCommand()) {
99 case HELLO:
100 logger.trace("Received unexpected HELLO message, channel {}", channel);
101 msgQueue.receivedMessage(msg);
102 sendDisconnect();
103 break;
104 case DISCONNECT:
105 msgQueue.receivedMessage(msg);
106 channel.getNodeStatistics().nodeDisconnectedRemote(((DisconnectMessage) msg).getReason());
107 processDisconnect((DisconnectMessage) msg);
108 break;
109 case PING:
110 logger.trace("Receive PING message, channel {}", channel);
111 msgQueue.receivedMessage(msg);
112 ctx.writeAndFlush(PONG_MESSAGE);
113 break;
114 case PONG:
115 logger.trace("Receive PONG message, channel {}", channel);
116 msgQueue.receivedMessage(msg);
117 break;
118 default:
119 ctx.fireChannelRead(msg);
120 break;
121 }
122 }
123
124 @Override
125 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
126 logger.info("channel inactive: ", ctx);
127 this.killTimers();
128 }
129
130 @Override
131 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
132 logger.error("P2p handling failed", cause);
133 ctx.close();
134 killTimers();
135 }
136
137 private void processDisconnect(DisconnectMessage msg) {
138
139 if (!logger.isInfoEnabled() || msg.getReason() != ReasonCode.USELESS_PEER) {
140 return;
141 }
142
143 if (channel.getNodeStatistics().getEthInbound().get() - ethInbound > 1 ||
144 channel.getNodeStatistics().getEthOutbound().get() - ethOutbound > 1) {
145
146 // it means that we've been disconnected
147 // after some incorrect action from our peer
148 // need to log this moment
149 logger.info("From: \t{}\t [DISCONNECT reason=BAD_PEER_ACTION]", channel);
150 }
151 }
152
153 public void setHandshake(HelloMessage msg) {
154
155 channel.getNodeStatistics().setClientId(msg.getClientId());
156
157 this.ethInbound = channel.getNodeStatistics().getEthInbound().get();
158 this.ethOutbound = channel.getNodeStatistics().getEthOutbound().get();
159
160 ethereumListener.onHandShakePeer(channel, msg);
161 }
162
163 public void sendDisconnect() {
164 msgQueue.disconnect();
165 }
166
167 private void startTimers() {
168 // sample for pinging in background
169 pingTask = pingTimer.scheduleAtFixedRate(new Runnable() {
170 @Override
171 public void run() {
172 try {
173 msgQueue.sendMessage(PING_MESSAGE);
174 } catch (Throwable t) {
175 logger.error("Unhandled exception", t);
176 }
177 }
178 }, 2, pingInterval, TimeUnit.SECONDS);
179 }
180
181 public void killTimers() {
182 pingTask.cancel(false);
183 msgQueue.close();
184 }
185
186 public void setChannel(Channel channel) {
187 this.channel = channel;
188 }
189
190 public static boolean isProtocolVersionSupported(byte ver) {
191 for (byte v : SUPPORTED_VERSIONS) {
192 if (v == ver) {
193 return true;
194 }
195 }
196 return false;
197 }
198
199 }