diff --git a/core/src/main/java/org/bitcoinj/core/GetDataMessage.java b/core/src/main/java/org/bitcoinj/core/GetDataMessage.java index fa6c765b6..7a2400da4 100644 --- a/core/src/main/java/org/bitcoinj/core/GetDataMessage.java +++ b/core/src/main/java/org/bitcoinj/core/GetDataMessage.java @@ -21,13 +21,14 @@ import org.bitcoinj.base.Sha256Hash; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.util.Collections; import java.util.List; /** *

Represents the "getdata" P2P network message, which requests the contents of blocks or transactions given their * hashes.

- * - *

Instances of this class are not safe for use by multiple threads.

+ * + *

Instances of this class -- that use deprecated methods -- are not safe for use by multiple threads.

*/ public class GetDataMessage extends ListMessage { /** @@ -41,23 +42,43 @@ public class GetDataMessage extends ListMessage { return new GetDataMessage(readItems(payload)); } + @Deprecated public GetDataMessage() { super(); } - private GetDataMessage(List items) { + GetDataMessage(List items) { super(items); } + public static GetDataMessage ofBlock(Sha256Hash blockHash, boolean includeWitness) { + return new GetDataMessage(Collections.singletonList( + new InventoryItem(includeWitness + ? InventoryItem.Type.WITNESS_BLOCK + : InventoryItem.Type.BLOCK, + blockHash))); + } + + public static GetDataMessage ofTransaction(Sha256Hash txId, boolean includeWitness) { + return new GetDataMessage(Collections.singletonList( + new InventoryItem(includeWitness + ? InventoryItem.Type.WITNESS_TRANSACTION + : InventoryItem.Type.TRANSACTION, + txId))); + } + + @Deprecated public void addTransaction(Sha256Hash hash, boolean includeWitness) { addItem(new InventoryItem( includeWitness ? InventoryItem.Type.WITNESS_TRANSACTION : InventoryItem.Type.TRANSACTION, hash)); } + @Deprecated public void addBlock(Sha256Hash hash, boolean includeWitness) { addItem(new InventoryItem(includeWitness ? InventoryItem.Type.WITNESS_BLOCK : InventoryItem.Type.BLOCK, hash)); } + @Deprecated public void addFilteredBlock(Sha256Hash hash) { addItem(new InventoryItem(InventoryItem.Type.FILTERED_BLOCK, hash)); } diff --git a/core/src/main/java/org/bitcoinj/core/InventoryItem.java b/core/src/main/java/org/bitcoinj/core/InventoryItem.java index df7f99b84..96fba29f5 100644 --- a/core/src/main/java/org/bitcoinj/core/InventoryItem.java +++ b/core/src/main/java/org/bitcoinj/core/InventoryItem.java @@ -57,6 +57,16 @@ public class InventoryItem { this.hash = hash; } + public InventoryItem(Block block) { + this.type = Type.BLOCK; + this.hash = block.getHash(); + } + + public InventoryItem(Transaction tx) { + this.type = Type.TRANSACTION; + this.hash = tx.getTxId(); + } + @Override public String toString() { return type + ": " + hash; diff --git a/core/src/main/java/org/bitcoinj/core/InventoryMessage.java b/core/src/main/java/org/bitcoinj/core/InventoryMessage.java index c48e96cee..c3caf0931 100644 --- a/core/src/main/java/org/bitcoinj/core/InventoryMessage.java +++ b/core/src/main/java/org/bitcoinj/core/InventoryMessage.java @@ -18,7 +18,10 @@ package org.bitcoinj.core; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import static org.bitcoinj.base.internal.Preconditions.checkArgument; @@ -27,8 +30,8 @@ import static org.bitcoinj.base.internal.Preconditions.checkArgument; * a bandwidth optimization - on receiving some data, a (fully validating) peer sends every connected peer an inv * containing the hash of what it saw. It'll only transmit the full thing if a peer asks for it with a * {@link GetDataMessage}.

- * - *

Instances of this class are not safe for use by multiple threads.

+ * + *

Instances of this class -- that use deprecated methods -- are not safe for use by multiple threads.

*/ public class InventoryMessage extends ListMessage { @@ -46,7 +49,8 @@ public class InventoryMessage extends ListMessage { return new InventoryMessage(readItems(payload)); } - public InventoryMessage() { + @Deprecated + protected InventoryMessage() { super(); } @@ -54,20 +58,50 @@ public class InventoryMessage extends ListMessage { super(items); } + public static InventoryMessage ofBlocks(List blocks) { + checkArgument(!blocks.isEmpty()); + return new InventoryMessage(blocks.stream() + .map(InventoryItem::new) + .collect(Collectors.toList())); + } + + public static InventoryMessage ofBlocks(Block ...blocks) { + return ofBlocks(Arrays.asList(blocks)); + } + + public static InventoryMessage ofTransactions(List transactions) { + checkArgument(!transactions.isEmpty()); + return new InventoryMessage(transactions.stream() + .map(InventoryItem::new) + .collect(Collectors.toList())); + } + + public static InventoryMessage ofTransactions(Transaction ...transactions) { + return ofTransactions(Arrays.asList(transactions)); + } + + /** + * @deprecated Use a constructor or factoring + */ + @Deprecated public void addBlock(Block block) { - addItem(new InventoryItem(InventoryItem.Type.BLOCK, block.getHash())); + addItem(new InventoryItem(block)); } + /** + * @deprecated Use a constructor or factoring + */ + @Deprecated public void addTransaction(Transaction tx) { - addItem(new InventoryItem(InventoryItem.Type.TRANSACTION, tx.getTxId())); + addItem(new InventoryItem(tx)); } - /** Creates a new inv message for the given transactions. */ + /** + * Creates a new inv message for the given transactions. + * @deprecated Use {@link #ofTransactions(Transaction...)} + */ + @Deprecated public static InventoryMessage with(Transaction... txns) { - checkArgument(txns.length > 0); - InventoryMessage result = new InventoryMessage(); - for (Transaction tx : txns) - result.addTransaction(tx); - return result; + return ofTransactions(txns); } } diff --git a/core/src/main/java/org/bitcoinj/core/ListMessage.java b/core/src/main/java/org/bitcoinj/core/ListMessage.java index 3fb2c5c8c..d0233dc68 100644 --- a/core/src/main/java/org/bitcoinj/core/ListMessage.java +++ b/core/src/main/java/org/bitcoinj/core/ListMessage.java @@ -35,12 +35,12 @@ import static org.bitcoinj.base.internal.Preconditions.check; /** *

Abstract superclass of classes with list based payload, ie InventoryMessage and GetDataMessage.

* - *

Instances of this class are not safe for use by multiple threads.

+ *

Instances of this class -- that use deprecated methods -- are not safe for use by multiple threads.

*/ public abstract class ListMessage extends BaseMessage { // For some reason the compiler complains if this is inside InventoryItem - protected List items; + protected final List items; public static final int MAX_INVENTORY_ITEMS = 50000; @@ -68,23 +68,26 @@ public abstract class ListMessage extends BaseMessage { return items; } + @Deprecated public ListMessage() { super(); - items = new ArrayList<>(); + items = new ArrayList<>(); // TODO: unmodifiable empty list } protected ListMessage(List items) { - this.items = items; + this.items = items; // TODO: unmodifiable defensive copy } public List getItems() { return Collections.unmodifiableList(items); } + @Deprecated public void addItem(InventoryItem item) { items.add(item); } + @Deprecated public void removeItem(int index) { items.remove(index); } diff --git a/core/src/main/java/org/bitcoinj/core/NotFoundMessage.java b/core/src/main/java/org/bitcoinj/core/NotFoundMessage.java index 90e1e7b7c..fdbfa2d82 100644 --- a/core/src/main/java/org/bitcoinj/core/NotFoundMessage.java +++ b/core/src/main/java/org/bitcoinj/core/NotFoundMessage.java @@ -24,8 +24,8 @@ import java.util.List; /** *

Sent by a peer when a getdata request doesn't find the requested data in the mempool. It has the same format * as an inventory message and lists the hashes of the missing items.

- * - *

Instances of this class are not safe for use by multiple threads.

+ * + *

Instances of this class -- that use deprecated methods -- are not safe for use by multiple threads.

*/ public class NotFoundMessage extends InventoryMessage { public static int MIN_PROTOCOL_VERSION = 70001; @@ -41,6 +41,7 @@ public class NotFoundMessage extends InventoryMessage { return new NotFoundMessage(readItems(payload)); } + @Deprecated public NotFoundMessage() { super(); } diff --git a/core/src/main/java/org/bitcoinj/core/Peer.java b/core/src/main/java/org/bitcoinj/core/Peer.java index 4122c424c..29214a140 100644 --- a/core/src/main/java/org/bitcoinj/core/Peer.java +++ b/core/src/main/java/org/bitcoinj/core/Peer.java @@ -910,10 +910,13 @@ public class Peer extends PeerSocketHandler { * @return A GetDataMessage that will query those IDs */ private GetDataMessage buildMultiTransactionDataMessage(Set txIds) { - GetDataMessage getdata = new GetDataMessage(); - txIds.forEach(txId -> - getdata.addTransaction(txId, vPeerVersionMessage.services().has(Services.NODE_WITNESS))); - return getdata; + InventoryItem.Type itemType = vPeerVersionMessage.services().has(Services.NODE_WITNESS) + ? InventoryItem.Type.WITNESS_TRANSACTION + : InventoryItem.Type.TRANSACTION; + List items = txIds.stream() + .map(hash -> new InventoryItem(itemType, hash)) + .collect(Collectors.toList()); + return new GetDataMessage(items); } /** @@ -1129,34 +1132,35 @@ public class Peer extends PeerSocketHandler { List items = inv.getItems(); // Separate out the blocks and transactions, we'll handle them differently - List transactions = new LinkedList<>(); - List blocks = new LinkedList<>(); + List transactions = new LinkedList<>(); + List blocks = new LinkedList<>(); for (InventoryItem item : items) { switch (item.type) { case TRANSACTION: - transactions.add(item); + transactions.add(item.hash); break; case BLOCK: - blocks.add(item); + blocks.add(item.hash); break; default: throw new IllegalStateException("Not implemented: " + item.type); } } + if (log.isDebugEnabled()) log.debug("{}: processing 'inv' with {} items: {} blocks, {} txns", this, items.size(), blocks.size(), transactions.size()); final boolean downloadData = this.vDownloadData; - if (transactions.size() == 0 && blocks.size() == 1) { + if (transactions.isEmpty() && blocks.size() == 1) { // Single block announcement. If we're downloading the chain this is just a tickle to make us continue // (the block chain download protocol is very implicit and not well thought out). If we're not downloading // the chain then this probably means a new block was solved and the peer believes it connects to the best // chain, so count it. This way getBestChainHeight() can be accurate. if (downloadData && blockChain != null) { - if (!blockChain.isOrphan(blocks.get(0).hash)) { + if (!blockChain.isOrphan(blocks.get(0))) { blocksAnnounced.incrementAndGet(); } } else { @@ -1164,11 +1168,14 @@ public class Peer extends PeerSocketHandler { } } - GetDataMessage getdata = new GetDataMessage(); + InventoryItem.Type txItemType = vPeerVersionMessage.services().has(Services.NODE_WITNESS) + ? InventoryItem.Type.WITNESS_TRANSACTION + : InventoryItem.Type.TRANSACTION; + List getDataItems = new ArrayList<>(); - Iterator it = transactions.iterator(); + Iterator it = transactions.iterator(); while (it.hasNext()) { - InventoryItem item = it.next(); + Sha256Hash item = it.next(); // Only download the transaction if we are the first peer that saw it be advertised. Other peers will also // see it be advertised in inv packets asynchronously, they co-ordinate via the memory pool. We could // potentially download transactions faster by always asking every peer for a tx when advertised, as remote @@ -1177,7 +1184,7 @@ public class Peer extends PeerSocketHandler { // sending us the transaction: currently we'll never try to re-fetch after a timeout. // // The line below can trigger confidence listeners. - TransactionConfidence conf = context.getConfidenceTable().seen(item.hash, this.getAddress()); + TransactionConfidence conf = context.getConfidenceTable().seen(item, this.getAddress()); if (conf.numBroadcastPeers() > 1) { // Some other peer already announced this so don't download. it.remove(); @@ -1186,8 +1193,8 @@ public class Peer extends PeerSocketHandler { it.remove(); } else { if (log.isDebugEnabled()) - log.debug("{}: getdata on tx {}", getAddress(), item.hash); - getdata.addTransaction(item.hash, vPeerVersionMessage.services().has(Services.NODE_WITNESS)); + log.debug("{}: getdata on tx {}", getAddress(), item); + getDataItems.add(new InventoryItem(txItemType, item)); if (pendingTxDownloads.size() > PENDING_TX_DOWNLOADS_LIMIT) { log.info("{}: Too many pending transactions, disconnecting", this); close(); @@ -1208,11 +1215,11 @@ public class Peer extends PeerSocketHandler { // Ideally, we'd only ask for the data here if we actually needed it. However that can imply a lot of // disk IO to figure out what we've got. Normally peers will not send us inv for things we already have // so we just re-request it here, and if we get duplicates the block chain / wallet will filter them out. - for (InventoryItem item : blocks) { - if (blockChain.isOrphan(item.hash) && downloadBlockBodies) { + for (Sha256Hash item : blocks) { + if (blockChain.isOrphan(item) && downloadBlockBodies) { // If an orphan was re-advertised, ask for more blocks unless we are not currently downloading // full block data because we have a getheaders outstanding. - final Block orphanRoot = Objects.requireNonNull(blockChain.getOrphanRoot(item.hash)); + final Block orphanRoot = Objects.requireNonNull(blockChain.getOrphanRoot(item)); blockChainDownloadLocked(orphanRoot.getHash()); } else { // Don't re-request blocks we already requested. Normally this should not happen. However there is @@ -1227,14 +1234,14 @@ public class Peer extends PeerSocketHandler { // part of chain download with newly announced blocks, so it should always be taken care of by // the duplicate check in blockChainDownloadLocked(). But Bitcoin Core may change in future so // it's better to be safe here. - if (!pendingBlockDownloads.contains(item.hash)) { + if (!pendingBlockDownloads.contains(item)) { if (isBloomFilteringSupported(vPeerVersionMessage) && useFilteredBlocks) { - getdata.addFilteredBlock(item.hash); + getDataItems.add(new InventoryItem(InventoryItem.Type.FILTERED_BLOCK, item)); pingAfterGetData = true; } else { - getdata.addBlock(item.hash, vPeerVersionMessage.services().has(Services.NODE_WITNESS)); + getDataItems.add(new InventoryItem(InventoryItem.Type.BLOCK, item)); } - pendingBlockDownloads.add(item.hash); + pendingBlockDownloads.add(item); } } } @@ -1248,8 +1255,9 @@ public class Peer extends PeerSocketHandler { lock.unlock(); } - if (!getdata.getItems().isEmpty()) { + if (!getDataItems.isEmpty()) { // This will cause us to receive a bunch of block or tx messages. + GetDataMessage getdata = new GetDataMessage(getDataItems); sendMessage(getdata); } @@ -1269,8 +1277,7 @@ public class Peer extends PeerSocketHandler { public ListenableCompletableFuture getBlock(Sha256Hash blockHash) { // This does not need to be locked. log.info("Request to fetch block {}", blockHash); - GetDataMessage getdata = new GetDataMessage(); - getdata.addBlock(blockHash, true); + GetDataMessage getdata = GetDataMessage.ofBlock(blockHash, true); return ListenableCompletableFuture.of(sendSingleGetData(getdata)); } @@ -1287,8 +1294,7 @@ public class Peer extends PeerSocketHandler { // This does not need to be locked. // TODO: Unit test this method. log.info("Request to fetch peer mempool tx {}", hash); - GetDataMessage getdata = new GetDataMessage(); - getdata.addTransaction(hash, vPeerVersionMessage.services().has(Services.NODE_WITNESS)); + GetDataMessage getdata = GetDataMessage.ofTransaction(hash, vPeerVersionMessage.services().has(Services.NODE_WITNESS)); return ListenableCompletableFuture.of(sendSingleGetData(getdata)); } @@ -1760,9 +1766,10 @@ public class Peer extends PeerSocketHandler { sendPing().thenRunAsync(() -> { lock.lock(); Objects.requireNonNull(awaitingFreshFilter); - GetDataMessage getdata = new GetDataMessage(); - for (Sha256Hash hash : awaitingFreshFilter) - getdata.addFilteredBlock(hash); + List items = awaitingFreshFilter.stream() + .map(hash -> new InventoryItem(InventoryItem.Type.FILTERED_BLOCK, hash)) + .collect(Collectors.toList()); + GetDataMessage getdata = new GetDataMessage(items); awaitingFreshFilter = null; lock.unlock(); diff --git a/core/src/test/java/org/bitcoinj/core/BitcoindComparisonTool.java b/core/src/test/java/org/bitcoinj/core/BitcoindComparisonTool.java index a34b33173..7387b8866 100644 --- a/core/src/test/java/org/bitcoinj/core/BitcoindComparisonTool.java +++ b/core/src/test/java/org/bitcoinj/core/BitcoindComparisonTool.java @@ -188,9 +188,7 @@ public class BitcoindComparisonTool { if (!found) sendHeaders = headers; bitcoind.sendMessage(new HeadersMessage(sendHeaders)); - InventoryMessage i = new InventoryMessage(); - for (Block b : sendHeaders) - i.addBlock(b); + InventoryMessage i = InventoryMessage.ofBlocks(sendHeaders); bitcoind.sendMessage(i); } catch (Exception e) { throw new RuntimeException(e); @@ -271,8 +269,7 @@ public class BitcoindComparisonTool { boolean shouldntRequest = blocksRequested.contains(nextBlock.getHash()); if (shouldntRequest) blocksRequested.remove(nextBlock.getHash()); - InventoryMessage message = new InventoryMessage(); - message.addBlock(nextBlock); + InventoryMessage message = InventoryMessage.ofBlocks(nextBlock); bitcoind.sendMessage(message); log.info("Sent inv with block " + nextBlock.getHashAsString()); if (blocksPendingSend.contains(nextBlock.getHash())) { diff --git a/integration-test/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTest.java b/integration-test/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTest.java index 0e1539ced..32b9cfdfd 100644 --- a/integration-test/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTest.java +++ b/integration-test/src/test/java/org/bitcoinj/core/FilteredBlockAndPartialMerkleTreeTest.java @@ -188,8 +188,7 @@ public class FilteredBlockAndPartialMerkleTreeTest extends TestWithPeerGroup { InboundMessageQueuer p1 = connectPeer(1); assertEquals(1, peerGroup.numConnectedPeers()); // Send an inv for block 100001 - InventoryMessage inv = new InventoryMessage(); - inv.addBlock(block); + InventoryMessage inv = InventoryMessage.ofBlocks(block); inbound(p1, inv); // Check that we properly requested the correct FilteredBlock diff --git a/integration-test/src/test/java/org/bitcoinj/core/PeerGroupTest.java b/integration-test/src/test/java/org/bitcoinj/core/PeerGroupTest.java index 3d1dde93e..73955606b 100644 --- a/integration-test/src/test/java/org/bitcoinj/core/PeerGroupTest.java +++ b/integration-test/src/test/java/org/bitcoinj/core/PeerGroupTest.java @@ -247,8 +247,7 @@ public class PeerGroupTest extends TestWithPeerGroup { Coin value = COIN; Transaction t1 = FakeTxBuilder.createFakeTx(UNITTEST.network(), value, address); - InventoryMessage inv = new InventoryMessage(); - inv.addTransaction(t1); + InventoryMessage inv = InventoryMessage.ofTransactions(t1); // Note: we start with p2 here to verify that transactions are downloaded from whichever peer announces first // which does not have to be the same as the download peer (which is really the "block download peer"). @@ -287,8 +286,7 @@ public class PeerGroupTest extends TestWithPeerGroup { Coin value = COIN; Transaction t1 = FakeTxBuilder.createFakeTx(UNITTEST.network(), value, address2); - InventoryMessage inv = new InventoryMessage(); - inv.addTransaction(t1); + InventoryMessage inv = InventoryMessage.ofTransactions(t1); inbound(p1, inv); assertTrue(outbound(p1) instanceof GetDataMessage); @@ -319,8 +317,7 @@ public class PeerGroupTest extends TestWithPeerGroup { Block b3 = FakeTxBuilder.makeSolvedTestBlock(b2); // Peer 1 and 2 receives an inv advertising a newly solved block. - InventoryMessage inv = new InventoryMessage(); - inv.addBlock(b3); + InventoryMessage inv = InventoryMessage.ofBlocks(b3); // Only peer 1 tries to download it. inbound(p1, inv); pingAndWait(p1); @@ -358,11 +355,8 @@ public class PeerGroupTest extends TestWithPeerGroup { GetBlocksMessage getblocks = (GetBlocksMessage) outbound(p1); assertEquals(Sha256Hash.ZERO_HASH, getblocks.getStopHash()); // We give back an inv with some blocks in it. - InventoryMessage inv = new InventoryMessage(); - inv.addBlock(b1); - inv.addBlock(b2); - inv.addBlock(b3); - + InventoryMessage inv = InventoryMessage.ofBlocks(b1, b2, b3); + inbound(p1, inv); assertTrue(outbound(p1) instanceof GetDataMessage); // We hand back the first block. @@ -388,8 +382,7 @@ public class PeerGroupTest extends TestWithPeerGroup { InboundMessageQueuer p3 = connectPeer(3); Transaction tx = FakeTxBuilder.createFakeTx(UNITTEST.network(), valueOf(20, 0), address); - InventoryMessage inv = new InventoryMessage(); - inv.addTransaction(tx); + InventoryMessage inv = InventoryMessage.ofTransactions(tx); assertEquals(0, tx.getConfidence().numBroadcastPeers()); assertFalse(tx.getConfidence().lastBroadcastTime().isPresent()); diff --git a/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java b/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java index dff03946b..bdb5bccb1 100644 --- a/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java +++ b/integration-test/src/test/java/org/bitcoinj/core/PeerTest.java @@ -30,6 +30,7 @@ import org.bitcoinj.testing.InboundMessageQueuer; import org.bitcoinj.testing.TestWithNetworkConnections; import org.bitcoinj.utils.Threading; import org.bitcoinj.wallet.Wallet; +import org.checkerframework.checker.units.qual.A; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -46,8 +47,10 @@ import java.net.SocketException; import java.nio.channels.CancelledKeyException; import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -140,9 +143,7 @@ public class PeerTest extends TestWithNetworkConnections { assertEquals(blockStore.getChainHead().getHeader().getHash(), getblocks.getLocator().get(0)); assertEquals(Sha256Hash.ZERO_HASH, getblocks.getStopHash()); // Remote peer sends us an inv with some blocks. - InventoryMessage inv = new InventoryMessage(); - inv.addBlock(b2); - inv.addBlock(b3); + InventoryMessage inv = InventoryMessage.ofBlocks(b2, b3); // We do a getdata on them. inbound(writeTarget, inv); GetDataMessage getdata = (GetDataMessage)outbound(writeTarget); @@ -154,8 +155,7 @@ public class PeerTest extends TestWithNetworkConnections { inbound(writeTarget, b2); inbound(writeTarget, b3); - inv = new InventoryMessage(); - inv.addBlock(b5); + inv = InventoryMessage.ofBlocks(b5); // We request the head block. inbound(writeTarget, inv); getdata = (GetDataMessage)outbound(writeTarget); @@ -173,8 +173,7 @@ public class PeerTest extends TestWithNetworkConnections { // because we walk backwards down the orphan chain and then discover we already asked for those blocks, so // nothing is done. Block b6 = makeSolvedTestBlock(b5); - inv = new InventoryMessage(); - inv.addBlock(b6); + inv = InventoryMessage.ofBlocks(b6); inbound(writeTarget, inv); getdata = (GetDataMessage)outbound(writeTarget); assertEquals(1, getdata.getItems().size()); @@ -182,9 +181,7 @@ public class PeerTest extends TestWithNetworkConnections { inbound(writeTarget, b6); assertNull(outbound(writeTarget)); // Nothing is sent at this point. // We're still waiting for the response to the getblocks (b3,b5) sent above. - inv = new InventoryMessage(); - inv.addBlock(b4); - inv.addBlock(b5); + inv = InventoryMessage.ofBlocks(b4, b5); inbound(writeTarget, inv); getdata = (GetDataMessage)outbound(writeTarget); assertEquals(1, getdata.getItems().size()); @@ -208,9 +205,7 @@ public class PeerTest extends TestWithNetworkConnections { Block b2 = makeSolvedTestBlock(b1); Block b3 = makeSolvedTestBlock(b2); inbound(writeTarget, b3); - InventoryMessage inv = new InventoryMessage(); - InventoryItem item = new InventoryItem(InventoryItem.Type.BLOCK, b3.getHash()); - inv.addItem(item); + InventoryMessage inv = InventoryMessage.ofBlocks(b3); inbound(writeTarget, inv); GetBlocksMessage getblocks = (GetBlocksMessage)outbound(writeTarget); @@ -237,9 +232,7 @@ public class PeerTest extends TestWithNetworkConnections { Block b2 = makeSolvedTestBlock(b1); // Receive an inv. - InventoryMessage inv = new InventoryMessage(); - InventoryItem item = new InventoryItem(InventoryItem.Type.BLOCK, b2.getHash()); - inv.addItem(item); + InventoryMessage inv = InventoryMessage.ofBlocks(b2); inbound(writeTarget, inv); // Peer does nothing with it. @@ -254,9 +247,7 @@ public class PeerTest extends TestWithNetworkConnections { // Make a transaction and tell the peer we have it. Coin value = COIN; Transaction tx = createFakeTx(TESTNET.network(), value, address); - InventoryMessage inv = new InventoryMessage(); - InventoryItem item = new InventoryItem(InventoryItem.Type.TRANSACTION, tx.getTxId()); - inv.addItem(item); + InventoryMessage inv = InventoryMessage.ofTransactions(tx); inbound(writeTarget, inv); // Peer hasn't seen it before, so will ask for it. GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); @@ -287,9 +278,7 @@ public class PeerTest extends TestWithNetworkConnections { // Make a tx and advertise it to one of the peers. Coin value = COIN; Transaction tx = createFakeTx(TESTNET.network(), value, this.address); - InventoryMessage inv = new InventoryMessage(); - InventoryItem item = new InventoryItem(InventoryItem.Type.TRANSACTION, tx.getTxId()); - inv.addItem(item); + InventoryMessage inv = InventoryMessage.ofTransactions(tx); inbound(writeTarget, inv); @@ -313,9 +302,7 @@ public class PeerTest extends TestWithNetworkConnections { blockChain.add(b1); final Block b2 = makeSolvedTestBlock(b1); // Receive notification of a new block. - final InventoryMessage inv = new InventoryMessage(); - InventoryItem item = new InventoryItem(InventoryItem.Type.BLOCK, b2.getHash()); - inv.addItem(item); + final InventoryMessage inv = InventoryMessage.ofBlocks(b2); final AtomicInteger newBlockMessagesReceived = new AtomicInteger(0); @@ -489,8 +476,7 @@ public class PeerTest extends TestWithNetworkConnections { assertEquals(expectedLocator, getblocks.getLocator()); assertEquals(Sha256Hash.ZERO_HASH, getblocks.getStopHash()); // We're supposed to get an inv here. - InventoryMessage inv = new InventoryMessage(); - inv.addItem(new InventoryItem(InventoryItem.Type.BLOCK, b3.getHash())); + InventoryMessage inv = InventoryMessage.ofBlocks(b3); inbound(writeTarget, inv); GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); assertEquals(b3.getHash(), getdata.getItems().get(0).hash); @@ -583,8 +569,7 @@ public class PeerTest extends TestWithNetworkConnections { t4 = roundTripTransaction(t4); // Announce the first one. Wait for it to be downloaded. - InventoryMessage inv = new InventoryMessage(); - inv.addTransaction(t1); + InventoryMessage inv = InventoryMessage.ofTransactions(t1); inbound(writeTarget, inv); GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); Threading.waitForUserCode(); @@ -605,17 +590,17 @@ public class PeerTest extends TestWithNetworkConnections { // Deliver the requested transactions. inbound(writeTarget, t2); inbound(writeTarget, t3); - NotFoundMessage notFound = new NotFoundMessage(); - notFound.addItem(new InventoryItem(InventoryItem.Type.TRANSACTION, t7hash)); - notFound.addItem(new InventoryItem(InventoryItem.Type.TRANSACTION, t8hash)); + List notFoundList = new ArrayList<>(); + notFoundList.add(new InventoryItem(InventoryItem.Type.TRANSACTION, t7hash)); + notFoundList.add(new InventoryItem(InventoryItem.Type.TRANSACTION, t8hash)); + NotFoundMessage notFound = new NotFoundMessage(notFoundList); inbound(writeTarget, notFound); assertFalse(futures.isDone()); // It will recursively ask for the dependencies of t2: t5 and t4, but not t3 because it already found t4. getdata = (GetDataMessage) outbound(writeTarget); assertEquals(getdata.getItems().get(0).hash, t2.getInput(0).getOutpoint().hash()); // t5 isn't found and t4 is. - notFound = new NotFoundMessage(); - notFound.addItem(new InventoryItem(InventoryItem.Type.TRANSACTION, t5hash)); + notFound = new NotFoundMessage(Collections.singletonList(new InventoryItem(InventoryItem.Type.TRANSACTION, t5hash))); inbound(writeTarget, notFound); assertFalse(futures.isDone()); // Request t4 ... @@ -625,8 +610,7 @@ public class PeerTest extends TestWithNetworkConnections { // Continue to explore the t4 branch and ask for t6, which is in the chain. getdata = (GetDataMessage) outbound(writeTarget); assertEquals(t6hash, getdata.getItems().get(0).hash); - notFound = new NotFoundMessage(); - notFound.addItem(new InventoryItem(InventoryItem.Type.TRANSACTION, t6hash)); + notFound = new NotFoundMessage(Collections.singletonList(new InventoryItem(InventoryItem.Type.TRANSACTION, t6hash))); inbound(writeTarget, notFound); pingAndWait(writeTarget); // That's it, we explored the entire tree. @@ -661,8 +645,7 @@ public class PeerTest extends TestWithNetworkConnections { t1 = roundTripTransaction(t1); // Announce the first one. Wait for it to be downloaded. - InventoryMessage inv = new InventoryMessage(); - inv.addTransaction(t1); + InventoryMessage inv = InventoryMessage.ofTransactions(t1); inbound(writeTarget, inv); GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); Threading.waitForUserCode(); @@ -758,8 +741,7 @@ public class PeerTest extends TestWithNetworkConnections { t1.addInput(t2.getOutput(0)); t1.addOutput(COIN, key); // Make it relevant. // Announce t1. - InventoryMessage inv = new InventoryMessage(); - inv.addTransaction(t1); + InventoryMessage inv = InventoryMessage.ofTransactions(t1); inbound(writeTarget, inv); // Send it. GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); @@ -775,8 +757,7 @@ public class PeerTest extends TestWithNetworkConnections { getdata = (GetDataMessage) outbound(writeTarget); assertEquals(t3, getdata.getItems().get(0).hash); // Can't find it: bottom of tree. - NotFoundMessage notFound = new NotFoundMessage(); - notFound.addItem(new InventoryItem(InventoryItem.Type.TRANSACTION, t3)); + NotFoundMessage notFound = new NotFoundMessage(Collections.singletonList(new InventoryItem(InventoryItem.Type.TRANSACTION, t3))); inbound(writeTarget, notFound); pingAndWait(writeTarget); Threading.waitForUserCode(); @@ -850,13 +831,14 @@ public class PeerTest extends TestWithNetworkConnections { MessageSerializer serializer = TESTNET.getDefaultSerializer(); // Now write some bogus truncated message. ByteArrayOutputStream out = new ByteArrayOutputStream(); - serializer.serialize("inv", new InventoryMessage() { + List items = new ArrayList<>(); + items.add(new InventoryItem(InventoryItem.Type.TRANSACTION, Sha256Hash.of(new byte[] { 1 }))); + items.add(new InventoryItem(InventoryItem.Type.TRANSACTION, Sha256Hash.of(new byte[] { 2 }))); + items.add(new InventoryItem(InventoryItem.Type.TRANSACTION, Sha256Hash.of(new byte[] { 3 }))); + serializer.serialize("inv", new InventoryMessage(items) { @Override public void bitcoinSerializeToStream(OutputStream stream) throws IOException { // Add some hashes. - addItem(new InventoryItem(InventoryItem.Type.TRANSACTION, Sha256Hash.of(new byte[] { 1 }))); - addItem(new InventoryItem(InventoryItem.Type.TRANSACTION, Sha256Hash.of(new byte[] { 2 }))); - addItem(new InventoryItem(InventoryItem.Type.TRANSACTION, Sha256Hash.of(new byte[] { 3 }))); // Write out a copy that's truncated in the middle. ByteArrayOutputStream bos = new ByteArrayOutputStream(); diff --git a/integration-test/src/test/java/org/bitcoinj/core/TransactionBroadcastTest.java b/integration-test/src/test/java/org/bitcoinj/core/TransactionBroadcastTest.java index 0ab93eaba..157bfaec8 100644 --- a/integration-test/src/test/java/org/bitcoinj/core/TransactionBroadcastTest.java +++ b/integration-test/src/test/java/org/bitcoinj/core/TransactionBroadcastTest.java @@ -110,7 +110,7 @@ public class TransactionBroadcastTest extends TestWithPeerGroup { Threading.waitForUserCode(); assertFalse(future.isDone()); assertEquals(0.0, lastProgress.get(), 0.0); - inbound(channels[1], InventoryMessage.with(tx)); + inbound(channels[1], InventoryMessage.ofTransactions(tx)); future.get(); Threading.waitForUserCode(); assertEquals(1.0, lastProgress.get(), 0.0); @@ -127,7 +127,7 @@ public class TransactionBroadcastTest extends TestWithPeerGroup { Transaction tx = FakeTxBuilder.createFakeTx(TESTNET.network(), CENT, address); tx.getConfidence().setSource(TransactionConfidence.Source.SELF); TransactionBroadcast broadcast = peerGroup.broadcastTransaction(tx); - inbound(channels[1], InventoryMessage.with(tx)); + inbound(channels[1], InventoryMessage.ofTransactions(tx)); pingAndWait(channels[1]); final AtomicDouble p = new AtomicDouble(); broadcast.setProgressCallback(p::set, Threading.SAME_THREAD); @@ -234,8 +234,7 @@ public class TransactionBroadcastTest extends TestWithPeerGroup { // 49 BTC in change. assertEquals(valueOf(49, 0), t1.getValueSentToMe(wallet)); // The future won't complete until it's heard back from the network on p2. - InventoryMessage inv = new InventoryMessage(); - inv.addTransaction(t1); + InventoryMessage inv = InventoryMessage.ofTransactions(t1); inbound(p2, inv); pingAndWait(p2); Threading.waitForUserCode();