Support BIP155 addrv2 messages.

This commit is contained in:
Andreas Schildbach 2021-04-20 11:47:57 +02:00
parent 9e9b6b4c74
commit d511effbce
16 changed files with 612 additions and 155 deletions

View file

@ -1,6 +1,5 @@
/*
* Copyright 2011 Google Inc.
* Copyright 2014 Andreas Schildbach
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,74 +18,18 @@ package org.bitcoinj.core;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* <p>Represents an "addr" message on the P2P network, which contains broadcast IP addresses of other peers. This is
* one of the ways peers can find each other without using the DNS or IRC discovery mechanisms. However storing and
* using addr messages is not presently implemented.</p>
*
* <p>Instances of this class are not safe for use by multiple threads.</p>
*/
public class AddressMessage extends Message {
public abstract class AddressMessage extends Message {
private static final long MAX_ADDRESSES = 1000;
private List<PeerAddress> addresses;
protected static final long MAX_ADDRESSES = 1000;
protected List<PeerAddress> addresses;
/**
* Construct a new 'addr' message.
* @param params NetworkParameters object.
* @param offset The location of the first payload byte within the array.
* @param serializer the serializer to use for this block.
* @param length The length of message if known. Usually this is provided when deserializing of the wire
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException
*/
AddressMessage(NetworkParameters params, byte[] payload, int offset, MessageSerializer serializer, int length) throws ProtocolException {
super(params, payload, offset, serializer, length);
}
/**
* Construct a new 'addr' message.
* @param params NetworkParameters object.
* @param serializer the serializer to use for this block.
* @param length The length of message if known. Usually this is provided when deserializing of the wire
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException
*/
AddressMessage(NetworkParameters params, byte[] payload, MessageSerializer serializer, int length) throws ProtocolException {
super(params, payload, 0, serializer, length);
}
AddressMessage(NetworkParameters params, byte[] payload, int offset) throws ProtocolException {
super(params, payload, offset, params.getDefaultSerializer(), UNKNOWN_LENGTH);
}
AddressMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
super(params, payload, 0, params.getDefaultSerializer(), UNKNOWN_LENGTH);
}
@Override
protected void parse() throws ProtocolException {
VarInt numAddressesVarInt = readVarInt();
int numAddresses = numAddressesVarInt.intValue();
// Guard against ultra large messages that will crash us.
if (numAddresses > MAX_ADDRESSES)
throw new ProtocolException("Address message too large.");
addresses = new ArrayList<>((int) numAddresses);
int protocolVersion = serializer.getProtocolVersion();
for (int i = 0; i < numAddresses; i++) {
PeerAddress addr = new PeerAddress(params, payload, cursor, this, serializer);
addresses.add(addr);
cursor += addr.getMessageSize();
}
length = numAddressesVarInt.getSizeInBytes();
// The 4 byte difference is the uint32 timestamp that was introduced in version 31402
length += addresses.size() * (protocolVersion > 31402 ? PeerAddress.MESSAGE_SIZE : PeerAddress.MESSAGE_SIZE - 4);
}
@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
if (addresses == null)
@ -97,35 +40,20 @@ public class AddressMessage extends Message {
}
}
/**
* @return An unmodifiableList view of the backing List of addresses. Addresses contained within the list may be safely modified.
*/
public List<PeerAddress> getAddresses() {
return Collections.unmodifiableList(addresses);
}
public void addAddress(PeerAddress address) {
unCache();
address.setParent(this);
addresses.add(address);
if (length == UNKNOWN_LENGTH)
getMessageSize();
else
length += address.getMessageSize();
}
public abstract void addAddress(PeerAddress address);
public void removeAddress(int index) {
unCache();
PeerAddress address = addresses.remove(index);
address.setParent(null);
if (length == UNKNOWN_LENGTH)
getMessageSize();
else
length -= address.getMessageSize();
length = UNKNOWN_LENGTH;
}
@Override
public String toString() {
return "addr: " + Utils.SPACE_JOINER.join(addresses);
/**
* @return An unmodifiableList view of the backing List of addresses. Addresses contained within the list may be
* safely modified.
*/
public List<PeerAddress> getAddresses() {
return Collections.unmodifiableList(addresses);
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright 2011 Google Inc.
* Copyright 2014 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
import java.util.ArrayList;
/**
* <p>Represents an "addr" message on the P2P network, which contains broadcast IP addresses of other peers. This is
* one of the ways peers can find each other without using the DNS or IRC discovery mechanisms. However storing and
* using addr messages is not presently implemented.</p>
*
* <p>Instances of this class are not safe for use by multiple threads.</p>
*/
public class AddressV1Message extends AddressMessage {
/**
* Construct a new 'addr' message.
* @param params NetworkParameters object.
* @param offset The location of the first payload byte within the array.
* @param serializer the serializer to use for this block.
* @param length The length of message if known. Usually this is provided when deserializing of the wire
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException
*/
AddressV1Message(NetworkParameters params, byte[] payload, int offset, MessageSerializer serializer, int length) throws ProtocolException {
super(params, payload, offset, serializer, length);
}
/**
* Construct a new 'addr' message.
* @param params NetworkParameters object.
* @param serializer the serializer to use for this block.
* @param length The length of message if known. Usually this is provided when deserializing of the wire
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException
*/
AddressV1Message(NetworkParameters params, byte[] payload, MessageSerializer serializer, int length) throws ProtocolException {
super(params, payload, 0, serializer, length);
}
AddressV1Message(NetworkParameters params, byte[] payload, int offset) throws ProtocolException {
super(params, payload, offset, params.getDefaultSerializer(), UNKNOWN_LENGTH);
}
AddressV1Message(NetworkParameters params, byte[] payload) throws ProtocolException {
super(params, payload, 0, params.getDefaultSerializer(), UNKNOWN_LENGTH);
}
@Override
protected void parse() throws ProtocolException {
final VarInt numAddressesVarInt = readVarInt();
int numAddresses = numAddressesVarInt.intValue();
// Guard against ultra large messages that will crash us.
if (numAddresses > MAX_ADDRESSES)
throw new ProtocolException("Address message too large.");
addresses = new ArrayList<>(numAddresses);
MessageSerializer serializer = this.serializer.withProtocolVersion(1);
length = numAddressesVarInt.getSizeInBytes();
for (int i = 0; i < numAddresses; i++) {
PeerAddress addr = new PeerAddress(params, payload, cursor, this, serializer);
addresses.add(addr);
cursor += addr.getMessageSize();
length += addr.getMessageSize();
}
}
public void addAddress(PeerAddress address) {
int protocolVersion = address.serializer.getProtocolVersion();
if (protocolVersion != 1)
throw new IllegalStateException("invalid protocolVersion: " + protocolVersion);
unCache();
address.setParent(this);
addresses.add(address);
length = UNKNOWN_LENGTH;
}
@Override
public String toString() {
return "addr: " + Utils.SPACE_JOINER.join(addresses);
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
import java.util.ArrayList;
/**
* <p>Represents an "addrv2" message on the P2P network, which contains broadcast IP addresses of other peers. This is
* one of the ways peers can find each other without using the DNS or IRC discovery mechanisms. However storing and
* using addrv2 messages is not presently implemented.</p>
*
* <p>See <a href="https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki">BIP155</a> for details.</p>
*
* <p>Instances of this class are not safe for use by multiple threads.</p>
*/
public class AddressV2Message extends AddressMessage {
/**
* Construct a new 'addrv2' message.
* @param params NetworkParameters object.
* @param offset The location of the first payload byte within the array.
* @param serializer the serializer to use for this block.
* @param length The length of message if known. Usually this is provided when deserializing of the wire
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException
*/
AddressV2Message(NetworkParameters params, byte[] payload, int offset, MessageSerializer serializer, int length) throws ProtocolException {
super(params, payload, offset, serializer, length);
}
/**
* Construct a new 'addrv2' message.
* @param params NetworkParameters object.
* @param serializer the serializer to use for this block.
* @param length The length of message if known. Usually this is provided when deserializing of the wire
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException
*/
AddressV2Message(NetworkParameters params, byte[] payload, MessageSerializer serializer, int length) throws ProtocolException {
super(params, payload, 0, serializer, length);
}
AddressV2Message(NetworkParameters params, byte[] payload) throws ProtocolException {
super(params, payload, 0, params.getDefaultSerializer(), UNKNOWN_LENGTH);
}
@Override
protected void parse() throws ProtocolException {
final VarInt numAddressesVarInt = readVarInt();
int numAddresses = numAddressesVarInt.intValue();
// Guard against ultra large messages that will crash us.
if (numAddresses > MAX_ADDRESSES)
throw new ProtocolException("Address message too large.");
addresses = new ArrayList<>(numAddresses);
MessageSerializer serializer = this.serializer.withProtocolVersion(2);
length = numAddressesVarInt.getSizeInBytes();
for (int i = 0; i < numAddresses; i++) {
PeerAddress addr = new PeerAddress(params, payload, cursor, this, serializer);
addresses.add(addr);
cursor += addr.getMessageSize();
length += addr.getMessageSize();
}
}
public void addAddress(PeerAddress address) {
int protocolVersion = address.serializer.getProtocolVersion();
if (protocolVersion != 2)
throw new IllegalStateException("invalid protocolVersion: " + protocolVersion);
unCache();
address.setParent(this);
addresses.add(address);
length = UNKNOWN_LENGTH;
}
@Override
public String toString() {
return "addrv2: " + Utils.SPACE_JOINER.join(addresses);
}
}

View file

@ -59,13 +59,15 @@ public class BitcoinSerializer extends MessageSerializer {
names.put(Block.class, "block");
names.put(GetDataMessage.class, "getdata");
names.put(Transaction.class, "tx");
names.put(AddressMessage.class, "addr");
names.put(AddressV1Message.class, "addr");
names.put(AddressV2Message.class, "addrv2");
names.put(Ping.class, "ping");
names.put(Pong.class, "pong");
names.put(VersionAck.class, "verack");
names.put(GetBlocksMessage.class, "getblocks");
names.put(GetHeadersMessage.class, "getheaders");
names.put(GetAddrMessage.class, "getaddr");
names.put(SendAddrV2Message.class, "sendaddrv2");
names.put(HeadersMessage.class, "headers");
names.put(BloomFilter.class, "filterload");
names.put(FilteredBlock.class, "merkleblock");
@ -231,8 +233,12 @@ public class BitcoinSerializer extends MessageSerializer {
return new GetHeadersMessage(params, payloadBytes);
} else if (command.equals("tx")) {
return makeTransaction(payloadBytes, 0, length, hash);
} else if (command.equals("sendaddrv2")) {
return new SendAddrV2Message(params);
} else if (command.equals("addr")) {
return makeAddressMessage(payloadBytes, length);
return makeAddressV1Message(payloadBytes, length);
} else if (command.equals("addrv2")) {
return makeAddressV2Message(payloadBytes, length);
} else if (command.equals("ping")) {
return new Ping(params, payloadBytes);
} else if (command.equals("pong")) {
@ -272,8 +278,17 @@ public class BitcoinSerializer extends MessageSerializer {
* serialization format support.
*/
@Override
public AddressMessage makeAddressMessage(byte[] payloadBytes, int length) throws ProtocolException {
return new AddressMessage(params, payloadBytes, this, length);
public AddressV1Message makeAddressV1Message(byte[] payloadBytes, int length) throws ProtocolException {
return new AddressV1Message(params, payloadBytes, this, length);
}
/**
* Make an address message from the payload. Extension point for alternative
* serialization format support.
*/
@Override
public AddressV2Message makeAddressV2Message(byte[] payloadBytes, int length) throws ProtocolException {
return new AddressV2Message(params, payloadBytes, this, length);
}
/**

View file

@ -64,7 +64,12 @@ class DummySerializer extends MessageSerializer {
}
@Override
public AddressMessage makeAddressMessage(byte[] payloadBytes, int length) throws UnsupportedOperationException {
public AddressV1Message makeAddressV1Message(byte[] payloadBytes, int length) throws UnsupportedOperationException {
throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE);
}
@Override
public AddressV2Message makeAddressV2Message(byte[] payloadBytes, int length) throws UnsupportedOperationException {
throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE);
}

View file

@ -65,7 +65,13 @@ public abstract class MessageSerializer {
* Make an address message from the payload. Extension point for alternative
* serialization format support.
*/
public abstract AddressMessage makeAddressMessage(byte[] payloadBytes, int length) throws ProtocolException, UnsupportedOperationException;
public abstract AddressV1Message makeAddressV1Message(byte[] payloadBytes, int length) throws ProtocolException, UnsupportedOperationException;
/**
* Make an address message from the payload. Extension point for alternative
* serialization format support.
*/
public abstract AddressV2Message makeAddressV2Message(byte[] payloadBytes, int length) throws ProtocolException, UnsupportedOperationException;
/**
* Make a block from the payload, using an offset of zero and the payload

View file

@ -442,7 +442,7 @@ public class Peer extends PeerSocketHandler {
}
// No further communication is possible until version handshake is complete.
if (!(m instanceof VersionMessage || m instanceof VersionAck
if (!(m instanceof VersionMessage || m instanceof VersionAck || m instanceof SendAddrV2Message
|| (versionHandshakeFuture.isDone() && !versionHandshakeFuture.isCancelled())))
throw new ProtocolException(
"Received " + m.getClass().getSimpleName() + " before version handshake is complete.");
@ -544,6 +544,8 @@ public class Peer extends PeerSocketHandler {
// In this case, it's a protocol violation.
throw new ProtocolException("Peer reports invalid best height: " + peerVersionMessage.bestHeight);
// Now it's our turn ...
// Send a sendaddrv2 message, indicating that we prefer to receive addrv2 messages.
sendMessage(new SendAddrV2Message(params));
// Send an ACK message stating we accept the peers protocol version.
sendMessage(new VersionAck());
if (log.isDebugEnabled())

View file

@ -21,6 +21,8 @@ package org.bitcoinj.core;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
@ -31,13 +33,13 @@ import static com.google.common.base.Preconditions.checkNotNull;
/**
* <p>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>
*
* <p>This class abuses the protocol version contained in its serializer. It can only contain 0 (format within
* {@link VersionMessage}), 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 ChildMessage {
static final int MESSAGE_SIZE = 30;
private InetAddress addr;
private String hostname; // Used for .onion addresses
private int port;
@ -65,14 +67,21 @@ public class PeerAddress extends ChildMessage {
this.port = port;
setSerializer(serializer);
this.services = services;
length = isSerializeTime() ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
this.time = Utils.currentTimeSeconds();
}
/**
* Constructs a peer address from the given IP address, port and services. Version number is default for the given parameters.
*/
public PeerAddress(NetworkParameters params, InetAddress addr, int port, BigInteger services) {
this(params, addr, port, services, params.getDefaultSerializer().withProtocolVersion(0));
}
/**
* Constructs a peer address from the given IP address and port. Version number is default for the given parameters.
*/
public PeerAddress(NetworkParameters params, InetAddress addr, int port) {
this(params, addr, port, BigInteger.ZERO, params.getDefaultSerializer());
this(params, addr, port, BigInteger.ZERO);
}
/**
@ -99,6 +108,7 @@ public class PeerAddress extends ChildMessage {
this.hostname = hostname;
this.port = port;
this.services = BigInteger.ZERO;
this.time = Utils.currentTimeSeconds();
}
public static PeerAddress localhost(NetworkParameters params) {
@ -107,54 +117,110 @@ public class PeerAddress extends ChildMessage {
@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
if (isSerializeTime()) {
//TODO this appears to be dynamic because the client only ever sends out it's own address
//so assumes itself to be up. For a fuller implementation this needs to be dynamic only if
//the address refers to this client.
int secs = (int) (Utils.currentTimeSeconds());
Utils.uint32ToByteStreamLE(secs, stream);
int protocolVersion = serializer.getProtocolVersion();
if (protocolVersion < 0 || protocolVersion > 2)
throw new IllegalStateException("invalid protocolVersion: " + protocolVersion);
if (protocolVersion >= 1) {
Utils.uint32ToByteStreamLE(time, stream);
}
Utils.uint64ToByteStreamLE(services, stream); // nServices.
// 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();
if (ipBytes.length == 4) {
byte[] v6addr = new byte[16];
System.arraycopy(ipBytes, 0, v6addr, 12, 4);
v6addr[10] = (byte) 0xFF;
v6addr[11] = (byte) 0xFF;
ipBytes = v6addr;
if (protocolVersion == 2) {
stream.write(new VarInt(services.longValue()).encode());
if (addr != null) {
if (addr instanceof Inet4Address) {
stream.write(0x01);
stream.write(new VarInt(4).encode());
stream.write(addr.getAddress());
} else if (addr instanceof Inet6Address) {
stream.write(0x02);
stream.write(new VarInt(16).encode());
stream.write(addr.getAddress());
} else {
throw new IllegalStateException();
}
} else {
throw new IllegalStateException();
}
} else {
Utils.uint64ToByteStreamLE(services, stream); // nServices.
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();
if (ipBytes.length == 4) {
byte[] v6addr = new byte[16];
System.arraycopy(ipBytes, 0, v6addr, 12, 4);
v6addr[10] = (byte) 0xFF;
v6addr[11] = (byte) 0xFF;
ipBytes = v6addr;
}
stream.write(ipBytes);
} else {
throw new IllegalStateException();
}
}
stream.write(ipBytes);
// And write out the port. Unlike the rest of the protocol, address and port is in big endian byte order.
Utils.uint16ToByteStreamBE(port, stream);
}
private boolean isSerializeTime() {
return serializer.getProtocolVersion() >= 31402 && !(parent instanceof VersionMessage);
}
@Override
protected void parse() throws ProtocolException {
// Format of a serialized address:
// uint32 timestamp
// uint64 services (flags determining what the node can do)
// 16 bytes ip address
// 2 bytes port num
if (isSerializeTime())
int protocolVersion = serializer.getProtocolVersion();
if (protocolVersion < 0 || protocolVersion > 2)
throw new IllegalStateException("invalid protocolVersion: " + protocolVersion);
length = 0;
if (protocolVersion >= 1) {
time = readUint32();
else
length += 4;
} else {
time = -1;
services = readUint64();
byte[] addrBytes = readBytes(16);
try {
addr = InetAddress.getByAddress(addrBytes);
} catch (UnknownHostException e) {
throw new RuntimeException(e); // Cannot happen.
}
if (protocolVersion == 2) {
VarInt servicesVarInt = readVarInt();
length += servicesVarInt.getSizeInBytes();
services = BigInteger.valueOf(servicesVarInt.longValue());
int networkId = readByte();
length += 1;
byte[] addrBytes = readByteArray();
int addrLen = addrBytes.length;
length += VarInt.sizeOf(addrLen) + addrLen;
if (networkId == 0x01) {
// IPv4
if (addrLen != 4)
throw new ProtocolException("invalid length of IPv4 address: " + addrLen);
addr = getByAddress(addrBytes);
hostname = null;
} else if (networkId == 0x02) {
// IPv6
if (addrLen != 16)
throw new ProtocolException("invalid length of IPv6 address: " + addrLen);
addr = getByAddress(addrBytes);
hostname = null;
} else {
// ignore unknown network IDs
addr = null;
hostname = null;
}
} else {
services = readUint64();
length += 8;
byte[] addrBytes = readBytes(16);
length += 16;
addr = getByAddress(addrBytes);
hostname = null;
}
port = Utils.readUint16BE(payload, cursor);
cursor += 2;
// The 4 byte difference is the uint32 timestamp that was introduced in version 31402
length = isSerializeTime() ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
length += 2;
}
private static InetAddress getByAddress(byte[] addrBytes) {
try {
return InetAddress.getByAddress(addrBytes);
} catch (UnknownHostException e) {
throw new RuntimeException(e); // Cannot happen.
}
}
public String getHostname() {

View file

@ -0,0 +1,31 @@
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
/**
* <p>Represents the {@code sendaddrv2} P2P protocol message, which indicates that a node can understand and prefers
* to receive {@code addrv2] messages instead of {@code addr} messages.</p>
*
* <p>See <a href="https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki">BIP155</a> for details.</p>
*
* <p>Instances of this class are not safe for use by multiple threads.</p>
*/
public class SendAddrV2Message extends EmptyMessage {
public SendAddrV2Message(NetworkParameters params) {
super(params);
}
}

View file

@ -112,6 +112,7 @@ public class VersionMessage extends Message {
// Note that the Bitcoin Core doesn't do anything with these, and finding out your own external IP address
// is kind of tricky anyway, so we just put nonsense here for now.
InetAddress localhost = InetAddresses.forString("127.0.0.1");
MessageSerializer serializer = this.serializer.withProtocolVersion(0);
receivingAddr = new PeerAddress(params, localhost, params.getPort(), BigInteger.ZERO, serializer);
receivingAddr.setParent(this);
fromAddr = new PeerAddress(params, localhost, params.getPort(), BigInteger.ZERO, serializer);
@ -119,9 +120,6 @@ public class VersionMessage extends Message {
subVer = LIBRARY_SUBVER;
bestHeight = newBestHeight;
relayTxesBeforeFilter = true;
length = 4 + 8 + 8 + receivingAddr.getMessageSize() + fromAddr.getMessageSize() + 8
+ VarInt.sizeOf(subVer.length()) + subVer.length() + 4 + 1;
}
@Override

View file

@ -805,7 +805,7 @@ public class WalletProtobufSerializer {
}
int port = proto.getPort();
BigInteger services = BigInteger.valueOf(proto.getServices());
PeerAddress address = new PeerAddress(params, ip, port, services, params.getDefaultSerializer());
PeerAddress address = new PeerAddress(params, ip, port, services);
confidence.markBroadcastBy(address);
}
if (confidenceProto.hasLastBroadcastedAt())

View file

@ -0,0 +1,84 @@
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
import org.bitcoinj.params.UnitTestParams;
import org.junit.Test;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.List;
import static org.bitcoinj.core.Utils.HEX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class AddressV1MessageTest {
private static final NetworkParameters UNITTEST = UnitTestParams.get();
// mostly copied from src/test/netbase_tests.cpp#stream_addrv1_hex
private static final String MESSAGE_HEX =
"03" // number of entries
+ "61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
+ "0000000000000000" // service flags, NODE_NONE
+ "00000000000000000000ffff00000001" // address, fixed 16 bytes (IPv4 embedded in IPv6)
+ "0000" // port
+ "79627683" // time, Tue Nov 22 11:22:33 UTC 2039
+ "0100000000000000" // service flags, NODE_NETWORK
+ "00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
+ "00f1" // port
+ "ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
+ "4804000000000000" // service flags, NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED
+ "00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
+ "f1f2"; // port
@Test
public void roundtrip() {
AddressMessage message = new AddressV1Message(UNITTEST, HEX.decode(MESSAGE_HEX));
List<PeerAddress> addresses = message.getAddresses();
assertEquals(3, addresses.size());
PeerAddress a0 = addresses.get(0);
assertEquals("2009-01-09T02:54:25Z", Utils.dateTimeFormat(a0.getTime() * 1000));
assertEquals(0, a0.getServices().intValue());
assertTrue(a0.getAddr() instanceof Inet4Address);
assertEquals("0.0.0.1", a0.getAddr().getHostAddress());
assertNull(a0.getHostname());
assertEquals(0, a0.getPort());
PeerAddress a1 = addresses.get(1);
assertEquals("2039-11-22T11:22:33Z", Utils.dateTimeFormat(a1.getTime() * 1000));
assertEquals(VersionMessage.NODE_NETWORK, a1.getServices().intValue());
assertTrue(a1.getAddr() instanceof Inet6Address);
assertEquals("0:0:0:0:0:0:0:1", a1.getAddr().getHostAddress());
assertNull(a1.getHostname());
assertEquals(0xf1, a1.getPort());
PeerAddress a2 = addresses.get(2);
assertEquals("2106-02-07T06:28:15Z", Utils.dateTimeFormat(a2.getTime() * 1000));
assertEquals(VersionMessage.NODE_WITNESS | 1 << 6 /* NODE_COMPACT_FILTERS */
| VersionMessage.NODE_NETWORK_LIMITED, a2.getServices().intValue());
assertTrue(a2.getAddr() instanceof Inet6Address);
assertEquals("0:0:0:0:0:0:0:1", a2.getAddr().getHostAddress());
assertNull(a2.getHostname());
assertEquals(0xf1f2, a2.getPort());
assertEquals(MESSAGE_HEX, HEX.encode(message.bitcoinSerialize()));
}
}

View file

@ -0,0 +1,90 @@
/*
* Copyright by the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
import org.bitcoinj.params.UnitTestParams;
import org.junit.Test;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.List;
import static org.bitcoinj.core.Utils.HEX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class AddressV2MessageTest {
private static final NetworkParameters UNITTEST = UnitTestParams.get();
// mostly copied from src/test/netbase_tests.cpp#stream_addrv2_hex
private static final String MESSAGE_HEX =
"03" // number of entries
+ "61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
+ "00" // service flags, COMPACTSIZE(NODE_NONE)
+ "01" // network id, IPv4
+ "04" // address length, COMPACTSIZE(4)
+ "00000001" // address
+ "0000" // port
+ "79627683" // time, Tue Nov 22 11:22:33 UTC 2039
+ "01" // service flags, COMPACTSIZE(NODE_NETWORK)
+ "02" // network id, IPv6
+ "10" // address length, COMPACTSIZE(16)
+ "00000000000000000000000000000001" // address
+ "00f1" // port
+ "ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
+ "fd4804" // service flags, COMPACTSIZE(NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED)
+ "02" // network id, IPv6
+ "10" // address length, COMPACTSIZE(16)
+ "00000000000000000000000000000001" // address
+ "f1f2"; // port
@Test
public void roundtrip() {
AddressMessage message = new AddressV2Message(UNITTEST, HEX.decode(MESSAGE_HEX));
List<PeerAddress> addresses = message.getAddresses();
assertEquals(3, addresses.size());
PeerAddress a0 = addresses.get(0);
assertEquals("2009-01-09T02:54:25Z", Utils.dateTimeFormat(a0.getTime() * 1000));
assertEquals(0, a0.getServices().intValue());
assertTrue(a0.getAddr() instanceof Inet4Address);
assertEquals("0.0.0.1", a0.getAddr().getHostAddress());
assertNull(a0.getHostname());
assertEquals(0, a0.getPort());
PeerAddress a1 = addresses.get(1);
assertEquals("2039-11-22T11:22:33Z", Utils.dateTimeFormat(a1.getTime() * 1000));
assertEquals(VersionMessage.NODE_NETWORK, a1.getServices().intValue());
assertTrue(a1.getAddr() instanceof Inet6Address);
assertEquals("0:0:0:0:0:0:0:1", a1.getAddr().getHostAddress());
assertNull(a1.getHostname());
assertEquals(0xf1, a1.getPort());
PeerAddress a2 = addresses.get(2);
assertEquals("2106-02-07T06:28:15Z", Utils.dateTimeFormat(a2.getTime() * 1000));
assertEquals(VersionMessage.NODE_WITNESS | 1 << 6 /* NODE_COMPACT_FILTERS */
| VersionMessage.NODE_NETWORK_LIMITED, a2.getServices().intValue());
assertTrue(a2.getAddr() instanceof Inet6Address);
assertEquals("0:0:0:0:0:0:0:1", a2.getAddr().getHostAddress());
assertNull(a2.getHostname());
assertEquals(0xf1f2, a2.getPort());
assertEquals(MESSAGE_HEX, HEX.encode(message.bitcoinSerialize()));
}
}

View file

@ -22,6 +22,7 @@ import org.bitcoinj.params.TestNet3Params;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.net.InetAddress;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
@ -66,11 +67,17 @@ public class BitcoinSerializerTest {
assertEquals("10.0.0.1", peerAddress.getAddr().getHostAddress());
ByteArrayOutputStream bos = new ByteArrayOutputStream(ADDRESS_MESSAGE_BYTES.length);
serializer.serialize(addressMessage, bos);
assertEquals(31, addressMessage.getMessageSize());
addressMessage.addAddress(new PeerAddress(MAINNET, InetAddress.getLocalHost()));
addressMessage.addAddress(new PeerAddress(MAINNET, InetAddress.getLocalHost(), MAINNET.getPort(),
BigInteger.ZERO, serializer.withProtocolVersion(1)));
bos = new ByteArrayOutputStream(61);
serializer.serialize(addressMessage, bos);
assertEquals(61, addressMessage.getMessageSize());
addressMessage.removeAddress(0);
bos = new ByteArrayOutputStream(31);
serializer.serialize(addressMessage, bos);
assertEquals(31, addressMessage.getMessageSize());
//this wont be true due to dynamic timestamps.

View file

@ -31,11 +31,12 @@ public class PeerAddressTest {
private static final NetworkParameters MAINNET = MainNetParams.get();
@Test
public void parse_ancientProtocolVersion() throws Exception {
public void parse_versionVariant() throws Exception {
MessageSerializer serializer = MAINNET.getDefaultSerializer().withProtocolVersion(0);
// copied from https://en.bitcoin.it/wiki/Protocol_documentation#Network_address
String hex = "010000000000000000000000000000000000ffff0a000001208d";
PeerAddress pa = new PeerAddress(MAINNET, HEX.decode(hex), 0, null,
MAINNET.getDefaultSerializer().withProtocolVersion(0));
serializer);
assertEquals(26, pa.length);
assertEquals(VersionMessage.NODE_NETWORK, pa.getServices().longValue());
assertEquals("10.0.0.1", pa.getAddr().getHostAddress());
@ -43,20 +44,22 @@ public class PeerAddressTest {
}
@Test
public void bitcoinSerialize_ancientProtocolVersion() throws Exception {
public void bitcoinSerialize_versionVariant() throws Exception {
MessageSerializer serializer = MAINNET.getDefaultSerializer().withProtocolVersion(0);
PeerAddress pa = new PeerAddress(MAINNET, InetAddress.getByName(null), 8333, BigInteger.ZERO,
MAINNET.getDefaultSerializer().withProtocolVersion(0));
assertEquals(26, pa.length);
serializer);
assertEquals("000000000000000000000000000000000000ffff7f000001208d", Utils.HEX.encode(pa.bitcoinSerialize()));
assertEquals(26, pa.length);
}
@Test
public void roundtrip_ipv4_currentProtocolVersion() throws Exception {
public void roundtrip_ipv4_addressV2Variant() throws Exception {
long time = Utils.currentTimeSeconds();
MessageSerializer serializer = MAINNET.getDefaultSerializer().withProtocolVersion(2);
PeerAddress pa = new PeerAddress(MAINNET, InetAddress.getByName("1.2.3.4"), 1234, BigInteger.ZERO,
MAINNET.getDefaultSerializer());
serializer);
byte[] serialized = pa.bitcoinSerialize();
PeerAddress pa2 = new PeerAddress(MAINNET, serialized, 0, null, MAINNET.getDefaultSerializer());
PeerAddress pa2 = new PeerAddress(MAINNET, serialized, 0, null, serializer);
assertEquals("1.2.3.4", pa2.getAddr().getHostAddress());
assertEquals(1234, pa2.getPort());
assertEquals(BigInteger.ZERO, pa2.getServices());
@ -64,12 +67,26 @@ public class PeerAddressTest {
}
@Test
public void roundtrip_ipv4_ancientProtocolVersion() throws Exception {
public void roundtrip_ipv4_addressVariant() throws Exception {
long time = Utils.currentTimeSeconds();
MessageSerializer serializer = MAINNET.getDefaultSerializer().withProtocolVersion(1);
PeerAddress pa = new PeerAddress(MAINNET, InetAddress.getByName("1.2.3.4"), 1234, BigInteger.ZERO,
MAINNET.getDefaultSerializer().withProtocolVersion(0));
serializer);
byte[] serialized = pa.bitcoinSerialize();
PeerAddress pa2 = new PeerAddress(MAINNET, serialized, 0, null,
MAINNET.getDefaultSerializer().withProtocolVersion(0));
PeerAddress pa2 = new PeerAddress(MAINNET, serialized, 0, null, serializer);
assertEquals("1.2.3.4", pa2.getAddr().getHostAddress());
assertEquals(1234, pa2.getPort());
assertEquals(BigInteger.ZERO, pa2.getServices());
assertTrue(pa2.getTime() >= time && pa2.getTime() < time + 5); // potentially racy
}
@Test
public void roundtrip_ipv4_versionVariant() throws Exception {
MessageSerializer serializer = MAINNET.getDefaultSerializer().withProtocolVersion(0);
PeerAddress pa = new PeerAddress(MAINNET, InetAddress.getByName("1.2.3.4"), 1234, BigInteger.ZERO,
serializer);
byte[] serialized = pa.bitcoinSerialize();
PeerAddress pa2 = new PeerAddress(MAINNET, serialized, 0, null, serializer);
assertEquals("1.2.3.4", pa2.getAddr().getHostAddress());
assertEquals(1234, pa2.getPort());
assertEquals(BigInteger.ZERO, pa2.getServices());
@ -77,12 +94,13 @@ public class PeerAddressTest {
}
@Test
public void roundtrip_ipv6_currentProtocolVersion() throws Exception {
public void roundtrip_ipv6_addressV2Variant() throws Exception {
long time = Utils.currentTimeSeconds();
MessageSerializer serializer = MAINNET.getDefaultSerializer().withProtocolVersion(2);
PeerAddress pa = new PeerAddress(MAINNET, InetAddress.getByName("2001:db8:85a3:0:0:8a2e:370:7334"), 1234,
BigInteger.ZERO, MAINNET.getDefaultSerializer());
BigInteger.ZERO, serializer);
byte[] serialized = pa.bitcoinSerialize();
PeerAddress pa2 = new PeerAddress(MAINNET, serialized, 0, null, MAINNET.getDefaultSerializer());
PeerAddress pa2 = new PeerAddress(MAINNET, serialized, 0, null, serializer);
assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", pa2.getAddr().getHostAddress());
assertEquals(1234, pa2.getPort());
assertEquals(BigInteger.ZERO, pa2.getServices());
@ -90,12 +108,27 @@ public class PeerAddressTest {
}
@Test
public void roundtrip_ipv6_ancientProtocolVersion() throws Exception {
public void roundtrip_ipv6_addressVariant() throws Exception {
long time = Utils.currentTimeSeconds();
MessageSerializer serializer = MAINNET.getDefaultSerializer().withProtocolVersion(1);
PeerAddress pa = new PeerAddress(MAINNET, InetAddress.getByName("2001:db8:85a3:0:0:8a2e:370:7334"), 1234,
BigInteger.ZERO, MAINNET.getDefaultSerializer().withProtocolVersion(0));
BigInteger.ZERO, serializer);
byte[] serialized = pa.bitcoinSerialize();
PeerAddress pa2 = new PeerAddress(MAINNET, serialized, 0, null, serializer);
assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", pa2.getAddr().getHostAddress());
assertEquals(1234, pa2.getPort());
assertEquals(BigInteger.ZERO, pa2.getServices());
assertTrue(pa2.getTime() >= time && pa2.getTime() < time + 5); // potentially racy
}
@Test
public void roundtrip_ipv6_versionVariant() throws Exception {
MessageSerializer serializer = MAINNET.getDefaultSerializer().withProtocolVersion(0);
PeerAddress pa = new PeerAddress(MAINNET, InetAddress.getByName("2001:db8:85a3:0:0:8a2e:370:7334"), 1234,
BigInteger.ZERO, serializer);
byte[] serialized = pa.bitcoinSerialize();
PeerAddress pa2 = new PeerAddress(MAINNET, serialized, 0, null,
MAINNET.getDefaultSerializer().withProtocolVersion(0));
serializer);
assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", pa2.getAddr().getHostAddress());
assertEquals(1234, pa2.getPort());
assertEquals(BigInteger.ZERO, pa2.getServices());

View file

@ -158,6 +158,7 @@ public class TestWithPeerGroup extends TestWithNetworkConnections {
private void stepThroughInit(VersionMessage versionMessage, InboundMessageQueuer writeTarget) throws InterruptedException {
checkState(writeTarget.nextMessageBlocking() instanceof VersionMessage);
checkState(writeTarget.nextMessageBlocking() instanceof SendAddrV2Message);
checkState(writeTarget.nextMessageBlocking() instanceof VersionAck);
if (versionMessage.isBloomFilteringSupported()) {
checkState(writeTarget.nextMessageBlocking() instanceof BloomFilter);