Coverage Summary for Class: RskWireProtocol (co.rsk.net.eth)
Class |
Method, %
|
Line, %
|
RskWireProtocol |
0%
(0/18)
|
0%
(0/120)
|
RskWireProtocol$1 |
0%
(0/1)
|
0%
(0/2)
|
RskWireProtocol$EthState |
0%
(0/1)
|
0%
(0/5)
|
Total |
0%
(0/20)
|
0%
(0/127)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package co.rsk.net.eth;
20
21 import co.rsk.config.RskSystemProperties;
22 import co.rsk.crypto.Keccak256;
23 import co.rsk.net.MessageHandler;
24 import co.rsk.net.NodeID;
25 import co.rsk.net.Status;
26 import co.rsk.net.StatusResolver;
27 import co.rsk.net.messages.BlockMessage;
28 import co.rsk.net.messages.GetBlockMessage;
29 import co.rsk.net.messages.Message;
30 import co.rsk.net.messages.StatusMessage;
31 import co.rsk.scoring.EventType;
32 import co.rsk.scoring.PeerScoringManager;
33 import io.netty.channel.ChannelHandlerContext;
34 import io.netty.channel.SimpleChannelInboundHandler;
35 import org.ethereum.core.Genesis;
36 import org.ethereum.listener.CompositeEthereumListener;
37 import org.ethereum.net.MessageQueue;
38 import org.ethereum.net.eth.EthVersion;
39 import org.ethereum.net.eth.handler.Eth;
40 import org.ethereum.net.eth.message.EthMessage;
41 import org.ethereum.net.eth.message.EthMessageCodes;
42 import org.ethereum.net.message.ReasonCode;
43 import org.ethereum.net.server.Channel;
44 import org.ethereum.sync.SyncStatistics;
45 import org.ethereum.util.ByteUtil;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 import java.net.InetAddress;
50 import java.net.InetSocketAddress;
51 import java.net.SocketAddress;
52 import java.util.NoSuchElementException;
53
54 import static org.ethereum.net.eth.EthVersion.V62;
55 import static org.ethereum.net.message.ReasonCode.USELESS_PEER;
56
57 public class RskWireProtocol extends SimpleChannelInboundHandler<EthMessage> implements Eth {
58
59 private static final Logger logger = LoggerFactory.getLogger("sync");
60 private static final Logger loggerNet = LoggerFactory.getLogger("net");
61 private final CompositeEthereumListener ethereumListener;
62 /**
63 * Header list sent in GET_BLOCK_BODIES message,
64 * used to create blocks from headers and bodies
65 * also, is useful when returned BLOCK_BODIES msg doesn't cover all sent hashes
66 * or in case when peer is disconnected
67 */
68 private final PeerScoringManager peerScoringManager;
69 private final SyncStatistics syncStats = new SyncStatistics();
70 private final Channel channel;
71 private final EthVersion version;
72 private EthState ethState = EthState.INIT;
73
74 private final RskSystemProperties config;
75 private final StatusResolver statusResolver;
76 private final MessageHandler messageHandler;
77 private final MessageRecorder messageRecorder;
78 private final Genesis genesis;
79 private final MessageQueue msgQueue;
80
81 public RskWireProtocol(RskSystemProperties config,
82 PeerScoringManager peerScoringManager,
83 MessageHandler messageHandler,
84 CompositeEthereumListener ethereumListener,
85 Genesis genesis,
86 MessageRecorder messageRecorder,
87 StatusResolver statusResolver,
88 MessageQueue msgQueue,
89 Channel channel) {
90 this.ethereumListener = ethereumListener;
91 this.version = V62;
92
93 this.msgQueue = msgQueue;
94 this.channel = channel;
95 this.peerScoringManager = peerScoringManager;
96 this.messageHandler = messageHandler;
97 this.config = config;
98 this.statusResolver = statusResolver;
99 this.messageRecorder = messageRecorder;
100 this.genesis = genesis;
101 }
102
103 @Override
104 public void channelRead0(final ChannelHandlerContext ctx, EthMessage msg) throws InterruptedException {
105 loggerNet.debug("Read message: {}", msg);
106
107 if (EthMessageCodes.inRange(msg.getCommand().asByte(), version)) {
108 loggerNet.trace("EthHandler invoke: [{}]", msg.getCommand());
109 }
110
111 ethereumListener.trace(String.format("EthHandler invoke: [%s]", msg.getCommand()));
112
113 channel.getNodeStatistics().getEthInbound().add();
114
115 msgQueue.receivedMessage(msg);
116
117 if (this.messageRecorder != null) {
118 this.messageRecorder.recordMessage(channel.getPeerNodeID(), msg);
119 }
120
121 if (!hasGoodReputation(ctx)) {
122 ctx.disconnect();
123 return;
124 }
125
126 switch (msg.getCommand()) {
127 case STATUS:
128 processStatus((org.ethereum.net.eth.message.StatusMessage) msg, ctx);
129 break;
130 case RSK_MESSAGE:
131 RskMessage rskmessage = (RskMessage)msg;
132 Message message = rskmessage.getMessage();
133
134 switch (message.getMessageType()) {
135 case BLOCK_MESSAGE:
136 loggerNet.trace("RSK Block Message: Block {} {} from {}", ((BlockMessage)message).getBlock().getNumber(), ((BlockMessage)message).getBlock().getPrintableHash(), channel.getPeerNodeID());
137 syncStats.addBlocks(1);
138 break;
139 case GET_BLOCK_MESSAGE:
140 loggerNet.trace("RSK Get Block Message: Block {} from {}", ByteUtil.toHexString(((GetBlockMessage)message).getBlockHash()), channel.getPeerNodeID());
141 syncStats.getBlock();
142 break;
143 case STATUS_MESSAGE:
144 loggerNet.trace("RSK Status Message: Block {} {} from {}", ((StatusMessage)message).getStatus().getBestBlockNumber(), ByteUtil.toHexString(((StatusMessage)message).getStatus().getBestBlockHash()), channel.getPeerNodeID());
145 syncStats.addStatus();
146 break;
147 }
148
149 if (this.messageHandler != null) {
150 this.messageHandler.postMessage(channel, rskmessage.getMessage());
151 }
152 break;
153 default:
154 break;
155 }
156 }
157
158 /*************************
159 * Message Processing *
160 *************************/
161
162 protected void processStatus(org.ethereum.net.eth.message.StatusMessage msg, ChannelHandlerContext ctx) {
163 try {
164 byte protocolVersion = msg.getProtocolVersion();
165 byte versionCode = version.getCode();
166 if (protocolVersion != versionCode) {
167 loggerNet.info("Removing EthHandler for {} due to protocol incompatibility", ctx.channel().remoteAddress());
168 loggerNet.info("Protocol version {} - message protocol version {}",
169 versionCode,
170 protocolVersion);
171 ethState = EthState.STATUS_FAILED;
172 recordEvent(EventType.INCOMPATIBLE_PROTOCOL);
173 disconnect(ReasonCode.INCOMPATIBLE_PROTOCOL);
174 ctx.pipeline().remove(this); // Peer is not compatible for the 'eth' sub-protocol
175 return;
176 }
177
178 int networkId = config.networkId();
179 int msgNetworkId = msg.getNetworkId();
180 if (msgNetworkId != networkId) {
181 loggerNet.info("Removing EthHandler for {} due to invalid network", ctx.channel().remoteAddress());
182 loggerNet.info("Different network received: config network ID {} - message network ID {}",
183 networkId, msgNetworkId);
184 ethState = EthState.STATUS_FAILED;
185 recordEvent(EventType.INVALID_NETWORK);
186 disconnect(ReasonCode.NULL_IDENTITY);
187 ctx.pipeline().remove(this);
188 return;
189 }
190
191 Keccak256 genesisHash = genesis.getHash();
192 Keccak256 msgGenesisHash = new Keccak256(msg.getGenesisHash());
193 if (!msgGenesisHash.equals(genesisHash)) {
194 loggerNet.info("Removing EthHandler for {} due to unexpected genesis", ctx.channel().remoteAddress());
195 loggerNet.info("Config genesis hash {} - message genesis hash {}",
196 genesisHash, msgGenesisHash);
197 ethState = EthState.STATUS_FAILED;
198 recordEvent(EventType.UNEXPECTED_GENESIS);
199 disconnect(ReasonCode.UNEXPECTED_GENESIS);
200 ctx.pipeline().remove(this);
201 return;
202 }
203
204 // basic checks passed, update statistics
205 channel.getNodeStatistics().ethHandshake(msg);
206 ethereumListener.onEthStatusUpdated(channel, msg);
207 } catch (NoSuchElementException e) {
208 loggerNet.debug("EthHandler already removed");
209 }
210 }
211
212 private boolean hasGoodReputation(ChannelHandlerContext ctx) {
213 SocketAddress socketAddress = ctx.channel().remoteAddress();
214
215 //TODO(mmarquez): and if not ???
216 if (socketAddress instanceof InetSocketAddress) {
217
218 InetAddress address = ((InetSocketAddress)socketAddress).getAddress();
219
220 if (!peerScoringManager.hasGoodReputation(address)) {
221 return false;
222 }
223
224 NodeID nodeID = channel.getNodeId();
225
226 if (nodeID != null && !peerScoringManager.hasGoodReputation(nodeID)) {
227 return false;
228 }
229
230 }
231
232 return true; //TODO(mmarquez): ugly
233 }
234
235 private void recordEvent(EventType event) {
236 peerScoringManager.recordEvent(
237 channel.getPeerNodeID(),
238 channel.getAddress(),
239 event);
240 }
241
242
243 /*************************
244 * Message Sending *
245 *************************/
246
247 @Override
248 public void sendStatus() {
249 byte protocolVersion = version.getCode();
250 int networkId = config.networkId();
251
252 Status status = statusResolver.currentStatus();
253
254 // Original status
255 org.ethereum.net.eth.message.StatusMessage msg = new org.ethereum.net.eth.message.StatusMessage(
256 protocolVersion,
257 networkId,
258 ByteUtil.bigIntegerToBytes(status.getTotalDifficulty().asBigInteger()),
259 status.getBestBlockHash(),
260 genesis.getHash().getBytes());
261 sendMessage(msg);
262
263 // RSK new protocol send status
264 RskMessage rskmessage = new RskMessage(new StatusMessage(status));
265 loggerNet.trace("Sending status best block {} to {}",
266 status.getBestBlockNumber(),
267 channel.getPeerNodeID());
268 sendMessage(rskmessage);
269
270 ethState = EthState.STATUS_SENT;
271 }
272
273
274 @Override
275 public boolean hasStatusPassed() {
276 return ethState.ordinal() > EthState.STATUS_SENT.ordinal();
277 }
278
279 @Override
280 public boolean hasStatusSucceeded() {
281 return ethState == EthState.STATUS_SUCCEEDED;
282 }
283
284 @Override
285 public SyncStatistics getStats() {
286 return syncStats;
287 }
288
289 @Override
290 public EthVersion getVersion() {
291 return version;
292 }
293
294 @Override
295 public void dropConnection() {
296
297 // todo: reduce reputation
298
299 logger.info("Peer {}: is a bad one, drop", channel.getPeerId());
300 disconnect(USELESS_PEER);
301 }
302
303 @Override
304 public boolean isUsingNewProtocol() {
305 return true;
306 }
307
308 @Override
309 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
310 loggerNet.error("Eth handling failed", cause);
311 ctx.close();
312 }
313
314 @Override
315 public void handlerRemoved(ChannelHandlerContext ctx) {
316 loggerNet.debug("handlerRemoved: kill timers in EthHandler");
317 }
318
319 public void activate() {
320 loggerNet.info("RSK protocol activated");
321 ethereumListener.trace("RSK protocol activated");
322 sendStatus();
323 }
324
325 protected void disconnect(ReasonCode reason) {
326 msgQueue.disconnect(reason);
327 channel.getNodeStatistics().nodeDisconnectedLocal(reason);
328 }
329
330 @Override
331 public void sendMessage(EthMessage message) {
332 loggerNet.debug("Send message: {}", message);
333
334 msgQueue.sendMessage(message);
335 channel.getNodeStatistics().getEthOutbound().add();
336 }
337
338 private enum EthState {
339 INIT,
340 STATUS_SENT,
341 STATUS_SUCCEEDED,
342 STATUS_FAILED
343 }
344
345 public interface Factory {
346 RskWireProtocol newInstance(MessageQueue messageQueue, Channel channel);
347 }
348 }