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:
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;
|
package org.bitcoinj.core;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -20,7 +37,6 @@ public class AddressMessage extends Message {
|
|||||||
* Contruct a new 'addr' message.
|
* Contruct a new 'addr' message.
|
||||||
* @param params NetworkParameters object.
|
* @param params NetworkParameters object.
|
||||||
* @param offset The location of the first payload byte within the array.
|
* @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.
|
* @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
|
* 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.
|
* 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
|
@Override
|
||||||
protected void parseLite() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void parse() throws ProtocolException {
|
|
||||||
long numAddresses = readVarInt();
|
long numAddresses = readVarInt();
|
||||||
// Guard against ultra large messages that will crash us.
|
// Guard against ultra large messages that will crash us.
|
||||||
if (numAddresses > MAX_ADDRESSES)
|
if (numAddresses > MAX_ADDRESSES)
|
||||||
@ -68,7 +80,9 @@ public class AddressMessage extends Message {
|
|||||||
addresses.add(addr);
|
addresses.add(addr);
|
||||||
cursor += addr.getMessageSize();
|
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)
|
/* (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.
|
* @return An unmodifiableList view of the backing List of addresses. Addresses contained within the list may be safely modified.
|
||||||
*/
|
*/
|
||||||
public List<PeerAddress> getAddresses() {
|
public List<PeerAddress> getAddresses() {
|
||||||
maybeParse();
|
|
||||||
return Collections.unmodifiableList(addresses);
|
return Collections.unmodifiableList(addresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAddress(PeerAddress address) {
|
public void addAddress(PeerAddress address) {
|
||||||
unCache();
|
unCache();
|
||||||
maybeParse();
|
|
||||||
address.setParent(this);
|
address.setParent(this);
|
||||||
addresses.add(address);
|
addresses.add(address);
|
||||||
if (length == UNKNOWN_LENGTH)
|
if (length == UNKNOWN_LENGTH)
|
||||||
@ -129,5 +129,4 @@ public class AddressMessage extends Message {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "addr: " + Utils.join(addresses);
|
return "addr: " + Utils.join(addresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011 Google Inc.
|
* Copyright 2011 Google Inc.
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -61,7 +62,7 @@ public class AlertMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
// 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.
|
// data structure containing the actual alert data.
|
||||||
int startPos = cursor;
|
int startPos = cursor;
|
||||||
@ -115,11 +116,6 @@ public class AlertMessage extends Message {
|
|||||||
return ECKey.verify(Sha256Hash.hashTwice(content), signature, params.getAlertSigningKey());
|
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.
|
// Field accessors.
|
||||||
|
|
||||||
|
@ -46,7 +46,6 @@ public class BitcoinSerializer implements MessageSerializer {
|
|||||||
private static final int COMMAND_LEN = 12;
|
private static final int COMMAND_LEN = 12;
|
||||||
|
|
||||||
private final NetworkParameters params;
|
private final NetworkParameters params;
|
||||||
private final boolean parseLazy;
|
|
||||||
private final boolean parseRetain;
|
private final boolean parseRetain;
|
||||||
|
|
||||||
private static final Map<Class<? extends Message>, String> names = new HashMap<Class<? extends Message>, String>();
|
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");
|
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.
|
* Constructs a BitcoinSerializer with the given behavior.
|
||||||
*
|
*
|
||||||
* @param params networkParams used to create Messages instances and termining packetMagic
|
* @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.
|
* @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.params = params;
|
||||||
this.parseLazy = parseLazy;
|
|
||||||
this.parseRetain = parseRetain;
|
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
|
* 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. */
|
/** Stores the hash of the block. If null, getHash() will recalculate it. */
|
||||||
private Sha256Hash hash;
|
private Sha256Hash hash;
|
||||||
|
|
||||||
protected boolean headerParsed;
|
|
||||||
protected boolean transactionsParsed;
|
|
||||||
|
|
||||||
protected boolean headerBytesValid;
|
protected boolean headerBytesValid;
|
||||||
protected boolean transactionBytesValid;
|
protected boolean transactionBytesValid;
|
||||||
|
|
||||||
@ -195,28 +192,6 @@ public class Block extends Message {
|
|||||||
return FIFTY_COINS.shiftRight(height / params.getSubsidyDecreaseBlockCount());
|
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.
|
* Parse transactions from the block.
|
||||||
*
|
*
|
||||||
@ -225,14 +200,10 @@ public class Block extends Message {
|
|||||||
* size.
|
* size.
|
||||||
*/
|
*/
|
||||||
protected void parseTransactions(final int transactionsOffset) throws ProtocolException {
|
protected void parseTransactions(final int transactionsOffset) throws ProtocolException {
|
||||||
if (transactionsParsed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
cursor = transactionsOffset;
|
cursor = transactionsOffset;
|
||||||
optimalEncodingMessageSize = HEADER_SIZE;
|
optimalEncodingMessageSize = HEADER_SIZE;
|
||||||
if (payload.length == cursor) {
|
if (payload.length == cursor) {
|
||||||
// This message is just a header, it has no transactions.
|
// This message is just a header, it has no transactions.
|
||||||
transactionsParsed = true;
|
|
||||||
transactionBytesValid = false;
|
transactionBytesValid = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -248,160 +219,36 @@ public class Block extends Message {
|
|||||||
cursor += tx.getMessageSize();
|
cursor += tx.getMessageSize();
|
||||||
optimalEncodingMessageSize += tx.getOptimalEncodingMessageSize();
|
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();
|
transactionBytesValid = serializer.isParseRetainMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
parseHeader();
|
// header
|
||||||
parseTransactions();
|
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;
|
length = cursor - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOptimalEncodingMessageSize() {
|
public int getOptimalEncodingMessageSize() {
|
||||||
if (optimalEncodingMessageSize != 0)
|
if (optimalEncodingMessageSize != 0)
|
||||||
return optimalEncodingMessageSize;
|
return optimalEncodingMessageSize;
|
||||||
maybeParseTransactions();
|
|
||||||
if (optimalEncodingMessageSize != 0)
|
if (optimalEncodingMessageSize != 0)
|
||||||
return optimalEncodingMessageSize;
|
return optimalEncodingMessageSize;
|
||||||
optimalEncodingMessageSize = bitcoinSerialize().length;
|
optimalEncodingMessageSize = bitcoinSerialize().length;
|
||||||
return optimalEncodingMessageSize;
|
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
|
// default for testing
|
||||||
void writeHeader(OutputStream stream) throws IOException {
|
void writeHeader(OutputStream stream) throws IOException {
|
||||||
// try for cached write first
|
// try for cached write first
|
||||||
@ -410,7 +257,6 @@ public class Block extends Message {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// fall back to manual write
|
// fall back to manual write
|
||||||
maybeParseHeader();
|
|
||||||
Utils.uint32ToByteStreamLE(version, stream);
|
Utils.uint32ToByteStreamLE(version, stream);
|
||||||
stream.write(prevBlockHash.getReversedBytes());
|
stream.write(prevBlockHash.getReversedBytes());
|
||||||
stream.write(getMerkleRoot().getReversedBytes());
|
stream.write(getMerkleRoot().getReversedBytes());
|
||||||
@ -422,7 +268,7 @@ public class Block extends Message {
|
|||||||
private void writeTransactions(OutputStream stream) throws IOException {
|
private void writeTransactions(OutputStream stream) throws IOException {
|
||||||
// check for no transaction conditions first
|
// check for no transaction conditions first
|
||||||
// must be a more efficient way to do this but I'm tired atm.
|
// must be a more efficient way to do this but I'm tired atm.
|
||||||
if (transactions == null && transactionsParsed) {
|
if (transactions == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,7 +355,6 @@ public class Block extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void unCacheHeader() {
|
private void unCacheHeader() {
|
||||||
maybeParseHeader();
|
|
||||||
headerBytesValid = false;
|
headerBytesValid = false;
|
||||||
if (!transactionBytesValid)
|
if (!transactionBytesValid)
|
||||||
payload = null;
|
payload = null;
|
||||||
@ -517,7 +362,6 @@ public class Block extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void unCacheTransactions() {
|
private void unCacheTransactions() {
|
||||||
maybeParseTransactions();
|
|
||||||
transactionBytesValid = false;
|
transactionBytesValid = false;
|
||||||
if (!headerBytesValid)
|
if (!headerBytesValid)
|
||||||
payload = null;
|
payload = null;
|
||||||
@ -584,7 +428,6 @@ public class Block extends Message {
|
|||||||
|
|
||||||
/** Returns a copy of the block, but without any transactions. */
|
/** Returns a copy of the block, but without any transactions. */
|
||||||
public Block cloneAsHeader() {
|
public Block cloneAsHeader() {
|
||||||
maybeParseHeader();
|
|
||||||
Block block = new Block(params, BLOCK_VERSION_GENESIS);
|
Block block = new Block(params, BLOCK_VERSION_GENESIS);
|
||||||
copyBitcoinHeaderTo(block);
|
copyBitcoinHeaderTo(block);
|
||||||
return block;
|
return block;
|
||||||
@ -633,7 +476,6 @@ public class Block extends Message {
|
|||||||
* extraNonce.</p>
|
* extraNonce.</p>
|
||||||
*/
|
*/
|
||||||
public void solve() {
|
public void solve() {
|
||||||
maybeParseHeader();
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
// Is our proof of work valid yet?
|
// Is our proof of work valid yet?
|
||||||
@ -653,7 +495,6 @@ public class Block extends Message {
|
|||||||
* is thrown.
|
* is thrown.
|
||||||
*/
|
*/
|
||||||
public BigInteger getDifficultyTargetAsInteger() throws VerificationException {
|
public BigInteger getDifficultyTargetAsInteger() throws VerificationException {
|
||||||
maybeParseHeader();
|
|
||||||
BigInteger target = Utils.decodeCompactBits(difficultyTarget);
|
BigInteger target = Utils.decodeCompactBits(difficultyTarget);
|
||||||
if (target.signum() <= 0 || target.compareTo(params.maxTarget) > 0)
|
if (target.signum() <= 0 || target.compareTo(params.maxTarget) > 0)
|
||||||
throw new VerificationException("Difficulty target is bad: " + target.toString());
|
throw new VerificationException("Difficulty target is bad: " + target.toString());
|
||||||
@ -685,7 +526,6 @@ public class Block extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkTimestamp() throws VerificationException {
|
private void checkTimestamp() throws VerificationException {
|
||||||
maybeParseHeader();
|
|
||||||
// Allow injection of a fake clock to allow unit testing.
|
// Allow injection of a fake clock to allow unit testing.
|
||||||
long currentTime = Utils.currentTimeSeconds();
|
long currentTime = Utils.currentTimeSeconds();
|
||||||
if (time > currentTime + ALLOWED_TIME_DRIFT)
|
if (time > currentTime + ALLOWED_TIME_DRIFT)
|
||||||
@ -747,7 +587,6 @@ public class Block extends Message {
|
|||||||
// 2 3 4 4
|
// 2 3 4 4
|
||||||
// / \ / \ / \
|
// / \ / \ / \
|
||||||
// t1 t2 t3 t4 t5 t5
|
// t1 t2 t3 t4 t5 t5
|
||||||
maybeParseTransactions();
|
|
||||||
ArrayList<byte[]> tree = new ArrayList<byte[]>();
|
ArrayList<byte[]> tree = new ArrayList<byte[]>();
|
||||||
// Start by adding all the hashes of the transactions as leaves of the tree.
|
// Start by adding all the hashes of the transactions as leaves of the tree.
|
||||||
for (Transaction t : transactions) {
|
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
|
// 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.
|
// enough, it's probably been done by the network.
|
||||||
maybeParseHeader();
|
|
||||||
checkProofOfWork(true);
|
checkProofOfWork(true);
|
||||||
checkTimestamp();
|
checkTimestamp();
|
||||||
}
|
}
|
||||||
@ -813,7 +651,6 @@ public class Block extends Message {
|
|||||||
// transactions that reference spent or non-existant inputs.
|
// transactions that reference spent or non-existant inputs.
|
||||||
if (transactions.isEmpty())
|
if (transactions.isEmpty())
|
||||||
throw new VerificationException("Block had no transactions");
|
throw new VerificationException("Block had no transactions");
|
||||||
maybeParseTransactions();
|
|
||||||
if (this.getOptimalEncodingMessageSize() > MAX_BLOCK_SIZE)
|
if (this.getOptimalEncodingMessageSize() > MAX_BLOCK_SIZE)
|
||||||
throw new VerificationException("Block larger than MAX_BLOCK_SIZE");
|
throw new VerificationException("Block larger than MAX_BLOCK_SIZE");
|
||||||
checkTransactions();
|
checkTransactions();
|
||||||
@ -847,7 +684,6 @@ public class Block extends Message {
|
|||||||
* Returns the merkle root in big endian form, calculating it from transactions if necessary.
|
* Returns the merkle root in big endian form, calculating it from transactions if necessary.
|
||||||
*/
|
*/
|
||||||
public Sha256Hash getMerkleRoot() {
|
public Sha256Hash getMerkleRoot() {
|
||||||
maybeParseHeader();
|
|
||||||
if (merkleRoot == null) {
|
if (merkleRoot == null) {
|
||||||
//TODO check if this is really necessary.
|
//TODO check if this is really necessary.
|
||||||
unCacheHeader();
|
unCacheHeader();
|
||||||
@ -888,7 +724,6 @@ public class Block extends Message {
|
|||||||
|
|
||||||
/** Returns the version of the block data structure as defined by the Bitcoin protocol. */
|
/** Returns the version of the block data structure as defined by the Bitcoin protocol. */
|
||||||
public long getVersion() {
|
public long getVersion() {
|
||||||
maybeParseHeader();
|
|
||||||
return version;
|
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.
|
* Returns the hash of the previous block in the chain, as defined by the block header.
|
||||||
*/
|
*/
|
||||||
public Sha256Hash getPrevBlockHash() {
|
public Sha256Hash getPrevBlockHash() {
|
||||||
maybeParseHeader();
|
|
||||||
return prevBlockHash;
|
return prevBlockHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -911,7 +745,6 @@ public class Block extends Message {
|
|||||||
* is measured in seconds since the UNIX epoch (midnight Jan 1st 1970).
|
* is measured in seconds since the UNIX epoch (midnight Jan 1st 1970).
|
||||||
*/
|
*/
|
||||||
public long getTimeSeconds() {
|
public long getTimeSeconds() {
|
||||||
maybeParseHeader();
|
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -938,7 +771,6 @@ public class Block extends Message {
|
|||||||
* Calculating the difficulty that way is currently unsupported.
|
* Calculating the difficulty that way is currently unsupported.
|
||||||
*/
|
*/
|
||||||
public long getDifficultyTarget() {
|
public long getDifficultyTarget() {
|
||||||
maybeParseHeader();
|
|
||||||
return difficultyTarget;
|
return difficultyTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -954,7 +786,6 @@ public class Block extends Message {
|
|||||||
* difficulty target.
|
* difficulty target.
|
||||||
*/
|
*/
|
||||||
public long getNonce() {
|
public long getNonce() {
|
||||||
maybeParseHeader();
|
|
||||||
return nonce;
|
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. */
|
/** Returns an immutable list of transactions held in this block, or null if this object represents just a header. */
|
||||||
@Nullable
|
@Nullable
|
||||||
public List<Transaction> getTransactions() {
|
public List<Transaction> getTransactions() {
|
||||||
maybeParseTransactions();
|
|
||||||
return transactions == null ? null : ImmutableList.copyOf(transactions);
|
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);
|
return createNextBlock(null, 1, null, Utils.currentTimeSeconds(), pubKey, FIFTY_COINS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean isParsedHeader() {
|
|
||||||
return headerParsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean isParsedTransactions() {
|
|
||||||
return transactionsParsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean isHeaderBytesValid() {
|
boolean isHeaderBytesValid() {
|
||||||
return headerBytesValid;
|
return headerBytesValid;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012 Matt Corallo
|
* Copyright 2012 Matt Corallo
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -130,7 +131,7 @@ public class BloomFilter extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
data = readByteArray();
|
data = readByteArray();
|
||||||
if (data.length > MAX_FILTER_SIZE)
|
if (data.length > MAX_FILTER_SIZE)
|
||||||
throw new ProtocolException ("Bloom filter out of size range.");
|
throw new ProtocolException ("Bloom filter out of size range.");
|
||||||
@ -154,11 +155,6 @@ public class BloomFilter extends Message {
|
|||||||
stream.write(nFlags);
|
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) {
|
private static int rotateLeft32(int x, int r) {
|
||||||
return (x << r) | (x >>> (32 - r));
|
return (x << r) | (x >>> (32 - r));
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.bitcoinj.core;
|
package org.bitcoinj.core;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -47,11 +48,6 @@ class DummySerializer implements MessageSerializer {
|
|||||||
throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE);
|
throw new UnsupportedOperationException(DEFAULT_EXCEPTION_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isParseLazyMode() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isParseRetainMode() {
|
public boolean isParseRetainMode() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011 Steve Coughlan.
|
* Copyright 2011 Steve Coughlan.
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.bitcoinj.core;
|
package org.bitcoinj.core;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -43,31 +45,7 @@ public abstract class EmptyMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMessageSize() {
|
protected void parse() throws ProtocolException {
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
@ -77,6 +55,4 @@ public abstract class EmptyMessage extends Message {
|
|||||||
public byte[] bitcoinSerialize() {
|
public byte[] bitcoinSerialize() {
|
||||||
return new byte[0];
|
return new byte[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2012 Matt Corallo
|
* Copyright 2012 Matt Corallo
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -57,7 +58,7 @@ public class FilteredBlock extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
byte[] headerBytes = new byte[Block.HEADER_SIZE];
|
byte[] headerBytes = new byte[Block.HEADER_SIZE];
|
||||||
System.arraycopy(payload, 0, headerBytes, 0, Block.HEADER_SIZE);
|
System.arraycopy(payload, 0, headerBytes, 0, Block.HEADER_SIZE);
|
||||||
header = params.getDefaultSerializer().makeBlock(headerBytes);
|
header = params.getDefaultSerializer().makeBlock(headerBytes);
|
||||||
@ -67,11 +68,6 @@ public class FilteredBlock extends Message {
|
|||||||
length = Block.HEADER_SIZE + merkleTree.getMessageSize();
|
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
|
* 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 2011 Google Inc.
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -43,22 +44,13 @@ public class GetBlocksMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parseLite() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
cursor = offset;
|
cursor = offset;
|
||||||
version = readUint32();
|
version = readUint32();
|
||||||
int startCount = (int) readVarInt();
|
int startCount = (int) readVarInt();
|
||||||
if (startCount > 500)
|
if (startCount > 500)
|
||||||
throw new ProtocolException("Number of locators cannot be > 500, received: " + startCount);
|
throw new ProtocolException("Number of locators cannot be > 500, received: " + startCount);
|
||||||
length = cursor - offset + ((startCount + 1) * 32);
|
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);
|
locator = new ArrayList<Sha256Hash>(startCount);
|
||||||
for (int i = 0; i < startCount; i++) {
|
for (int i = 0; i < startCount; i++) {
|
||||||
locator.add(readHash());
|
locator.add(readHash());
|
||||||
|
@ -78,11 +78,6 @@ public class GetUTXOsMessage extends Message {
|
|||||||
return outPoints;
|
return outPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void parseLite() throws ProtocolException {
|
|
||||||
// Not needed.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||||
stream.write(new byte[]{includeMempool ? (byte) 1 : 0}); // include mempool.
|
stream.write(new byte[]{includeMempool ? (byte) 1 : 0}); // include mempool.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011 Google Inc.
|
* Copyright 2011 Google Inc.
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -62,7 +63,7 @@ public class HeadersMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parseLite() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
if (length == UNKNOWN_LENGTH) {
|
if (length == UNKNOWN_LENGTH) {
|
||||||
int saveCursor = cursor;
|
int saveCursor = cursor;
|
||||||
long numHeaders = readVarInt();
|
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.
|
// Each header has 80 bytes and one more byte for transactions number which is 00.
|
||||||
length = 81 * (int)numHeaders;
|
length = 81 * (int)numHeaders;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void parse() throws ProtocolException {
|
|
||||||
long numHeaders = readVarInt();
|
long numHeaders = readVarInt();
|
||||||
if (numHeaders > MAX_HEADERS)
|
if (numHeaders > MAX_HEADERS)
|
||||||
throw new ProtocolException("Too many headers: got " + numHeaders + " which is larger than " +
|
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);
|
byte[] blockHeader = readBytes(81);
|
||||||
if (blockHeader[80] != 0)
|
if (blockHeader[80] != 0)
|
||||||
throw new ProtocolException("Block header does not end with a null byte");
|
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);
|
.makeBlock(blockHeader, 81);
|
||||||
blockHeaders.add(newBlockHeader);
|
blockHeaders.add(newBlockHeader);
|
||||||
}
|
}
|
||||||
@ -100,7 +98,6 @@ public class HeadersMessage extends Message {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<Block> getBlockHeaders() {
|
public List<Block> getBlockHeaders() {
|
||||||
return blockHeaders;
|
return blockHeaders;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011 Google Inc.
|
* Copyright 2011 Google Inc.
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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() {
|
public List<InventoryItem> getItems() {
|
||||||
maybeParse();
|
|
||||||
return Collections.unmodifiableList(items);
|
return Collections.unmodifiableList(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,15 +68,12 @@ public abstract class ListMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parseLite() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
arrayLen = readVarInt();
|
arrayLen = readVarInt();
|
||||||
if (arrayLen > MAX_INVENTORY_ITEMS)
|
if (arrayLen > MAX_INVENTORY_ITEMS)
|
||||||
throw new ProtocolException("Too many items in INV message: " + arrayLen);
|
throw new ProtocolException("Too many items in INV message: " + arrayLen);
|
||||||
length = (int) (cursor - offset + (arrayLen * InventoryItem.MESSAGE_LENGTH));
|
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.
|
// 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);
|
items = new ArrayList<InventoryItem>((int) arrayLen);
|
||||||
for (int i = 0; i < arrayLen; i++) {
|
for (int i = 0; i < arrayLen; i++) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012 Google Inc.
|
* Copyright 2012 Google Inc.
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 {
|
public class MemoryPoolMessage extends Message {
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {}
|
protected void parse() throws ProtocolException {}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void parseLite() throws ProtocolException {}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void bitcoinSerializeToStream(OutputStream stream) throws IOException {}
|
void bitcoinSerializeToStream(OutputStream stream) throws IOException {}
|
||||||
|
@ -52,7 +52,6 @@ public abstract class Message {
|
|||||||
// The raw message payload bytes themselves.
|
// The raw message payload bytes themselves.
|
||||||
protected byte[] payload;
|
protected byte[] payload;
|
||||||
|
|
||||||
protected boolean parsed = false;
|
|
||||||
protected boolean recached = false;
|
protected boolean recached = false;
|
||||||
protected MessageSerializer serializer;
|
protected MessageSerializer serializer;
|
||||||
|
|
||||||
@ -61,17 +60,15 @@ public abstract class Message {
|
|||||||
protected NetworkParameters params;
|
protected NetworkParameters params;
|
||||||
|
|
||||||
protected Message() {
|
protected Message() {
|
||||||
parsed = true;
|
|
||||||
serializer = DummySerializer.DEFAULT;
|
serializer = DummySerializer.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message(NetworkParameters params) {
|
protected Message(NetworkParameters params) {
|
||||||
this.params = params;
|
this.params = params;
|
||||||
parsed = true;
|
|
||||||
serializer = params.getDefaultSerializer();
|
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);
|
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
|
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
|
||||||
* @throws ProtocolException
|
* @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.serializer = serializer;
|
||||||
this.protocolVersion = protocolVersion;
|
this.protocolVersion = protocolVersion;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
this.cursor = this.offset = offset;
|
this.cursor = this.offset = offset;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
if (serializer.isParseLazyMode()) {
|
|
||||||
parseLite();
|
parse();
|
||||||
} else {
|
|
||||||
parseLite();
|
|
||||||
parse();
|
|
||||||
parsed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.length == UNKNOWN_LENGTH)
|
if (this.length == UNKNOWN_LENGTH)
|
||||||
checkState(false, "Length field has not been set in constructor for %s after %s parse. " +
|
checkState(false, "Length field has not been set in constructor for %s after parse.",
|
||||||
"Refer to Message.parseLite() for detail of required Length field contract.",
|
getClass().getSimpleName());
|
||||||
getClass().getSimpleName(), serializer.isParseLazyMode() ? "lite" : "full");
|
|
||||||
|
|
||||||
if (SELF_CHECK) {
|
if (SELF_CHECK) {
|
||||||
selfCheck(payload, offset);
|
selfCheck(payload, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serializer.isParseRetainMode() || !parsed)
|
if (!serializer.isParseRetainMode())
|
||||||
return;
|
this.payload = null;
|
||||||
this.payload = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selfCheck(byte[] payload, int offset) {
|
private void selfCheck(byte[] payload, int offset) {
|
||||||
if (!(this instanceof VersionMessage)) {
|
if (!(this instanceof VersionMessage)) {
|
||||||
maybeParse();
|
|
||||||
byte[] payloadBytes = new byte[cursor - offset];
|
byte[] payloadBytes = new byte[cursor - offset];
|
||||||
System.arraycopy(payload, offset, payloadBytes, 0, cursor - offset);
|
System.arraycopy(payload, offset, payloadBytes, 0, cursor - offset);
|
||||||
byte[] reserialized = bitcoinSerialize();
|
byte[] reserialized = bitcoinSerialize();
|
||||||
@ -138,68 +127,15 @@ public abstract class Message {
|
|||||||
|
|
||||||
// These methods handle the serialization/deserialization using the custom Bitcoin protocol.
|
// 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.
|
* <p>To be called before any change of internal values including any setters. This ensures any cached byte array is
|
||||||
* This is only required for subclasses of ChildMessage as root level messages will have their length passed
|
* removed.<p/>
|
||||||
* into the constructor.
|
* <p>Child messages of this object(e.g. Transactions belonging to a Block) will not have their internal byte caches
|
||||||
* <p/>
|
* invalidated unless they are also modified internally.</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.
|
|
||||||
*/
|
*/
|
||||||
protected void unCache() {
|
protected void unCache() {
|
||||||
maybeParse();
|
|
||||||
payload = null;
|
payload = null;
|
||||||
recached = false;
|
recached = false;
|
||||||
}
|
}
|
||||||
@ -220,13 +156,6 @@ public abstract class Message {
|
|||||||
length += VarInt.sizeOf(newArraySize) - VarInt.sizeOf(newArraySize - 1);
|
length += VarInt.sizeOf(newArraySize) - VarInt.sizeOf(newArraySize - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* used for unit testing
|
|
||||||
*/
|
|
||||||
public boolean isParsed() {
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* used for unit testing
|
* 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
|
* This returns a correct value by parsing the message.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
public int getMessageSize() {
|
public final int getMessageSize() {
|
||||||
if (length != UNKNOWN_LENGTH)
|
|
||||||
return length;
|
|
||||||
maybeParse();
|
|
||||||
if (length == UNKNOWN_LENGTH)
|
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;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,16 +371,4 @@ public abstract class Message {
|
|||||||
this.serializer = params.getDefaultSerializer();
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.bitcoinj.core;
|
package org.bitcoinj.core;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -45,11 +46,6 @@ public interface MessageSerializer {
|
|||||||
*/
|
*/
|
||||||
Message deserializePayload(BitcoinSerializer.BitcoinPacketHeader header, ByteBuffer in) throws ProtocolException, BufferUnderflowException, UnsupportedOperationException;
|
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
|
* 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
|
// As the serializers are intended to be immutable, creating
|
||||||
// two due to a race condition should not be a problem, however
|
// two due to a race condition should not be a problem, however
|
||||||
// to be safe we ensure only one exists for each network.
|
// 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.
|
* 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
|
* The number of blocks in the last {@link getMajorityWindow()} blocks
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2012 The Bitcoin Developers
|
* Copyright 2012 The Bitcoin Developers
|
||||||
* Copyright 2012 Matt Corallo
|
* Copyright 2012 Matt Corallo
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -109,7 +110,7 @@ public class PartialMerkleTree extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
transactionCount = (int)readUint32();
|
transactionCount = (int)readUint32();
|
||||||
|
|
||||||
int nHashes = (int) readVarInt();
|
int nHashes = (int) readVarInt();
|
||||||
@ -167,11 +168,6 @@ public class PartialMerkleTree extends Message {
|
|||||||
return combineLeftRight(left.getBytes(), right.getBytes());
|
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
|
// helper function to efficiently calculate the number of nodes at given height in the merkle tree
|
||||||
private static int getTreeWidth(int transactionCount, int height) {
|
private static int getTreeWidth(int transactionCount, int height) {
|
||||||
return (transactionCount + (1 << height) - 1) >> height;
|
return (transactionCount + (1 << height) - 1) >> height;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011 Google Inc.
|
* Copyright 2011 Google Inc.
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 {
|
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);
|
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.
|
* Construct a peer address from a memorized or hardcoded address.
|
||||||
*/
|
*/
|
||||||
@ -139,11 +136,6 @@ public class PeerAddress extends ChildMessage {
|
|||||||
stream.write((byte) (0xFF & port));
|
stream.write((byte) (0xFF & port));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void parseLite() {
|
|
||||||
length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
// Format of a serialized address:
|
// Format of a serialized address:
|
||||||
@ -163,22 +155,15 @@ public class PeerAddress extends ChildMessage {
|
|||||||
throw new RuntimeException(e); // Cannot happen.
|
throw new RuntimeException(e); // Cannot happen.
|
||||||
}
|
}
|
||||||
port = ((0xFF & payload[cursor++]) << 8) | (0xFF & payload[cursor++]);
|
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
|
// The 4 byte difference is the uint32 timestamp that was introduced in version 31402
|
||||||
length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
|
length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
|
||||||
return length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHostname() {
|
public String getHostname() {
|
||||||
maybeParse();
|
|
||||||
return hostname;
|
return hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InetAddress getAddr() {
|
public InetAddress getAddr() {
|
||||||
maybeParse();
|
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,43 +176,33 @@ public class PeerAddress extends ChildMessage {
|
|||||||
this.addr = addr;
|
this.addr = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int getPort() {
|
public int getPort() {
|
||||||
maybeParse();
|
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setPort(int port) {
|
public void setPort(int port) {
|
||||||
unCache();
|
unCache();
|
||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public BigInteger getServices() {
|
public BigInteger getServices() {
|
||||||
maybeParse();
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setServices(BigInteger services) {
|
public void setServices(BigInteger services) {
|
||||||
unCache();
|
unCache();
|
||||||
this.services = services;
|
this.services = services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public long getTime() {
|
public long getTime() {
|
||||||
maybeParse();
|
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setTime(long time) {
|
public void setTime(long time) {
|
||||||
unCache();
|
unCache();
|
||||||
this.time = time;
|
this.time = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (hostname != null) {
|
if (hostname != null) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2011 Noa Resare
|
* Copyright 2011 Noa Resare
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -51,7 +52,7 @@ public class Ping extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
try {
|
try {
|
||||||
nonce = readInt64();
|
nonce = readInt64();
|
||||||
hasNonce = true;
|
hasNonce = true;
|
||||||
@ -61,11 +62,6 @@ public class Ping extends Message {
|
|||||||
length = hasNonce ? 8 : 0;
|
length = hasNonce ? 8 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void parseLite() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNonce() {
|
public boolean hasNonce() {
|
||||||
return hasNonce;
|
return hasNonce;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2012 Matt Corallo
|
* Copyright 2012 Matt Corallo
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -38,7 +39,7 @@ public class Pong extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
nonce = readInt64();
|
nonce = readInt64();
|
||||||
length = 8;
|
length = 8;
|
||||||
}
|
}
|
||||||
@ -48,10 +49,6 @@ public class Pong extends Message {
|
|||||||
Utils.int64ToByteStreamLE(nonce, stream);
|
Utils.int64ToByteStreamLE(nonce, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void parseLite() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the nonce sent by the remote peer. */
|
/** Returns the nonce sent by the remote peer. */
|
||||||
public long getNonce() {
|
public long getNonce() {
|
||||||
return nonce;
|
return nonce;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013 Matt Corallo
|
* Copyright 2013 Matt Corallo
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -82,7 +83,7 @@ public class RejectMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parseLite() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
message = readStr();
|
message = readStr();
|
||||||
code = RejectCode.fromCode(readBytes(1)[0]);
|
code = RejectCode.fromCode(readBytes(1)[0]);
|
||||||
reason = readStr();
|
reason = readStr();
|
||||||
@ -91,12 +92,6 @@ public class RejectMessage extends Message {
|
|||||||
length = cursor - offset;
|
length = cursor - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void parse() throws ProtocolException {
|
|
||||||
if (length == UNKNOWN_LENGTH)
|
|
||||||
parseLite();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||||
byte[] messageBytes = message.getBytes("UTF-8");
|
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.
|
* Note that this is ENTIRELY UNTRUSTED and should be sanity-checked before it is printed or processed.
|
||||||
*/
|
*/
|
||||||
public String getRejectedMessage() {
|
public String getRejectedMessage() {
|
||||||
ensureParsed();
|
|
||||||
return message;
|
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.
|
* Provides the hash of the rejected object (if getRejectedMessage() is either "tx" or "block"), otherwise null.
|
||||||
*/
|
*/
|
||||||
public Sha256Hash getRejectedObjectHash() {
|
public Sha256Hash getRejectedObjectHash() {
|
||||||
ensureParsed();
|
|
||||||
return messageHash;
|
return messageHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +201,6 @@ public class Transaction extends ChildMessage {
|
|||||||
* @param params NetworkParameters object.
|
* @param params NetworkParameters object.
|
||||||
* @param payload Bitcoin protocol formatted byte array containing message content.
|
* @param payload Bitcoin protocol formatted byte array containing message content.
|
||||||
* @param offset The location of the first payload byte within the array.
|
* @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.
|
* @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
|
* 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.
|
* 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.
|
* include spent outputs or not.
|
||||||
*/
|
*/
|
||||||
Coin getValueSentToMe(TransactionBag transactionBag, boolean includeSpent) {
|
Coin getValueSentToMe(TransactionBag transactionBag, boolean includeSpent) {
|
||||||
maybeParse();
|
|
||||||
// This is tested in WalletTest.
|
// This is tested in WalletTest.
|
||||||
Coin v = Coin.ZERO;
|
Coin v = Coin.ZERO;
|
||||||
for (TransactionOutput o : outputs) {
|
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
|
* @return sum of the inputs that are spending coins with keys in the wallet
|
||||||
*/
|
*/
|
||||||
public Coin getValueSentFromMe(TransactionBag wallet) throws ScriptException {
|
public Coin getValueSentFromMe(TransactionBag wallet) throws ScriptException {
|
||||||
maybeParse();
|
|
||||||
// This is tested in WalletTest.
|
// This is tested in WalletTest.
|
||||||
Coin v = Coin.ZERO;
|
Coin v = Coin.ZERO;
|
||||||
for (TransactionInput input : inputs) {
|
for (TransactionInput input : inputs) {
|
||||||
@ -421,7 +418,6 @@ public class Transaction extends ChildMessage {
|
|||||||
* Returns true if any of the outputs is marked as spent.
|
* Returns true if any of the outputs is marked as spent.
|
||||||
*/
|
*/
|
||||||
public boolean isAnyOutputSpent() {
|
public boolean isAnyOutputSpent() {
|
||||||
maybeParse();
|
|
||||||
for (TransactionOutput output : outputs) {
|
for (TransactionOutput output : outputs) {
|
||||||
if (!output.isAvailableForSpending())
|
if (!output.isAvailableForSpending())
|
||||||
return true;
|
return true;
|
||||||
@ -434,7 +430,6 @@ public class Transaction extends ChildMessage {
|
|||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isEveryOwnedOutputSpent(TransactionBag transactionBag) {
|
public boolean isEveryOwnedOutputSpent(TransactionBag transactionBag) {
|
||||||
maybeParse();
|
|
||||||
for (TransactionOutput output : outputs) {
|
for (TransactionOutput output : outputs) {
|
||||||
if (output.isAvailableForSpending() && output.isMineOrWatched(transactionBag))
|
if (output.isAvailableForSpending() && output.isMineOrWatched(transactionBag))
|
||||||
return false;
|
return false;
|
||||||
@ -479,29 +474,6 @@ public class Transaction extends ChildMessage {
|
|||||||
hash = null;
|
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) {
|
protected static int calcLength(byte[] buf, int offset) {
|
||||||
VarInt varint;
|
VarInt varint;
|
||||||
// jump past version (uint32)
|
// jump past version (uint32)
|
||||||
@ -539,11 +511,7 @@ public class Transaction extends ChildMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
|
|
||||||
if (parsed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
cursor = offset;
|
cursor = offset;
|
||||||
|
|
||||||
version = readUint32();
|
version = readUint32();
|
||||||
@ -579,7 +547,6 @@ public class Transaction extends ChildMessage {
|
|||||||
public int getOptimalEncodingMessageSize() {
|
public int getOptimalEncodingMessageSize() {
|
||||||
if (optimalEncodingMessageSize != 0)
|
if (optimalEncodingMessageSize != 0)
|
||||||
return optimalEncodingMessageSize;
|
return optimalEncodingMessageSize;
|
||||||
maybeParse();
|
|
||||||
if (optimalEncodingMessageSize != 0)
|
if (optimalEncodingMessageSize != 0)
|
||||||
return optimalEncodingMessageSize;
|
return optimalEncodingMessageSize;
|
||||||
optimalEncodingMessageSize = getMessageSize();
|
optimalEncodingMessageSize = getMessageSize();
|
||||||
@ -610,7 +577,6 @@ public class Transaction extends ChildMessage {
|
|||||||
* position in a block but by the data in the inputs.
|
* position in a block but by the data in the inputs.
|
||||||
*/
|
*/
|
||||||
public boolean isCoinBase() {
|
public boolean isCoinBase() {
|
||||||
maybeParse();
|
|
||||||
return inputs.size() == 1 && inputs.get(0).isCoinBase();
|
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.
|
* standard and won't be relayed or included in the memory pool either.
|
||||||
*/
|
*/
|
||||||
public long getLockTime() {
|
public long getLockTime() {
|
||||||
maybeParse();
|
|
||||||
return lockTime;
|
return lockTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1098,19 +1063,16 @@ public class Transaction extends ChildMessage {
|
|||||||
* @return the version
|
* @return the version
|
||||||
*/
|
*/
|
||||||
public long getVersion() {
|
public long getVersion() {
|
||||||
maybeParse();
|
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns an unmodifiable view of all inputs. */
|
/** Returns an unmodifiable view of all inputs. */
|
||||||
public List<TransactionInput> getInputs() {
|
public List<TransactionInput> getInputs() {
|
||||||
maybeParse();
|
|
||||||
return Collections.unmodifiableList(inputs);
|
return Collections.unmodifiableList(inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns an unmodifiable view of all outputs. */
|
/** Returns an unmodifiable view of all outputs. */
|
||||||
public List<TransactionOutput> getOutputs() {
|
public List<TransactionOutput> getOutputs() {
|
||||||
maybeParse();
|
|
||||||
return Collections.unmodifiableList(outputs);
|
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
|
* @return linked list of outputs relevant to the wallet in this transaction
|
||||||
*/
|
*/
|
||||||
public List<TransactionOutput> getWalletOutputs(TransactionBag transactionBag){
|
public List<TransactionOutput> getWalletOutputs(TransactionBag transactionBag){
|
||||||
maybeParse();
|
|
||||||
List<TransactionOutput> walletOutputs = new LinkedList<TransactionOutput>();
|
List<TransactionOutput> walletOutputs = new LinkedList<TransactionOutput>();
|
||||||
for (TransactionOutput o : outputs) {
|
for (TransactionOutput o : outputs) {
|
||||||
if (!o.isMineOrWatched(transactionBag)) continue;
|
if (!o.isMineOrWatched(transactionBag)) continue;
|
||||||
@ -1135,19 +1096,16 @@ public class Transaction extends ChildMessage {
|
|||||||
|
|
||||||
/** Randomly re-orders the transaction outputs: good for privacy */
|
/** Randomly re-orders the transaction outputs: good for privacy */
|
||||||
public void shuffleOutputs() {
|
public void shuffleOutputs() {
|
||||||
maybeParse();
|
|
||||||
Collections.shuffle(outputs);
|
Collections.shuffle(outputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Same as getInputs().get(index). */
|
/** Same as getInputs().get(index). */
|
||||||
public TransactionInput getInput(long index) {
|
public TransactionInput getInput(long index) {
|
||||||
maybeParse();
|
|
||||||
return inputs.get((int)index);
|
return inputs.get((int)index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Same as getOutputs().get(index) */
|
/** Same as getOutputs().get(index) */
|
||||||
public TransactionOutput getOutput(long index) {
|
public TransactionOutput getOutput(long index) {
|
||||||
maybeParse();
|
|
||||||
return outputs.get((int)index);
|
return outputs.get((int)index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1197,7 +1155,6 @@ public class Transaction extends ChildMessage {
|
|||||||
* Gets the count of regular SigOps in this transactions
|
* Gets the count of regular SigOps in this transactions
|
||||||
*/
|
*/
|
||||||
public int getSigOpCount() throws ScriptException {
|
public int getSigOpCount() throws ScriptException {
|
||||||
maybeParse();
|
|
||||||
int sigOps = 0;
|
int sigOps = 0;
|
||||||
for (TransactionInput input : inputs)
|
for (TransactionInput input : inputs)
|
||||||
sigOps += Script.getSigOpCount(input.getScriptBytes());
|
sigOps += Script.getSigOpCount(input.getScriptBytes());
|
||||||
@ -1223,7 +1180,6 @@ public class Transaction extends ChildMessage {
|
|||||||
* @throws VerificationException
|
* @throws VerificationException
|
||||||
*/
|
*/
|
||||||
public void verify() throws VerificationException {
|
public void verify() throws VerificationException {
|
||||||
maybeParse();
|
|
||||||
if (inputs.size() == 0 || outputs.size() == 0)
|
if (inputs.size() == 0 || outputs.size() == 0)
|
||||||
throw new VerificationException.EmptyInputsOrOutputs();
|
throw new VerificationException.EmptyInputsOrOutputs();
|
||||||
if (this.getMessageSize() > Block.MAX_BLOCK_SIZE)
|
if (this.getMessageSize() > Block.MAX_BLOCK_SIZE)
|
||||||
|
@ -25,7 +25,6 @@ import com.google.common.base.Objects;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -126,18 +125,11 @@ public class TransactionInput extends ChildMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parseLite() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
int curs = cursor;
|
|
||||||
int scriptLen = (int) readVarInt(36);
|
|
||||||
length = cursor - offset + scriptLen + 4;
|
|
||||||
cursor = curs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void parse() throws ProtocolException {
|
|
||||||
outpoint = new TransactionOutPoint(params, payload, cursor, this, serializer);
|
outpoint = new TransactionOutPoint(params, payload, cursor, this, serializer);
|
||||||
cursor += outpoint.getMessageSize();
|
cursor += outpoint.getMessageSize();
|
||||||
int scriptLen = (int) readVarInt();
|
int scriptLen = (int) readVarInt();
|
||||||
|
length = cursor - offset + scriptLen + 4;
|
||||||
scriptBytes = readBytes(scriptLen);
|
scriptBytes = readBytes(scriptLen);
|
||||||
sequence = readUint32();
|
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.
|
* Coinbase transactions have special inputs with hashes of zero. If this is such an input, returns true.
|
||||||
*/
|
*/
|
||||||
public boolean isCoinBase() {
|
public boolean isCoinBase() {
|
||||||
maybeParse();
|
|
||||||
return outpoint.getHash().equals(Sha256Hash.ZERO_HASH) &&
|
return outpoint.getHash().equals(Sha256Hash.ZERO_HASH) &&
|
||||||
(outpoint.getIndex() & 0xFFFFFFFFL) == 0xFFFFFFFFL; // -1 but all is serialized to the wire as unsigned int.
|
(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.
|
// parameter is overloaded to be something totally different.
|
||||||
Script script = scriptSig == null ? null : scriptSig.get();
|
Script script = scriptSig == null ? null : scriptSig.get();
|
||||||
if (script == null) {
|
if (script == null) {
|
||||||
maybeParse();
|
|
||||||
script = new Script(scriptBytes);
|
script = new Script(scriptBytes);
|
||||||
scriptSig = new WeakReference<Script>(script);
|
scriptSig = new WeakReference<Script>(script);
|
||||||
}
|
}
|
||||||
@ -204,7 +194,6 @@ public class TransactionInput extends ChildMessage {
|
|||||||
* feature is disabled so sequence numbers are unusable.
|
* feature is disabled so sequence numbers are unusable.
|
||||||
*/
|
*/
|
||||||
public long getSequenceNumber() {
|
public long getSequenceNumber() {
|
||||||
maybeParse();
|
|
||||||
return sequence;
|
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.
|
* data needed to connect to the output of the transaction we're gathering coins from.
|
||||||
*/
|
*/
|
||||||
public TransactionOutPoint getOutpoint() {
|
public TransactionOutPoint getOutpoint() {
|
||||||
maybeParse();
|
|
||||||
return outpoint;
|
return outpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +224,6 @@ public class TransactionInput extends ChildMessage {
|
|||||||
* @return the scriptBytes
|
* @return the scriptBytes
|
||||||
*/
|
*/
|
||||||
public byte[] getScriptBytes() {
|
public byte[] getScriptBytes() {
|
||||||
maybeParse();
|
|
||||||
return scriptBytes;
|
return scriptBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011 Google Inc.
|
* Copyright 2011 Google Inc.
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -88,24 +89,12 @@ public class TransactionOutPoint extends ChildMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parseLite() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
length = MESSAGE_LENGTH;
|
length = MESSAGE_LENGTH;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void parse() throws ProtocolException {
|
|
||||||
hash = readHash();
|
hash = readHash();
|
||||||
index = readUint32();
|
index = readUint32();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see Message#getMessageSize()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int getMessageSize() {
|
|
||||||
return MESSAGE_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||||
stream.write(hash.getReversedBytes());
|
stream.write(hash.getReversedBytes());
|
||||||
@ -197,7 +186,6 @@ public class TransactionOutPoint extends ChildMessage {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Sha256Hash getHash() {
|
public Sha256Hash getHash() {
|
||||||
maybeParse();
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +194,6 @@ public class TransactionOutPoint extends ChildMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getIndex() {
|
public long getIndex() {
|
||||||
maybeParse();
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,6 @@ public class TransactionOutput extends ChildMessage {
|
|||||||
|
|
||||||
public Script getScriptPubKey() throws ScriptException {
|
public Script getScriptPubKey() throws ScriptException {
|
||||||
if (scriptPubKey == null) {
|
if (scriptPubKey == null) {
|
||||||
maybeParse();
|
|
||||||
scriptPubKey = new Script(scriptBytes);
|
scriptPubKey = new Script(scriptBytes);
|
||||||
}
|
}
|
||||||
return scriptPubKey;
|
return scriptPubKey;
|
||||||
@ -154,21 +153,16 @@ public class TransactionOutput extends ChildMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parseLite() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
value = readInt64();
|
value = readInt64();
|
||||||
scriptLen = (int) readVarInt();
|
scriptLen = (int) readVarInt();
|
||||||
length = cursor - offset + scriptLen;
|
length = cursor - offset + scriptLen;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void parse() throws ProtocolException {
|
|
||||||
scriptBytes = readBytes(scriptLen);
|
scriptBytes = readBytes(scriptLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||||
checkNotNull(scriptBytes);
|
checkNotNull(scriptBytes);
|
||||||
maybeParse();
|
|
||||||
Utils.int64ToByteStreamLE(value, stream);
|
Utils.int64ToByteStreamLE(value, stream);
|
||||||
// TODO: Move script serialization into the Script class, where it belongs.
|
// TODO: Move script serialization into the Script class, where it belongs.
|
||||||
stream.write(new VarInt(scriptBytes.length).encode());
|
stream.write(new VarInt(scriptBytes.length).encode());
|
||||||
@ -180,7 +174,6 @@ public class TransactionOutput extends ChildMessage {
|
|||||||
* receives.
|
* receives.
|
||||||
*/
|
*/
|
||||||
public Coin getValue() {
|
public Coin getValue() {
|
||||||
maybeParse();
|
|
||||||
try {
|
try {
|
||||||
return Coin.valueOf(value);
|
return Coin.valueOf(value);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@ -286,7 +279,6 @@ public class TransactionOutput extends ChildMessage {
|
|||||||
* @return the scriptBytes
|
* @return the scriptBytes
|
||||||
*/
|
*/
|
||||||
public byte[] getScriptBytes() {
|
public byte[] getScriptBytes() {
|
||||||
maybeParse();
|
|
||||||
return scriptBytes;
|
return scriptBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.bitcoinj.core;
|
package org.bitcoinj.core;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
@ -90,7 +91,7 @@ public class UTXOsMessage extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
// Format is:
|
// Format is:
|
||||||
// uint32 chainHeight
|
// uint32 chainHeight
|
||||||
// uint256 chainHeadHash
|
// uint256 chainHeadHash
|
||||||
@ -123,11 +124,6 @@ public class UTXOsMessage extends Message {
|
|||||||
length = cursor;
|
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.
|
* 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
|
@Override
|
||||||
protected void parseLite() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
// NOP. VersionMessage is never lazy parsed.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void parse() throws ProtocolException {
|
|
||||||
if (parsed)
|
|
||||||
return;
|
|
||||||
parsed = true;
|
|
||||||
|
|
||||||
clientVersion = (int) readUint32();
|
clientVersion = (int) readUint32();
|
||||||
localServices = readUint64().longValue();
|
localServices = readUint64().longValue();
|
||||||
time = readUint64().longValue();
|
time = readUint64().longValue();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013 Google Inc.
|
* Copyright 2013 Google Inc.
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import org.bitcoinj.core.BitcoinSerializer;
|
import org.bitcoinj.core.BitcoinSerializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,8 +137,8 @@ public abstract class AbstractBitcoinNetParams extends NetworkParameters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BitcoinSerializer getSerializer(boolean parseLazy, boolean parseRetain) {
|
public BitcoinSerializer getSerializer(boolean parseRetain) {
|
||||||
return new BitcoinSerializer(this, parseLazy, parseRetain);
|
return new BitcoinSerializer(this, parseRetain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -76,75 +76,50 @@ public class BitcoinSerializerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLazyParsing() throws Exception {
|
public void testCachedParsing() throws Exception {
|
||||||
MessageSerializer bs = MainNetParams.get().getSerializer(true, false);
|
MessageSerializer bs = MainNetParams.get().getSerializer(true);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
//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));
|
Transaction tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage));
|
||||||
assertNotNull(tx);
|
assertNotNull(tx);
|
||||||
assertEquals(!lazy, tx.isParsed());
|
|
||||||
assertEquals(true, tx.isCached());
|
assertEquals(true, tx.isCached());
|
||||||
|
|
||||||
tx.setLockTime(1);
|
tx.setLockTime(1);
|
||||||
//parent should have been uncached
|
// parent should have been uncached
|
||||||
assertEquals(false, tx.isCached());
|
assertEquals(false, tx.isCached());
|
||||||
//child should remain cached.
|
// child should remain cached.
|
||||||
assertEquals(true, tx.getInputs().get(0).isCached());
|
assertEquals(true, tx.getInputs().get(0).isCached());
|
||||||
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
bs.serialize(tx, bos);
|
bs.serialize(tx, bos);
|
||||||
assertEquals(true, !Arrays.equals(txMessage, bos.toByteArray()));
|
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));
|
tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage));
|
||||||
assertNotNull(tx);
|
assertNotNull(tx);
|
||||||
assertEquals(!lazy, tx.isParsed());
|
|
||||||
assertEquals(true, tx.isCached());
|
assertEquals(true, tx.isCached());
|
||||||
|
|
||||||
tx.getInputs().get(0).setSequenceNumber(1);
|
tx.getInputs().get(0).setSequenceNumber(1);
|
||||||
//parent should have been uncached
|
// parent should have been uncached
|
||||||
assertEquals(false, tx.isCached());
|
assertEquals(false, tx.isCached());
|
||||||
//so should child
|
// so should child
|
||||||
assertEquals(false, tx.getInputs().get(0).isCached());
|
assertEquals(false, tx.getInputs().get(0).isCached());
|
||||||
|
|
||||||
bos = new ByteArrayOutputStream();
|
bos = new ByteArrayOutputStream();
|
||||||
bs.serialize(tx, bos);
|
bs.serialize(tx, bos);
|
||||||
assertEquals(true, !Arrays.equals(txMessage, bos.toByteArray()));
|
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));
|
tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage));
|
||||||
assertNotNull(tx);
|
assertNotNull(tx);
|
||||||
assertEquals(!lazy, tx.isParsed());
|
|
||||||
assertEquals(true, tx.isCached());
|
assertEquals(true, tx.isCached());
|
||||||
bos = new ByteArrayOutputStream();
|
bos = new ByteArrayOutputStream();
|
||||||
bs.serialize(tx, bos);
|
bs.serialize(tx, bos);
|
||||||
assertEquals(true, Arrays.equals(txMessage, bos.toByteArray()));
|
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));
|
tx = (Transaction)bs.deserialize(ByteBuffer.wrap(txMessage));
|
||||||
assertNotNull(tx);
|
assertNotNull(tx);
|
||||||
assertEquals(!lazy, tx.isParsed());
|
|
||||||
assertEquals(true, tx.isCached());
|
assertEquals(true, tx.isCached());
|
||||||
|
|
||||||
tx.getInputs().get(0).setSequenceNumber(tx.getInputs().get(0).getSequenceNumber());
|
tx.getInputs().get(0).setSequenceNumber(tx.getInputs().get(0).getSequenceNumber());
|
||||||
@ -152,10 +127,8 @@ public class BitcoinSerializerTest {
|
|||||||
bos = new ByteArrayOutputStream();
|
bos = new ByteArrayOutputStream();
|
||||||
bs.serialize(tx, bos);
|
bs.serialize(tx, bos);
|
||||||
assertEquals(true, Arrays.equals(txMessage, bos.toByteArray()));
|
assertEquals(true, Arrays.equals(txMessage, bos.toByteArray()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get 1 header of the block number 1 (the first one is 0) in the chain
|
* 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();
|
String hash = block.getHashAsString();
|
||||||
assertEquals(hash, "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048");
|
assertEquals(hash, "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048");
|
||||||
|
|
||||||
assertNull(block.transactions);
|
assertNotNull(block.transactions);
|
||||||
|
|
||||||
assertEquals(Utils.HEX.encode(block.getMerkleRoot().getBytes()),
|
assertEquals(Utils.HEX.encode(block.getMerkleRoot().getBytes()),
|
||||||
"0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
"0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
||||||
@ -294,12 +267,7 @@ public class BitcoinSerializerTest {
|
|||||||
*/
|
*/
|
||||||
class UnknownMessage extends Message {
|
class UnknownMessage extends Message {
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void parseLite() throws ProtocolException {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1218,7 +1218,7 @@ public class FullBlockTestGenerator {
|
|||||||
|
|
||||||
for (Transaction transaction : b64Original.block.getTransactions())
|
for (Transaction transaction : b64Original.block.getTransactions())
|
||||||
transaction.bitcoinSerialize(stream);
|
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
|
// 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
|
// 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 2014 Piotr Włodarek
|
||||||
|
* Copyright 2015 Andreas Schildbach
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -36,12 +37,9 @@ public class MessageTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
readStr();
|
readStr();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void parseLite() throws ProtocolException {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If readBytes() is vulnerable this causes OutOfMemory
|
// If readBytes() is vulnerable this causes OutOfMemory
|
||||||
@ -59,12 +57,8 @@ public class MessageTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
protected void parse() throws ProtocolException {
|
||||||
readByteArray();
|
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.bitcoinj.testing.FakeTxBuilder.createFakeTx;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class LazyParseByteCacheTest {
|
public class ParseByteCacheTest {
|
||||||
|
|
||||||
private final byte[] txMessage = HEX.withSeparator(" ", 2).decode(
|
private final byte[] txMessage = HEX.withSeparator(" ", 2).decode(
|
||||||
"f9 be b4 d9 74 78 00 00 00 00 00 00 00 00 00 00" +
|
"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),
|
valueOf(2, 0),
|
||||||
wallet.currentReceiveKey().toAddress(unitTestParams));
|
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);
|
Transaction prevTx = new Transaction(unitTestParams);
|
||||||
TransactionOutput prevOut = new TransactionOutput(unitTestParams, prevTx, COIN, wallet.currentReceiveKey().toAddress(unitTestParams));
|
TransactionOutput prevOut = new TransactionOutput(unitTestParams, prevTx, COIN, wallet.currentReceiveKey().toAddress(unitTestParams));
|
||||||
prevTx.addOutput(prevOut);
|
prevTx.addOutput(prevOut);
|
||||||
@ -136,235 +136,176 @@ public class LazyParseByteCacheTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionsLazyRetain() throws Exception {
|
public void testTransactionsRetain() throws Exception {
|
||||||
testTransaction(MainNetParams.get(), txMessage, false, true, true);
|
testTransaction(MainNetParams.get(), txMessage, false, true);
|
||||||
testTransaction(unitTestParams, tx1BytesWithHeader, false, true, true);
|
testTransaction(unitTestParams, tx1BytesWithHeader, false, true);
|
||||||
testTransaction(unitTestParams, tx2BytesWithHeader, false, true, true);
|
testTransaction(unitTestParams, tx2BytesWithHeader, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionsLazyNoRetain() throws Exception {
|
public void testTransactionsNoRetain() throws Exception {
|
||||||
testTransaction(MainNetParams.get(), txMessage, false, true, false);
|
testTransaction(MainNetParams.get(), txMessage, false, false);
|
||||||
testTransaction(unitTestParams, tx1BytesWithHeader, false, true, false);
|
testTransaction(unitTestParams, tx1BytesWithHeader, false, false);
|
||||||
testTransaction(unitTestParams, tx2BytesWithHeader, false, true, false);
|
testTransaction(unitTestParams, tx2BytesWithHeader, false, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBlockAll() throws Exception {
|
public void testBlockAll() throws Exception {
|
||||||
testBlock(b1BytesWithHeader, false, false, false);
|
testBlock(b1BytesWithHeader, false, false);
|
||||||
testBlock(b1BytesWithHeader, false, true, true);
|
testBlock(b1BytesWithHeader, false, true);
|
||||||
testBlock(b1BytesWithHeader, false, true, false);
|
|
||||||
testBlock(b1BytesWithHeader, false, false, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testBlock(byte[] blockBytes, boolean isChild, boolean retain) throws Exception {
|
||||||
public void testBlock(byte[] blockBytes, boolean isChild, boolean lazy, boolean retain) throws Exception {
|
// reference serializer to produce comparison serialization output after changes to
|
||||||
//reference serializer to produce comparison serialization output after changes to
|
// message structure.
|
||||||
//message structure.
|
MessageSerializer bsRef = unitTestParams.getSerializer(false);
|
||||||
MessageSerializer bsRef = unitTestParams.getSerializer(false, false);
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
BitcoinSerializer bs = unitTestParams.getSerializer(lazy, retain);
|
BitcoinSerializer bs = unitTestParams.getSerializer(retain);
|
||||||
Block b1;
|
Block b1;
|
||||||
Block bRef;
|
Block bRef;
|
||||||
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
bRef = (Block) bsRef.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();
|
bos.reset();
|
||||||
bsRef.serialize(bRef, bos);
|
bsRef.serialize(bRef, bos);
|
||||||
assertTrue(Arrays.equals(bos.toByteArray(), blockBytes));
|
assertTrue(Arrays.equals(bos.toByteArray(), blockBytes));
|
||||||
|
|
||||||
//check lazy and retain status survive both before and after a serialization
|
// check retain status survive both before and after a serialization
|
||||||
assertEquals(!lazy, b1.isParsedTransactions());
|
assertEquals(retain, b1.isHeaderBytesValid());
|
||||||
assertEquals(!lazy, b1.isParsedHeader());
|
assertEquals(retain, b1.isTransactionBytesValid());
|
||||||
if (b1.isParsedHeader())
|
|
||||||
assertEquals(retain, b1.isHeaderBytesValid());
|
|
||||||
if (b1.isParsedTransactions())
|
|
||||||
assertEquals(retain, b1.isTransactionBytesValid());
|
|
||||||
|
|
||||||
serDeser(bs, b1, blockBytes, null, null);
|
serDeser(bs, b1, blockBytes, null, null);
|
||||||
|
|
||||||
assertEquals(!lazy, b1.isParsedTransactions());
|
assertEquals(retain, b1.isHeaderBytesValid());
|
||||||
assertEquals(!lazy, b1.isParsedHeader());
|
assertEquals(retain, b1.isTransactionBytesValid());
|
||||||
if (b1.isParsedHeader())
|
|
||||||
assertEquals(retain, b1.isHeaderBytesValid());
|
|
||||||
if (b1.isParsedTransactions())
|
|
||||||
assertEquals(retain, b1.isTransactionBytesValid());
|
|
||||||
|
|
||||||
//compare to ref block
|
// compare to ref block
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bsRef.serialize(bRef, bos);
|
bsRef.serialize(bRef, bos);
|
||||||
serDeser(bs, b1, bos.toByteArray(), null, null);
|
serDeser(bs, b1, bos.toByteArray(), null, null);
|
||||||
|
|
||||||
//retrieve a value from a child
|
// retrieve a value from a child
|
||||||
b1.getTransactions();
|
b1.getTransactions();
|
||||||
assertTrue(b1.isParsedTransactions());
|
|
||||||
if (b1.getTransactions().size() > 0) {
|
if (b1.getTransactions().size() > 0) {
|
||||||
assertTrue(b1.isParsedTransactions());
|
|
||||||
Transaction tx1 = b1.getTransactions().get(0);
|
Transaction tx1 = b1.getTransactions().get(0);
|
||||||
|
|
||||||
//this will always be true for all children of a block once they are retrieved.
|
// 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.
|
// the tx child inputs/outputs may not be parsed however.
|
||||||
|
|
||||||
//no longer forced to parse if length not provided.
|
assertEquals(retain, tx1.isCached());
|
||||||
//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);
|
serDeser(bs, b1, bos.toByteArray(), null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
//refresh block
|
// refresh block
|
||||||
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
|
|
||||||
//retrieve a value from header
|
// retrieve a value from header
|
||||||
b1.getDifficultyTarget();
|
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);
|
serDeser(bs, b1, bos.toByteArray(), null, null);
|
||||||
|
|
||||||
|
|
||||||
//refresh block
|
// refresh block
|
||||||
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
bRef = (Block) bsRef.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();
|
b1.getDifficultyTarget();
|
||||||
assertTrue(b1.isParsedHeader());
|
|
||||||
assertEquals(lazy, !b1.isParsedTransactions());
|
|
||||||
|
|
||||||
b1.getTransactions();
|
b1.getTransactions();
|
||||||
assertTrue(b1.isParsedTransactions());
|
|
||||||
if (b1.getTransactions().size() > 0) {
|
if (b1.getTransactions().size() > 0) {
|
||||||
assertTrue(b1.isParsedTransactions());
|
|
||||||
Transaction tx1 = b1.getTransactions().get(0);
|
Transaction tx1 = b1.getTransactions().get(0);
|
||||||
|
|
||||||
//no longer forced to parse if length not provided.
|
assertEquals(retain, tx1.isCached());
|
||||||
//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);
|
serDeser(bs, b1, bos.toByteArray(), null, null);
|
||||||
|
|
||||||
//refresh block
|
// refresh block
|
||||||
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
|
|
||||||
//change a value in header
|
// change a value in header
|
||||||
b1.setNonce(23);
|
b1.setNonce(23);
|
||||||
bRef.setNonce(23);
|
bRef.setNonce(23);
|
||||||
assertTrue(b1.isParsedHeader());
|
|
||||||
assertEquals(lazy, !b1.isParsedTransactions());
|
|
||||||
assertFalse(b1.isHeaderBytesValid());
|
assertFalse(b1.isHeaderBytesValid());
|
||||||
if (b1.isParsedTransactions())
|
assertEquals(retain , b1.isTransactionBytesValid());
|
||||||
assertEquals(retain , b1.isTransactionBytesValid());
|
// does it still match ref block?
|
||||||
else
|
|
||||||
assertEquals(true, b1.isTransactionBytesValid());
|
|
||||||
//does it still match ref block?
|
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bsRef.serialize(bRef, bos);
|
bsRef.serialize(bRef, bos);
|
||||||
serDeser(bs, b1, bos.toByteArray(), null, null);
|
serDeser(bs, b1, bos.toByteArray(), null, null);
|
||||||
|
|
||||||
//refresh block
|
// refresh block
|
||||||
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
bRef = (Block) bsRef.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();
|
b1.getTransactions();
|
||||||
if (b1.getTransactions().size() > 0) {
|
if (b1.getTransactions().size() > 0) {
|
||||||
Transaction tx1 = b1.getTransactions().get(0);
|
Transaction tx1 = b1.getTransactions().get(0);
|
||||||
|
|
||||||
TransactionInput tin = tx1.getInputs().get(0);
|
TransactionInput tin = tx1.getInputs().get(0);
|
||||||
|
|
||||||
assertTrue(tx1.isParsed());
|
assertEquals(retain, tin.isCached());
|
||||||
assertTrue(b1.isParsedTransactions());
|
|
||||||
assertEquals(!lazy, b1.isParsedHeader());
|
|
||||||
|
|
||||||
assertEquals(!lazy, tin.isParsed());
|
// does it still match ref tx?
|
||||||
assertEquals(!tin.isParsed() || retain, tin.isCached());
|
|
||||||
|
|
||||||
//does it still match ref tx?
|
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bsRef.serialize(bRef, bos);
|
bsRef.serialize(bRef, bos);
|
||||||
serDeser(bs, b1, bos.toByteArray(), null, null);
|
serDeser(bs, b1, bos.toByteArray(), null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
//refresh block
|
// refresh block
|
||||||
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
|
|
||||||
//add an input
|
// add an input
|
||||||
b1.getTransactions();
|
b1.getTransactions();
|
||||||
if (b1.getTransactions().size() > 0) {
|
if (b1.getTransactions().size() > 0) {
|
||||||
Transaction tx1 = b1.getTransactions().get(0);
|
Transaction tx1 = b1.getTransactions().get(0);
|
||||||
|
|
||||||
if (tx1.getInputs().size() > 0) {
|
if (tx1.getInputs().size() > 0) {
|
||||||
tx1.addInput(tx1.getInputs().get(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));
|
bRef.getTransactions().get(0).addInput(bRef.getTransactions().get(0).getInputs().get(0));
|
||||||
|
|
||||||
assertFalse(tx1.isCached());
|
assertFalse(tx1.isCached());
|
||||||
assertTrue(tx1.isParsed());
|
|
||||||
assertFalse(b1.isTransactionBytesValid());
|
assertFalse(b1.isTransactionBytesValid());
|
||||||
assertTrue(b1.isParsedHeader());
|
|
||||||
|
|
||||||
//confirm sibling cache status was unaffected
|
// confirm sibling cache status was unaffected
|
||||||
if (tx1.getInputs().size() > 1) {
|
if (tx1.getInputs().size() > 1) {
|
||||||
boolean parsed = tx1.getInputs().get(1).isParsed();
|
assertEquals(retain, tx1.getInputs().get(1).isCached());
|
||||||
assertEquals(!parsed || retain, tx1.getInputs().get(1).isCached());
|
|
||||||
assertEquals(!lazy, parsed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//this has to be false. Altering a tx invalidates the merkle root.
|
// 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
|
// when we have seperate merkle caching then the entire header won't need to be
|
||||||
//invalidated.
|
// invalidated.
|
||||||
assertFalse(b1.isHeaderBytesValid());
|
assertFalse(b1.isHeaderBytesValid());
|
||||||
|
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bsRef.serialize(bRef, bos);
|
bsRef.serialize(bRef, bos);
|
||||||
byte[] source = bos.toByteArray();
|
byte[] source = bos.toByteArray();
|
||||||
//confirm we still match the reference tx.
|
// confirm we still match the reference tx.
|
||||||
serDeser(bs, b1, source, null, null);
|
serDeser(bs, b1, source, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
//does it still match ref tx?
|
// does it still match ref tx?
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bsRef.serialize(bRef, bos);
|
bsRef.serialize(bRef, bos);
|
||||||
serDeser(bs, b1, bos.toByteArray(), null, null);
|
serDeser(bs, b1, bos.toByteArray(), null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
//refresh block
|
// refresh block
|
||||||
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
Block b2 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
Block b2 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
Block bRef2 = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
Block bRef2 = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
|
|
||||||
//reparent an input
|
// reparent an input
|
||||||
b1.getTransactions();
|
b1.getTransactions();
|
||||||
if (b1.getTransactions().size() > 0) {
|
if (b1.getTransactions().size() > 0) {
|
||||||
Transaction tx1 = b1.getTransactions().get(0);
|
Transaction tx1 = b1.getTransactions().get(0);
|
||||||
@ -374,109 +315,98 @@ public class LazyParseByteCacheTest {
|
|||||||
TransactionInput fromTx1 = tx1.getInputs().get(0);
|
TransactionInput fromTx1 = tx1.getInputs().get(0);
|
||||||
tx2.addInput(fromTx1);
|
tx2.addInput(fromTx1);
|
||||||
|
|
||||||
//replicate on reference tx
|
// replicate on reference tx
|
||||||
TransactionInput fromTxRef = bRef.getTransactions().get(0).getInputs().get(0);
|
TransactionInput fromTxRef = bRef.getTransactions().get(0).getInputs().get(0);
|
||||||
bRef2.getTransactions().get(0).addInput(fromTxRef);
|
bRef2.getTransactions().get(0).addInput(fromTxRef);
|
||||||
|
|
||||||
//b1 hasn't changed but it's no longer in the parent
|
// 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
|
// chain of fromTx1 so has to have been uncached since it won't be
|
||||||
//notified of changes throught the parent chain anymore.
|
// notified of changes throught the parent chain anymore.
|
||||||
assertFalse(b1.isTransactionBytesValid());
|
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());
|
assertFalse(b2.isTransactionBytesValid());
|
||||||
|
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bsRef.serialize(bRef2, bos);
|
bsRef.serialize(bRef2, bos);
|
||||||
byte[] source = bos.toByteArray();
|
byte[] source = bos.toByteArray();
|
||||||
//confirm altered block matches altered ref block.
|
// confirm altered block matches altered ref block.
|
||||||
serDeser(bs, b2, source, null, null);
|
serDeser(bs, b2, source, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
//does unaltered block still match ref block?
|
// does unaltered block still match ref block?
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bsRef.serialize(bRef, bos);
|
bsRef.serialize(bRef, bos);
|
||||||
serDeser(bs, b1, bos.toByteArray(), null, null);
|
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));
|
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bsRef.serialize(bRef, bos);
|
bsRef.serialize(bRef, bos);
|
||||||
serDeser(bs, b1, bos.toByteArray(), null, null);
|
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
|
// reference serializer to produce comparison serialization output after changes to
|
||||||
//message structure.
|
// message structure.
|
||||||
MessageSerializer bsRef = params.getSerializer(false, false);
|
MessageSerializer bsRef = params.getSerializer(false);
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
BitcoinSerializer bs = params.getSerializer(lazy, retain);
|
BitcoinSerializer bs = params.getSerializer(retain);
|
||||||
Transaction t1;
|
Transaction t1;
|
||||||
Transaction tRef;
|
Transaction tRef;
|
||||||
t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes));
|
t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes));
|
||||||
tRef = (Transaction) bsRef.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();
|
bos.reset();
|
||||||
bsRef.serialize(tRef, bos);
|
bsRef.serialize(tRef, bos);
|
||||||
assertTrue(Arrays.equals(bos.toByteArray(), txBytes));
|
assertTrue(Arrays.equals(bos.toByteArray(), txBytes));
|
||||||
|
|
||||||
//check lazy and retain status survive both before and after a serialization
|
// check and retain status survive both before and after a serialization
|
||||||
assertEquals(!lazy, t1.isParsed());
|
assertEquals(retain, t1.isCached());
|
||||||
if (t1.isParsed())
|
|
||||||
assertEquals(retain, t1.isCached());
|
|
||||||
|
|
||||||
serDeser(bs, t1, txBytes, null, null);
|
serDeser(bs, t1, txBytes, null, null);
|
||||||
|
|
||||||
assertEquals(lazy, !t1.isParsed());
|
assertEquals(retain, t1.isCached());
|
||||||
if (t1.isParsed())
|
|
||||||
assertEquals(retain, t1.isCached());
|
// compare to ref tx
|
||||||
|
|
||||||
//compare to ref tx
|
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bsRef.serialize(tRef, bos);
|
bsRef.serialize(tRef, bos);
|
||||||
serDeser(bs, t1, bos.toByteArray(), null, null);
|
serDeser(bs, t1, bos.toByteArray(), null, null);
|
||||||
|
|
||||||
//retrieve a value from a child
|
// retrieve a value from a child
|
||||||
t1.getInputs();
|
t1.getInputs();
|
||||||
assertTrue(t1.isParsed());
|
|
||||||
if (t1.getInputs().size() > 0) {
|
if (t1.getInputs().size() > 0) {
|
||||||
assertTrue(t1.isParsed());
|
|
||||||
TransactionInput tin = t1.getInputs().get(0);
|
TransactionInput tin = t1.getInputs().get(0);
|
||||||
assertEquals(!lazy, tin.isParsed());
|
assertEquals(retain, tin.isCached());
|
||||||
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);
|
serDeser(bs, t1, bos.toByteArray(), null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
//refresh tx
|
// refresh tx
|
||||||
t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes));
|
t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes));
|
||||||
tRef = (Transaction) bsRef.deserialize(ByteBuffer.wrap(txBytes));
|
tRef = (Transaction) bsRef.deserialize(ByteBuffer.wrap(txBytes));
|
||||||
|
|
||||||
//add an input
|
// add an input
|
||||||
if (t1.getInputs().size() > 0) {
|
if (t1.getInputs().size() > 0) {
|
||||||
|
|
||||||
t1.addInput(t1.getInputs().get(0));
|
t1.addInput(t1.getInputs().get(0));
|
||||||
|
|
||||||
//replicate on reference tx
|
// replicate on reference tx
|
||||||
tRef.addInput(tRef.getInputs().get(0));
|
tRef.addInput(tRef.getInputs().get(0));
|
||||||
|
|
||||||
assertFalse(t1.isCached());
|
assertFalse(t1.isCached());
|
||||||
assertTrue(t1.isParsed());
|
|
||||||
|
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bsRef.serialize(tRef, bos);
|
bsRef.serialize(tRef, bos);
|
||||||
byte[] source = bos.toByteArray();
|
byte[] source = bos.toByteArray();
|
||||||
//confirm we still match the reference tx.
|
//confirm we still match the reference tx.
|
||||||
serDeser(bs, t1, source, null, null);
|
serDeser(bs, t1, source, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void serDeser(MessageSerializer bs, Message message, byte[] sourceBytes, byte[] containedBytes, byte[] containingBytes) throws Exception {
|
private void serDeser(MessageSerializer bs, Message message, byte[] sourceBytes, byte[] containedBytes, byte[] containingBytes) throws Exception {
|
||||||
@ -487,18 +417,18 @@ public class LazyParseByteCacheTest {
|
|||||||
Message m2 = bs.deserialize(ByteBuffer.wrap(b1));
|
Message m2 = bs.deserialize(ByteBuffer.wrap(b1));
|
||||||
|
|
||||||
assertEquals(message, m2);
|
assertEquals(message, m2);
|
||||||
|
|
||||||
bos.reset();
|
bos.reset();
|
||||||
bs.serialize(m2, bos);
|
bs.serialize(m2, bos);
|
||||||
byte[] b2 = bos.toByteArray();
|
byte[] b2 = bos.toByteArray();
|
||||||
assertTrue(Arrays.equals(b1, b2));
|
assertTrue(Arrays.equals(b1, b2));
|
||||||
|
|
||||||
if (sourceBytes != null) {
|
if (sourceBytes != null) {
|
||||||
assertTrue(arrayContains(sourceBytes, b1));
|
assertTrue(arrayContains(sourceBytes, b1));
|
||||||
|
|
||||||
assertTrue(arrayContains(sourceBytes, b2));
|
assertTrue(arrayContains(sourceBytes, b2));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (containedBytes != null) {
|
if (containedBytes != null) {
|
||||||
assertTrue(arrayContains(b1, containedBytes));
|
assertTrue(arrayContains(b1, containedBytes));
|
||||||
}
|
}
|
||||||
@ -524,6 +454,5 @@ public class LazyParseByteCacheTest {
|
|||||||
//System.out.println(sb.append(substring).toString());
|
//System.out.println(sb.append(substring).toString());
|
||||||
//System.out.println();
|
//System.out.println();
|
||||||
return ind > -1;
|
return ind > -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user