From b41e47b7c6dfb2bc414d4c1797727aebca396940 Mon Sep 17 00:00:00 2001 From: nicksavers Date: Sat, 18 Oct 2014 22:03:31 +0200 Subject: [PATCH] Extract capabilities to separate class --- .../org/ethereum/net/client/Capability.java | 41 +++++++++++++++ .../ethereum/net/message/StaticMessages.java | 7 ++- .../org/ethereum/net/p2p/HelloMessage.java | 31 ++++++----- .../java/org/ethereum/net/p2p/P2pHandler.java | 37 +++++++------ .../ethereum/net/peerdiscovery/PeerData.java | 10 ++-- .../org/ethereum/net/HelloMessageTest.java | 52 +++++++++++-------- .../net/wire/AdaptiveMessageIdsTest.java | 19 ++++--- 7 files changed, 131 insertions(+), 66 deletions(-) create mode 100644 ethereumj-core/src/main/java/org/ethereum/net/client/Capability.java diff --git a/ethereumj-core/src/main/java/org/ethereum/net/client/Capability.java b/ethereumj-core/src/main/java/org/ethereum/net/client/Capability.java new file mode 100644 index 00000000..bd6b77e5 --- /dev/null +++ b/ethereumj-core/src/main/java/org/ethereum/net/client/Capability.java @@ -0,0 +1,41 @@ +package org.ethereum.net.client; + +/** + * The protocols and versions of those protocols that this peer support + */ +public class Capability { + + public final static String ETH = "eth"; + public final static String SHH = "shh"; + + private String name; + private byte version; + + public Capability(String name, byte version) { + this.name = name; + this.version = version; + } + + public String getName() { + return name; + } + + public byte getVersion() { + return version; + } + + public boolean equals(Object o) { + if(o instanceof Capability) { + Capability cap = (Capability) o; + if (cap.getName() == null) + return this.name == null; + else + return cap.getName().equals(this.name) && cap.getVersion() == this.version; + } + return false; + } + + public String toString() { + return name + ":" + version; + } +} \ No newline at end of file diff --git a/ethereumj-core/src/main/java/org/ethereum/net/message/StaticMessages.java b/ethereumj-core/src/main/java/org/ethereum/net/message/StaticMessages.java index 3299a159..a92a2e99 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/message/StaticMessages.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/message/StaticMessages.java @@ -5,12 +5,15 @@ import java.util.List; import org.ethereum.config.SystemProperties; import org.ethereum.crypto.HashUtil; +import org.ethereum.net.client.Capability; +import org.ethereum.net.eth.EthHandler; import org.ethereum.net.eth.GetTransactionsMessage; import org.ethereum.net.p2p.GetPeersMessage; import org.ethereum.net.p2p.HelloMessage; import org.ethereum.net.p2p.P2pHandler; import org.ethereum.net.p2p.PingMessage; import org.ethereum.net.p2p.PongMessage; +import org.ethereum.net.shh.ShhHandler; import org.spongycastle.util.encoders.Hex; /** @@ -35,7 +38,9 @@ public class StaticMessages { private static HelloMessage generateHelloMessage() { String helloAnnouncement = buildHelloAnnouncement(); byte p2pVersion = P2pHandler.VERSION; - List capabilities = Arrays.asList("eth", "shh"); + List capabilities = Arrays.asList( + new Capability(Capability.ETH, EthHandler.VERSION), + new Capability(Capability.SHH, ShhHandler.VERSION)); int listenPort = 30303; return new HelloMessage(p2pVersion, helloAnnouncement, diff --git a/ethereumj-core/src/main/java/org/ethereum/net/p2p/HelloMessage.java b/ethereumj-core/src/main/java/org/ethereum/net/p2p/HelloMessage.java index 919abd35..9ee7949f 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/p2p/HelloMessage.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/p2p/HelloMessage.java @@ -3,9 +3,8 @@ package org.ethereum.net.p2p; import static org.ethereum.net.p2p.P2pMessageCodes.HELLO; import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY; -import org.ethereum.net.eth.EthHandler; +import org.ethereum.net.client.Capability; import org.ethereum.net.p2p.P2pMessage; -import org.ethereum.net.shh.ShhHandler; import org.ethereum.util.*; import org.spongycastle.util.encoders.Hex; @@ -27,7 +26,7 @@ public class HelloMessage extends P2pMessage { private String clientId; /** A peer-network capability code, readable ASCII and 3 letters. * Currently only "eth", "shh" and "bzz" are known. */ - private List capabilities; + private List capabilities; /** The port on which the peer is listening for an incoming connection */ private int listenPort; /** The identity and public key of the peer */ @@ -38,7 +37,7 @@ public class HelloMessage extends P2pMessage { } public HelloMessage(byte p2pVersion, String clientId, - List capabilities, int listenPort, String peerId) { + List capabilities, int listenPort, String peerId) { this.p2pVersion = p2pVersion; this.clientId = clientId; this.capabilities = capabilities; @@ -63,8 +62,14 @@ public class HelloMessage extends P2pMessage { this.capabilities = new ArrayList<>(); for (int i = 0; i < capabilityList.size(); i++) { - RLPElement capabilitiesID = ((RLPList)capabilityList.get(i)).get(0); - this.capabilities.add(new String(capabilitiesID.getRLPData())); + RLPElement capId = ((RLPList)capabilityList.get(i)).get(0); + RLPElement capVersion = ((RLPList)capabilityList.get(i)).get(1); + + String name = new String(capId.getRLPData()); + byte version = capVersion.getRLPData() == null ? 0 : capVersion.getRLPData()[0]; + + Capability cap = new Capability(name, version); + this.capabilities.add(cap); } byte[] peerPortBytes = ((RLPItem) paramsList.get(4)).getRLPData(); @@ -81,16 +86,10 @@ public class HelloMessage extends P2pMessage { byte[] clientId = RLP.encodeString(this.clientId); byte[][] capabilities = new byte[this.capabilities.size()][]; for (int i = 0; i < this.capabilities.size(); i++) { - - String capability = this.capabilities.get(i); - byte version = 0; - - if (capability.equals("eth")) version = EthHandler.VERSION; - if (capability.equals("shh")) version = ShhHandler.VERSION; - + Capability capability = this.capabilities.get(i); capabilities[i] = RLP.encodeList( - RLP.encodeElement( capability.getBytes() ), - RLP.encodeElement( new byte[]{version} )); + RLP.encodeElement(capability.getName().getBytes()), + RLP.encodeElement(new byte[] {capability.getVersion() })); } byte[] capabilityList = RLP.encodeList(capabilities); byte[] peerPort = RLP.encodeInt(this.listenPort); @@ -116,7 +115,7 @@ public class HelloMessage extends P2pMessage { return clientId; } - public List getCapabilities() { + public List getCapabilities() { if (!parsed) parse(); return capabilities; } diff --git a/ethereumj-core/src/main/java/org/ethereum/net/p2p/P2pHandler.java b/ethereumj-core/src/main/java/org/ethereum/net/p2p/P2pHandler.java index 8359e0e8..d122dc11 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/p2p/P2pHandler.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/p2p/P2pHandler.java @@ -6,6 +6,7 @@ import static org.ethereum.net.message.StaticMessages.HELLO_MESSAGE; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.Timer; @@ -17,6 +18,7 @@ import io.netty.channel.SimpleChannelInboundHandler; import org.ethereum.manager.WorldManager; import org.ethereum.net.MessageQueue; import org.ethereum.net.PeerListener; +import org.ethereum.net.client.Capability; import org.ethereum.net.eth.EthHandler; import org.ethereum.net.eth.EthMessageCodes; import org.ethereum.net.shh.ShhHandler; @@ -100,7 +102,7 @@ public class P2pHandler extends SimpleChannelInboundHandler { break; case GET_PEERS: msgQueue.receivedMessage(msg); - // sendPeers(); // todo: implement session management for peer request + //sendPeers(); // todo: implement session management for peer request break; case PEERS: msgQueue.receivedMessage(msg); @@ -154,18 +156,19 @@ public class P2pHandler extends SimpleChannelInboundHandler { if (msg.getP2PVersion() != P2pHandler.VERSION) msgQueue.sendMessage(new DisconnectMessage(ReasonCode.INCOMPATIBLE_PROTOCOL)); else { - - adaptMessageIds(msg.getCapabilities()); - - if (msg.getCapabilities().contains("eth")) { - // Activate EthHandler for this peer - ctx.pipeline().addLast("eth", new EthHandler(msg.getPeerId(), peerListener, msgQueue)); + List capInCommon = new ArrayList<>(); + for (Capability capability : msg.getCapabilities()) { + if (HELLO_MESSAGE.getCapabilities().contains(capability)) { + if (capability.getName().equals(Capability.ETH)) + // Activate EthHandler for this peer + ctx.pipeline().addLast(Capability.ETH, new EthHandler(msg.getPeerId(), peerListener, msgQueue)); + else if (capability.getName().equals(Capability.SHH)) + // Activate ShhHandler for this peer + ctx.pipeline().addLast(Capability.SHH, new ShhHandler(msg.getPeerId(), peerListener)); + capInCommon.add(capability); + } } - - if (msg.getCapabilities().contains("shh")) { - // Activate ShhHandler for this peer - ctx.pipeline().addLast("shh", new ShhHandler(msg.getPeerId(), peerListener)); - } + adaptMessageIds(capInCommon); InetAddress address = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress(); int port = msg.getListenPort(); @@ -178,17 +181,17 @@ public class P2pHandler extends SimpleChannelInboundHandler { } } - public void adaptMessageIds(List capabilities) { + public void adaptMessageIds(List capabilities) { byte offset = (byte) (P2pMessageCodes.USER.asByte() + 1); - for (String capability : capabilities){ + for (Capability capability : capabilities) { - if (capability.equals("eth")){ + if (capability.getName().equals(Capability.ETH)) { EthMessageCodes.setOffset(offset); offset += EthMessageCodes.values().length; - } + } - if (capability.equals("shh")){ + if (capability.getName().equals(Capability.SHH)) { ShhMessageCodes.setOffset(offset); offset += ShhMessageCodes.values().length; } diff --git a/ethereumj-core/src/main/java/org/ethereum/net/peerdiscovery/PeerData.java b/ethereumj-core/src/main/java/org/ethereum/net/peerdiscovery/PeerData.java index 3de677a0..55443f65 100644 --- a/ethereumj-core/src/main/java/org/ethereum/net/peerdiscovery/PeerData.java +++ b/ethereumj-core/src/main/java/org/ethereum/net/peerdiscovery/PeerData.java @@ -1,5 +1,6 @@ package org.ethereum.net.peerdiscovery; +import org.ethereum.net.client.Capability; import org.ethereum.util.RLP; import org.spongycastle.util.encoders.Hex; @@ -16,7 +17,7 @@ public class PeerData { private int port; private String peerId; - private List capabilities; + private List capabilities; private transient boolean isOnline = false; private transient long lastCheckTime = 0; @@ -58,7 +59,7 @@ public class PeerData { this.lastCheckTime = lastCheckTime; } - public List getCapabilities() { + public List getCapabilities() { return capabilities; } @@ -67,8 +68,9 @@ public class PeerData { byte[] port = RLP.encodeInt(this.port); byte[] peerId = RLP.encodeElement(Hex.decode(this.peerId)); byte[][] encodedCaps = new byte[this.capabilities.size()][]; - for (int i = 0; i < this.capabilities.size(); i++) { - encodedCaps[i] = RLP.encodeString(this.capabilities.get(i)); + for (int i = 0; i < this.capabilities.size()*2; i++) { + encodedCaps[i] = RLP.encodeString(this.capabilities.get(i).getName()); + encodedCaps[i] = RLP.encodeByte(this.capabilities.get(i).getVersion()); } byte[] capabilities = RLP.encodeList(encodedCaps); return RLP.encodeList(ip, port, peerId, capabilities); diff --git a/ethereumj-core/src/test/java/org/ethereum/net/HelloMessageTest.java b/ethereumj-core/src/test/java/org/ethereum/net/HelloMessageTest.java index 2200dd1a..9b5422e7 100644 --- a/ethereumj-core/src/test/java/org/ethereum/net/HelloMessageTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/net/HelloMessageTest.java @@ -2,12 +2,14 @@ package org.ethereum.net; import static org.junit.Assert.assertEquals; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.ethereum.net.client.Capability; +import org.ethereum.net.eth.EthHandler; import org.ethereum.net.p2p.HelloMessage; import org.ethereum.net.p2p.P2pMessageCodes; +import org.ethereum.net.shh.ShhHandler; import org.junit.Test; import org.spongycastle.util.encoders.Hex; @@ -26,7 +28,7 @@ public class HelloMessageTest { "4E 0B D2 54 42 F3 9E 41 11 65 5A 48 72 57 AA 7E " + "4E D3 09 E8 B4 D5 5B E5 FA 8D 8D 6E 97 B7 2C 67 " + "D7 6A A0 3E B6 9A D9 81 ED 60"; - + byte[] payload = Hex.decode(helloMessageRaw); HelloMessage helloMessage = new HelloMessage(payload); System.out.println(helloMessage); @@ -43,34 +45,42 @@ public class HelloMessageTest { @Test /* HelloMessage 2 from Node */ public void testNode() { - String helloMessageRaw = "F8 7B 80 80 AE 4E 45 74 68 65 72 65 " - + "75 6D 28 2B 2B 29 2F 5A 65 72 6F 47 6F 78 2F 76 30 " - + "2E 36 2E 39 2F 6E 63 75 72 73 65 73 2F 4C 69 6E 75 " - + "78 2F 67 2B 2B C4 83 65 74 68 82 76 5F B8 40 CA DF " - + "B9 3D 2B B5 FB E2 94 35 84 D9 3E D9 0E 37 46 67 C9 " - + "E8 B2 50 2E 97 46 93 CC C6 B3 D3 70 BD 4C DE 77 38 " - + "D0 B6 26 E3 D2 F3 CA EC C5 9E 13 02 D1 71 1B F5 95 " - + "71 10 60 D7 B4 92 1E 18 B9 76 56"; + + String helloMessageRaw = "F8 80 80 02 AB 41 6C 65 " + + "74 68 5A 65 72 6F 2F 76 30 2E 37 2E 34 2F 52 65 " + + "6C 65 61 73 65 2D 78 36 34 2F 57 69 6E 64 6F 77 " + + "73 2F 56 53 32 30 31 33 CC C5 83 65 74 68 23 C5 " + + "83 73 68 68 01 82 76 5F B8 40 E1 01 2B 75 38 C4 " + + "1D 31 9F B4 BE D7 DF E9 D7 ED C7 2B 82 F2 E6 BE " + + "2D 20 F8 3C 60 14 11 51 5C 74 2B 3C E3 71 F5 61 " + + "47 29 56 36 27 4D 34 91 D6 BC C5 1F 0A 09 20 EB " + + "41 F2 C0 36 04 28 C9 A9 80 01"; byte[] payload = Hex.decode(helloMessageRaw); HelloMessage helloMessage = new HelloMessage(payload); System.out.println(helloMessage); assertEquals(P2pMessageCodes.HELLO, helloMessage.getCommand()); - assertEquals(0, helloMessage.getP2PVersion()); - assertEquals("NEthereum(++)/ZeroGox/v0.6.9/ncurses/Linux/g++", helloMessage.getClientId()); - assertEquals(1, helloMessage.getCapabilities().size()); - assertEquals("eth", helloMessage.getCapabilities().get(0)); + assertEquals(2, helloMessage.getP2PVersion()); + assertEquals("AlethZero/v0.7.4/Release-x64/Windows/VS2013", helloMessage.getClientId()); + assertEquals(2, helloMessage.getCapabilities().size()); + assertEquals(Capability.ETH, helloMessage.getCapabilities().get(0).getName()); + assertEquals(35, helloMessage.getCapabilities().get(0).getVersion()); + assertEquals(Capability.SHH, helloMessage.getCapabilities().get(1).getName()); + assertEquals(1, helloMessage.getCapabilities().get(1).getVersion()); + assertEquals(30303, helloMessage.getListenPort()); - assertEquals("cadfb93d2bb5fbe2943584d93ed90e374667c9e8b2502e974693ccc6b3d370bd4cde7738d0b626e3d2f3caecc59e1302d1711bf595711060d7b4921e18b97656", + assertEquals("e1012b7538c41d319fb4bed7dfe9d7edc72b82f2e6be2d20f83c601411515c742b3ce371f56147295636274d3491d6bcc51f0a0920eb41f2c0360428c9a98001", helloMessage.getPeerId()); } @Test /* HelloMessage 3 from new */ public void testFromNew() { String helloAnnouncement = "Ethereum(J)/0.6.1/dev/Windows/Java"; - byte p2pVersion = 0x00; - List capabilities = new ArrayList<>(Arrays.asList("eth", "shh")); + byte p2pVersion = 0x0; + List capabilities = Arrays.asList( + new Capability(Capability.ETH, EthHandler.VERSION), + new Capability(Capability.SHH, ShhHandler.VERSION)); int listenPort = 30303; String peerId = "CAB0D93EEE1F44EF1286367101F1553450E3DDCE" + "EA45ABCAB0AC21E1EFB48A6610EBE88CE7317EB09229558311BA8B7250911D" @@ -80,10 +90,10 @@ public class HelloMessageTest { capabilities, listenPort, peerId); System.out.println(helloMessage); // rlp encoded hello message - String expected = "F8738080A2457468657265756D284A292F302E362E312F6465762F5" - + "7696E646F77732F4A617661C8836574688373686882765FB840CAB0D93EEE1F" - + "44EF1286367101F1553450E3DDCEEA45ABCAB0AC21E1EFB48A6610EBE88CE73" - + "17EB09229558311BA8B7250911D7E49562C3988CA3143329DA3EA"; + String expected = "F8778080A2457468657265756D284A292F302E362E312F6465762F5" + + "7696E646F77732F4A617661CCC58365746823C5837368680182765FB840CAB0" + + "D93EEE1F44EF1286367101F1553450E3DDCEEA45ABCAB0AC21E1EFB48A6610E" + + "BE88CE7317EB09229558311BA8B7250911D7E49562C3988CA3143329DA3EA"; assertEquals(P2pMessageCodes.HELLO, helloMessage.getCommand()); assertEquals(expected, Hex.toHexString(helloMessage.getEncoded()).toUpperCase()); diff --git a/ethereumj-core/src/test/java/org/ethereum/net/wire/AdaptiveMessageIdsTest.java b/ethereumj-core/src/test/java/org/ethereum/net/wire/AdaptiveMessageIdsTest.java index aa97d072..9984a2c7 100644 --- a/ethereumj-core/src/test/java/org/ethereum/net/wire/AdaptiveMessageIdsTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/net/wire/AdaptiveMessageIdsTest.java @@ -1,14 +1,17 @@ package org.ethereum.net.wire; +import org.ethereum.net.client.Capability; +import org.ethereum.net.eth.EthHandler; import org.ethereum.net.eth.EthMessageCodes; import org.ethereum.net.p2p.P2pHandler; import org.ethereum.net.p2p.P2pMessageCodes; +import org.ethereum.net.shh.ShhHandler; import org.ethereum.net.shh.ShhMessageCodes; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -94,9 +97,10 @@ public class AdaptiveMessageIdsTest { P2pHandler p2pHandler = new P2pHandler(); - List capabilities = new ArrayList(); - capabilities.add("eth"); capabilities.add("shh"); - p2pHandler.adaptMessageIds( capabilities); + List capabilities = Arrays.asList( + new Capability(Capability.ETH, EthHandler.VERSION), + new Capability(Capability.SHH, ShhHandler.VERSION)); + p2pHandler.adaptMessageIds(capabilities); Assert.assertEquals(0x10 + 1, EthMessageCodes.STATUS.asByte()); Assert.assertEquals(0x10 + 2, EthMessageCodes.GET_TRANSACTIONS.asByte()); @@ -120,9 +124,10 @@ public class AdaptiveMessageIdsTest { P2pHandler p2pHandler = new P2pHandler(); - List capabilities = new ArrayList(); - capabilities.add("shh"); capabilities.add("eth"); - p2pHandler.adaptMessageIds( capabilities); + List capabilities = Arrays.asList( + new Capability(Capability.ETH, EthHandler.VERSION), + new Capability(Capability.SHH, ShhHandler.VERSION)); + p2pHandler.adaptMessageIds(capabilities); Assert.assertEquals(0x10 + 1, ShhMessageCodes.STATUS.asByte()); Assert.assertEquals(0x10 + 2, ShhMessageCodes.MESSAGE.asByte());