diff --git a/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java b/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java index 402f12581..c484c8393 100644 --- a/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java +++ b/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java @@ -217,7 +217,7 @@ public class BitcoinSerializer extends MessageSerializer { ByteBuffer payload = ByteBuffer.wrap(payloadBytes); // We use an if ladder rather than reflection because reflection is very slow on Android. if (command.equals("version")) { - return new VersionMessage(payload); + return VersionMessage.read(payload); } else if (command.equals("inv")) { return makeInventoryMessage(payload); } else if (command.equals("block")) { diff --git a/core/src/main/java/org/bitcoinj/core/VersionMessage.java b/core/src/main/java/org/bitcoinj/core/VersionMessage.java index b1ada111f..a13093fb8 100644 --- a/core/src/main/java/org/bitcoinj/core/VersionMessage.java +++ b/core/src/main/java/org/bitcoinj/core/VersionMessage.java @@ -92,8 +92,38 @@ public class VersionMessage extends BaseMessage { private static int NETADDR_BYTES = Services.BYTES + /* IPv6 */ 16 + /* port */ Short.BYTES; - public VersionMessage(ByteBuffer payload) throws ProtocolException { - super(payload); + /** + * Deserialize this message from a given payload. + * + * @param payload payload to deserialize from + * @return read message + * @throws BufferUnderflowException if the read message extends beyond the remaining bytes of the payload + */ + public static VersionMessage read(ByteBuffer payload) throws BufferUnderflowException, ProtocolException { + int clientVersion = (int) ByteUtils.readUint32(payload); + check(clientVersion >= ProtocolVersion.MINIMUM.intValue(), + ProtocolException::new); + Services localServices = Services.read(payload); + Instant time = Instant.ofEpochSecond(ByteUtils.readInt64(payload)); + Services receivingServices = Services.read(payload); + InetAddress receivingInetAddress = PeerAddress.getByAddress(Buffers.readBytes(payload, 16)); + int receivingPort = ByteUtils.readUint16BE(payload); + InetSocketAddress receivingAddr = new InetSocketAddress(receivingInetAddress, receivingPort); + Buffers.skipBytes(payload, NETADDR_BYTES); // addr_from + // uint64 localHostNonce (random data) + // We don't care about the localhost nonce. It's used to detect connecting back to yourself in cases where + // there are NATs and proxies in the way. However we don't listen for inbound connections so it's + // irrelevant. + Buffers.skipBytes(payload, 8); + // string subVer (currently "") + String subVer = Buffers.readLengthPrefixedString(payload); + // int bestHeight (size of known block chain). + long bestHeight = ByteUtils.readUint32(payload); + boolean relayTxesBeforeFilter = clientVersion >= ProtocolVersion.BLOOM_FILTER.intValue() ? + payload.get() != 0 : + true; + return new VersionMessage(clientVersion, localServices, time, receivingServices, receivingAddr, subVer, + bestHeight, relayTxesBeforeFilter); } /** @@ -147,29 +177,7 @@ public class VersionMessage extends BaseMessage { @Override protected void parse(ByteBuffer payload) throws BufferUnderflowException, ProtocolException { - clientVersion = (int) ByteUtils.readUint32(payload); - check(clientVersion >= ProtocolVersion.MINIMUM.intValue(), - ProtocolException::new); - localServices = Services.read(payload); - time = Instant.ofEpochSecond(ByteUtils.readInt64(payload)); - receivingServices = Services.read(payload); - InetAddress receivingInetAddress = PeerAddress.getByAddress(Buffers.readBytes(payload, 16)); - int receivingPort = ByteUtils.readUint16BE(payload); - receivingAddr = new InetSocketAddress(receivingInetAddress, receivingPort); - Buffers.skipBytes(payload, NETADDR_BYTES); // addr_from - // uint64 localHostNonce (random data) - // We don't care about the localhost nonce. It's used to detect connecting back to yourself in cases where - // there are NATs and proxies in the way. However we don't listen for inbound connections so it's - // irrelevant. - Buffers.skipBytes(payload, 8); - // string subVer (currently "") - subVer = Buffers.readLengthPrefixedString(payload); - // int bestHeight (size of known block chain). - bestHeight = ByteUtils.readUint32(payload); - relayTxesBeforeFilter = - clientVersion >= ProtocolVersion.BLOOM_FILTER.intValue() ? - payload.get() != 0 : - true; + throw new UnsupportedOperationException(); } @Override diff --git a/core/src/test/java/org/bitcoinj/core/VersionMessageTest.java b/core/src/test/java/org/bitcoinj/core/VersionMessageTest.java index 8b9e8ee49..c1842be70 100644 --- a/core/src/test/java/org/bitcoinj/core/VersionMessageTest.java +++ b/core/src/test/java/org/bitcoinj/core/VersionMessageTest.java @@ -37,7 +37,7 @@ public class VersionMessageTest { public void decode_noRelay_bestHeight_subVer() { // Test that we can decode version messages which miss data which some old nodes may not include String hex = "7111010000000000000000003334a85500000000000000000000000000000000000000000000ffff7f000001479d000000000000000000000000000000000000ffff7f000001479d00000000000000000f2f626974636f696e6a3a302e31332f0004000000"; - VersionMessage ver = new VersionMessage(ByteBuffer.wrap(ByteUtils.parseHex(hex))); + VersionMessage ver = VersionMessage.read(ByteBuffer.wrap(ByteUtils.parseHex(hex))); assertFalse(ver.relayTxesBeforeFilter); assertEquals(1024, ver.bestHeight); assertEquals("/bitcoinj:0.13/", ver.subVer); @@ -46,7 +46,7 @@ public class VersionMessageTest { @Test public void decode_relay_bestHeight_subVer() { String hex = "711101000000000000000000a634a85500000000000000000000000000000000000000000000ffff7f000001479d000000000000000000000000000000000000ffff7f000001479d00000000000000000f2f626974636f696e6a3a302e31332f0004000001"; - VersionMessage ver = new VersionMessage(ByteBuffer.wrap(ByteUtils.parseHex(hex))); + VersionMessage ver = VersionMessage.read(ByteBuffer.wrap(ByteUtils.parseHex(hex))); assertTrue(ver.relayTxesBeforeFilter); assertEquals(1024, ver.bestHeight); assertEquals("/bitcoinj:0.13/", ver.subVer); @@ -55,7 +55,7 @@ public class VersionMessageTest { @Test public void decode_relay_noBestHeight_subVer() { String hex = "711101000000000000000000c334a85500000000000000000000000000000000000000000000ffff7f000001479d000000000000000000000000000000000000ffff7f000001479d00000000000000000f2f626974636f696e6a3a302e31332f0000000001"; - VersionMessage ver = new VersionMessage(ByteBuffer.wrap(ByteUtils.parseHex(hex))); + VersionMessage ver = VersionMessage.read(ByteBuffer.wrap(ByteUtils.parseHex(hex))); assertTrue(ver.relayTxesBeforeFilter); assertEquals(0, ver.bestHeight); assertEquals("/bitcoinj:0.13/", ver.subVer); @@ -64,7 +64,7 @@ public class VersionMessageTest { @Test(expected = ProtocolException.class) public void decode_relay_noBestHeight_noSubVer() { String hex = "00000000000000000000000048e5e95000000000000000000000000000000000000000000000ffff7f000001479d000000000000000000000000000000000000ffff7f000001479d0000000000000000"; - VersionMessage ver = new VersionMessage(ByteBuffer.wrap(ByteUtils.parseHex(hex))); + VersionMessage ver = VersionMessage.read(ByteBuffer.wrap(ByteUtils.parseHex(hex))); } @Test @@ -75,7 +75,7 @@ public class VersionMessageTest { ver.localServices = Services.of(1); ver.receivingAddr = new InetSocketAddress(InetAddress.getByName("4.3.2.1"), 8333); byte[] serialized = ver.bitcoinSerialize(); - VersionMessage ver2 = new VersionMessage(ByteBuffer.wrap(serialized)); + VersionMessage ver2 = VersionMessage.read(ByteBuffer.wrap(serialized)); assertEquals(1234, ver2.bestHeight); assertEquals(Instant.ofEpochSecond(23456), ver2.time); assertEquals("/bitcoinj/", ver2.subVer); @@ -93,7 +93,7 @@ public class VersionMessageTest { ver.localServices = Services.of(1); ver.receivingAddr = new InetSocketAddress(InetAddress.getByName("2002:db8:85a3:0:0:8a2e:370:7335"), 8333); byte[] serialized = ver.bitcoinSerialize(); - VersionMessage ver2 = new VersionMessage(ByteBuffer.wrap(serialized)); + VersionMessage ver2 = VersionMessage.read(ByteBuffer.wrap(serialized)); assertEquals(1234, ver2.bestHeight); assertEquals(Instant.ofEpochSecond(23456), ver2.time); assertEquals("/bitcoinj/", ver2.subVer);