@Test public void getLargeBlock() throws Exception { connect(); Block b1 = createFakeBlock(blockStore).block; blockChain.add(b1); Block b2 = makeSolvedTestBlock(b1); Transaction t = new Transaction(unitTestParams); t.addInput(b1.getTransactions().get(0).getOutput(0)); t.addOutput( new TransactionOutput( unitTestParams, t, BigInteger.ZERO, new byte[Block.MAX_BLOCK_SIZE - 1000])); b2.addTransaction(t); // Request the block. Future<Block> resultFuture = peer.getBlock(b2.getHash()); assertFalse(resultFuture.isDone()); // Peer asks for it. GetDataMessage message = (GetDataMessage) outbound(writeTarget); assertEquals(message.getItems().get(0).hash, b2.getHash()); assertFalse(resultFuture.isDone()); // Peer receives it. inbound(writeTarget, b2); Block b = resultFuture.get(); assertEquals(b, b2); }
// Check that it starts downloading the block chain correctly on request. @Test public void startBlockChainDownload() throws Exception { Block b1 = createFakeBlock(blockStore).block; blockChain.add(b1); Block b2 = makeSolvedTestBlock(b1); blockChain.add(b2); connect(); fail.set(true); peer.addEventListener( new AbstractPeerEventListener() { @Override public void onChainDownloadStarted(Peer p, int blocksLeft) { if (p == peer && blocksLeft == 108) fail.set(false); } }, Threading.SAME_THREAD); peer.startBlockChainDownload(); List<Sha256Hash> expectedLocator = new ArrayList<Sha256Hash>(); expectedLocator.add(b2.getHash()); expectedLocator.add(b1.getHash()); expectedLocator.add(unitTestParams.getGenesisBlock().getHash()); GetBlocksMessage message = (GetBlocksMessage) outbound(writeTarget); assertEquals(message.getLocator(), expectedLocator); assertEquals(Sha256Hash.ZERO_HASH, message.getStopHash()); }
@Test public void testLastBlockSeenHash() throws Exception { // Test the lastBlockSeenHash field works. // LastBlockSeenHash should be empty if never set. Wallet wallet = new Wallet(params); Protos.Wallet walletProto = new WalletProtobufSerializer().walletToProto(wallet); ByteString lastSeenBlockHash = walletProto.getLastSeenBlockHash(); assertTrue(lastSeenBlockHash.isEmpty()); // Create a block. Block block = params.getDefaultSerializer().makeBlock(BlockTest.blockBytes); Sha256Hash blockHash = block.getHash(); wallet.setLastBlockSeenHash(blockHash); wallet.setLastBlockSeenHeight(1); // Roundtrip the wallet and check it has stored the blockHash. Wallet wallet1 = roundTrip(wallet); assertEquals(blockHash, wallet1.getLastBlockSeenHash()); assertEquals(1, wallet1.getLastBlockSeenHeight()); // Test the Satoshi genesis block (hash of all zeroes) is roundtripped ok. Block genesisBlock = MainNetParams.get().getGenesisBlock(); wallet.setLastBlockSeenHash(genesisBlock.getHash()); Wallet wallet2 = roundTrip(wallet); assertEquals(genesisBlock.getHash(), wallet2.getLastBlockSeenHash()); }
// Check that inventory message containing blocks we want is processed correctly. @Test public void newBlock() throws Exception { Block b1 = createFakeBlock(blockStore).block; blockChain.add(b1); final Block b2 = makeSolvedTestBlock(b1); // Receive notification of a new block. final InventoryMessage inv = new InventoryMessage(unitTestParams); InventoryItem item = new InventoryItem(InventoryItem.Type.Block, b2.getHash()); inv.addItem(item); final AtomicInteger newBlockMessagesReceived = new AtomicInteger(0); connect(); // Round-trip a ping so that we never see the response verack if we attach too quick pingAndWait(writeTarget); peer.addEventListener( new AbstractPeerEventListener() { @Override public synchronized Message onPreMessageReceived(Peer p, Message m) { if (p != peer) fail.set(true); if (m instanceof Pong) return m; int newValue = newBlockMessagesReceived.incrementAndGet(); if (newValue == 1 && !inv.equals(m)) fail.set(true); else if (newValue == 2 && !b2.equals(m)) fail.set(true); else if (newValue > 3) fail.set(true); return m; } @Override public synchronized void onBlocksDownloaded(Peer p, Block block, int blocksLeft) { int newValue = newBlockMessagesReceived.incrementAndGet(); if (newValue != 3 || p != peer || !block.equals(b2) || blocksLeft != OTHER_PEER_CHAIN_HEIGHT - 2) fail.set(true); } }, Threading.SAME_THREAD); long height = peer.getBestHeight(); inbound(writeTarget, inv); pingAndWait(writeTarget); assertEquals(height + 1, peer.getBestHeight()); // Response to the getdata message. inbound(writeTarget, b2); pingAndWait(writeTarget); Threading.waitForUserCode(); pingAndWait(writeTarget); assertEquals(3, newBlockMessagesReceived.get()); GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); List<InventoryItem> items = getdata.getItems(); assertEquals(1, items.size()); assertEquals(b2.getHash(), items.get(0).hash); assertEquals(InventoryItem.Type.Block, items.get(0).type); }
@Test public void fastCatchup() throws Exception { connect(); // Check that blocks before the fast catchup point are retrieved using getheaders, and after // using getblocks. // This test is INCOMPLETE because it does not check we handle >2000 blocks correctly. Block b1 = createFakeBlock(blockStore).block; blockChain.add(b1); Utils.rollMockClock(60 * 10); // 10 minutes later. Block b2 = makeSolvedTestBlock(b1); Utils.rollMockClock(60 * 10); // 10 minutes later. Block b3 = makeSolvedTestBlock(b2); Utils.rollMockClock(60 * 10); Block b4 = makeSolvedTestBlock(b3); // Request headers until the last 2 blocks. peer.setDownloadParameters((Utils.now().getTime() / 1000) - (600 * 2) + 1, false); peer.startBlockChainDownload(); GetHeadersMessage getheaders = (GetHeadersMessage) outbound(writeTarget); List<Sha256Hash> expectedLocator = new ArrayList<Sha256Hash>(); expectedLocator.add(b1.getHash()); expectedLocator.add(unitTestParams.getGenesisBlock().getHash()); assertEquals(getheaders.getLocator(), expectedLocator); assertEquals(getheaders.getStopHash(), Sha256Hash.ZERO_HASH); // Now send all the headers. HeadersMessage headers = new HeadersMessage( unitTestParams, b2.cloneAsHeader(), b3.cloneAsHeader(), b4.cloneAsHeader()); // We expect to be asked for b3 and b4 again, but this time, with a body. expectedLocator.clear(); expectedLocator.add(b2.getHash()); expectedLocator.add(b1.getHash()); expectedLocator.add(unitTestParams.getGenesisBlock().getHash()); inbound(writeTarget, headers); GetBlocksMessage getblocks = (GetBlocksMessage) outbound(writeTarget); assertEquals(expectedLocator, getblocks.getLocator()); assertEquals(Sha256Hash.ZERO_HASH, getblocks.getStopHash()); // We're supposed to get an inv here. InventoryMessage inv = new InventoryMessage(unitTestParams); inv.addItem(new InventoryItem(InventoryItem.Type.Block, b3.getHash())); inbound(writeTarget, inv); GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); assertEquals(b3.getHash(), getdata.getItems().get(0).hash); // All done. inbound(writeTarget, b3); pingAndWait(writeTarget); closePeer(peer); }
@Test public void getBlock() throws Exception { connect(); Block b1 = createFakeBlock(blockStore).block; blockChain.add(b1); Block b2 = makeSolvedTestBlock(b1); Block b3 = makeSolvedTestBlock(b2); // Request the block. Future<Block> resultFuture = peer.getBlock(b3.getHash()); assertFalse(resultFuture.isDone()); // Peer asks for it. GetDataMessage message = (GetDataMessage) outbound(writeTarget); assertEquals(message.getItems().get(0).hash, b3.getHash()); assertFalse(resultFuture.isDone()); // Peer receives it. inbound(writeTarget, b3); Block b = resultFuture.get(); assertEquals(b, b3); }
// Check that an inventory tickle is processed correctly when downloading missing blocks is // active. @Test public void invTickle() throws Exception { connect(); Block b1 = createFakeBlock(blockStore).block; blockChain.add(b1); // Make a missing block. Block b2 = makeSolvedTestBlock(b1); Block b3 = makeSolvedTestBlock(b2); inbound(writeTarget, b3); InventoryMessage inv = new InventoryMessage(unitTestParams); InventoryItem item = new InventoryItem(InventoryItem.Type.Block, b3.getHash()); inv.addItem(item); inbound(writeTarget, inv); GetBlocksMessage getblocks = (GetBlocksMessage) outbound(writeTarget); List<Sha256Hash> expectedLocator = new ArrayList<Sha256Hash>(); expectedLocator.add(b1.getHash()); expectedLocator.add(unitTestParams.getGenesisBlock().getHash()); assertEquals(getblocks.getLocator(), expectedLocator); assertEquals(getblocks.getStopHash(), b3.getHash()); assertNull(outbound(writeTarget)); }
// Check that an inv to a peer that is not set to download missing blocks does nothing. @Test public void invNoDownload() throws Exception { // Don't download missing blocks. peer.setDownloadData(false); connect(); // Make a missing block that we receive. Block b1 = createFakeBlock(blockStore).block; blockChain.add(b1); Block b2 = makeSolvedTestBlock(b1); // Receive an inv. InventoryMessage inv = new InventoryMessage(unitTestParams); InventoryItem item = new InventoryItem(InventoryItem.Type.Block, b2.getHash()); inv.addItem(item); inbound(writeTarget, inv); // Peer does nothing with it. assertNull(outbound(writeTarget)); }
@Test public void testFinalizedBlocks() throws Exception { final int UNDOABLE_BLOCKS_STORED = 10; store = createStore(params, UNDOABLE_BLOCKS_STORED); chain = new FullPrunedBlockChain(params, store); // Check that we aren't accidentally leaving any references // to the full StoredUndoableBlock's lying around (ie memory leaks) ECKey outKey = new ECKey(); int height = 1; // Build some blocks on genesis block to create a spendable output Block rollingBlock = params .getGenesisBlock() .createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++); chain.add(rollingBlock); TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, rollingBlock.getTransactions().get(0).getHash()); byte[] spendableOutputScriptPubKey = rollingBlock.getTransactions().get(0).getOutputs().get(0).getScriptBytes(); for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { rollingBlock = rollingBlock.createNextBlockWithCoinbase( Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++); chain.add(rollingBlock); } WeakReference<UTXO> out = new WeakReference<UTXO>( store.getTransactionOutput(spendableOutput.getHash(), spendableOutput.getIndex())); rollingBlock = rollingBlock.createNextBlock(null); Transaction t = new Transaction(params); // Entirely invalid scriptPubKey t.addOutput(new TransactionOutput(params, t, FIFTY_COINS, new byte[] {})); t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey); rollingBlock.addTransaction(t); rollingBlock.solve(); chain.add(rollingBlock); WeakReference<StoredUndoableBlock> undoBlock = new WeakReference<StoredUndoableBlock>(store.getUndoBlock(rollingBlock.getHash())); StoredUndoableBlock storedUndoableBlock = undoBlock.get(); assertNotNull(storedUndoableBlock); assertNull(storedUndoableBlock.getTransactions()); WeakReference<TransactionOutputChanges> changes = new WeakReference<TransactionOutputChanges>(storedUndoableBlock.getTxOutChanges()); assertNotNull(changes.get()); storedUndoableBlock = null; // Blank the reference so it can be GCd. // Create a chain longer than UNDOABLE_BLOCKS_STORED for (int i = 0; i < UNDOABLE_BLOCKS_STORED; i++) { rollingBlock = rollingBlock.createNextBlock(null); chain.add(rollingBlock); } // Try to get the garbage collector to run System.gc(); assertNull(undoBlock.get()); assertNull(changes.get()); assertNull(out.get()); try { store.close(); } catch (Exception e) { } }
@Test public void chainDownloadEnd2End() throws Exception { // A full end-to-end test of the chain download process, with a new block being solved in the // middle. Block b1 = createFakeBlock(blockStore).block; blockChain.add(b1); Block b2 = makeSolvedTestBlock(b1); Block b3 = makeSolvedTestBlock(b2); Block b4 = makeSolvedTestBlock(b3); Block b5 = makeSolvedTestBlock(b4); connect(); peer.startBlockChainDownload(); GetBlocksMessage getblocks = (GetBlocksMessage) outbound(writeTarget); 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(unitTestParams); inv.addBlock(b2); inv.addBlock(b3); // We do a getdata on them. inbound(writeTarget, inv); GetDataMessage getdata = (GetDataMessage) outbound(writeTarget); assertEquals(b2.getHash(), getdata.getItems().get(0).hash); assertEquals(b3.getHash(), getdata.getItems().get(1).hash); assertEquals(2, getdata.getItems().size()); // Remote peer sends us the blocks. The act of doing a getdata for b3 results in getting an inv // with just the // best chain head in it. inbound(writeTarget, b2); inbound(writeTarget, b3); inv = new InventoryMessage(unitTestParams); inv.addBlock(b5); // We request the head block. inbound(writeTarget, inv); getdata = (GetDataMessage) outbound(writeTarget); assertEquals(b5.getHash(), getdata.getItems().get(0).hash); assertEquals(1, getdata.getItems().size()); // Peer sends us the head block. The act of receiving the orphan block triggers a getblocks to // fill in the // rest of the chain. inbound(writeTarget, b5); getblocks = (GetBlocksMessage) outbound(writeTarget); assertEquals(b5.getHash(), getblocks.getStopHash()); assertEquals(b3.getHash(), getblocks.getLocator().get(0)); // At this point another block is solved and broadcast. The inv triggers a getdata but we do NOT // send another // getblocks afterwards, because that would result in us receiving the same set of blocks twice // which is a // timewaste. The getblocks message that would have been generated is set to be the same as the // previous // 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(unitTestParams); inv.addBlock(b6); inbound(writeTarget, inv); getdata = (GetDataMessage) outbound(writeTarget); assertEquals(1, getdata.getItems().size()); assertEquals(b6.getHash(), getdata.getItems().get(0).hash); 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(unitTestParams); inv.addBlock(b4); inv.addBlock(b5); inbound(writeTarget, inv); getdata = (GetDataMessage) outbound(writeTarget); assertEquals(1, getdata.getItems().size()); assertEquals(b4.getHash(), getdata.getItems().get(0).hash); // We already have b5 from before, so it's not requested again. inbound(writeTarget, b4); assertNull(outbound(writeTarget)); // b5 and b6 are now connected by the block chain and we're done. assertNull(outbound(writeTarget)); closePeer(peer); }