Extract capabilities to separate class

This commit is contained in:
nicksavers 2014-10-18 22:03:31 +02:00
parent 4f10a0bec2
commit b41e47b7c6
7 changed files with 131 additions and 66 deletions

View File

@ -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;
}
}

View File

@ -5,12 +5,15 @@ import java.util.List;
import org.ethereum.config.SystemProperties; import org.ethereum.config.SystemProperties;
import org.ethereum.crypto.HashUtil; 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.eth.GetTransactionsMessage;
import org.ethereum.net.p2p.GetPeersMessage; import org.ethereum.net.p2p.GetPeersMessage;
import org.ethereum.net.p2p.HelloMessage; import org.ethereum.net.p2p.HelloMessage;
import org.ethereum.net.p2p.P2pHandler; import org.ethereum.net.p2p.P2pHandler;
import org.ethereum.net.p2p.PingMessage; import org.ethereum.net.p2p.PingMessage;
import org.ethereum.net.p2p.PongMessage; import org.ethereum.net.p2p.PongMessage;
import org.ethereum.net.shh.ShhHandler;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
/** /**
@ -35,7 +38,9 @@ public class StaticMessages {
private static HelloMessage generateHelloMessage() { private static HelloMessage generateHelloMessage() {
String helloAnnouncement = buildHelloAnnouncement(); String helloAnnouncement = buildHelloAnnouncement();
byte p2pVersion = P2pHandler.VERSION; byte p2pVersion = P2pHandler.VERSION;
List<String> capabilities = Arrays.asList("eth", "shh"); List<Capability> capabilities = Arrays.asList(
new Capability(Capability.ETH, EthHandler.VERSION),
new Capability(Capability.SHH, ShhHandler.VERSION));
int listenPort = 30303; int listenPort = 30303;
return new HelloMessage(p2pVersion, helloAnnouncement, return new HelloMessage(p2pVersion, helloAnnouncement,

View File

@ -3,9 +3,8 @@ package org.ethereum.net.p2p;
import static org.ethereum.net.p2p.P2pMessageCodes.HELLO; import static org.ethereum.net.p2p.P2pMessageCodes.HELLO;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY; 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.p2p.P2pMessage;
import org.ethereum.net.shh.ShhHandler;
import org.ethereum.util.*; import org.ethereum.util.*;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
@ -27,7 +26,7 @@ public class HelloMessage extends P2pMessage {
private String clientId; private String clientId;
/** A peer-network capability code, readable ASCII and 3 letters. /** A peer-network capability code, readable ASCII and 3 letters.
* Currently only "eth", "shh" and "bzz" are known. */ * Currently only "eth", "shh" and "bzz" are known. */
private List<String> capabilities; private List<Capability> capabilities;
/** The port on which the peer is listening for an incoming connection */ /** The port on which the peer is listening for an incoming connection */
private int listenPort; private int listenPort;
/** The identity and public key of the peer */ /** The identity and public key of the peer */
@ -38,7 +37,7 @@ public class HelloMessage extends P2pMessage {
} }
public HelloMessage(byte p2pVersion, String clientId, public HelloMessage(byte p2pVersion, String clientId,
List<String> capabilities, int listenPort, String peerId) { List<Capability> capabilities, int listenPort, String peerId) {
this.p2pVersion = p2pVersion; this.p2pVersion = p2pVersion;
this.clientId = clientId; this.clientId = clientId;
this.capabilities = capabilities; this.capabilities = capabilities;
@ -63,8 +62,14 @@ public class HelloMessage extends P2pMessage {
this.capabilities = new ArrayList<>(); this.capabilities = new ArrayList<>();
for (int i = 0; i < capabilityList.size(); i++) { for (int i = 0; i < capabilityList.size(); i++) {
RLPElement capabilitiesID = ((RLPList)capabilityList.get(i)).get(0); RLPElement capId = ((RLPList)capabilityList.get(i)).get(0);
this.capabilities.add(new String(capabilitiesID.getRLPData())); 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(); byte[] peerPortBytes = ((RLPItem) paramsList.get(4)).getRLPData();
@ -81,16 +86,10 @@ public class HelloMessage extends P2pMessage {
byte[] clientId = RLP.encodeString(this.clientId); byte[] clientId = RLP.encodeString(this.clientId);
byte[][] capabilities = new byte[this.capabilities.size()][]; byte[][] capabilities = new byte[this.capabilities.size()][];
for (int i = 0; i < this.capabilities.size(); i++) { for (int i = 0; i < this.capabilities.size(); i++) {
Capability capability = this.capabilities.get(i);
String capability = this.capabilities.get(i);
byte version = 0;
if (capability.equals("eth")) version = EthHandler.VERSION;
if (capability.equals("shh")) version = ShhHandler.VERSION;
capabilities[i] = RLP.encodeList( capabilities[i] = RLP.encodeList(
RLP.encodeElement( capability.getBytes() ), RLP.encodeElement(capability.getName().getBytes()),
RLP.encodeElement( new byte[]{version} )); RLP.encodeElement(new byte[] {capability.getVersion() }));
} }
byte[] capabilityList = RLP.encodeList(capabilities); byte[] capabilityList = RLP.encodeList(capabilities);
byte[] peerPort = RLP.encodeInt(this.listenPort); byte[] peerPort = RLP.encodeInt(this.listenPort);
@ -116,7 +115,7 @@ public class HelloMessage extends P2pMessage {
return clientId; return clientId;
} }
public List<String> getCapabilities() { public List<Capability> getCapabilities() {
if (!parsed) parse(); if (!parsed) parse();
return capabilities; return capabilities;
} }

View File

@ -6,6 +6,7 @@ import static org.ethereum.net.message.StaticMessages.HELLO_MESSAGE;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.Timer; import java.util.Timer;
@ -17,6 +18,7 @@ import io.netty.channel.SimpleChannelInboundHandler;
import org.ethereum.manager.WorldManager; import org.ethereum.manager.WorldManager;
import org.ethereum.net.MessageQueue; import org.ethereum.net.MessageQueue;
import org.ethereum.net.PeerListener; import org.ethereum.net.PeerListener;
import org.ethereum.net.client.Capability;
import org.ethereum.net.eth.EthHandler; import org.ethereum.net.eth.EthHandler;
import org.ethereum.net.eth.EthMessageCodes; import org.ethereum.net.eth.EthMessageCodes;
import org.ethereum.net.shh.ShhHandler; import org.ethereum.net.shh.ShhHandler;
@ -100,7 +102,7 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
break; break;
case GET_PEERS: case GET_PEERS:
msgQueue.receivedMessage(msg); msgQueue.receivedMessage(msg);
// sendPeers(); // todo: implement session management for peer request //sendPeers(); // todo: implement session management for peer request
break; break;
case PEERS: case PEERS:
msgQueue.receivedMessage(msg); msgQueue.receivedMessage(msg);
@ -154,18 +156,19 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
if (msg.getP2PVersion() != P2pHandler.VERSION) if (msg.getP2PVersion() != P2pHandler.VERSION)
msgQueue.sendMessage(new DisconnectMessage(ReasonCode.INCOMPATIBLE_PROTOCOL)); msgQueue.sendMessage(new DisconnectMessage(ReasonCode.INCOMPATIBLE_PROTOCOL));
else { else {
List<Capability> capInCommon = new ArrayList<>();
adaptMessageIds(msg.getCapabilities()); for (Capability capability : msg.getCapabilities()) {
if (HELLO_MESSAGE.getCapabilities().contains(capability)) {
if (msg.getCapabilities().contains("eth")) { if (capability.getName().equals(Capability.ETH))
// Activate EthHandler for this peer // Activate EthHandler for this peer
ctx.pipeline().addLast("eth", new EthHandler(msg.getPeerId(), peerListener, msgQueue)); 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);
}
} }
adaptMessageIds(capInCommon);
if (msg.getCapabilities().contains("shh")) {
// Activate ShhHandler for this peer
ctx.pipeline().addLast("shh", new ShhHandler(msg.getPeerId(), peerListener));
}
InetAddress address = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress(); InetAddress address = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress();
int port = msg.getListenPort(); int port = msg.getListenPort();
@ -178,17 +181,17 @@ public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> {
} }
} }
public void adaptMessageIds(List<String> capabilities) { public void adaptMessageIds(List<Capability> capabilities) {
byte offset = (byte) (P2pMessageCodes.USER.asByte() + 1); 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); EthMessageCodes.setOffset(offset);
offset += EthMessageCodes.values().length; offset += EthMessageCodes.values().length;
} }
if (capability.equals("shh")){ if (capability.getName().equals(Capability.SHH)) {
ShhMessageCodes.setOffset(offset); ShhMessageCodes.setOffset(offset);
offset += ShhMessageCodes.values().length; offset += ShhMessageCodes.values().length;
} }

View File

@ -1,5 +1,6 @@
package org.ethereum.net.peerdiscovery; package org.ethereum.net.peerdiscovery;
import org.ethereum.net.client.Capability;
import org.ethereum.util.RLP; import org.ethereum.util.RLP;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
@ -16,7 +17,7 @@ public class PeerData {
private int port; private int port;
private String peerId; private String peerId;
private List<String> capabilities; private List<Capability> capabilities;
private transient boolean isOnline = false; private transient boolean isOnline = false;
private transient long lastCheckTime = 0; private transient long lastCheckTime = 0;
@ -58,7 +59,7 @@ public class PeerData {
this.lastCheckTime = lastCheckTime; this.lastCheckTime = lastCheckTime;
} }
public List<String> getCapabilities() { public List<Capability> getCapabilities() {
return capabilities; return capabilities;
} }
@ -67,8 +68,9 @@ public class PeerData {
byte[] port = RLP.encodeInt(this.port); byte[] port = RLP.encodeInt(this.port);
byte[] peerId = RLP.encodeElement(Hex.decode(this.peerId)); byte[] peerId = RLP.encodeElement(Hex.decode(this.peerId));
byte[][] encodedCaps = new byte[this.capabilities.size()][]; byte[][] encodedCaps = new byte[this.capabilities.size()][];
for (int i = 0; i < this.capabilities.size(); i++) { for (int i = 0; i < this.capabilities.size()*2; i++) {
encodedCaps[i] = RLP.encodeString(this.capabilities.get(i)); encodedCaps[i] = RLP.encodeString(this.capabilities.get(i).getName());
encodedCaps[i] = RLP.encodeByte(this.capabilities.get(i).getVersion());
} }
byte[] capabilities = RLP.encodeList(encodedCaps); byte[] capabilities = RLP.encodeList(encodedCaps);
return RLP.encodeList(ip, port, peerId, capabilities); return RLP.encodeList(ip, port, peerId, capabilities);

View File

@ -2,12 +2,14 @@ package org.ethereum.net;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; 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.HelloMessage;
import org.ethereum.net.p2p.P2pMessageCodes; import org.ethereum.net.p2p.P2pMessageCodes;
import org.ethereum.net.shh.ShhHandler;
import org.junit.Test; import org.junit.Test;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
@ -43,34 +45,42 @@ public class HelloMessageTest {
@Test /* HelloMessage 2 from Node */ @Test /* HelloMessage 2 from Node */
public void testNode() { 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 " String helloMessageRaw = "F8 80 80 02 AB 41 6C 65 "
+ "2E 36 2E 39 2F 6E 63 75 72 73 65 73 2F 4C 69 6E 75 " + "74 68 5A 65 72 6F 2F 76 30 2E 37 2E 34 2F 52 65 "
+ "78 2F 67 2B 2B C4 83 65 74 68 82 76 5F B8 40 CA DF " + "6C 65 61 73 65 2D 78 36 34 2F 57 69 6E 64 6F 77 "
+ "B9 3D 2B B5 FB E2 94 35 84 D9 3E D9 0E 37 46 67 C9 " + "73 2F 56 53 32 30 31 33 CC C5 83 65 74 68 23 C5 "
+ "E8 B2 50 2E 97 46 93 CC C6 B3 D3 70 BD 4C DE 77 38 " + "83 73 68 68 01 82 76 5F B8 40 E1 01 2B 75 38 C4 "
+ "D0 B6 26 E3 D2 F3 CA EC C5 9E 13 02 D1 71 1B F5 95 " + "1D 31 9F B4 BE D7 DF E9 D7 ED C7 2B 82 F2 E6 BE "
+ "71 10 60 D7 B4 92 1E 18 B9 76 56"; + "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); byte[] payload = Hex.decode(helloMessageRaw);
HelloMessage helloMessage = new HelloMessage(payload); HelloMessage helloMessage = new HelloMessage(payload);
System.out.println(helloMessage); System.out.println(helloMessage);
assertEquals(P2pMessageCodes.HELLO, helloMessage.getCommand()); assertEquals(P2pMessageCodes.HELLO, helloMessage.getCommand());
assertEquals(0, helloMessage.getP2PVersion()); assertEquals(2, helloMessage.getP2PVersion());
assertEquals("NEthereum(++)/ZeroGox/v0.6.9/ncurses/Linux/g++", helloMessage.getClientId()); assertEquals("AlethZero/v0.7.4/Release-x64/Windows/VS2013", helloMessage.getClientId());
assertEquals(1, helloMessage.getCapabilities().size()); assertEquals(2, helloMessage.getCapabilities().size());
assertEquals("eth", helloMessage.getCapabilities().get(0)); 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(30303, helloMessage.getListenPort());
assertEquals("cadfb93d2bb5fbe2943584d93ed90e374667c9e8b2502e974693ccc6b3d370bd4cde7738d0b626e3d2f3caecc59e1302d1711bf595711060d7b4921e18b97656", assertEquals("e1012b7538c41d319fb4bed7dfe9d7edc72b82f2e6be2d20f83c601411515c742b3ce371f56147295636274d3491d6bcc51f0a0920eb41f2c0360428c9a98001",
helloMessage.getPeerId()); helloMessage.getPeerId());
} }
@Test /* HelloMessage 3 from new */ @Test /* HelloMessage 3 from new */
public void testFromNew() { public void testFromNew() {
String helloAnnouncement = "Ethereum(J)/0.6.1/dev/Windows/Java"; String helloAnnouncement = "Ethereum(J)/0.6.1/dev/Windows/Java";
byte p2pVersion = 0x00; byte p2pVersion = 0x0;
List<String> capabilities = new ArrayList<>(Arrays.asList("eth", "shh")); List<Capability> capabilities = Arrays.asList(
new Capability(Capability.ETH, EthHandler.VERSION),
new Capability(Capability.SHH, ShhHandler.VERSION));
int listenPort = 30303; int listenPort = 30303;
String peerId = "CAB0D93EEE1F44EF1286367101F1553450E3DDCE" String peerId = "CAB0D93EEE1F44EF1286367101F1553450E3DDCE"
+ "EA45ABCAB0AC21E1EFB48A6610EBE88CE7317EB09229558311BA8B7250911D" + "EA45ABCAB0AC21E1EFB48A6610EBE88CE7317EB09229558311BA8B7250911D"
@ -80,10 +90,10 @@ public class HelloMessageTest {
capabilities, listenPort, peerId); capabilities, listenPort, peerId);
System.out.println(helloMessage); System.out.println(helloMessage);
// rlp encoded hello message // rlp encoded hello message
String expected = "F8738080A2457468657265756D284A292F302E362E312F6465762F5" String expected = "F8778080A2457468657265756D284A292F302E362E312F6465762F5"
+ "7696E646F77732F4A617661C8836574688373686882765FB840CAB0D93EEE1F" + "7696E646F77732F4A617661CCC58365746823C5837368680182765FB840CAB0"
+ "44EF1286367101F1553450E3DDCEEA45ABCAB0AC21E1EFB48A6610EBE88CE73" + "D93EEE1F44EF1286367101F1553450E3DDCEEA45ABCAB0AC21E1EFB48A6610E"
+ "17EB09229558311BA8B7250911D7E49562C3988CA3143329DA3EA"; + "BE88CE7317EB09229558311BA8B7250911D7E49562C3988CA3143329DA3EA";
assertEquals(P2pMessageCodes.HELLO, helloMessage.getCommand()); assertEquals(P2pMessageCodes.HELLO, helloMessage.getCommand());
assertEquals(expected, Hex.toHexString(helloMessage.getEncoded()).toUpperCase()); assertEquals(expected, Hex.toHexString(helloMessage.getEncoded()).toUpperCase());

View File

@ -1,14 +1,17 @@
package org.ethereum.net.wire; 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.eth.EthMessageCodes;
import org.ethereum.net.p2p.P2pHandler; import org.ethereum.net.p2p.P2pHandler;
import org.ethereum.net.p2p.P2pMessageCodes; import org.ethereum.net.p2p.P2pMessageCodes;
import org.ethereum.net.shh.ShhHandler;
import org.ethereum.net.shh.ShhMessageCodes; import org.ethereum.net.shh.ShhMessageCodes;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@ -94,9 +97,10 @@ public class AdaptiveMessageIdsTest {
P2pHandler p2pHandler = new P2pHandler(); P2pHandler p2pHandler = new P2pHandler();
List<String> capabilities = new ArrayList<String>(); List<Capability> capabilities = Arrays.asList(
capabilities.add("eth"); capabilities.add("shh"); new Capability(Capability.ETH, EthHandler.VERSION),
p2pHandler.adaptMessageIds( capabilities); new Capability(Capability.SHH, ShhHandler.VERSION));
p2pHandler.adaptMessageIds(capabilities);
Assert.assertEquals(0x10 + 1, EthMessageCodes.STATUS.asByte()); Assert.assertEquals(0x10 + 1, EthMessageCodes.STATUS.asByte());
Assert.assertEquals(0x10 + 2, EthMessageCodes.GET_TRANSACTIONS.asByte()); Assert.assertEquals(0x10 + 2, EthMessageCodes.GET_TRANSACTIONS.asByte());
@ -120,9 +124,10 @@ public class AdaptiveMessageIdsTest {
P2pHandler p2pHandler = new P2pHandler(); P2pHandler p2pHandler = new P2pHandler();
List<String> capabilities = new ArrayList<String>(); List<Capability> capabilities = Arrays.asList(
capabilities.add("shh"); capabilities.add("eth"); new Capability(Capability.ETH, EthHandler.VERSION),
p2pHandler.adaptMessageIds( capabilities); new Capability(Capability.SHH, ShhHandler.VERSION));
p2pHandler.adaptMessageIds(capabilities);
Assert.assertEquals(0x10 + 1, ShhMessageCodes.STATUS.asByte()); Assert.assertEquals(0x10 + 1, ShhMessageCodes.STATUS.asByte());
Assert.assertEquals(0x10 + 2, ShhMessageCodes.MESSAGE.asByte()); Assert.assertEquals(0x10 + 2, ShhMessageCodes.MESSAGE.asByte());