PeerAddress: divorce from Message

It is never sent on its own, so it doesn't need to be a `Message`.

* Static constructor `read(protocolVariant)` replaces the native constructor that deserialized
  from a payload.
* `write()` helper replaces `bitcoinSerializeToStream()`.
* `serialize()` and `getMessageSize()` helpers replace `bitcoinSerialize()`.
This commit is contained in:
Andreas Schildbach 2023-04-10 13:08:36 +02:00
parent 0401a0cde2
commit 2c62951a31
6 changed files with 249 additions and 187 deletions

View File

@ -16,11 +16,8 @@
package org.bitcoinj.core; package org.bitcoinj.core;
import org.bitcoinj.base.VarInt;
import org.bitcoinj.net.discovery.PeerDiscovery; import org.bitcoinj.net.discovery.PeerDiscovery;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -38,16 +35,6 @@ public abstract class AddressMessage extends Message {
super(payload); super(payload);
} }
@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
if (addresses == null)
return;
stream.write(VarInt.of(addresses.size()).serialize());
for (PeerAddress addr : addresses) {
addr.bitcoinSerializeToStream(stream);
}
}
public abstract void addAddress(PeerAddress address); public abstract void addAddress(PeerAddress address);
public void removeAddress(int index) { public void removeAddress(int index) {

View File

@ -21,9 +21,14 @@ import org.bitcoinj.base.VarInt;
import org.bitcoinj.base.internal.InternalUtils; import org.bitcoinj.base.internal.InternalUtils;
import org.bitcoinj.net.discovery.PeerDiscovery; import org.bitcoinj.net.discovery.PeerDiscovery;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.BufferUnderflowException; import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects;
import static org.bitcoinj.base.internal.Preconditions.checkArgument;
/** /**
* Represents an "addr" message on the P2P network, which contains broadcast IP addresses of other peers. This is * Represents an "addr" message on the P2P network, which contains broadcast IP addresses of other peers. This is
@ -49,10 +54,8 @@ public class AddressV1Message extends AddressMessage {
if (numAddresses > MAX_ADDRESSES) if (numAddresses > MAX_ADDRESSES)
throw new ProtocolException("Address message too large."); throw new ProtocolException("Address message too large.");
addresses = new ArrayList<>(numAddresses); addresses = new ArrayList<>(numAddresses);
MessageSerializer serializer = new DummySerializer(1);
for (int i = 0; i < numAddresses; i++) { for (int i = 0; i < numAddresses; i++) {
PeerAddress addr = new PeerAddress(payload, serializer); addresses.add(PeerAddress.read(payload, 1));
addresses.add(addr);
} }
} }
@ -60,6 +63,16 @@ public class AddressV1Message extends AddressMessage {
addresses.add(address); addresses.add(address);
} }
@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
if (addresses == null)
return;
stream.write(VarInt.of(addresses.size()).serialize());
for (PeerAddress addr : addresses) {
stream.write(addr.serialize(1));
}
}
@Override @Override
public String toString() { public String toString() {
return "addr: " + InternalUtils.SPACE_JOINER.join(addresses); return "addr: " + InternalUtils.SPACE_JOINER.join(addresses);

View File

@ -20,10 +20,14 @@ import org.bitcoinj.base.VarInt;
import org.bitcoinj.base.internal.InternalUtils; import org.bitcoinj.base.internal.InternalUtils;
import org.bitcoinj.net.discovery.PeerDiscovery; import org.bitcoinj.net.discovery.PeerDiscovery;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.BufferUnderflowException; import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import static org.bitcoinj.base.internal.Preconditions.checkArgument;
/** /**
* Represents an "addrv2" message on the P2P network, which contains broadcast addresses of other peers. This is * Represents an "addrv2" message on the P2P network, which contains broadcast addresses of other peers. This is
* one of the ways peers can find each other without using the {@link PeerDiscovery} mechanism. * one of the ways peers can find each other without using the {@link PeerDiscovery} mechanism.
@ -49,10 +53,8 @@ public class AddressV2Message extends AddressMessage {
if (numAddresses > MAX_ADDRESSES) if (numAddresses > MAX_ADDRESSES)
throw new ProtocolException("Address message too large."); throw new ProtocolException("Address message too large.");
addresses = new ArrayList<>(numAddresses); addresses = new ArrayList<>(numAddresses);
MessageSerializer serializer = new DummySerializer(2);
for (int i = 0; i < numAddresses; i++) { for (int i = 0; i < numAddresses; i++) {
PeerAddress addr = new PeerAddress(payload, serializer); addresses.add(PeerAddress.read(payload, 2));
addresses.add(addr);
} }
} }
@ -60,6 +62,16 @@ public class AddressV2Message extends AddressMessage {
addresses.add(address); addresses.add(address);
} }
@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
if (addresses == null)
return;
stream.write(VarInt.of(addresses.size()).serialize());
for (PeerAddress addr : addresses) {
stream.write(addr.serialize(2));
}
}
@Override @Override
public String toString() { public String toString() {
return "addrv2: " + InternalUtils.SPACE_JOINER.join(addresses); return "addrv2: " + InternalUtils.SPACE_JOINER.join(addresses);

View File

@ -20,21 +20,18 @@ package org.bitcoinj.core;
import com.google.common.io.BaseEncoding; import com.google.common.io.BaseEncoding;
import org.bitcoinj.base.VarInt; import org.bitcoinj.base.VarInt;
import org.bitcoinj.base.internal.Buffers; import org.bitcoinj.base.internal.Buffers;
import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.crypto.internal.CryptoUtils; import org.bitcoinj.crypto.internal.CryptoUtils;
import org.bitcoinj.base.internal.ByteUtils; import org.bitcoinj.base.internal.ByteUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.Inet6Address; import java.net.Inet6Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException; import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
@ -44,17 +41,14 @@ import java.util.stream.Stream;
import static org.bitcoinj.base.internal.Preconditions.checkArgument; import static org.bitcoinj.base.internal.Preconditions.checkArgument;
/** /**
* <p>A PeerAddress holds an IP address and port number representing the network location of * A PeerAddress holds an IP address and port number representing the network location of
* a peer in the Bitcoin P2P network. It exists primarily for serialization purposes.</p> * a peer in the Bitcoin P2P network. It exists primarily for serialization purposes.
* * <p>
* <p>This class abuses the protocol version contained in its serializer. It can only contain * Instances of this class are not safe for use by multiple threads.
* 1 ({@link AddressV1Message}) or 2 ({@link AddressV2Message}).</p>
*
* <p>Instances of this class are not safe for use by multiple threads.</p>
*/ */
public class PeerAddress extends Message { public class PeerAddress {
private InetAddress addr; // Used for IPV4, IPV6, null otherwise or if not-yet-parsed private InetAddress addr; // Used for IPV4, IPV6, null otherwise
private String hostname; // Used for (.onion addresses) TORV2, TORV3, null otherwise or if not-yet-parsed private String hostname; // Used for (.onion addresses) TORV2, TORV3, null otherwise
private int port; private int port;
private Services services; private Services services;
private Instant time; private Instant time;
@ -85,141 +79,23 @@ public class PeerAddress extends Message {
} }
/** /**
* Construct a peer address from a serialized payload. * Deserialize this peer address from a given payload, using a given protocol variant. The variant can be
* @param payload Bitcoin protocol formatted byte array containing message content. * 1 ({@link AddressV1Message}) or 2 ({@link AddressV2Message}).
* @param serializer the serializer to use for this message. *
* @throws ProtocolException * @param payload payload to deserialize from
* @param protocolVariant variant of protocol to use for parsing
* @return read message
* @throws BufferUnderflowException if the read message extends beyond the remaining bytes of the payload
*/ */
public PeerAddress(ByteBuffer payload, MessageSerializer serializer) throws ProtocolException { public static PeerAddress read(ByteBuffer payload, int protocolVariant) throws BufferUnderflowException, ProtocolException {
super(payload, serializer); if (protocolVariant < 0 || protocolVariant > 2)
} throw new IllegalStateException("invalid protocolVariant: " + protocolVariant);
/** Instant time = Instant.ofEpochSecond(ByteUtils.readUint32(payload));
* Construct a peer address from a memorized or hardcoded address. Services services;
*/ InetAddress addr = null;
public PeerAddress(InetAddress addr, int port, Services services, MessageSerializer serializer) { String hostname = null;
super(serializer); if (protocolVariant == 2) {
this.addr = Objects.requireNonNull(addr);
this.port = port;
this.services = services;
this.time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS);
}
/**
* Constructs a peer address from the given IP address, port and services. Version number is default for the given parameters.
*/
public PeerAddress(InetAddress addr, int port, Services services) {
this(addr, port, services, new DummySerializer(0));
}
/**
* Constructs a peer address from the given IP address and port. Version number is default for the given parameters.
*/
public PeerAddress(InetAddress addr, int port) {
this(addr, port, Services.none());
}
/**
* Constructs a peer address from an {@link InetSocketAddress}. An InetSocketAddress can take in as parameters an
* InetAddress or a String hostname. If you want to connect to a .onion, set the hostname to the .onion address.
*/
public PeerAddress(InetSocketAddress addr) {
this(addr.getAddress(), addr.getPort());
}
/**
* Constructs a peer address from a stringified hostname+port. Use this if you want to connect to a Tor .onion address.
*/
public PeerAddress(String hostname, int port) {
super();
this.hostname = hostname;
this.port = port;
this.services = Services.none();
this.time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS);
}
public static PeerAddress localhost(NetworkParameters params) {
return new PeerAddress(InetAddress.getLoopbackAddress(), params.getPort());
}
@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
int protocolVersion = serializer.getProtocolVersion();
if (protocolVersion < 1 || protocolVersion > 2)
throw new IllegalStateException("invalid protocolVersion: " + protocolVersion);
ByteUtils.writeInt32LE(time.getEpochSecond(), stream);
if (protocolVersion == 2) {
stream.write(VarInt.of(services.bits()).serialize());
if (addr != null) {
if (addr instanceof Inet4Address) {
stream.write(0x01);
stream.write(VarInt.of(4).serialize());
stream.write(addr.getAddress());
} else if (addr instanceof Inet6Address) {
stream.write(0x02);
stream.write(VarInt.of(16).serialize());
stream.write(addr.getAddress());
} else {
throw new IllegalStateException();
}
} else if (addr == null && hostname != null && hostname.toLowerCase(Locale.ROOT).endsWith(".onion")) {
byte[] onionAddress = BASE32.decode(hostname.substring(0, hostname.length() - 6));
if (onionAddress.length == 10) {
// TORv2
stream.write(0x03);
stream.write(VarInt.of(10).serialize());
stream.write(onionAddress);
} else if (onionAddress.length == 32 + 2 + 1) {
// TORv3
stream.write(0x04);
stream.write(VarInt.of(32).serialize());
byte[] pubkey = Arrays.copyOfRange(onionAddress, 0, 32);
byte[] checksum = Arrays.copyOfRange(onionAddress, 32, 34);
byte torVersion = onionAddress[34];
if (torVersion != 0x03)
throw new IllegalStateException("version");
if (!Arrays.equals(checksum, CryptoUtils.onionChecksum(pubkey, torVersion)))
throw new IllegalStateException("checksum");
stream.write(pubkey);
} else {
throw new IllegalStateException();
}
} else {
throw new IllegalStateException();
}
} else {
stream.write(services.serialize());
if (addr != null) {
// Java does not provide any utility to map an IPv4 address into IPv6 space, so we have to do it by
// hand.
byte[] ipBytes = addr.getAddress();
stream.write(mapIntoIPv6(ipBytes));
} else if (hostname != null && hostname.toLowerCase(Locale.ROOT).endsWith(".onion")) {
byte[] onionAddress = BASE32.decode(hostname.substring(0, hostname.length() - 6));
if (onionAddress.length == 10) {
// TORv2
stream.write(ONIONCAT_PREFIX);
stream.write(onionAddress);
} else {
throw new IllegalStateException();
}
} else {
throw new IllegalStateException();
}
}
// And write out the port. Unlike the rest of the protocol, address and port is in big endian byte order.
ByteUtils.writeInt16BE(port, stream);
}
@Override
protected void parse(ByteBuffer payload) throws BufferUnderflowException, ProtocolException {
int protocolVersion = serializer.getProtocolVersion();
if (protocolVersion < 1 || protocolVersion > 2)
throw new IllegalStateException("invalid protocolVersion: " + protocolVersion);
time = Instant.ofEpochSecond(ByteUtils.readUint32(payload));
if (protocolVersion == 2) {
services = Services.of(VarInt.read(payload).longValue()); services = Services.of(VarInt.read(payload).longValue());
int networkId = payload.get(); int networkId = payload.get();
byte[] addrBytes = Buffers.readLengthPrefixedBytes(payload); byte[] addrBytes = Buffers.readLengthPrefixedBytes(payload);
@ -279,7 +155,184 @@ public class PeerAddress extends Message {
hostname = null; hostname = null;
} }
} }
port = ByteUtils.readUint16BE(payload); int port = ByteUtils.readUint16BE(payload);
return new PeerAddress(addr, hostname, port, services, time);
}
/**
* Constructs a peer address from the given IP address, port and services.
*/
public PeerAddress(InetAddress addr, int port, Services services, Instant time) {
this(addr, null, port, services, Objects.requireNonNull(time));
}
/**
* Constructs a peer address from the given IP address, port and services.
*/
public PeerAddress(InetAddress addr, int port, Services services) {
this(addr, null, port, services, null);
}
/**
* Constructs a peer address from the given IP address and port.
*/
public PeerAddress(InetAddress addr, int port) {
this(addr, null, port, Services.none(), null);
}
/**
* Constructs a peer address from an {@link InetSocketAddress}. An InetSocketAddress can take in as parameters an
* InetAddress or a String hostname. If you want to connect to a .onion, set the hostname to the .onion address.
*/
public PeerAddress(InetSocketAddress addr) {
this(addr.getAddress(), null, addr.getPort(), Services.none(), null);
}
private PeerAddress(InetAddress addr, String hostname, int port, Services services, Instant time) {
this.addr = addr;
this.hostname = hostname;
this.port = port;
this.services = services;
this.time = time;
}
public static PeerAddress localhost(NetworkParameters params) {
return new PeerAddress(InetAddress.getLoopbackAddress(), params.getPort());
}
/**
* Write this peer address into the given buffer, using a given protocol variant. The variant can be
* 1 ({@link AddressV1Message}) or 2 ({@link AddressV2Message})..
*
* @param buf buffer to write into
* @param protocolVariant variant of protocol used
* @return the buffer
* @throws BufferOverflowException if the peer addressdoesn't fit the remaining buffer
*/
public ByteBuffer write(ByteBuffer buf, int protocolVariant) throws BufferOverflowException {
if (protocolVariant < 1 || protocolVariant > 2)
throw new IllegalStateException("invalid protocolVariant: " + protocolVariant);
ByteUtils.writeInt32LE(time.getEpochSecond(), buf);
if (protocolVariant == 2) {
VarInt.of(services.bits()).write(buf);
if (addr != null) {
if (addr instanceof Inet4Address) {
buf.put((byte) 0x01);
VarInt.of(4).write(buf);
buf.put(addr.getAddress());
} else if (addr instanceof Inet6Address) {
buf.put((byte) 0x02);
VarInt.of(16).write(buf);
buf.put(addr.getAddress());
} else {
throw new IllegalStateException();
}
} else if (addr == null && hostname != null && hostname.toLowerCase(Locale.ROOT).endsWith(".onion")) {
byte[] onionAddress = BASE32.decode(hostname.substring(0, hostname.length() - 6));
if (onionAddress.length == 10) {
// TORv2
buf.put((byte) 0x03);
VarInt.of(10).write(buf);
buf.put(onionAddress);
} else if (onionAddress.length == 32 + 2 + 1) {
// TORv3
buf.put((byte) 0x04);
VarInt.of(32).write(buf);
byte[] pubkey = Arrays.copyOfRange(onionAddress, 0, 32);
byte[] checksum = Arrays.copyOfRange(onionAddress, 32, 34);
byte torVersion = onionAddress[34];
if (torVersion != 0x03)
throw new IllegalStateException("version");
if (!Arrays.equals(checksum, CryptoUtils.onionChecksum(pubkey, torVersion)))
throw new IllegalStateException("checksum");
buf.put(pubkey);
} else {
throw new IllegalStateException();
}
} else {
throw new IllegalStateException();
}
} else {
services.write(buf);
if (addr != null) {
// Java does not provide any utility to map an IPv4 address into IPv6 space, so we have to do it by
// hand.
byte[] ipBytes = addr.getAddress();
buf.put(mapIntoIPv6(ipBytes));
} else if (hostname != null && hostname.toLowerCase(Locale.ROOT).endsWith(".onion")) {
byte[] onionAddress = BASE32.decode(hostname.substring(0, hostname.length() - 6));
if (onionAddress.length == 10) {
// TORv2
buf.put(ONIONCAT_PREFIX);
buf.put(onionAddress);
} else {
throw new IllegalStateException();
}
} else {
throw new IllegalStateException();
}
}
// And write out the port. Unlike the rest of the protocol, address and port is in big endian byte order.
ByteUtils.writeInt16BE(port, buf);
return buf;
}
/**
* Allocates a byte array and writes this peer address into it, using a given protocol variant. The variant can be
* 1 ({@link AddressV1Message}) or 2 ({@link AddressV2Message}).
*
* @param protocolVariant variant of protocol used
* @return byte array containing the peer address
*/
public byte[] serialize(int protocolVariant) {
return write(ByteBuffer.allocate(getMessageSize(protocolVariant)), protocolVariant).array();
}
/**
* Return the size of the serialized message, using a given protocol variant. The variant can be
* 1 ({@link AddressV1Message}) or 2 ({@link AddressV2Message}).. Note that if the message was deserialized from
* a payload, this size can differ from the size of the original payload.
*
* @param protocolVariant variant of protocol used
* @return size of the serialized message in bytes
*/
public int getMessageSize(int protocolVariant) {
if (protocolVariant < 1 || protocolVariant > 2)
throw new IllegalStateException("invalid protocolVariant: " + protocolVariant);
int size = 0;
size += 4; // time
if (protocolVariant == 2) {
size += VarInt.sizeOf(services.bits());
size += 1; // network id
if (addr != null) {
if (addr instanceof Inet4Address) {
size += VarInt.sizeOf(4) + 4;
} else if (addr instanceof Inet6Address) {
size += VarInt.sizeOf(16) + 16;
} else {
throw new IllegalStateException();
}
} else if (addr == null && hostname != null && hostname.toLowerCase(Locale.ROOT).endsWith(".onion")) {
byte[] onionAddress = BASE32.decode(hostname.substring(0, hostname.length() - 6));
if (onionAddress.length == 10) {
// TORv2
size += VarInt.sizeOf(10) + 10;
} else if (onionAddress.length == 32 + 2 + 1) {
// TORv3
size += VarInt.sizeOf(32) + 32;
} else {
throw new IllegalStateException();
}
} else {
throw new IllegalStateException();
}
} else {
size += Services.BYTES;
size += 16; // ip
}
size += 2; // port
return size;
} }
public static InetAddress getByAddress(byte[] addrBytes) { public static InetAddress getByAddress(byte[] addrBytes) {

View File

@ -19,6 +19,7 @@ package org.bitcoinj.core;
import com.google.common.io.BaseEncoding; import com.google.common.io.BaseEncoding;
import org.bitcoinj.base.internal.ByteUtils; import org.bitcoinj.base.internal.ByteUtils;
import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.params.MainNetParams; import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.TestNet3Params; import org.bitcoinj.params.TestNet3Params;
import org.junit.Test; import org.junit.Test;
@ -28,6 +29,7 @@ import java.math.BigInteger;
import java.net.InetAddress; import java.net.InetAddress;
import java.nio.BufferUnderflowException; import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.time.temporal.ChronoUnit;
import java.util.Arrays; import java.util.Arrays;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
@ -76,7 +78,7 @@ public class BitcoinSerializerTest {
assertEquals(31, addressMessage.getMessageSize()); assertEquals(31, addressMessage.getMessageSize());
addressMessage.addAddress(new PeerAddress(InetAddress.getLocalHost(), MAINNET.getPort(), addressMessage.addAddress(new PeerAddress(InetAddress.getLocalHost(), MAINNET.getPort(),
Services.none(), new DummySerializer(1))); Services.none(), TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS)));
bos = new ByteArrayOutputStream(61); bos = new ByteArrayOutputStream(61);
serializer.serialize(addressMessage, bos); serializer.serialize(addressMessage, bos);
assertEquals(61, addressMessage.getMessageSize()); assertEquals(61, addressMessage.getMessageSize());

View File

@ -44,7 +44,7 @@ public class PeerAddressTest {
public void equalsContract() { public void equalsContract() {
EqualsVerifier.forClass(PeerAddress.class) EqualsVerifier.forClass(PeerAddress.class)
.suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.NONFINAL_FIELDS)
.withIgnoredFields("time", "serializer") .withIgnoredFields("time")
.usingGetClass() .usingGetClass()
.verify(); .verify();
} }
@ -52,10 +52,9 @@ public class PeerAddressTest {
@Test @Test
public void roundtrip_ipv4_addressV2Variant() throws Exception { public void roundtrip_ipv4_addressV2Variant() throws Exception {
Instant time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS); Instant time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS);
MessageSerializer serializer = new DummySerializer(2); PeerAddress pa = new PeerAddress(InetAddress.getByName("1.2.3.4"), 1234, Services.none(), time);
PeerAddress pa = new PeerAddress(InetAddress.getByName("1.2.3.4"), 1234, Services.none(), serializer); byte[] serialized = pa.serialize(2);
byte[] serialized = pa.bitcoinSerialize(); PeerAddress pa2 = PeerAddress.read(ByteBuffer.wrap(serialized), 2);
PeerAddress pa2 = new PeerAddress(ByteBuffer.wrap(serialized), serializer);
assertEquals("1.2.3.4", pa2.getAddr().getHostAddress()); assertEquals("1.2.3.4", pa2.getAddr().getHostAddress());
assertEquals(1234, pa2.getPort()); assertEquals(1234, pa2.getPort());
assertEquals(Services.none(), pa2.getServices()); assertEquals(Services.none(), pa2.getServices());
@ -65,10 +64,9 @@ public class PeerAddressTest {
@Test @Test
public void roundtrip_ipv4_addressVariant() throws Exception { public void roundtrip_ipv4_addressVariant() throws Exception {
Instant time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS); Instant time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS);
MessageSerializer serializer = new DummySerializer(1); PeerAddress pa = new PeerAddress(InetAddress.getByName("1.2.3.4"), 1234, Services.none(), time);
PeerAddress pa = new PeerAddress(InetAddress.getByName("1.2.3.4"), 1234, Services.none(), serializer); byte[] serialized = pa.serialize(1);
byte[] serialized = pa.bitcoinSerialize(); PeerAddress pa2 = PeerAddress.read(ByteBuffer.wrap(serialized), 1);
PeerAddress pa2 = new PeerAddress(ByteBuffer.wrap(serialized), serializer);
assertEquals("1.2.3.4", pa2.getAddr().getHostAddress()); assertEquals("1.2.3.4", pa2.getAddr().getHostAddress());
assertEquals(1234, pa2.getPort()); assertEquals(1234, pa2.getPort());
assertEquals(Services.none(), pa2.getServices()); assertEquals(Services.none(), pa2.getServices());
@ -78,11 +76,10 @@ public class PeerAddressTest {
@Test @Test
public void roundtrip_ipv6_addressV2Variant() throws Exception { public void roundtrip_ipv6_addressV2Variant() throws Exception {
Instant time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS); Instant time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS);
MessageSerializer serializer = new DummySerializer(2);
PeerAddress pa = new PeerAddress(InetAddress.getByName("2001:db8:85a3:0:0:8a2e:370:7334"), 1234, PeerAddress pa = new PeerAddress(InetAddress.getByName("2001:db8:85a3:0:0:8a2e:370:7334"), 1234,
Services.none(), serializer); Services.none(), time);
byte[] serialized = pa.bitcoinSerialize(); byte[] serialized = pa.serialize(2);
PeerAddress pa2 = new PeerAddress(ByteBuffer.wrap(serialized), serializer); PeerAddress pa2 = PeerAddress.read(ByteBuffer.wrap(serialized), 2);
assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", pa2.getAddr().getHostAddress()); assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", pa2.getAddr().getHostAddress());
assertEquals(1234, pa2.getPort()); assertEquals(1234, pa2.getPort());
assertEquals(Services.none(), pa2.getServices()); assertEquals(Services.none(), pa2.getServices());
@ -92,11 +89,10 @@ public class PeerAddressTest {
@Test @Test
public void roundtrip_ipv6_addressVariant() throws Exception { public void roundtrip_ipv6_addressVariant() throws Exception {
Instant time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS); Instant time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS);
MessageSerializer serializer = new DummySerializer(1);
PeerAddress pa = new PeerAddress(InetAddress.getByName("2001:db8:85a3:0:0:8a2e:370:7334"), 1234, PeerAddress pa = new PeerAddress(InetAddress.getByName("2001:db8:85a3:0:0:8a2e:370:7334"), 1234,
Services.none(), serializer); Services.none(), time);
byte[] serialized = pa.bitcoinSerialize(); byte[] serialized = pa.serialize(1);
PeerAddress pa2 = new PeerAddress(ByteBuffer.wrap(serialized), serializer); PeerAddress pa2 = PeerAddress.read(ByteBuffer.wrap(serialized), 1);
assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", pa2.getAddr().getHostAddress()); assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", pa2.getAddr().getHostAddress());
assertEquals(1234, pa2.getPort()); assertEquals(1234, pa2.getPort());
assertEquals(Services.none(), pa2.getServices()); assertEquals(Services.none(), pa2.getServices());
@ -106,8 +102,7 @@ public class PeerAddressTest {
@Test @Test
@Parameters(method = "deserializeToStringValues") @Parameters(method = "deserializeToStringValues")
public void deserializeToString(int version, String expectedToString, String hex) { public void deserializeToString(int version, String expectedToString, String hex) {
MessageSerializer serializer = new DummySerializer(version); PeerAddress pa = PeerAddress.read(ByteBuffer.wrap(ByteUtils.parseHex(hex)), version);
PeerAddress pa = new PeerAddress(ByteBuffer.wrap(ByteUtils.parseHex(hex)), serializer);
assertEquals(expectedToString, pa.toString()); assertEquals(expectedToString, pa.toString());
} }