3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-31 07:12:17 +00:00

First part of Steves changes in preparation for a high performance multiplexing proxy:

1) Introduce partial support for caching the underlying byte arrays during message deserialization, so re-serialization can be skipped in the case where a message is not modified.
 
 2) Add c'tors that allow a message to be configured to lazily parse on-demand.
 
 Note that the getters/setters that make lazy parsing transparent are coming in future commits.
This commit is contained in:
Mike Hearn 2011-10-11 13:08:54 +00:00
parent ba2351f5aa
commit 34fea86708
17 changed files with 384 additions and 48 deletions

12
TODO
View File

@ -23,3 +23,15 @@ Cleanup:
- Find a way to avoid some horrid hacks when shutting down the network connection.
- Implement a BitCoin class that encapsulates a BigInteger and formatting.
- Make NetworkParameters use subclasses instead of static methods to construct.
==========
Impacts from Steves changes:
- Reformat affected files
- JavaDocs for the c'tors that take parseLazy/parseRetain
- Copyright header/correct author for EmptyMessage/ChildMessage
- Remove superfluous empty lines in ListMessage.java
- Remove VersionMessage check in Message serialization roundtrip checks
- Delete dead code in Message.checkParsed

View File

@ -8,12 +8,20 @@ public class AddressMessage extends Message {
private static final long MAX_ADDRESSES = 1024;
List<PeerAddress> addresses;
AddressMessage(NetworkParameters params, byte[] payload, int offset, boolean parseLazy, boolean parseRetain) throws ProtocolException {
super(params, payload, offset, parseLazy, parseRetain);
}
AddressMessage(NetworkParameters params, byte[] payload, boolean parseLazy, boolean parseRetain) throws ProtocolException {
super(params, payload, 0, parseLazy, parseRetain);
}
AddressMessage(NetworkParameters params, byte[] payload, int offset) throws ProtocolException {
super(params, payload, offset);
super(params, payload, offset, false, false);
}
AddressMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
super(params, payload, 0);
super(params, payload, 0, false, false);
}
@Override
@ -24,7 +32,7 @@ public class AddressMessage extends Message {
throw new ProtocolException("Address message too large.");
addresses = new ArrayList<PeerAddress>((int)numAddresses);
for (int i = 0; i < numAddresses; i++) {
PeerAddress addr = new PeerAddress(params, bytes, cursor, protocolVersion);
PeerAddress addr = new PeerAddress(params, bytes, cursor, protocolVersion, this, parseLazy, parseRetain);
addresses.add(addr);
cursor += addr.getMessageSize();
}

View File

@ -79,6 +79,10 @@ public class Block extends Message {
super(params, payloadBytes, 0);
}
public Block(NetworkParameters params, byte[] payloadBytes, boolean parseLazy, boolean parseRetain) throws ProtocolException {
super(params, payloadBytes, 0, parseLazy, parseRetain);
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
// This code is not actually necessary, as transient fields are initialized to the default value which is in
@ -104,7 +108,7 @@ public class Block extends Message {
int numTransactions = (int) readVarInt();
transactions = new ArrayList<Transaction>(numTransactions);
for (int i = 0; i < numTransactions; i++) {
Transaction tx = new Transaction(params, bytes, cursor);
Transaction tx = new Transaction(params, bytes, cursor, this, parseLazy, parseRetain);
transactions.add(tx);
cursor += tx.getMessageSize();
}
@ -120,13 +124,13 @@ public class Block extends Message {
}
@Override
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
writeHeader(stream);
// We may only have enough data to write the header.
if (transactions == null) return;
stream.write(new VarInt(transactions.size()).encode());
for (Transaction tx : transactions) {
tx.bitcoinSerializeToStream(stream);
tx.bitcoinSerialize(stream);
}
}

View File

@ -0,0 +1,63 @@
package com.google.bitcoin.core;
/**
* Represents a Message type that can be contained within another Message. ChildMessages that have a cached
* backing byte array need to invalidate their parent's caches as well as their own if they are modified.
*
* @author git
*
*/
public abstract class ChildMessage extends Message {
private Message parent;
protected ChildMessage() {
}
public ChildMessage(NetworkParameters params) {
super(params);
}
public ChildMessage(NetworkParameters params, byte[] msg, int offset, int protocolVersion) throws ProtocolException {
super(params, msg, offset, protocolVersion);
}
public ChildMessage(NetworkParameters params, byte[] msg, int offset, int protocolVersion, Message parent, boolean parseLazy,
boolean parseRetain) throws ProtocolException {
super(params, msg, offset, protocolVersion, parseLazy, parseRetain);
this.parent = parent;
}
public ChildMessage(NetworkParameters params, byte[] msg, int offset) throws ProtocolException {
super(params, msg, offset);
}
public ChildMessage(NetworkParameters params, byte[] msg, int offset, Message parent, boolean parseLazy, boolean parseRetain)
throws ProtocolException {
super(params, msg, offset, parseLazy, parseRetain);
this.parent = parent;
}
public void setParent(Message parent) {
if (this.parent != null && this.parent != parent && parent !=null) {
//after old parent is unlinked it won't be able to receive notice if this ChildMessage
//changes internally. To be safe we invalidate the parent cache to ensure it rebuilds
//manually on serialization.
this.parent.unCache();
}
this.parent = parent;
}
/* (non-Javadoc)
* @see com.google.bitcoin.core.Message#unCache()
*/
@Override
protected void unCache() {
super.unCache();
if (parent != null)
parent.unCache();
}
}

View File

@ -4,7 +4,8 @@ import java.io.IOException;
import java.io.OutputStream;
/**
* Parent class for header only message that don't have a payload
* Parent class for header only messages that don't have a payload.
* Currently this includes getaddr, ping, verack as well as the special bitcoinj class UnknownMessage
* @author git
*
*/
@ -17,16 +18,12 @@ public abstract class EmptyMessage extends Message {
super(params);
}
public EmptyMessage(NetworkParameters params, byte[] msg, int offset, int protocolVersion) throws ProtocolException {
super(params, msg, offset, protocolVersion);
}
public EmptyMessage(NetworkParameters params, byte[] msg, int offset) throws ProtocolException {
super(params, msg, offset);
}
@Override
final void bitcoinSerializeToStream(OutputStream stream) throws IOException {
final protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
}
}

View File

@ -17,6 +17,7 @@
package com.google.bitcoin.core;
public class GetAddrMessage extends EmptyMessage {
public GetAddrMessage(NetworkParameters params) {
super(params);
}

View File

@ -52,7 +52,7 @@ public class GetBlocksMessage extends Message {
return b.toString();
}
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
// Version, for some reason.
Utils.uint32ToByteStreamLE(NetworkParameters.PROTOCOL_VERSION, stream);
// Then a vector of block hashes. This is actually a "block locator", a set of block

View File

@ -23,6 +23,11 @@ public class GetDataMessage extends ListMessage {
super(params, payloadBytes);
}
public GetDataMessage(NetworkParameters params, byte[] msg, boolean parseLazy, boolean parseRetain)
throws ProtocolException {
super(params, msg, parseLazy, parseRetain);
}
public GetDataMessage(NetworkParameters params) {
super(params);
}

View File

@ -23,6 +23,11 @@ public class InventoryMessage extends ListMessage {
super(params, bytes);
}
public InventoryMessage(NetworkParameters params, byte[] msg, boolean parseLazy, boolean parseRetain)
throws ProtocolException {
super(params, msg, parseLazy, parseRetain);
}
public InventoryMessage(NetworkParameters params) {
super(params);
}

View File

@ -37,6 +37,14 @@ public abstract class ListMessage extends Message
}
public ListMessage(NetworkParameters params, byte[] msg, boolean parseLazy, boolean parseRetain)
throws ProtocolException {
super(params, msg, 0, parseLazy, parseRetain);
}
public ListMessage(NetworkParameters params) {
super(params);
items = new ArrayList<InventoryItem>();

View File

@ -48,6 +48,10 @@ public abstract class Message implements Serializable {
// The raw message bytes themselves.
protected transient byte[] bytes;
private transient boolean parsed = false;
protected transient final boolean parseLazy;
protected transient final boolean parseRetain;
protected transient int protocolVersion;
// This will be saved by subclasses that implement Serializable.
@ -55,20 +59,36 @@ public abstract class Message implements Serializable {
/** This exists for the Java serialization framework to use only. */
protected Message() {
parsed = true;
parseLazy = false;
parseRetain = false;
}
Message(NetworkParameters params) {
this.params = params;
parsed = true;
parseLazy = false;
parseRetain = false;
}
Message(NetworkParameters params, byte[] msg, int offset, int protocolVersion) throws ProtocolException {
this(params, msg, offset, protocolVersion, false, false);
}
@SuppressWarnings("unused")
Message(NetworkParameters params, byte[] msg, int offset, int protocolVersion) throws ProtocolException {
Message(NetworkParameters params, byte[] msg, int offset, int protocolVersion, final boolean parseLazy, final boolean parseRetain) throws ProtocolException {
this.parseLazy = parseLazy;
this.parseRetain = parseRetain;
this.protocolVersion = protocolVersion;
this.params = params;
this.bytes = msg;
this.cursor = this.offset = offset;
if (!parseLazy) {
parse();
parsed = true;
}
if (SELF_CHECK && !this.getClass().getSimpleName().equals("VersionMessage")) {
checkParse();
byte[] msgbytes = new byte[cursor - offset];
System.arraycopy(msg, offset, msgbytes, 0, cursor - offset);
byte[] reserialized = bitcoinSerialize();
@ -77,11 +97,17 @@ public abstract class Message implements Serializable {
Utils.bytesToHexString(reserialized) + " vs \n" +
Utils.bytesToHexString(msgbytes));
}
if (parseRetain || !parsed)
return;
this.bytes = null;
}
Message(NetworkParameters params, byte[] msg, int offset) throws ProtocolException {
this(params, msg, offset, NetworkParameters.PROTOCOL_VERSION);
this(params, msg, offset, NetworkParameters.PROTOCOL_VERSION, false, false);
}
Message(NetworkParameters params, byte[] msg, int offset, final boolean parseLazy, final boolean parseRetain) throws ProtocolException {
this(params, msg, offset, NetworkParameters.PROTOCOL_VERSION, parseLazy, parseRetain);
}
// These methods handle the serialization/deserialization using the custom BitCoin protocol.
@ -90,7 +116,86 @@ public abstract class Message implements Serializable {
// are serialized to the wallet.
abstract void parse() 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 checkParse() {
if (parsed || bytes == null)
return;
try {
parse();
parsed = true;
//if (!parseRetain)
//bytes = null;
} catch (ProtocolException e) {
throw new RuntimeException("Lazy parsing of message failed", 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.
*
* 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() {
/*
* This is a NOP at the moment. Will complete lazy parsing as a separate patch first.
* safe retention of backing byte array is tricky in cases where a parent Message object
* may have child message objects (e.g. block - tx). There has to be a way to either
* mark the cursor at the end of the parent portion of the array or a way the child can
* invalidate the parent array. This might require a ByteArrayView class which implements List
* and retains a reference to it's parent ByteArrayView so it can invalidate it.
* Alternately the child message can hold a reference to
* it's parent and propagate a call to unCache up the chain to the parent. This way only those children on the
* invalidated branch lose their caching. On the other hand this might introduce more overhead than it's worth
* since this call has to made in every setter.
* Perhaps a simpler approach where in the special cases where a cached array is wanted it is the callers responsibility
* to keep track of whether the cache is valid or not.
*/
//if (!parseRetain)
// return;
//checkParse();
//bytes = null;
}
/**
* Serialize this message to a byte array that conforms to the bitcoin wire protocol.
* <br/>
* This method may return the original byte array used to construct this message if the
* following conditions are met:
* <ol>
* <li>1) The message was parsed from a byte array with parseRetain = true</li>
* <li>2) The message has not been modified</li>
* <li>3) The array had an offset of 0 and no surplus bytes</li>
* </ol>
*
* If condition 3 is not met then an copy of the relevant portion of the array will be returned.
* Otherwise a full serialize will occur.
*
* @return
*/
final public byte[] bitcoinSerialize() {
//1st attempt to use a cached array
if (bytes != null) {
if (offset == 0 && cursor == bytes.length) {
//cached byte array is the entire message with no extras
//so we can return as is and avoid an array copy.
return bytes;
}
int len = cursor - offset;
byte[] buf = new byte[len];
System.arraycopy(bytes, offset, buf, 0, len);
return buf;
}
//no cached array available so serialize parts by stream.
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
bitcoinSerializeToStream(stream);
@ -101,6 +206,20 @@ public abstract class Message implements Serializable {
return stream.toByteArray();
}
/**
* Serialize this message to the provided OutputStream using the bitcoin wire format.
* @param stream
* @throws IOException
*/
final public void bitcoinSerialize(OutputStream stream) throws IOException {
//1st check for cached bytes
if (bytes != null) {
stream.write(bytes, offset, cursor - offset);
return;
}
bitcoinSerializeToStream(stream);
}
/**
* Serializes this message to the provided stream. If you just want the raw bytes use bitcoinSerialize().
*/

View File

@ -31,7 +31,7 @@ import static com.google.bitcoin.core.Utils.uint64ToByteStreamLE;
* A PeerAddress holds an IP address and port number representing the network location of
* a peer in the BitCoin P2P network. It exists primarily for serialization purposes.
*/
public class PeerAddress extends Message {
public class PeerAddress extends ChildMessage {
private static final long serialVersionUID = 7501293709324197411L;
InetAddress addr;
@ -46,6 +46,17 @@ public class PeerAddress extends Message {
super(params, payload, offset, protocolVersion);
}
/**
* Construct a peer address from a serialized payload.
*/
public PeerAddress(NetworkParameters params, byte[] msg, int offset, int protocolVersion, Message parent, boolean parseLazy,
boolean parseRetain) throws ProtocolException {
super(params, msg, offset, protocolVersion, parent, parseLazy, parseRetain);
}
/**
* Construct a peer address from a memorized or hardcoded address.
*/
@ -69,7 +80,7 @@ public class PeerAddress extends Message {
}
@Override
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
if (protocolVersion >= 31402) {
int secs = (int)(Utils.now().getTime() / 1000);
uint32ToByteStreamLE(secs, stream);

View File

@ -18,6 +18,7 @@ package com.google.bitcoin.core;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.math.BigInteger;
@ -37,7 +38,7 @@ import static com.google.bitcoin.core.Utils.*;
* serialization which is used for the wallet. This allows us to easily add extra fields used for our own accounting
* or UI purposes.
*/
public class Transaction extends Message implements Serializable {
public class Transaction extends ChildMessage implements Serializable {
private static final Logger log = LoggerFactory.getLogger(Transaction.class);
private static final long serialVersionUID = -8567546957352643140L;
@ -89,6 +90,22 @@ public class Transaction extends Message implements Serializable {
// inputs/outputs will be created in parse()
}
/**
* Creates a transaction by reading payload starting from offset bytes in. Length of a transaction is fixed.
*/
public Transaction(NetworkParameters params, byte[] msg, int offset, Message parent, boolean parseLazy, boolean parseRetain)
throws ProtocolException {
super(params, msg, offset, parent, parseLazy, parseRetain);
}
/**
* Creates a transaction by reading payload starting from offset bytes in. Length of a transaction is fixed.
*/
public Transaction(NetworkParameters params, byte[] msg, Message parent, boolean parseLazy, boolean parseRetain)
throws ProtocolException {
super(params, msg, 0, parent, parseLazy, parseRetain);
}
/**
* Returns a read-only list of the inputs of this transaction.
*/
@ -272,7 +289,7 @@ public class Transaction extends Message implements Serializable {
long numInputs = readVarInt();
inputs = new ArrayList<TransactionInput>((int)numInputs);
for (long i = 0; i < numInputs; i++) {
TransactionInput input = new TransactionInput(params, this, bytes, cursor);
TransactionInput input = new TransactionInput(params, this, bytes, cursor, parseLazy, parseRetain);
inputs.add(input);
cursor += input.getMessageSize();
}
@ -280,7 +297,7 @@ public class Transaction extends Message implements Serializable {
long numOutputs = readVarInt();
outputs = new ArrayList<TransactionOutput>((int)numOutputs);
for (long i = 0; i < numOutputs; i++) {
TransactionOutput output = new TransactionOutput(params, this, bytes, cursor);
TransactionOutput output = new TransactionOutput(params, this, bytes, cursor, parseLazy, parseRetain);
outputs.add(output);
cursor += output.getMessageSize();
}
@ -457,14 +474,14 @@ public class Transaction extends Message implements Serializable {
}
@Override
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
uint32ToByteStreamLE(version, stream);
stream.write(new VarInt(inputs.size()).encode());
for (TransactionInput in : inputs)
in.bitcoinSerializeToStream(stream);
in.bitcoinSerialize(stream);
stream.write(new VarInt(outputs.size()).encode());
for (TransactionOutput out : outputs)
out.bitcoinSerializeToStream(stream);
out.bitcoinSerialize(stream);
uint32ToByteStreamLE(lockTime, stream);
}
@ -480,4 +497,15 @@ public class Transaction extends Message implements Serializable {
public int hashCode() {
return getHash().hashCode();
}
/**
* Ensure object is fully parsed before invoking java serialization. The backing byte array
* is transient so if the object has parseLazy = true and hasn't invoked checkParse yet
* then data will be lost during serialization.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
checkParse();
out.defaultWriteObject();
}
}

View File

@ -17,6 +17,7 @@
package com.google.bitcoin.core;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Map;
@ -27,7 +28,7 @@ import java.util.Map;
* transaction as being a module which is wired up to others, the inputs of one have to be wired
* to the outputs of another. The exceptions are coinbase transactions, which create new coins.
*/
public class TransactionInput extends Message implements Serializable {
public class TransactionInput extends ChildMessage implements Serializable {
private static final long serialVersionUID = 2;
public static final byte[] EMPTY_ARRAY = new byte[0];
@ -73,8 +74,15 @@ public class TransactionInput extends Message implements Serializable {
this.parentTransaction = parentTransaction;
}
/** Deserializes an input message. This is usually part of a transaction message. */
public TransactionInput(NetworkParameters params, Transaction parentTransaction, byte[] msg, int offset, boolean parseLazy, boolean parseRetain)
throws ProtocolException {
super(params, msg, offset, parentTransaction, parseLazy, parseRetain);
this.parentTransaction = parentTransaction;
}
void parse() throws ProtocolException {
outpoint = new TransactionOutPoint(params, bytes, cursor);
outpoint = new TransactionOutPoint(params, bytes, cursor, this, parseLazy, parseRetain);
cursor += outpoint.getMessageSize();
int scriptLen = (int) readVarInt();
scriptBytes = readBytes(scriptLen);
@ -82,8 +90,8 @@ public class TransactionInput extends Message implements Serializable {
}
@Override
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
outpoint.bitcoinSerializeToStream(stream);
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
outpoint.bitcoinSerialize(stream);
stream.write(new VarInt(scriptBytes.length).encode());
stream.write(scriptBytes);
Utils.uint32ToByteStreamLE(sequence, stream);
@ -187,4 +195,14 @@ public class TransactionInput extends Message implements Serializable {
outpoint.fromTx = null;
return true;
}
/**
* Ensure object is fully parsed before invoking java serialization. The backing byte array
* is transient so if the object has parseLazy = true and hasn't invoked checkParse yet
* then data will be lost during serialization.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
checkParse();
out.defaultWriteObject();
}
}

View File

@ -17,6 +17,7 @@
package com.google.bitcoin.core;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
@ -25,7 +26,7 @@ import java.io.Serializable;
/**
* This message is a reference or pointer to an output of a different transaction.
*/
public class TransactionOutPoint extends Message implements Serializable {
public class TransactionOutPoint extends ChildMessage implements Serializable {
private static final long serialVersionUID = -6320880638344662579L;
/** Hash of the transaction to which we refer. */
@ -54,6 +55,11 @@ public class TransactionOutPoint extends Message implements Serializable {
super(params, payload, offset);
}
/** Deserializes the message. This is usually part of a transaction message. */
public TransactionOutPoint(NetworkParameters params, byte[] payload, int offset, Message parent, boolean parseLazy, boolean parseRetain) throws ProtocolException {
super(params, payload, offset, parent, parseLazy, parseRetain);
}
@Override
void parse() throws ProtocolException {
hash = readHash();
@ -61,7 +67,7 @@ public class TransactionOutPoint extends Message implements Serializable {
}
@Override
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
stream.write(Utils.reverseBytes(hash.getBytes()));
Utils.uint32ToByteStreamLE(index, stream);
}
@ -96,4 +102,14 @@ public class TransactionOutPoint extends Message implements Serializable {
public String toString() {
return "outpoint " + index + ":" + hash.toString();
}
/**
* Ensure object is fully parsed before invoking java serialization. The backing byte array
* is transient so if the object has parseLazy = true and hasn't invoked checkParse yet
* then data will be lost during serialization.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
checkParse();
out.defaultWriteObject();
}
}

View File

@ -20,6 +20,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.math.BigInteger;
@ -28,7 +29,7 @@ import java.math.BigInteger;
* A TransactionOutput message contains a scriptPubKey that controls who is able to spend its value. It is a sub-part
* of the Transaction message.
*/
public class TransactionOutput extends Message implements Serializable {
public class TransactionOutput extends ChildMessage implements Serializable {
private static final Logger log = LoggerFactory.getLogger(TransactionOutput.class);
private static final long serialVersionUID = -590332479859256824L;
@ -58,6 +59,14 @@ public class TransactionOutput extends Message implements Serializable {
availableForSpending = true;
}
/** Deserializes a transaction output message. This is usually part of a transaction message. */
public TransactionOutput(NetworkParameters params, Transaction parent, byte[] msg, int offset, boolean parseLazy, boolean parseRetain)
throws ProtocolException {
super(params, msg, offset, parent, parseLazy, parseRetain);
parentTransaction = parent;
availableForSpending = true;
}
TransactionOutput(NetworkParameters params, Transaction parent, BigInteger value, Address to) {
super(params);
this.value = value;
@ -88,7 +97,7 @@ public class TransactionOutput extends Message implements Serializable {
}
@Override
public void bitcoinSerializeToStream( OutputStream stream) throws IOException {
protected void bitcoinSerializeToStream( OutputStream stream) throws IOException {
assert scriptBytes != null;
Utils.uint64ToByteStreamLE(getValue(), stream);
// TODO: Move script serialization into the Script class, where it belongs.
@ -162,4 +171,14 @@ public class TransactionOutput extends Message implements Serializable {
TransactionInput getSpentBy() {
return spentBy;
}
/**
* Ensure object is fully parsed before invoking java serialization. The backing byte array
* is transient so if the object has parseLazy = true and hasn't invoked checkParse yet
* then data will be lost during serialization.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
checkParse();
out.defaultWriteObject();
}
}

View File

@ -51,6 +51,15 @@ public class VersionMessage extends Message {
super(params, msg, 0);
}
/**
* It doesn't really make sense to ever lazily parse a version message or to retain the backing bytes.
* If you're receiving this on the wire you need to check the protocol version and it will never need to be sent
* back down the wire.
*/
// public VersionMessage(NetworkParameters params, byte[] msg, boolean parseLazy, boolean parseRetain) throws ProtocolException {
// super(params, msg, 0, parseLazy, parseRetain);
// }
public VersionMessage(NetworkParameters params, int newBestHeight) {
super(params);
clientVersion = NetworkParameters.PROTOCOL_VERSION;
@ -96,9 +105,9 @@ public class VersionMessage extends Message {
Utils.uint32ToByteStreamLE(time >> 32, buf);
try {
// My address.
myAddr.bitcoinSerializeToStream(buf);
myAddr.bitcoinSerialize(buf);
// Their address.
theirAddr.bitcoinSerializeToStream(buf);
theirAddr.bitcoinSerialize(buf);
} catch (UnknownHostException e) {
throw new RuntimeException(e); // Can't happen.
} catch (IOException e) {
@ -143,4 +152,17 @@ public class VersionMessage extends Message {
return (int)bestHeight ^ clientVersion ^ (int)localServices ^ (int)time ^ subVer.hashCode() ^ myAddr.hashCode()
^ theirAddr.hashCode();
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("\n");
sb.append("client version: ").append(clientVersion).append("\n");
sb.append("local services: ").append(localServices).append("\n");
sb.append("time: ").append(time).append("\n");
sb.append("my addr: ").append(myAddr).append("\n");
sb.append("their addr: ").append(theirAddr).append("\n");
sb.append("sub version: ").append(subVer).append("\n");
sb.append("best height: ").append(bestHeight).append("\n");
return sb.toString();
}
}