Skip to content

Commit

Permalink
Support BIP155 addrv2 messages.
Browse files Browse the repository at this point in the history
 Conflicts:
	core/src/main/java/org/bitcoinj/core/PeerAddress.java
	core/src/main/java/org/bitcoinj/wallet/WalletProtobufSerializer.java
  • Loading branch information
Andreas Schildbach authored and ripcurlx committed Aug 23, 2021
1 parent e3cb622 commit c810538
Show file tree
Hide file tree
Showing 17 changed files with 636 additions and 195 deletions.
96 changes: 12 additions & 84 deletions core/src/main/java/org/bitcoinj/core/AddressMessage.java
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -19,74 +18,18 @@

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)
Expand All @@ -97,35 +40,20 @@ protected void bitcoinSerializeToStream(OutputStream stream) throws IOException
}
}

/**
* @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);
}
}
97 changes: 97 additions & 0 deletions core/src/main/java/org/bitcoinj/core/AddressV1Message.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
94 changes: 94 additions & 0 deletions core/src/main/java/org/bitcoinj/core/AddressV2Message.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
23 changes: 19 additions & 4 deletions core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -231,8 +233,12 @@ private Message makeMessage(String command, int length, byte[] payloadBytes, byt
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")) {
Expand Down Expand Up @@ -272,8 +278,17 @@ public NetworkParameters getParameters() {
* 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);
}

/**
Expand Down
7 changes: 6 additions & 1 deletion core/src/main/java/org/bitcoinj/core/DummySerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ public boolean isParseRetainMode() {
}

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

Expand Down
Loading

0 comments on commit c810538

Please sign in to comment.