private void serDeser( BitcoinSerializer bs, Message message, byte[] sourceBytes, byte[] containedBytes, byte[] containingBytes) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); bs.serialize(message, bos); byte[] b1 = bos.toByteArray(); Message m2 = bs.deserialize(new ByteArrayInputStream(b1)); assertEquals(message, m2); bos.reset(); bs.serialize(m2, bos); byte[] b2 = bos.toByteArray(); assertTrue(Arrays.equals(b1, b2)); if (sourceBytes != null) { assertTrue(arrayContains(sourceBytes, b1)); assertTrue(arrayContains(sourceBytes, b2)); } if (containedBytes != null) { assertTrue(arrayContains(b1, containedBytes)); } if (containingBytes != null) { assertTrue(arrayContains(containingBytes, b1)); } }
@Before public void setUp() throws Exception { unitTestParams = UnitTestParams.get(); wallet = new Wallet(unitTestParams); wallet.addKey(new ECKey()); resetBlockStore(); Transaction tx1 = createFakeTx( unitTestParams, Utils.toNanoCoins(2, 0), wallet.getKeys().get(0).toAddress(unitTestParams)); // add a second input so can test granularity of byte cache. Transaction prevTx = new Transaction(unitTestParams); TransactionOutput prevOut = new TransactionOutput( unitTestParams, prevTx, Utils.toNanoCoins(1, 0), wallet.getKeys().get(0).toAddress(unitTestParams)); prevTx.addOutput(prevOut); // Connect it. tx1.addInput(prevOut); Transaction tx2 = createFakeTx( unitTestParams, Utils.toNanoCoins(1, 0), new ECKey().toAddress(unitTestParams)); Block b1 = createFakeBlock(blockStore, tx1, tx2).block; BitcoinSerializer bs = new BitcoinSerializer(unitTestParams); ByteArrayOutputStream bos = new ByteArrayOutputStream(); bs.serialize(tx1, bos); tx1BytesWithHeader = bos.toByteArray(); tx1Bytes = tx1.bitcoinSerialize(); bos.reset(); bs.serialize(tx2, bos); tx2BytesWithHeader = bos.toByteArray(); tx2Bytes = tx2.bitcoinSerialize(); bos.reset(); bs.serialize(b1, bos); b1BytesWithHeader = bos.toByteArray(); b1Bytes = b1.bitcoinSerialize(); }
public void testBlock(byte[] blockBytes, boolean isChild, boolean lazy, boolean retain) throws Exception { // reference serializer to produce comparison serialization output after changes to // message structure. BitcoinSerializer bsRef = new BitcoinSerializer(unitTestParams, false, false); ByteArrayOutputStream bos = new ByteArrayOutputStream(); BitcoinSerializer bs = new BitcoinSerializer(unitTestParams, lazy, retain); Block b1; Block bRef; b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes)); bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes)); // verify our reference BitcoinSerializer produces matching byte array. bos.reset(); bsRef.serialize(bRef, bos); assertTrue(Arrays.equals(bos.toByteArray(), blockBytes)); // check lazy and retain status survive both before and after a serialization assertEquals(!lazy, b1.isParsedTransactions()); assertEquals(!lazy, b1.isParsedHeader()); if (b1.isParsedHeader()) assertEquals(retain, b1.isHeaderBytesValid()); if (b1.isParsedTransactions()) assertEquals(retain, b1.isTransactionBytesValid()); serDeser(bs, b1, blockBytes, null, null); assertEquals(!lazy, b1.isParsedTransactions()); assertEquals(!lazy, b1.isParsedHeader()); if (b1.isParsedHeader()) assertEquals(retain, b1.isHeaderBytesValid()); if (b1.isParsedTransactions()) assertEquals(retain, b1.isTransactionBytesValid()); // compare to ref block bos.reset(); bsRef.serialize(bRef, bos); serDeser(bs, b1, bos.toByteArray(), null, null); // retrieve a value from a child b1.getTransactions(); assertTrue(b1.isParsedTransactions()); if (b1.getTransactions().size() > 0) { assertTrue(b1.isParsedTransactions()); Transaction tx1 = b1.getTransactions().get(0); // this will always be true for all children of a block once they are retrieved. // the tx child inputs/outputs may not be parsed however. // no longer forced to parse if length not provided. // assertEquals(true, tx1.isParsed()); if (tx1.isParsed()) assertEquals(retain, tx1.isCached()); else assertTrue(tx1.isCached()); // does it still match ref block? serDeser(bs, b1, bos.toByteArray(), null, null); } // refresh block b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes)); bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes)); // retrieve a value from header b1.getDifficultyTarget(); assertTrue(b1.isParsedHeader()); assertEquals(lazy, !b1.isParsedTransactions()); // does it still match ref block? serDeser(bs, b1, bos.toByteArray(), null, null); // refresh block b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes)); bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes)); // retrieve a value from a child and header b1.getDifficultyTarget(); assertTrue(b1.isParsedHeader()); assertEquals(lazy, !b1.isParsedTransactions()); b1.getTransactions(); assertTrue(b1.isParsedTransactions()); if (b1.getTransactions().size() > 0) { assertTrue(b1.isParsedTransactions()); Transaction tx1 = b1.getTransactions().get(0); // no longer forced to parse if length not provided. // assertEquals(true, tx1.isParsed()); if (tx1.isParsed()) assertEquals(retain, tx1.isCached()); else assertTrue(tx1.isCached()); } // does it still match ref block? serDeser(bs, b1, bos.toByteArray(), null, null); // refresh block b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes)); bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes)); // change a value in header b1.setNonce(23); bRef.setNonce(23); assertTrue(b1.isParsedHeader()); assertEquals(lazy, !b1.isParsedTransactions()); assertFalse(b1.isHeaderBytesValid()); if (b1.isParsedTransactions()) assertEquals(retain, b1.isTransactionBytesValid()); else assertEquals(true, b1.isTransactionBytesValid()); // does it still match ref block? bos.reset(); bsRef.serialize(bRef, bos); serDeser(bs, b1, bos.toByteArray(), null, null); // refresh block b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes)); bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes)); // retrieve a value from a child of a child b1.getTransactions(); if (b1.getTransactions().size() > 0) { Transaction tx1 = b1.getTransactions().get(0); TransactionInput tin = tx1.getInputs().get(0); assertTrue(tx1.isParsed()); assertTrue(b1.isParsedTransactions()); assertEquals(!lazy, b1.isParsedHeader()); assertEquals(!lazy, tin.isParsed()); assertEquals(tin.isParsed() ? retain : true, tin.isCached()); // does it still match ref tx? bos.reset(); bsRef.serialize(bRef, bos); serDeser(bs, b1, bos.toByteArray(), null, null); } // refresh block b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes)); bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes)); // add an input b1.getTransactions(); if (b1.getTransactions().size() > 0) { Transaction tx1 = b1.getTransactions().get(0); if (tx1.getInputs().size() > 0) { tx1.addInput(tx1.getInputs().get(0)); // replicate on reference tx bRef.getTransactions().get(0).addInput(bRef.getTransactions().get(0).getInputs().get(0)); assertFalse(tx1.isCached()); assertTrue(tx1.isParsed()); assertFalse(b1.isTransactionBytesValid()); assertTrue(b1.isParsedHeader()); // confirm sibling cache status was unaffected if (tx1.getInputs().size() > 1) { boolean parsed = tx1.getInputs().get(1).isParsed(); assertEquals(parsed ? retain : true, tx1.getInputs().get(1).isCached()); assertEquals(!lazy, parsed); } // this has to be false. Altering a tx invalidates the merkle root. // when we have seperate merkle caching then the entire header won't need to be // invalidated. assertFalse(b1.isHeaderBytesValid()); bos.reset(); bsRef.serialize(bRef, bos); byte[] source = bos.toByteArray(); // confirm we still match the reference tx. serDeser(bs, b1, source, null, null); } // does it still match ref tx? bos.reset(); bsRef.serialize(bRef, bos); serDeser(bs, b1, bos.toByteArray(), null, null); } // refresh block b1 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes)); Block b2 = (Block) bs.deserialize(new ByteArrayInputStream(blockBytes)); bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes)); Block bRef2 = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes)); // reparent an input b1.getTransactions(); if (b1.getTransactions().size() > 0) { Transaction tx1 = b1.getTransactions().get(0); Transaction tx2 = b2.getTransactions().get(0); if (tx1.getInputs().size() > 0) { TransactionInput fromTx1 = tx1.getInputs().get(0); tx2.addInput(fromTx1); // replicate on reference tx TransactionInput fromTxRef = bRef.getTransactions().get(0).getInputs().get(0); bRef2.getTransactions().get(0).addInput(fromTxRef); // b1 hasn't changed but it's no longer in the parent // chain of fromTx1 so has to have been uncached since it won't be // notified of changes throught the parent chain anymore. assertFalse(b1.isTransactionBytesValid()); // b2 should have it's cache invalidated because it has changed. assertFalse(b2.isTransactionBytesValid()); bos.reset(); bsRef.serialize(bRef2, bos); byte[] source = bos.toByteArray(); // confirm altered block matches altered ref block. serDeser(bs, b2, source, null, null); } // does unaltered block still match ref block? bos.reset(); bsRef.serialize(bRef, bos); serDeser(bs, b1, bos.toByteArray(), null, null); // how about if we refresh it? bRef = (Block) bsRef.deserialize(new ByteArrayInputStream(blockBytes)); bos.reset(); bsRef.serialize(bRef, bos); serDeser(bs, b1, bos.toByteArray(), null, null); } }
public void testTransaction( NetworkParameters params, byte[] txBytes, boolean isChild, boolean lazy, boolean retain) throws Exception { // reference serializer to produce comparison serialization output after changes to // message structure. BitcoinSerializer bsRef = new BitcoinSerializer(params, false, false); ByteArrayOutputStream bos = new ByteArrayOutputStream(); BitcoinSerializer bs = new BitcoinSerializer(params, lazy, retain); Transaction t1; Transaction tRef; t1 = (Transaction) bs.deserialize(new ByteArrayInputStream(txBytes)); tRef = (Transaction) bsRef.deserialize(new ByteArrayInputStream(txBytes)); // verify our reference BitcoinSerializer produces matching byte array. bos.reset(); bsRef.serialize(tRef, bos); assertTrue(Arrays.equals(bos.toByteArray(), txBytes)); // check lazy and retain status survive both before and after a serialization assertEquals(!lazy, t1.isParsed()); if (t1.isParsed()) assertEquals(retain, t1.isCached()); serDeser(bs, t1, txBytes, null, null); assertEquals(lazy, !t1.isParsed()); if (t1.isParsed()) assertEquals(retain, t1.isCached()); // compare to ref tx bos.reset(); bsRef.serialize(tRef, bos); serDeser(bs, t1, bos.toByteArray(), null, null); // retrieve a value from a child t1.getInputs(); assertTrue(t1.isParsed()); if (t1.getInputs().size() > 0) { assertTrue(t1.isParsed()); TransactionInput tin = t1.getInputs().get(0); assertEquals(!lazy, tin.isParsed()); if (tin.isParsed()) assertEquals(retain, tin.isCached()); // does it still match ref tx? serDeser(bs, t1, bos.toByteArray(), null, null); } // refresh tx t1 = (Transaction) bs.deserialize(new ByteArrayInputStream(txBytes)); tRef = (Transaction) bsRef.deserialize(new ByteArrayInputStream(txBytes)); // add an input if (t1.getInputs().size() > 0) { t1.addInput(t1.getInputs().get(0)); // replicate on reference tx tRef.addInput(tRef.getInputs().get(0)); assertFalse(t1.isCached()); assertTrue(t1.isParsed()); bos.reset(); bsRef.serialize(tRef, bos); byte[] source = bos.toByteArray(); // confirm we still match the reference tx. serDeser(bs, t1, source, null, null); } }
@Test public void badMessage() throws Exception { // Bring up an actual network connection and feed it bogus data. final SettableFuture<Void> result = SettableFuture.create(); Threading.uncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread thread, Throwable throwable) { result.setException(throwable); } }; connect(); // Writes out a verack+version. final SettableFuture<Void> peerDisconnected = SettableFuture.create(); writeTarget.peer.addEventListener( new AbstractPeerEventListener() { @Override public void onPeerDisconnected(Peer p, int peerCount) { peerDisconnected.set(null); } }); final NetworkParameters params = TestNet3Params.get(); BitcoinSerializer serializer = new BitcoinSerializer(params); // Now write some bogus truncated message. ByteArrayOutputStream out = new ByteArrayOutputStream(); serializer.serialize( "inv", new InventoryMessage(params) { @Override public void bitcoinSerializeToStream(OutputStream stream) throws IOException { // Add some hashes. addItem( new InventoryItem( InventoryItem.Type.Transaction, Sha256Hash.create(new byte[] {1}))); addItem( new InventoryItem( InventoryItem.Type.Transaction, Sha256Hash.create(new byte[] {2}))); addItem( new InventoryItem( InventoryItem.Type.Transaction, Sha256Hash.create(new byte[] {3}))); // Write out a copy that's truncated in the middle. ByteArrayOutputStream bos = new ByteArrayOutputStream(); super.bitcoinSerializeToStream(bos); byte[] bits = bos.toByteArray(); bits = Arrays.copyOf(bits, bits.length / 2); stream.write(bits); } }.bitcoinSerialize(), out); writeTarget.writeTarget.writeBytes(out.toByteArray()); try { result.get(); fail(); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof ProtocolException); } peerDisconnected.get(); try { peer.writeTarget.writeBytes(new byte[1]); fail(); } catch (IOException e) { assertTrue( (e.getCause() != null && e.getCause() instanceof CancelledKeyException) || (e instanceof SocketException && e.getMessage().equals("Socket is closed"))); } }