mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-30 23:02:15 +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:
parent
ba2351f5aa
commit
34fea86708
12
TODO
12
TODO
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -78,6 +78,10 @@ public class Block extends Message {
|
||||
public Block(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
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();
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
63
src/com/google/bitcoin/core/ChildMessage.java
Normal file
63
src/com/google/bitcoin/core/ChildMessage.java
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.google.bitcoin.core;
|
||||
|
||||
public class GetAddrMessage extends EmptyMessage {
|
||||
|
||||
public GetAddrMessage(NetworkParameters params) {
|
||||
super(params);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -22,8 +22,13 @@ public class GetDataMessage extends ListMessage {
|
||||
public GetDataMessage(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes);
|
||||
}
|
||||
|
||||
public GetDataMessage(NetworkParameters params, byte[] msg, boolean parseLazy, boolean parseRetain)
|
||||
throws ProtocolException {
|
||||
super(params, msg, parseLazy, parseRetain);
|
||||
}
|
||||
|
||||
public GetDataMessage(NetworkParameters params) {
|
||||
public GetDataMessage(NetworkParameters params) {
|
||||
super(params);
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,12 @@ public class InventoryMessage extends ListMessage {
|
||||
super(params, bytes);
|
||||
}
|
||||
|
||||
public InventoryMessage(NetworkParameters params) {
|
||||
public InventoryMessage(NetworkParameters params, byte[] msg, boolean parseLazy, boolean parseRetain)
|
||||
throws ProtocolException {
|
||||
super(params, msg, parseLazy, parseRetain);
|
||||
}
|
||||
|
||||
public InventoryMessage(NetworkParameters params) {
|
||||
super(params);
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,16 @@ public abstract class ListMessage extends Message
|
||||
super(params, bytes, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ListMessage(NetworkParameters params) {
|
||||
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>();
|
||||
}
|
||||
|
@ -47,6 +47,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;
|
||||
|
||||
@ -55,21 +59,37 @@ 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;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
Message(NetworkParameters params, byte[] msg, int offset, int protocolVersion) throws ProtocolException {
|
||||
this.protocolVersion = protocolVersion;
|
||||
this(params, msg, offset, protocolVersion, false, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
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;
|
||||
parse();
|
||||
if (!parseLazy) {
|
||||
parse();
|
||||
parsed = true;
|
||||
}
|
||||
if (SELF_CHECK && !this.getClass().getSimpleName().equals("VersionMessage")) {
|
||||
byte[] msgbytes = new byte[cursor - offset];
|
||||
checkParse();
|
||||
byte[] msgbytes = new byte[cursor - offset];
|
||||
System.arraycopy(msg, offset, msgbytes, 0, cursor - offset);
|
||||
byte[] reserialized = bitcoinSerialize();
|
||||
if (!Arrays.equals(reserialized, msgbytes))
|
||||
@ -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,8 +116,87 @@ 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() {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
//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);
|
||||
} catch (IOException e) {
|
||||
@ -100,6 +205,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().
|
||||
|
@ -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,7 +46,18 @@ 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.
|
||||
*/
|
||||
public PeerAddress(InetAddress addr, int port, int protocolVersion) {
|
||||
@ -68,8 +79,8 @@ public class PeerAddress extends Message {
|
||||
this(addr.getAddress(), addr.getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
@Override
|
||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
if (protocolVersion >= 31402) {
|
||||
int secs = (int)(Utils.now().getTime() / 1000);
|
||||
uint32ToByteStreamLE(secs, stream);
|
||||
|
@ -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;
|
||||
|
||||
@ -80,7 +81,7 @@ public class Transaction extends Message implements Serializable {
|
||||
public Transaction(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a transaction by reading payload starting from offset bytes in. Length of a transaction is fixed.
|
||||
*/
|
||||
@ -88,8 +89,24 @@ public class Transaction extends Message implements Serializable {
|
||||
super(params, payload, offset);
|
||||
// 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.
|
||||
*/
|
||||
public List<TransactionInput> getInputs() {
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
void parse() throws ProtocolException {
|
||||
outpoint = new TransactionOutPoint(params, bytes, cursor);
|
||||
/** 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, 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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
@ -53,12 +54,20 @@ public class TransactionOutput extends Message implements Serializable {
|
||||
/** Deserializes a transaction output message. This is usually part of a transaction message. */
|
||||
public TransactionOutput(NetworkParameters params, Transaction parent, byte[] payload,
|
||||
int offset) throws ProtocolException {
|
||||
super(params, payload, offset);
|
||||
parentTransaction = parent;
|
||||
super(params, payload, offset);
|
||||
parentTransaction = parent;
|
||||
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) {
|
||||
TransactionOutput(NetworkParameters params, Transaction parent, BigInteger value, Address to) {
|
||||
super(params);
|
||||
this.value = value;
|
||||
this.scriptBytes = Script.createOutputScript(to);
|
||||
@ -75,7 +84,7 @@ public class TransactionOutput extends Message implements Serializable {
|
||||
availableForSpending = true;
|
||||
}
|
||||
|
||||
public Script getScriptPubKey() throws ScriptException {
|
||||
public Script getScriptPubKey() throws ScriptException {
|
||||
if (scriptPubKey == null)
|
||||
scriptPubKey = new Script(params, scriptBytes, 0, scriptBytes.length);
|
||||
return scriptPubKey;
|
||||
@ -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();
|
||||
}
|
||||
}
|
@ -50,6 +50,15 @@ public class VersionMessage extends Message {
|
||||
public VersionMessage(NetworkParameters params, byte[] msg) throws ProtocolException {
|
||||
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);
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user