3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-13 18:55:52 +00:00

Remove lazy parsing of messages.

In particular:

* Message.parse(): is now always called once, in the Message constructor
* Message.parsed: removed
* Message.parseLite(): folded into Message.parse()
* Message.maybeParse(): removed
* Message.ensureParsed(): removed
* Message.isParsed(): removed
* Block.parseHeader(): folded into Block.parse()
* Block.parseTransactions(): folded into Block.parse()
* Block.maybeParseHeader(): removed
* Block.maybeParseTransactions(): removed
* Block.ensureParsedHeader(): removed
* Block.ensureParsedTransactions(): removed
* Block.isParsedHeader(): removed
* Block.isParsedTransasctions(): removed
* MessageSerializer.isParseLazyMode(): removed
* BitcoinSerializer.parseLazy: removed
* BitcoinSerializer.getSerializer(): parseLazy parameter removed
* LazyParseException: removed
* LazyParseByteCacheTest: renamed to ParseByteCacheTest
This commit is contained in:
Andreas Schildbach 2015-07-28 17:37:23 +02:00
parent d753d28ba5
commit 7744a00629
32 changed files with 211 additions and 816 deletions

View File

@ -1,3 +1,20 @@
/*
* 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.io.IOException;
@ -20,7 +37,6 @@ public class AddressMessage extends Message {
* Contruct a new 'addr' message.
* @param params NetworkParameters object.
* @param offset The location of the first payload byte within the array.
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
* If true and the backing byte array is invalidated due to modification of a field then
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
@ -53,11 +69,7 @@ public class AddressMessage extends Message {
}
@Override
protected void parseLite() throws ProtocolException {
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
long numAddresses = readVarInt();
// Guard against ultra large messages that will crash us.
if (numAddresses > MAX_ADDRESSES)
@ -68,7 +80,9 @@ public class AddressMessage extends Message {
addresses.add(addr);
cursor += addr.getMessageSize();
}
length = cursor - offset;
length = new VarInt(addresses.size()).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);
}
/* (non-Javadoc)
@ -84,29 +98,15 @@ public class AddressMessage extends Message {
}
}
@Override
public int getMessageSize() {
if (length != UNKNOWN_LENGTH)
return length;
if (addresses != null) {
length = new VarInt(addresses.size()).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);
}
return length;
}
/**
* @return An unmodifiableList view of the backing List of addresses. Addresses contained within the list may be safely modified.
*/
public List<PeerAddress> getAddresses() {
maybeParse();
return Collections.unmodifiableList(addresses);
}
public void addAddress(PeerAddress address) {
unCache();
maybeParse();
address.setParent(this);
addresses.add(address);
if (length == UNKNOWN_LENGTH)
@ -129,5 +129,4 @@ public class AddressMessage extends Message {
public String toString() {
return "addr: " + Utils.join(addresses);
}
}

View File

@ -1,5 +1,6 @@
/*
* Copyright 2011 Google Inc.
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -61,7 +62,7 @@ public class AlertMessage extends Message {
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
// Alerts are formatted in two levels. The top level contains two byte arrays: a signature, and a serialized
// data structure containing the actual alert data.
int startPos = cursor;
@ -115,11 +116,6 @@ public class AlertMessage extends Message {
return ECKey.verify(Sha256Hash.hashTwice(content), signature, params.getAlertSigningKey());
}
@Override
protected void parseLite() throws ProtocolException {
// Do nothing, lazy parsing isn't useful for alerts.
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Field accessors.

View File

@ -46,7 +46,6 @@ public class BitcoinSerializer implements MessageSerializer {
private static final int COMMAND_LEN = 12;
private final NetworkParameters params;
private final boolean parseLazy;
private final boolean parseRetain;
private static final Map<Class<? extends Message>, String> names = new HashMap<Class<? extends Message>, String>();
@ -74,32 +73,14 @@ public class BitcoinSerializer implements MessageSerializer {
names.put(UTXOsMessage.class, "utxos");
}
/**
* Constructs a partial BitcoinSerializer with the given behavior. This is
* intended for use by messages which do not understand the network they
* belong to.
*
* @param parseLazy deserialize messages in lazy mode.
* @param parseRetain retain the backing byte array of a message for fast reserialization.
* @deprecated use BitcoinSerializer(NetworkParameters, boolean, boolean) instead.
*/
@Deprecated
BitcoinSerializer(boolean parseLazy, boolean parseRetain) {
this.params = null;
this.parseLazy = parseLazy;
this.parseRetain = parseRetain;
}
/**
* Constructs a BitcoinSerializer with the given behavior.
*
* @param params networkParams used to create Messages instances and termining packetMagic
* @param parseLazy deserialize messages in lazy mode.
* @param parseRetain retain the backing byte array of a message for fast reserialization.
*/
public BitcoinSerializer(NetworkParameters params, boolean parseLazy, boolean parseRetain) {
public BitcoinSerializer(NetworkParameters params, boolean parseRetain) {
this.params = params;
this.parseLazy = parseLazy;
this.parseRetain = parseRetain;
}
@ -370,14 +351,6 @@ public class BitcoinSerializer implements MessageSerializer {
}
}
/**
* Whether the serializer will produce lazy parse mode Messages
*/
@Override
public boolean isParseLazyMode() {
return parseLazy;
}
/**
* Whether the serializer will produce cached mode Messages
*/

View File

@ -93,9 +93,6 @@ public class Block extends Message {
/** Stores the hash of the block. If null, getHash() will recalculate it. */
private Sha256Hash hash;
protected boolean headerParsed;
protected boolean transactionsParsed;
protected boolean headerBytesValid;
protected boolean transactionBytesValid;
@ -195,28 +192,6 @@ public class Block extends Message {
return FIFTY_COINS.shiftRight(height / params.getSubsidyDecreaseBlockCount());
}
protected void parseHeader() throws ProtocolException {
if (headerParsed)
return;
cursor = offset;
version = readUint32();
prevBlockHash = readHash();
merkleRoot = readHash();
time = readUint32();
difficultyTarget = readUint32();
nonce = readUint32();
hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(payload, offset, cursor));
headerParsed = true;
headerBytesValid = serializer.isParseRetainMode();
}
protected void parseTransactions() throws ProtocolException {
parseTransactions(offset + HEADER_SIZE);
}
/**
* Parse transactions from the block.
*
@ -225,14 +200,10 @@ public class Block extends Message {
* size.
*/
protected void parseTransactions(final int transactionsOffset) throws ProtocolException {
if (transactionsParsed)
return;
cursor = transactionsOffset;
optimalEncodingMessageSize = HEADER_SIZE;
if (payload.length == cursor) {
// This message is just a header, it has no transactions.
transactionsParsed = true;
transactionBytesValid = false;
return;
}
@ -248,160 +219,36 @@ public class Block extends Message {
cursor += tx.getMessageSize();
optimalEncodingMessageSize += tx.getOptimalEncodingMessageSize();
}
// No need to set length here. If length was not provided then it should be set at the end of parseLight().
// If this is a genuine lazy parse then length must have been provided to the constructor.
transactionsParsed = true;
transactionBytesValid = serializer.isParseRetainMode();
}
@Override
void parse() throws ProtocolException {
parseHeader();
parseTransactions();
protected void parse() throws ProtocolException {
// header
cursor = offset;
version = readUint32();
prevBlockHash = readHash();
merkleRoot = readHash();
time = readUint32();
difficultyTarget = readUint32();
nonce = readUint32();
hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(payload, offset, cursor));
headerBytesValid = serializer.isParseRetainMode();
// transactions
parseTransactions(offset + HEADER_SIZE);
length = cursor - offset;
}
public int getOptimalEncodingMessageSize() {
if (optimalEncodingMessageSize != 0)
return optimalEncodingMessageSize;
maybeParseTransactions();
if (optimalEncodingMessageSize != 0)
return optimalEncodingMessageSize;
optimalEncodingMessageSize = bitcoinSerialize().length;
return optimalEncodingMessageSize;
}
@Override
protected void parseLite() throws ProtocolException {
// Ignore the header since it has fixed length. If length is not provided we will have to
// invoke a light parse of transactions to calculate the length.
if (length == UNKNOWN_LENGTH) {
Preconditions.checkState(serializer.isParseLazyMode(),
"Performing lite parse of block transaction as block was initialised from byte array " +
"without providing length. This should never need to happen.");
parseTransactions();
length = cursor - offset;
} else {
transactionBytesValid = !transactionsParsed || serializer.isParseRetainMode() && length > HEADER_SIZE;
}
headerBytesValid = !headerParsed || serializer.isParseRetainMode() && length >= HEADER_SIZE;
}
/*
* Block uses some special handling for lazy parsing and retention of cached bytes. Parsing and serializing the
* block header and the transaction list are both non-trivial so there are good efficiency gains to be had by
* separating them. There are many cases where a user may need to access or change one or the other but not both.
*
* With this in mind we ignore the inherited checkParse() and unCache() methods and implement a separate version
* of them for both header and transactions.
*
* Serializing methods are also handled in their own way. Whilst they deal with separate parts of the block structure
* there are some interdependencies. For example altering a tx requires invalidating the Merkle root and therefore
* the cached header bytes.
*/
private void maybeParseHeader() {
if (headerParsed || payload == null)
return;
try {
parseHeader();
if (!(headerBytesValid || transactionBytesValid))
payload = null;
} catch (ProtocolException e) {
throw new LazyParseException(
"ProtocolException caught during lazy parse. For safe access to fields call ensureParsed before attempting read or write access",
e);
}
}
private void maybeParseTransactions() {
if (transactionsParsed || payload == null)
return;
try {
parseTransactions();
if (!serializer.isParseRetainMode()) {
transactionBytesValid = false;
if (headerParsed)
payload = null;
}
} catch (ProtocolException e) {
throw new LazyParseException(
"ProtocolException caught during lazy parse. For safe access to fields call ensureParsed before attempting read or write access",
e);
}
}
/**
* Ensure the object is parsed if needed. This should be called in every getter before returning a value. If the
* lazy parse flag is not set this is a method returns immediately.
*/
@Override
protected void maybeParse() {
throw new LazyParseException(
"checkParse() should never be called on a Block. Instead use checkParseHeader() and checkParseTransactions()");
}
/**
* In lazy parsing mode access to getters and setters may throw an unchecked LazyParseException. If guaranteed
* safe access is required this method will force parsing to occur immediately thus ensuring LazyParseExeption will
* never be thrown from this Message. If the Message contains child messages (e.g. a Block containing Transaction
* messages) this will not force child messages to parse.
*
* This method ensures parsing of both headers and transactions.
*
* @throws ProtocolException
*/
@Override
public void ensureParsed() throws ProtocolException {
try {
maybeParseHeader();
maybeParseTransactions();
} catch (LazyParseException e) {
if (e.getCause() instanceof ProtocolException)
throw (ProtocolException) e.getCause();
throw new ProtocolException(e);
}
}
/**
* In lazy parsing mode access to getters and setters may throw an unchecked LazyParseException. If guaranteed
* safe access is required this method will force parsing to occur immediately thus ensuring LazyParseExeption
* will never be thrown from this Message. If the Message contains child messages (e.g. a Block containing
* Transaction messages) this will not force child messages to parse.
*
* This method ensures parsing of headers only.
*
* @throws ProtocolException
*/
public void ensureParsedHeader() throws ProtocolException {
try {
maybeParseHeader();
} catch (LazyParseException e) {
if (e.getCause() instanceof ProtocolException)
throw (ProtocolException) e.getCause();
throw new ProtocolException(e);
}
}
/**
* In lazy parsing mode access to getters and setters may throw an unchecked LazyParseException. If guaranteed
* safe access is required this method will force parsing to occur immediately thus ensuring LazyParseExeption will
* never be thrown from this Message. If the Message contains child messages (e.g. a Block containing Transaction
* messages) this will not force child messages to parse.
*
* This method ensures parsing of transactions only.
*
* @throws ProtocolException
*/
public void ensureParsedTransactions() throws ProtocolException {
try {
maybeParseTransactions();
} catch (LazyParseException e) {
if (e.getCause() instanceof ProtocolException)
throw (ProtocolException) e.getCause();
throw new ProtocolException(e);
}
}
// default for testing
void writeHeader(OutputStream stream) throws IOException {
// try for cached write first
@ -410,7 +257,6 @@ public class Block extends Message {
return;
}
// fall back to manual write
maybeParseHeader();
Utils.uint32ToByteStreamLE(version, stream);
stream.write(prevBlockHash.getReversedBytes());
stream.write(getMerkleRoot().getReversedBytes());
@ -422,7 +268,7 @@ public class Block extends Message {
private void writeTransactions(OutputStream stream) throws IOException {
// check for no transaction conditions first
// must be a more efficient way to do this but I'm tired atm.
if (transactions == null && transactionsParsed) {
if (transactions == null) {
return;
}
@ -509,7 +355,6 @@ public class Block extends Message {
}
private void unCacheHeader() {
maybeParseHeader();
headerBytesValid = false;
if (!transactionBytesValid)
payload = null;
@ -517,7 +362,6 @@ public class Block extends Message {
}
private void unCacheTransactions() {
maybeParseTransactions();
transactionBytesValid = false;
if (!headerBytesValid)
payload = null;
@ -584,7 +428,6 @@ public class Block extends Message {
/** Returns a copy of the block, but without any transactions. */
public Block cloneAsHeader() {
maybeParseHeader();
Block block = new Block(params, BLOCK_VERSION_GENESIS);
copyBitcoinHeaderTo(block);
return block;
@ -633,7 +476,6 @@ public class Block extends Message {
* extraNonce.</p>
*/
public void solve() {
maybeParseHeader();
while (true) {
try {
// Is our proof of work valid yet?
@ -653,7 +495,6 @@ public class Block extends Message {
* is thrown.
*/
public BigInteger getDifficultyTargetAsInteger() throws VerificationException {
maybeParseHeader();
BigInteger target = Utils.decodeCompactBits(difficultyTarget);
if (target.signum() <= 0 || target.compareTo(params.maxTarget) > 0)
throw new VerificationException("Difficulty target is bad: " + target.toString());
@ -685,7 +526,6 @@ public class Block extends Message {
}
private void checkTimestamp() throws VerificationException {
maybeParseHeader();
// Allow injection of a fake clock to allow unit testing.
long currentTime = Utils.currentTimeSeconds();
if (time > currentTime + ALLOWED_TIME_DRIFT)
@ -747,7 +587,6 @@ public class Block extends Message {
// 2 3 4 4
// / \ / \ / \
// t1 t2 t3 t4 t5 t5
maybeParseTransactions();
ArrayList<byte[]> tree = new ArrayList<byte[]>();
// Start by adding all the hashes of the transactions as leaves of the tree.
for (Transaction t : transactions) {
@ -796,7 +635,6 @@ public class Block extends Message {
//
// Firstly we need to ensure this block does in fact represent real work done. If the difficulty is high
// enough, it's probably been done by the network.
maybeParseHeader();
checkProofOfWork(true);
checkTimestamp();
}
@ -813,7 +651,6 @@ public class Block extends Message {
// transactions that reference spent or non-existant inputs.
if (transactions.isEmpty())
throw new VerificationException("Block had no transactions");
maybeParseTransactions();
if (this.getOptimalEncodingMessageSize() > MAX_BLOCK_SIZE)
throw new VerificationException("Block larger than MAX_BLOCK_SIZE");
checkTransactions();
@ -847,7 +684,6 @@ public class Block extends Message {
* Returns the merkle root in big endian form, calculating it from transactions if necessary.
*/
public Sha256Hash getMerkleRoot() {
maybeParseHeader();
if (merkleRoot == null) {
//TODO check if this is really necessary.
unCacheHeader();
@ -888,7 +724,6 @@ public class Block extends Message {
/** Returns the version of the block data structure as defined by the Bitcoin protocol. */
public long getVersion() {
maybeParseHeader();
return version;
}
@ -896,7 +731,6 @@ public class Block extends Message {
* Returns the hash of the previous block in the chain, as defined by the block header.
*/
public Sha256Hash getPrevBlockHash() {
maybeParseHeader();
return prevBlockHash;
}
@ -911,7 +745,6 @@ public class Block extends Message {
* is measured in seconds since the UNIX epoch (midnight Jan 1st 1970).
*/
public long getTimeSeconds() {
maybeParseHeader();
return time;
}
@ -938,7 +771,6 @@ public class Block extends Message {
* Calculating the difficulty that way is currently unsupported.
*/
public long getDifficultyTarget() {
maybeParseHeader();
return difficultyTarget;
}
@ -954,7 +786,6 @@ public class Block extends Message {
* difficulty target.
*/
public long getNonce() {
maybeParseHeader();
return nonce;
}
@ -968,7 +799,6 @@ public class Block extends Message {
/** Returns an immutable list of transactions held in this block, or null if this object represents just a header. */
@Nullable
public List<Transaction> getTransactions() {
maybeParseTransactions();
return transactions == null ? null : ImmutableList.copyOf(transactions);
}
@ -1087,16 +917,6 @@ public class Block extends Message {
return createNextBlock(null, 1, null, Utils.currentTimeSeconds(), pubKey, FIFTY_COINS);
}
@VisibleForTesting
boolean isParsedHeader() {
return headerParsed;
}
@VisibleForTesting
boolean isParsedTransactions() {
return transactionsParsed;
}
@VisibleForTesting
boolean isHeaderBytesValid() {
return headerBytesValid;

View File

@ -1,5 +1,6 @@
/*
* Copyright 2012 Matt Corallo
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -130,7 +131,7 @@ public class BloomFilter extends Message {
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
data = readByteArray();
if (data.length > MAX_FILTER_SIZE)
throw new ProtocolException ("Bloom filter out of size range.");
@ -154,11 +155,6 @@ public class BloomFilter extends Message {
stream.write(nFlags);
}
@Override
protected void parseLite() throws ProtocolException {
// Do nothing, lazy parsing isn't useful for bloom filters.
}
private static int rotateLeft32(int x, int r) {
return (x << r) | (x >>> (32 - r));
}

View File

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
import java.io.IOException;
@ -47,11 +48,6 @@ class DummySerializer implements MessageSerializer {
throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE);
}
@Override
public boolean isParseLazyMode() {
return false;
}
@Override
public boolean isParseRetainMode() {
return false;

View File

@ -1,5 +1,6 @@
/*
* Copyright 2011 Steve Coughlan.
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
import java.io.IOException;
@ -43,31 +45,7 @@ public abstract class EmptyMessage extends Message {
}
@Override
public int getMessageSize() {
return 0;
}
/* (non-Javadoc)
* @see Message#parse()
*/
@Override
void parse() throws ProtocolException {
}
/* (non-Javadoc)
* @see Message#parseLite()
*/
@Override
protected void parseLite() throws ProtocolException {
length = 0;
}
/* (non-Javadoc)
* @see Message#ensureParsed()
*/
@Override
public void ensureParsed() throws ProtocolException {
parsed = true;
protected void parse() throws ProtocolException {
}
/* (non-Javadoc)
@ -77,6 +55,4 @@ public abstract class EmptyMessage extends Message {
public byte[] bitcoinSerialize() {
return new byte[0];
}
}

View File

@ -1,5 +1,6 @@
/**
* Copyright 2012 Matt Corallo
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -57,7 +58,7 @@ public class FilteredBlock extends Message {
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
byte[] headerBytes = new byte[Block.HEADER_SIZE];
System.arraycopy(payload, 0, headerBytes, 0, Block.HEADER_SIZE);
header = params.getDefaultSerializer().makeBlock(headerBytes);
@ -67,11 +68,6 @@ public class FilteredBlock extends Message {
length = Block.HEADER_SIZE + merkleTree.getMessageSize();
}
@Override
protected void parseLite() throws ProtocolException {
}
/**
* Gets a list of leaf hashes which are contained in the partial merkle tree in this filtered block
*

View File

@ -1,5 +1,6 @@
/*
* Copyright 2011 Google Inc.
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -43,22 +44,13 @@ public class GetBlocksMessage extends Message {
}
@Override
protected void parseLite() throws ProtocolException {
protected void parse() throws ProtocolException {
cursor = offset;
version = readUint32();
int startCount = (int) readVarInt();
if (startCount > 500)
throw new ProtocolException("Number of locators cannot be > 500, received: " + startCount);
length = cursor - offset + ((startCount + 1) * 32);
}
@Override
public void parse() throws ProtocolException {
cursor = offset;
version = readUint32();
int startCount = (int) readVarInt();
if (startCount > 500)
throw new ProtocolException("Number of locators cannot be > 500, received: " + startCount);
locator = new ArrayList<Sha256Hash>(startCount);
for (int i = 0; i < startCount; i++) {
locator.add(readHash());

View File

@ -78,11 +78,6 @@ public class GetUTXOsMessage extends Message {
return outPoints;
}
@Override
protected void parseLite() throws ProtocolException {
// Not needed.
}
@Override
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
stream.write(new byte[]{includeMempool ? (byte) 1 : 0}); // include mempool.

View File

@ -1,5 +1,6 @@
/*
* Copyright 2011 Google Inc.
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -62,7 +63,7 @@ public class HeadersMessage extends Message {
}
@Override
protected void parseLite() throws ProtocolException {
protected void parse() throws ProtocolException {
if (length == UNKNOWN_LENGTH) {
int saveCursor = cursor;
long numHeaders = readVarInt();
@ -71,10 +72,7 @@ public class HeadersMessage extends Message {
// Each header has 80 bytes and one more byte for transactions number which is 00.
length = 81 * (int)numHeaders;
}
}
@Override
void parse() throws ProtocolException {
long numHeaders = readVarInt();
if (numHeaders > MAX_HEADERS)
throw new ProtocolException("Too many headers: got " + numHeaders + " which is larger than " +
@ -88,7 +86,7 @@ public class HeadersMessage extends Message {
byte[] blockHeader = readBytes(81);
if (blockHeader[80] != 0)
throw new ProtocolException("Block header does not end with a null byte");
Block newBlockHeader = this.params.getSerializer(true, true)
Block newBlockHeader = this.params.getSerializer(true)
.makeBlock(blockHeader, 81);
blockHeaders.add(newBlockHeader);
}
@ -100,7 +98,6 @@ public class HeadersMessage extends Message {
}
}
public List<Block> getBlockHeaders() {
return blockHeaders;
}

View File

@ -1,5 +1,6 @@
/*
* Copyright 2011 Google Inc.
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -49,7 +50,6 @@ public abstract class ListMessage extends Message {
}
public List<InventoryItem> getItems() {
maybeParse();
return Collections.unmodifiableList(items);
}
@ -68,15 +68,12 @@ public abstract class ListMessage extends Message {
}
@Override
protected void parseLite() throws ProtocolException {
protected void parse() throws ProtocolException {
arrayLen = readVarInt();
if (arrayLen > MAX_INVENTORY_ITEMS)
throw new ProtocolException("Too many items in INV message: " + arrayLen);
length = (int) (cursor - offset + (arrayLen * InventoryItem.MESSAGE_LENGTH));
}
@Override
public void parse() throws ProtocolException {
// An inv is vector<CInv> where CInv is int+hash. The int is either 1 or 2 for tx or block.
items = new ArrayList<InventoryItem>((int) arrayLen);
for (int i = 0; i < arrayLen; i++) {

View File

@ -1,5 +1,6 @@
/*
* Copyright 2012 Google Inc.
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,10 +29,7 @@ import java.io.OutputStream;
*/
public class MemoryPoolMessage extends Message {
@Override
void parse() throws ProtocolException {}
@Override
protected void parseLite() throws ProtocolException {}
protected void parse() throws ProtocolException {}
@Override
void bitcoinSerializeToStream(OutputStream stream) throws IOException {}

View File

@ -52,7 +52,6 @@ public abstract class Message {
// The raw message payload bytes themselves.
protected byte[] payload;
protected boolean parsed = false;
protected boolean recached = false;
protected MessageSerializer serializer;
@ -61,17 +60,15 @@ public abstract class Message {
protected NetworkParameters params;
protected Message() {
parsed = true;
serializer = DummySerializer.DEFAULT;
}
Message(NetworkParameters params) {
protected Message(NetworkParameters params) {
this.params = params;
parsed = true;
serializer = params.getDefaultSerializer();
}
Message(NetworkParameters params, byte[] payload, int offset, int protocolVersion) throws ProtocolException {
protected Message(NetworkParameters params, byte[] payload, int offset, int protocolVersion) throws ProtocolException {
this(params, payload, offset, protocolVersion, params.getDefaultSerializer(), UNKNOWN_LENGTH);
}
@ -86,38 +83,30 @@ public abstract class Message {
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException
*/
Message(NetworkParameters params, byte[] payload, int offset, int protocolVersion, MessageSerializer serializer, int length) throws ProtocolException {
protected Message(NetworkParameters params, byte[] payload, int offset, int protocolVersion, MessageSerializer serializer, int length) throws ProtocolException {
this.serializer = serializer;
this.protocolVersion = protocolVersion;
this.params = params;
this.payload = payload;
this.cursor = this.offset = offset;
this.length = length;
if (serializer.isParseLazyMode()) {
parseLite();
} else {
parseLite();
parse();
parsed = true;
}
if (this.length == UNKNOWN_LENGTH)
checkState(false, "Length field has not been set in constructor for %s after %s parse. " +
"Refer to Message.parseLite() for detail of required Length field contract.",
getClass().getSimpleName(), serializer.isParseLazyMode() ? "lite" : "full");
checkState(false, "Length field has not been set in constructor for %s after parse.",
getClass().getSimpleName());
if (SELF_CHECK) {
selfCheck(payload, offset);
}
if (serializer.isParseRetainMode() || !parsed)
return;
if (!serializer.isParseRetainMode())
this.payload = null;
}
private void selfCheck(byte[] payload, int offset) {
if (!(this instanceof VersionMessage)) {
maybeParse();
byte[] payloadBytes = new byte[cursor - offset];
System.arraycopy(payload, offset, payloadBytes, 0, cursor - offset);
byte[] reserialized = bitcoinSerialize();
@ -138,68 +127,15 @@ public abstract class Message {
// These methods handle the serialization/deserialization using the custom Bitcoin protocol.
abstract void parse() throws ProtocolException;
protected abstract void parse() throws ProtocolException;
/**
* Perform the most minimal parse possible to calculate the length of the message payload.
* This is only required for subclasses of ChildMessage as root level messages will have their length passed
* into the constructor.
* <p/>
* Implementations should adhere to the following contract: If parseLazy = true the 'length'
* field must be set before returning. If parseLazy = false the length field must be set either
* within the parseLite() method OR the parse() method. The overriding requirement is that length
* must be set to non UNKNOWN_MESSAGE value by the time the constructor exits.
*
* @return
* @throws ProtocolException
*/
protected abstract void parseLite() throws ProtocolException;
/**
* Ensure the object is parsed if needed. This should be called in every getter before returning a value.
* If the lazy parse flag is not set this is a method returns immediately.
*/
protected synchronized void maybeParse() {
if (parsed || payload == null)
return;
try {
parse();
parsed = true;
if (!serializer.isParseRetainMode())
payload = null;
} catch (ProtocolException e) {
throw new LazyParseException("ProtocolException caught during lazy parse. For safe access to fields call ensureParsed before attempting read or write access", e);
}
}
/**
* In lazy parsing mode access to getters and setters may throw an unchecked LazyParseException. If guaranteed safe access is required
* this method will force parsing to occur immediately thus ensuring LazyParseExeption will never be thrown from this Message.
* If the Message contains child messages (e.g. a Block containing Transaction messages) this will not force child messages to parse.
* <p/>
* This could be overidden for Transaction and it's child classes to ensure the entire tree of Message objects is parsed.
*
* @throws ProtocolException
*/
public void ensureParsed() throws ProtocolException {
try {
maybeParse();
} catch (LazyParseException e) {
if (e.getCause() instanceof ProtocolException)
throw (ProtocolException) e.getCause();
throw new ProtocolException(e);
}
}
/**
* To be called before any change of internal values including any setters. This ensures any cached byte array is
* removed after performing a lazy parse if necessary to ensure the object is fully populated.
* <p/>
* Child messages of this object(e.g. Transactions belonging to a Block) will not have their internal byte caches
* invalidated unless they are also modified internally.
* <p>To be called before any change of internal values including any setters. This ensures any cached byte array is
* removed.<p/>
* <p>Child messages of this object(e.g. Transactions belonging to a Block) will not have their internal byte caches
* invalidated unless they are also modified internally.</p>
*/
protected void unCache() {
maybeParse();
payload = null;
recached = false;
}
@ -220,13 +156,6 @@ public abstract class Message {
length += VarInt.sizeOf(newArraySize) - VarInt.sizeOf(newArraySize - 1);
}
/**
* used for unit testing
*/
public boolean isParsed() {
return parsed;
}
/**
* used for unit testing
*/
@ -346,17 +275,11 @@ public abstract class Message {
}
/**
* This should be overridden to extract correct message size in the case of lazy parsing. Until this method is
* implemented in a subclass of ChildMessage lazy parsing may have no effect.
*
* This default implementation is a safe fall back that will ensure it returns a correct value by parsing the message.
* This returns a correct value by parsing the message.
*/
public int getMessageSize() {
if (length != UNKNOWN_LENGTH)
return length;
maybeParse();
public final int getMessageSize() {
if (length == UNKNOWN_LENGTH)
checkState(false, "Length field has not been set in %s after full parse.", getClass().getSimpleName());
checkState(false, "Length field has not been set in %s.", getClass().getSimpleName());
return length;
}
@ -448,16 +371,4 @@ public abstract class Message {
this.serializer = params.getDefaultSerializer();
}
}
public static class LazyParseException extends RuntimeException {
public LazyParseException(String message, Throwable cause) {
super(message, cause);
}
public LazyParseException(String message) {
super(message);
}
}
}

View File

@ -15,6 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
import java.io.IOException;
@ -45,11 +46,6 @@ public interface MessageSerializer {
*/
Message deserializePayload(BitcoinSerializer.BitcoinPacketHeader header, ByteBuffer in) throws ProtocolException, BufferUnderflowException, UnsupportedOperationException;
/**
* Whether the serializer will produce lazy parse mode Messages
*/
boolean isParseLazyMode();
/**
* Whether the serializer will produce cached mode Messages
*/

View File

@ -439,7 +439,7 @@ public abstract class NetworkParameters {
// As the serializers are intended to be immutable, creating
// two due to a race condition should not be a problem, however
// to be safe we ensure only one exists for each network.
this.defaultSerializer = getSerializer(false, false);
this.defaultSerializer = getSerializer(false);
}
}
}
@ -449,7 +449,7 @@ public abstract class NetworkParameters {
/**
* Construct and return a custom serializer.
*/
public abstract BitcoinSerializer getSerializer(boolean parseLazy, boolean parseRetain);
public abstract BitcoinSerializer getSerializer(boolean parseRetain);
/**
* The number of blocks in the last {@link getMajorityWindow()} blocks

View File

@ -1,6 +1,7 @@
/**
* Copyright 2012 The Bitcoin Developers
* Copyright 2012 Matt Corallo
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -109,7 +110,7 @@ public class PartialMerkleTree extends Message {
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
transactionCount = (int)readUint32();
int nHashes = (int) readVarInt();
@ -167,11 +168,6 @@ public class PartialMerkleTree extends Message {
return combineLeftRight(left.getBytes(), right.getBytes());
}
@Override
protected void parseLite() {
}
// helper function to efficiently calculate the number of nodes at given height in the merkle tree
private static int getTreeWidth(int transactionCount, int height) {
return (transactionCount + (1 << height) - 1) >> height;

View File

@ -1,5 +1,6 @@
/*
* Copyright 2011 Google Inc.
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -63,12 +64,8 @@ public class PeerAddress extends ChildMessage {
*/
public PeerAddress(NetworkParameters params, byte[] payload, int offset, int protocolVersion, Message parent, MessageSerializer serializer) throws ProtocolException {
super(params, payload, offset, protocolVersion, parent, serializer, UNKNOWN_LENGTH);
// Message length is calculated in parseLite which is guaranteed to be called before it is ever read.
// Even though message length is static for a PeerAddress it is safer to leave it there
// as it will be set regardless of which constructor was used.
}
/**
* Construct a peer address from a memorized or hardcoded address.
*/
@ -139,11 +136,6 @@ public class PeerAddress extends ChildMessage {
stream.write((byte) (0xFF & port));
}
@Override
protected void parseLite() {
length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
}
@Override
protected void parse() throws ProtocolException {
// Format of a serialized address:
@ -163,22 +155,15 @@ public class PeerAddress extends ChildMessage {
throw new RuntimeException(e); // Cannot happen.
}
port = ((0xFF & payload[cursor++]) << 8) | (0xFF & payload[cursor++]);
}
@Override
public int getMessageSize() {
// The 4 byte difference is the uint32 timestamp that was introduced in version 31402
length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
return length;
}
public String getHostname() {
maybeParse();
return hostname;
}
public InetAddress getAddr() {
maybeParse();
return addr;
}
@ -191,43 +176,33 @@ public class PeerAddress extends ChildMessage {
this.addr = addr;
}
public int getPort() {
maybeParse();
return port;
}
public void setPort(int port) {
unCache();
this.port = port;
}
public BigInteger getServices() {
maybeParse();
return services;
}
public void setServices(BigInteger services) {
unCache();
this.services = services;
}
public long getTime() {
maybeParse();
return time;
}
public void setTime(long time) {
unCache();
this.time = time;
}
@Override
public String toString() {
if (hostname != null) {

View File

@ -1,5 +1,6 @@
/**
* Copyright 2011 Noa Resare
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -51,7 +52,7 @@ public class Ping extends Message {
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
try {
nonce = readInt64();
hasNonce = true;
@ -61,11 +62,6 @@ public class Ping extends Message {
length = hasNonce ? 8 : 0;
}
@Override
protected void parseLite() {
}
public boolean hasNonce() {
return hasNonce;
}

View File

@ -1,5 +1,6 @@
/**
* Copyright 2012 Matt Corallo
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -38,7 +39,7 @@ public class Pong extends Message {
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
nonce = readInt64();
length = 8;
}
@ -48,10 +49,6 @@ public class Pong extends Message {
Utils.int64ToByteStreamLE(nonce, stream);
}
@Override
protected void parseLite() {
}
/** Returns the nonce sent by the remote peer. */
public long getNonce() {
return nonce;

View File

@ -1,5 +1,6 @@
/*
* Copyright 2013 Matt Corallo
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -82,7 +83,7 @@ public class RejectMessage extends Message {
}
@Override
protected void parseLite() throws ProtocolException {
protected void parse() throws ProtocolException {
message = readStr();
code = RejectCode.fromCode(readBytes(1)[0]);
reason = readStr();
@ -91,12 +92,6 @@ public class RejectMessage extends Message {
length = cursor - offset;
}
@Override
public void parse() throws ProtocolException {
if (length == UNKNOWN_LENGTH)
parseLite();
}
@Override
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
byte[] messageBytes = message.getBytes("UTF-8");
@ -115,7 +110,6 @@ public class RejectMessage extends Message {
* Note that this is ENTIRELY UNTRUSTED and should be sanity-checked before it is printed or processed.
*/
public String getRejectedMessage() {
ensureParsed();
return message;
}
@ -123,7 +117,6 @@ public class RejectMessage extends Message {
* Provides the hash of the rejected object (if getRejectedMessage() is either "tx" or "block"), otherwise null.
*/
public Sha256Hash getRejectedObjectHash() {
ensureParsed();
return messageHash;
}

View File

@ -201,7 +201,6 @@ public class Transaction extends ChildMessage {
* @param params NetworkParameters object.
* @param payload Bitcoin protocol formatted byte array containing message content.
* @param offset The location of the first payload byte within the array.
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
* If true and the backing byte array is invalidated due to modification of a field then
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
@ -253,7 +252,6 @@ public class Transaction extends ChildMessage {
* include spent outputs or not.
*/
Coin getValueSentToMe(TransactionBag transactionBag, boolean includeSpent) {
maybeParse();
// This is tested in WalletTest.
Coin v = Coin.ZERO;
for (TransactionOutput o : outputs) {
@ -357,7 +355,6 @@ public class Transaction extends ChildMessage {
* @return sum of the inputs that are spending coins with keys in the wallet
*/
public Coin getValueSentFromMe(TransactionBag wallet) throws ScriptException {
maybeParse();
// This is tested in WalletTest.
Coin v = Coin.ZERO;
for (TransactionInput input : inputs) {
@ -421,7 +418,6 @@ public class Transaction extends ChildMessage {
* Returns true if any of the outputs is marked as spent.
*/
public boolean isAnyOutputSpent() {
maybeParse();
for (TransactionOutput output : outputs) {
if (!output.isAvailableForSpending())
return true;
@ -434,7 +430,6 @@ public class Transaction extends ChildMessage {
* otherwise.
*/
public boolean isEveryOwnedOutputSpent(TransactionBag transactionBag) {
maybeParse();
for (TransactionOutput output : outputs) {
if (output.isAvailableForSpending() && output.isMineOrWatched(transactionBag))
return false;
@ -479,29 +474,6 @@ public class Transaction extends ChildMessage {
hash = null;
}
@Override
protected void parseLite() throws ProtocolException {
//skip this if the length has been provided i.e. the tx is not part of a block
if (serializer.isParseLazyMode() && length == UNKNOWN_LENGTH) {
//If length hasn't been provided this tx is probably contained within a block.
//In parseRetain mode the block needs to know how long the transaction is
//unfortunately this requires a fairly deep (though not total) parse.
//This is due to the fact that transactions in the block's list do not include a
//size header and inputs/outputs are also variable length due the contained
//script so each must be instantiated so the scriptlength varint can be read
//to calculate total length of the transaction.
//We will still persist will this semi-light parsing because getting the lengths
//of the various components gains us the ability to cache the backing bytearrays
//so that only those subcomponents that have changed will need to be reserialized.
//parse();
//parsed = true;
length = calcLength(payload, offset);
cursor = offset + length;
}
}
protected static int calcLength(byte[] buf, int offset) {
VarInt varint;
// jump past version (uint32)
@ -539,11 +511,7 @@ public class Transaction extends ChildMessage {
}
@Override
void parse() throws ProtocolException {
if (parsed)
return;
protected void parse() throws ProtocolException {
cursor = offset;
version = readUint32();
@ -579,7 +547,6 @@ public class Transaction extends ChildMessage {
public int getOptimalEncodingMessageSize() {
if (optimalEncodingMessageSize != 0)
return optimalEncodingMessageSize;
maybeParse();
if (optimalEncodingMessageSize != 0)
return optimalEncodingMessageSize;
optimalEncodingMessageSize = getMessageSize();
@ -610,7 +577,6 @@ public class Transaction extends ChildMessage {
* position in a block but by the data in the inputs.
*/
public boolean isCoinBase() {
maybeParse();
return inputs.size() == 1 && inputs.get(0).isCoinBase();
}
@ -1067,7 +1033,6 @@ public class Transaction extends ChildMessage {
* standard and won't be relayed or included in the memory pool either.
*/
public long getLockTime() {
maybeParse();
return lockTime;
}
@ -1098,19 +1063,16 @@ public class Transaction extends ChildMessage {
* @return the version
*/
public long getVersion() {
maybeParse();
return version;
}
/** Returns an unmodifiable view of all inputs. */
public List<TransactionInput> getInputs() {
maybeParse();
return Collections.unmodifiableList(inputs);
}
/** Returns an unmodifiable view of all outputs. */
public List<TransactionOutput> getOutputs() {
maybeParse();
return Collections.unmodifiableList(outputs);
}
@ -1123,7 +1085,6 @@ public class Transaction extends ChildMessage {
* @return linked list of outputs relevant to the wallet in this transaction
*/
public List<TransactionOutput> getWalletOutputs(TransactionBag transactionBag){
maybeParse();
List<TransactionOutput> walletOutputs = new LinkedList<TransactionOutput>();
for (TransactionOutput o : outputs) {
if (!o.isMineOrWatched(transactionBag)) continue;
@ -1135,19 +1096,16 @@ public class Transaction extends ChildMessage {
/** Randomly re-orders the transaction outputs: good for privacy */
public void shuffleOutputs() {
maybeParse();
Collections.shuffle(outputs);
}
/** Same as getInputs().get(index). */
public TransactionInput getInput(long index) {
maybeParse();
return inputs.get((int)index);
}
/** Same as getOutputs().get(index) */
public TransactionOutput getOutput(long index) {
maybeParse();
return outputs.get((int)index);
}
@ -1197,7 +1155,6 @@ public class Transaction extends ChildMessage {
* Gets the count of regular SigOps in this transactions
*/
public int getSigOpCount() throws ScriptException {
maybeParse();
int sigOps = 0;
for (TransactionInput input : inputs)
sigOps += Script.getSigOpCount(input.getScriptBytes());
@ -1223,7 +1180,6 @@ public class Transaction extends ChildMessage {
* @throws VerificationException
*/
public void verify() throws VerificationException {
maybeParse();
if (inputs.size() == 0 || outputs.size() == 0)
throw new VerificationException.EmptyInputsOrOutputs();
if (this.getMessageSize() > Block.MAX_BLOCK_SIZE)

View File

@ -25,7 +25,6 @@ import com.google.common.base.Objects;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.Arrays;
@ -126,18 +125,11 @@ public class TransactionInput extends ChildMessage {
}
@Override
protected void parseLite() throws ProtocolException {
int curs = cursor;
int scriptLen = (int) readVarInt(36);
length = cursor - offset + scriptLen + 4;
cursor = curs;
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
outpoint = new TransactionOutPoint(params, payload, cursor, this, serializer);
cursor += outpoint.getMessageSize();
int scriptLen = (int) readVarInt();
length = cursor - offset + scriptLen + 4;
scriptBytes = readBytes(scriptLen);
sequence = readUint32();
}
@ -154,7 +146,6 @@ public class TransactionInput extends ChildMessage {
* Coinbase transactions have special inputs with hashes of zero. If this is such an input, returns true.
*/
public boolean isCoinBase() {
maybeParse();
return outpoint.getHash().equals(Sha256Hash.ZERO_HASH) &&
(outpoint.getIndex() & 0xFFFFFFFFL) == 0xFFFFFFFFL; // -1 but all is serialized to the wire as unsigned int.
}
@ -168,7 +159,6 @@ public class TransactionInput extends ChildMessage {
// parameter is overloaded to be something totally different.
Script script = scriptSig == null ? null : scriptSig.get();
if (script == null) {
maybeParse();
script = new Script(scriptBytes);
scriptSig = new WeakReference<Script>(script);
}
@ -204,7 +194,6 @@ public class TransactionInput extends ChildMessage {
* feature is disabled so sequence numbers are unusable.
*/
public long getSequenceNumber() {
maybeParse();
return sequence;
}
@ -225,7 +214,6 @@ public class TransactionInput extends ChildMessage {
* data needed to connect to the output of the transaction we're gathering coins from.
*/
public TransactionOutPoint getOutpoint() {
maybeParse();
return outpoint;
}
@ -236,7 +224,6 @@ public class TransactionInput extends ChildMessage {
* @return the scriptBytes
*/
public byte[] getScriptBytes() {
maybeParse();
return scriptBytes;
}

View File

@ -1,5 +1,6 @@
/*
* Copyright 2011 Google Inc.
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -88,24 +89,12 @@ public class TransactionOutPoint extends ChildMessage {
}
@Override
protected void parseLite() throws ProtocolException {
protected void parse() throws ProtocolException {
length = MESSAGE_LENGTH;
}
@Override
void parse() throws ProtocolException {
hash = readHash();
index = readUint32();
}
/* (non-Javadoc)
* @see Message#getMessageSize()
*/
@Override
public int getMessageSize() {
return MESSAGE_LENGTH;
}
@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
stream.write(hash.getReversedBytes());
@ -197,7 +186,6 @@ public class TransactionOutPoint extends ChildMessage {
*/
@Override
public Sha256Hash getHash() {
maybeParse();
return hash;
}
@ -206,7 +194,6 @@ public class TransactionOutPoint extends ChildMessage {
}
public long getIndex() {
maybeParse();
return index;
}

View File

@ -110,7 +110,6 @@ public class TransactionOutput extends ChildMessage {
public Script getScriptPubKey() throws ScriptException {
if (scriptPubKey == null) {
maybeParse();
scriptPubKey = new Script(scriptBytes);
}
return scriptPubKey;
@ -154,21 +153,16 @@ public class TransactionOutput extends ChildMessage {
}
@Override
protected void parseLite() throws ProtocolException {
protected void parse() throws ProtocolException {
value = readInt64();
scriptLen = (int) readVarInt();
length = cursor - offset + scriptLen;
}
@Override
void parse() throws ProtocolException {
scriptBytes = readBytes(scriptLen);
}
@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
checkNotNull(scriptBytes);
maybeParse();
Utils.int64ToByteStreamLE(value, stream);
// TODO: Move script serialization into the Script class, where it belongs.
stream.write(new VarInt(scriptBytes.length).encode());
@ -180,7 +174,6 @@ public class TransactionOutput extends ChildMessage {
* receives.
*/
public Coin getValue() {
maybeParse();
try {
return Coin.valueOf(value);
} catch (IllegalArgumentException e) {
@ -286,7 +279,6 @@ public class TransactionOutput extends ChildMessage {
* @return the scriptBytes
*/
public byte[] getScriptBytes() {
maybeParse();
return scriptBytes;
}

View File

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core;
import com.google.common.base.Objects;
@ -90,7 +91,7 @@ public class UTXOsMessage extends Message {
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
// Format is:
// uint32 chainHeight
// uint256 chainHeadHash
@ -123,11 +124,6 @@ public class UTXOsMessage extends Message {
length = cursor;
}
@Override
protected void parseLite() throws ProtocolException {
// Not used.
}
/**
* Returns a bit map indicating which of the queried outputs were found in the UTXO set.
*/

View File

@ -114,16 +114,7 @@ public class VersionMessage extends Message {
}
@Override
protected void parseLite() throws ProtocolException {
// NOP. VersionMessage is never lazy parsed.
}
@Override
public void parse() throws ProtocolException {
if (parsed)
return;
parsed = true;
protected void parse() throws ProtocolException {
clientVersion = (int) readUint32();
localServices = readUint64().longValue();
time = readUint64().longValue();

View File

@ -1,5 +1,6 @@
/*
* Copyright 2013 Google Inc.
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,7 +32,6 @@ import org.bitcoinj.store.BlockStoreException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkState;
import org.bitcoinj.core.BitcoinSerializer;
/**
@ -137,8 +137,8 @@ public abstract class AbstractBitcoinNetParams extends NetworkParameters {
}
@Override
public BitcoinSerializer getSerializer(boolean parseLazy, boolean parseRetain) {
return new BitcoinSerializer(this, parseLazy, parseRetain);
public BitcoinSerializer getSerializer(boolean parseRetain) {
return new BitcoinSerializer(this, parseRetain);
}
@Override

View File

@ -75,76 +75,51 @@ public class BitcoinSerializerTest {
//assertTrue(LazyParseByteCacheTest.arrayContains(bos.toByteArray(), addrMessage));
}
@Test
public void testLazyParsing() throws Exception {
MessageSerializer bs = MainNetParams.get().getSerializer(true, false);
Transaction tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage));
assertNotNull(tx);
assertEquals(false, tx.isParsed());
assertEquals(true, tx.isCached());
tx.getInputs();
assertEquals(true, tx.isParsed());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bs.serialize(tx, bos);
assertEquals(true, Arrays.equals(txMessage, bos.toByteArray()));
}
@Test
public void testCachedParsing() throws Exception {
testCachedParsing(true);
testCachedParsing(false);
}
MessageSerializer bs = MainNetParams.get().getSerializer(true);
private void testCachedParsing(boolean lazy) throws Exception {
MessageSerializer bs = MainNetParams.get().getSerializer(lazy, true);
//first try writing to a fields to ensure uncaching and children are not affected
// first try writing to a fields to ensure uncaching and children are not affected
Transaction tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage));
assertNotNull(tx);
assertEquals(!lazy, tx.isParsed());
assertEquals(true, tx.isCached());
tx.setLockTime(1);
//parent should have been uncached
// parent should have been uncached
assertEquals(false, tx.isCached());
//child should remain cached.
// child should remain cached.
assertEquals(true, tx.getInputs().get(0).isCached());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bs.serialize(tx, bos);
assertEquals(true, !Arrays.equals(txMessage, bos.toByteArray()));
//now try writing to a child to ensure uncaching is propagated up to parent but not to siblings
// now try writing to a child to ensure uncaching is propagated up to parent but not to siblings
tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage));
assertNotNull(tx);
assertEquals(!lazy, tx.isParsed());
assertEquals(true, tx.isCached());
tx.getInputs().get(0).setSequenceNumber(1);
//parent should have been uncached
// parent should have been uncached
assertEquals(false, tx.isCached());
//so should child
// so should child
assertEquals(false, tx.getInputs().get(0).isCached());
bos = new ByteArrayOutputStream();
bs.serialize(tx, bos);
assertEquals(true, !Arrays.equals(txMessage, bos.toByteArray()));
//deserialize/reserialize to check for equals.
// deserialize/reserialize to check for equals.
tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage));
assertNotNull(tx);
assertEquals(!lazy, tx.isParsed());
assertEquals(true, tx.isCached());
bos = new ByteArrayOutputStream();
bs.serialize(tx, bos);
assertEquals(true, Arrays.equals(txMessage, bos.toByteArray()));
//deserialize/reserialize to check for equals. Set a field to it's existing value to trigger uncache
// deserialize/reserialize to check for equals. Set a field to it's existing value to trigger uncache
tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage));
assertNotNull(tx);
assertEquals(!lazy, tx.isParsed());
assertEquals(true, tx.isCached());
tx.getInputs().get(0).setSequenceNumber(tx.getInputs().get(0).getSequenceNumber());
@ -152,10 +127,8 @@ public class BitcoinSerializerTest {
bos = new ByteArrayOutputStream();
bs.serialize(tx, bos);
assertEquals(true, Arrays.equals(txMessage, bos.toByteArray()));
}
/**
* Get 1 header of the block number 1 (the first one is 0) in the chain
*/
@ -176,7 +149,7 @@ public class BitcoinSerializerTest {
String hash = block.getHashAsString();
assertEquals(hash, "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048");
assertNull(block.transactions);
assertNotNull(block.transactions);
assertEquals(Utils.HEX.encode(block.getMerkleRoot().getBytes()),
"0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
@ -294,12 +267,7 @@ public class BitcoinSerializerTest {
*/
class UnknownMessage extends Message {
@Override
void parse() throws ProtocolException {
}
@Override
protected void parseLite() throws ProtocolException {
protected void parse() throws ProtocolException {
}
}
}

View File

@ -1218,7 +1218,7 @@ public class FullBlockTestGenerator {
for (Transaction transaction : b64Original.block.getTransactions())
transaction.bitcoinSerialize(stream);
b64 = params.getSerializer(false, true).makeBlock(stream.toByteArray(), stream.size());
b64 = params.getSerializer(true).makeBlock(stream.toByteArray(), stream.size());
// The following checks are checking to ensure block serialization functions in the way needed for this test
// If they fail, it is likely not an indication of error, but an indication that this test needs rewritten

View File

@ -1,5 +1,6 @@
/*
* Copyright 2014 Piotr Włodarek
* Copyright 2015 Andreas Schildbach
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -36,12 +37,9 @@ public class MessageTest {
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
readStr();
}
@Override
protected void parseLite() throws ProtocolException {}
}
// If readBytes() is vulnerable this causes OutOfMemory
@ -59,12 +57,8 @@ public class MessageTest {
}
@Override
void parse() throws ProtocolException {
protected void parse() throws ProtocolException {
readByteArray();
}
@Override
protected void parseLite() throws ProtocolException {}
}
}

View File

@ -34,7 +34,7 @@ import static org.bitcoinj.testing.FakeTxBuilder.createFakeBlock;
import static org.bitcoinj.testing.FakeTxBuilder.createFakeTx;
import static org.junit.Assert.*;
public class LazyParseByteCacheTest {
public class ParseByteCacheTest {
private final byte[] txMessage = HEX.withSeparator(" ", 2).decode(
"f9 be b4 d9 74 78 00 00 00 00 00 00 00 00 00 00" +
@ -91,7 +91,7 @@ public class LazyParseByteCacheTest {
valueOf(2, 0),
wallet.currentReceiveKey().toAddress(unitTestParams));
//add a second input so can test granularity of byte cache.
// add a second input so can test granularity of byte cache.
Transaction prevTx = new Transaction(unitTestParams);
TransactionOutput prevOut = new TransactionOutput(unitTestParams, prevTx, COIN, wallet.currentReceiveKey().toAddress(unitTestParams));
prevTx.addOutput(prevOut);
@ -136,235 +136,176 @@ public class LazyParseByteCacheTest {
}
@Test
public void testTransactionsLazyRetain() throws Exception {
testTransaction(MainNetParams.get(), txMessage, false, true, true);
testTransaction(unitTestParams, tx1BytesWithHeader, false, true, true);
testTransaction(unitTestParams, tx2BytesWithHeader, false, true, true);
public void testTransactionsRetain() throws Exception {
testTransaction(MainNetParams.get(), txMessage, false, true);
testTransaction(unitTestParams, tx1BytesWithHeader, false, true);
testTransaction(unitTestParams, tx2BytesWithHeader, false, true);
}
@Test
public void testTransactionsLazyNoRetain() throws Exception {
testTransaction(MainNetParams.get(), txMessage, false, true, false);
testTransaction(unitTestParams, tx1BytesWithHeader, false, true, false);
testTransaction(unitTestParams, tx2BytesWithHeader, false, true, false);
}
@Test
public void testTransactionsNoLazyNoRetain() throws Exception {
testTransaction(MainNetParams.get(), txMessage, false, false, false);
testTransaction(unitTestParams, tx1BytesWithHeader, false, false, false);
testTransaction(unitTestParams, tx2BytesWithHeader, false, false, false);
}
@Test
public void testTransactionsNoLazyRetain() throws Exception {
testTransaction(MainNetParams.get(), txMessage, false, false, true);
testTransaction(unitTestParams, tx1BytesWithHeader, false, false, true);
testTransaction(unitTestParams, tx2BytesWithHeader, false, false, true);
public void testTransactionsNoRetain() throws Exception {
testTransaction(MainNetParams.get(), txMessage, false, false);
testTransaction(unitTestParams, tx1BytesWithHeader, false, false);
testTransaction(unitTestParams, tx2BytesWithHeader, false, false);
}
@Test
public void testBlockAll() throws Exception {
testBlock(b1BytesWithHeader, false, false, false);
testBlock(b1BytesWithHeader, false, true, true);
testBlock(b1BytesWithHeader, false, true, false);
testBlock(b1BytesWithHeader, false, false, true);
testBlock(b1BytesWithHeader, false, false);
testBlock(b1BytesWithHeader, false, true);
}
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.
MessageSerializer bsRef = unitTestParams.getSerializer(false, false);
public void testBlock(byte[] blockBytes, boolean isChild, boolean retain) throws Exception {
// reference serializer to produce comparison serialization output after changes to
// message structure.
MessageSerializer bsRef = unitTestParams.getSerializer(false);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
BitcoinSerializer bs = unitTestParams.getSerializer(lazy, retain);
BitcoinSerializer bs = unitTestParams.getSerializer(retain);
Block b1;
Block bRef;
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
//verify our reference BitcoinSerializer produces matching byte array.
// 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())
// check retain status survive both before and after a serialization
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
// compare to ref block
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
//retrieve a value from a child
// 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.
// 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?
// does it still match ref block?
serDeser(bs, b1, bos.toByteArray(), null, null);
}
//refresh block
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
//retrieve a value from header
// retrieve a value from header
b1.getDifficultyTarget();
assertTrue(b1.isParsedHeader());
assertEquals(lazy, !b1.isParsedTransactions());
//does it still match ref block?
// does it still match ref block?
serDeser(bs, b1, bos.toByteArray(), null, null);
//refresh block
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
//retrieve a value from a child and header
// 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?
// does it still match ref block?
serDeser(bs, b1, bos.toByteArray(), null, null);
//refresh block
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
//change a value in header
// 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?
// does it still match ref block?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
//refresh block
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
//retrieve a value from a child of a child
// 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(retain, tin.isCached());
assertEquals(!lazy, tin.isParsed());
assertEquals(!tin.isParsed() || retain, tin.isCached());
//does it still match ref tx?
// does it still match ref tx?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
}
//refresh block
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
//add an input
// 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
// 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
// confirm sibling cache status was unaffected
if (tx1.getInputs().size() > 1) {
boolean parsed = tx1.getInputs().get(1).isParsed();
assertEquals(!parsed || retain, tx1.getInputs().get(1).isCached());
assertEquals(!lazy, parsed);
assertEquals(retain, tx1.getInputs().get(1).isCached());
}
//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.
// 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.
// confirm we still match the reference tx.
serDeser(bs, b1, source, null, null);
}
//does it still match ref tx?
// does it still match ref tx?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
}
//refresh block
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
Block b2 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
Block bRef2 = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
//reparent an input
// reparent an input
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
@ -374,101 +315,91 @@ public class LazyParseByteCacheTest {
TransactionInput fromTx1 = tx1.getInputs().get(0);
tx2.addInput(fromTx1);
//replicate on reference tx
// 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.
// 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.
// 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.
// confirm altered block matches altered ref block.
serDeser(bs, b2, source, null, null);
}
//does unaltered block still match ref block?
// 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?
// how about if we refresh it?
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(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 {
public void testTransaction(NetworkParameters params, byte[] txBytes, boolean isChild, boolean retain) throws Exception {
//reference serializer to produce comparison serialization output after changes to
//message structure.
MessageSerializer bsRef = params.getSerializer(false, false);
// reference serializer to produce comparison serialization output after changes to
// message structure.
MessageSerializer bsRef = params.getSerializer(false);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
BitcoinSerializer bs = params.getSerializer(lazy, retain);
BitcoinSerializer bs = params.getSerializer(retain);
Transaction t1;
Transaction tRef;
t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes));
tRef = (Transaction) bsRef.deserialize(ByteBuffer.wrap(txBytes));
//verify our reference BitcoinSerializer produces matching byte array.
// 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())
// check and retain status survive both before and after a serialization
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
// compare to ref tx
bos.reset();
bsRef.serialize(tRef, bos);
serDeser(bs, t1, bos.toByteArray(), null, null);
//retrieve a value from a child
// 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?
// does it still match ref tx?
serDeser(bs, t1, bos.toByteArray(), null, null);
}
//refresh tx
// refresh tx
t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes));
tRef = (Transaction) bsRef.deserialize(ByteBuffer.wrap(txBytes));
//add an input
// add an input
if (t1.getInputs().size() > 0) {
t1.addInput(t1.getInputs().get(0));
//replicate on reference tx
// replicate on reference tx
tRef.addInput(tRef.getInputs().get(0));
assertFalse(t1.isCached());
assertTrue(t1.isParsed());
bos.reset();
bsRef.serialize(tRef, bos);
@ -476,7 +407,6 @@ public class LazyParseByteCacheTest {
//confirm we still match the reference tx.
serDeser(bs, t1, source, null, null);
}
}
private void serDeser(MessageSerializer bs, Message message, byte[] sourceBytes, byte[] containedBytes, byte[] containingBytes) throws Exception {
@ -524,6 +454,5 @@ public class LazyParseByteCacheTest {
//System.out.println(sb.append(substring).toString());
//System.out.println();
return ind > -1;
}
}