mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-13 10:45:51 +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:
parent
d753d28ba5
commit
7744a00629
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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++) {
|
||||
|
@ -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 {}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -75,35 +75,13 @@ 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);
|
||||
}
|
||||
|
||||
private void testCachedParsing(boolean lazy) throws Exception {
|
||||
MessageSerializer bs = MainNetParams.get().getSerializer(lazy, true);
|
||||
MessageSerializer bs = MainNetParams.get().getSerializer(true);
|
||||
|
||||
// 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);
|
||||
@ -119,7 +97,6 @@ public class BitcoinSerializerTest {
|
||||
// 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);
|
||||
@ -135,7 +112,6 @@ public class BitcoinSerializerTest {
|
||||
// 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);
|
||||
@ -144,7 +120,6 @@ public class BitcoinSerializerTest {
|
||||
// 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 {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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" +
|
||||
@ -136,49 +136,32 @@ 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 {
|
||||
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, false);
|
||||
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));
|
||||
@ -189,21 +172,13 @@ public class LazyParseByteCacheTest {
|
||||
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
|
||||
@ -213,20 +188,13 @@ public class LazyParseByteCacheTest {
|
||||
|
||||
// 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);
|
||||
@ -238,35 +206,22 @@ public class LazyParseByteCacheTest {
|
||||
|
||||
// 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(ByteBuffer.wrap(blockBytes));
|
||||
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(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);
|
||||
@ -278,13 +233,8 @@ public class LazyParseByteCacheTest {
|
||||
// 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);
|
||||
@ -301,12 +251,7 @@ public class LazyParseByteCacheTest {
|
||||
|
||||
TransactionInput tin = tx1.getInputs().get(0);
|
||||
|
||||
assertTrue(tx1.isParsed());
|
||||
assertTrue(b1.isParsedTransactions());
|
||||
assertEquals(!lazy, b1.isParsedHeader());
|
||||
|
||||
assertEquals(!lazy, tin.isParsed());
|
||||
assertEquals(!tin.isParsed() || retain, tin.isCached());
|
||||
assertEquals(retain, tin.isCached());
|
||||
|
||||
// does it still match ref tx?
|
||||
bos.reset();
|
||||
@ -329,15 +274,11 @@ public class LazyParseByteCacheTest {
|
||||
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, 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.
|
||||
@ -404,17 +345,16 @@ public class LazyParseByteCacheTest {
|
||||
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);
|
||||
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));
|
||||
@ -425,15 +365,11 @@ public class LazyParseByteCacheTest {
|
||||
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
|
||||
@ -443,12 +379,8 @@ public class LazyParseByteCacheTest {
|
||||
|
||||
// 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?
|
||||
@ -468,7 +400,6 @@ public class LazyParseByteCacheTest {
|
||||
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;
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user