mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-07 14:54: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.
|
- Find a way to avoid some horrid hacks when shutting down the network connection.
|
||||||
- Implement a BitCoin class that encapsulates a BigInteger and formatting.
|
- Implement a BitCoin class that encapsulates a BigInteger and formatting.
|
||||||
- Make NetworkParameters use subclasses instead of static methods to construct.
|
- 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;
|
private static final long MAX_ADDRESSES = 1024;
|
||||||
List<PeerAddress> addresses;
|
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 {
|
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 {
|
AddressMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
|
||||||
super(params, payload, 0);
|
super(params, payload, 0, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -24,7 +32,7 @@ public class AddressMessage extends Message {
|
|||||||
throw new ProtocolException("Address message too large.");
|
throw new ProtocolException("Address message too large.");
|
||||||
addresses = new ArrayList<PeerAddress>((int)numAddresses);
|
addresses = new ArrayList<PeerAddress>((int)numAddresses);
|
||||||
for (int i = 0; i < numAddresses; i++) {
|
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);
|
addresses.add(addr);
|
||||||
cursor += addr.getMessageSize();
|
cursor += addr.getMessageSize();
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,10 @@ public class Block extends Message {
|
|||||||
super(params, payloadBytes, 0);
|
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 {
|
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
|
||||||
ois.defaultReadObject();
|
ois.defaultReadObject();
|
||||||
// This code is not actually necessary, as transient fields are initialized to the default value which is in
|
// 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();
|
int numTransactions = (int) readVarInt();
|
||||||
transactions = new ArrayList<Transaction>(numTransactions);
|
transactions = new ArrayList<Transaction>(numTransactions);
|
||||||
for (int i = 0; i < numTransactions; i++) {
|
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);
|
transactions.add(tx);
|
||||||
cursor += tx.getMessageSize();
|
cursor += tx.getMessageSize();
|
||||||
}
|
}
|
||||||
@ -120,13 +124,13 @@ public class Block extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||||
writeHeader(stream);
|
writeHeader(stream);
|
||||||
// We may only have enough data to write the header.
|
// We may only have enough data to write the header.
|
||||||
if (transactions == null) return;
|
if (transactions == null) return;
|
||||||
stream.write(new VarInt(transactions.size()).encode());
|
stream.write(new VarInt(transactions.size()).encode());
|
||||||
for (Transaction tx : transactions) {
|
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;
|
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
|
* @author git
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -17,16 +18,12 @@ public abstract class EmptyMessage extends Message {
|
|||||||
super(params);
|
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 {
|
public EmptyMessage(NetworkParameters params, byte[] msg, int offset) throws ProtocolException {
|
||||||
super(params, msg, offset);
|
super(params, msg, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
public class GetAddrMessage extends EmptyMessage {
|
public class GetAddrMessage extends EmptyMessage {
|
||||||
|
|
||||||
public GetAddrMessage(NetworkParameters params) {
|
public GetAddrMessage(NetworkParameters params) {
|
||||||
super(params);
|
super(params);
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ public class GetBlocksMessage extends Message {
|
|||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||||
// Version, for some reason.
|
// Version, for some reason.
|
||||||
Utils.uint32ToByteStreamLE(NetworkParameters.PROTOCOL_VERSION, stream);
|
Utils.uint32ToByteStreamLE(NetworkParameters.PROTOCOL_VERSION, stream);
|
||||||
// Then a vector of block hashes. This is actually a "block locator", a set of block
|
// Then a vector of block hashes. This is actually a "block locator", a set of block
|
||||||
|
@ -23,6 +23,11 @@ public class GetDataMessage extends ListMessage {
|
|||||||
super(params, payloadBytes);
|
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);
|
super(params);
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,11 @@ public class InventoryMessage extends ListMessage {
|
|||||||
super(params, bytes);
|
super(params, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InventoryMessage(NetworkParameters params, byte[] msg, boolean parseLazy, boolean parseRetain)
|
||||||
|
throws ProtocolException {
|
||||||
|
super(params, msg, parseLazy, parseRetain);
|
||||||
|
}
|
||||||
|
|
||||||
public InventoryMessage(NetworkParameters params) {
|
public InventoryMessage(NetworkParameters params) {
|
||||||
super(params);
|
super(params);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
public ListMessage(NetworkParameters params) {
|
||||||
super(params);
|
super(params);
|
||||||
items = new ArrayList<InventoryItem>();
|
items = new ArrayList<InventoryItem>();
|
||||||
|
@ -48,6 +48,10 @@ public abstract class Message implements Serializable {
|
|||||||
// The raw message bytes themselves.
|
// The raw message bytes themselves.
|
||||||
protected transient byte[] bytes;
|
protected transient byte[] bytes;
|
||||||
|
|
||||||
|
private transient boolean parsed = false;
|
||||||
|
protected transient final boolean parseLazy;
|
||||||
|
protected transient final boolean parseRetain;
|
||||||
|
|
||||||
protected transient int protocolVersion;
|
protected transient int protocolVersion;
|
||||||
|
|
||||||
// This will be saved by subclasses that implement Serializable.
|
// 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. */
|
/** This exists for the Java serialization framework to use only. */
|
||||||
protected Message() {
|
protected Message() {
|
||||||
|
parsed = true;
|
||||||
|
parseLazy = false;
|
||||||
|
parseRetain = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message(NetworkParameters params) {
|
Message(NetworkParameters params) {
|
||||||
this.params = 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")
|
@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.protocolVersion = protocolVersion;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.bytes = msg;
|
this.bytes = msg;
|
||||||
this.cursor = this.offset = offset;
|
this.cursor = this.offset = offset;
|
||||||
|
if (!parseLazy) {
|
||||||
parse();
|
parse();
|
||||||
|
parsed = true;
|
||||||
|
}
|
||||||
if (SELF_CHECK && !this.getClass().getSimpleName().equals("VersionMessage")) {
|
if (SELF_CHECK && !this.getClass().getSimpleName().equals("VersionMessage")) {
|
||||||
|
checkParse();
|
||||||
byte[] msgbytes = new byte[cursor - offset];
|
byte[] msgbytes = new byte[cursor - offset];
|
||||||
System.arraycopy(msg, offset, msgbytes, 0, cursor - offset);
|
System.arraycopy(msg, offset, msgbytes, 0, cursor - offset);
|
||||||
byte[] reserialized = bitcoinSerialize();
|
byte[] reserialized = bitcoinSerialize();
|
||||||
@ -77,11 +97,17 @@ public abstract class Message implements Serializable {
|
|||||||
Utils.bytesToHexString(reserialized) + " vs \n" +
|
Utils.bytesToHexString(reserialized) + " vs \n" +
|
||||||
Utils.bytesToHexString(msgbytes));
|
Utils.bytesToHexString(msgbytes));
|
||||||
}
|
}
|
||||||
|
if (parseRetain || !parsed)
|
||||||
|
return;
|
||||||
this.bytes = null;
|
this.bytes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message(NetworkParameters params, byte[] msg, int offset) throws ProtocolException {
|
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.
|
// 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.
|
// are serialized to the wallet.
|
||||||
abstract void parse() throws ProtocolException;
|
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() {
|
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();
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
try {
|
try {
|
||||||
bitcoinSerializeToStream(stream);
|
bitcoinSerializeToStream(stream);
|
||||||
@ -101,6 +206,20 @@ public abstract class Message implements Serializable {
|
|||||||
return stream.toByteArray();
|
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().
|
* 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 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.
|
* 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;
|
private static final long serialVersionUID = 7501293709324197411L;
|
||||||
|
|
||||||
InetAddress addr;
|
InetAddress addr;
|
||||||
@ -46,6 +46,17 @@ public class PeerAddress extends Message {
|
|||||||
super(params, payload, offset, protocolVersion);
|
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.
|
* Construct a peer address from a memorized or hardcoded address.
|
||||||
*/
|
*/
|
||||||
@ -69,7 +80,7 @@ public class PeerAddress extends Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||||
if (protocolVersion >= 31402) {
|
if (protocolVersion >= 31402) {
|
||||||
int secs = (int)(Utils.now().getTime() / 1000);
|
int secs = (int)(Utils.now().getTime() / 1000);
|
||||||
uint32ToByteStreamLE(secs, stream);
|
uint32ToByteStreamLE(secs, stream);
|
||||||
|
@ -18,6 +18,7 @@ package com.google.bitcoin.core;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigInteger;
|
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
|
* serialization which is used for the wallet. This allows us to easily add extra fields used for our own accounting
|
||||||
* or UI purposes.
|
* 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 Logger log = LoggerFactory.getLogger(Transaction.class);
|
||||||
private static final long serialVersionUID = -8567546957352643140L;
|
private static final long serialVersionUID = -8567546957352643140L;
|
||||||
|
|
||||||
@ -89,6 +90,22 @@ public class Transaction extends Message implements Serializable {
|
|||||||
// inputs/outputs will be created in parse()
|
// 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.
|
* 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();
|
long numInputs = readVarInt();
|
||||||
inputs = new ArrayList<TransactionInput>((int)numInputs);
|
inputs = new ArrayList<TransactionInput>((int)numInputs);
|
||||||
for (long i = 0; i < numInputs; i++) {
|
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);
|
inputs.add(input);
|
||||||
cursor += input.getMessageSize();
|
cursor += input.getMessageSize();
|
||||||
}
|
}
|
||||||
@ -280,7 +297,7 @@ public class Transaction extends Message implements Serializable {
|
|||||||
long numOutputs = readVarInt();
|
long numOutputs = readVarInt();
|
||||||
outputs = new ArrayList<TransactionOutput>((int)numOutputs);
|
outputs = new ArrayList<TransactionOutput>((int)numOutputs);
|
||||||
for (long i = 0; i < numOutputs; i++) {
|
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);
|
outputs.add(output);
|
||||||
cursor += output.getMessageSize();
|
cursor += output.getMessageSize();
|
||||||
}
|
}
|
||||||
@ -457,14 +474,14 @@ public class Transaction extends Message implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||||
uint32ToByteStreamLE(version, stream);
|
uint32ToByteStreamLE(version, stream);
|
||||||
stream.write(new VarInt(inputs.size()).encode());
|
stream.write(new VarInt(inputs.size()).encode());
|
||||||
for (TransactionInput in : inputs)
|
for (TransactionInput in : inputs)
|
||||||
in.bitcoinSerializeToStream(stream);
|
in.bitcoinSerialize(stream);
|
||||||
stream.write(new VarInt(outputs.size()).encode());
|
stream.write(new VarInt(outputs.size()).encode());
|
||||||
for (TransactionOutput out : outputs)
|
for (TransactionOutput out : outputs)
|
||||||
out.bitcoinSerializeToStream(stream);
|
out.bitcoinSerialize(stream);
|
||||||
uint32ToByteStreamLE(lockTime, stream);
|
uint32ToByteStreamLE(lockTime, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,4 +497,15 @@ public class Transaction extends Message implements Serializable {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return getHash().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;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Map;
|
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
|
* 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.
|
* 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;
|
private static final long serialVersionUID = 2;
|
||||||
public static final byte[] EMPTY_ARRAY = new byte[0];
|
public static final byte[] EMPTY_ARRAY = new byte[0];
|
||||||
|
|
||||||
@ -73,8 +74,15 @@ public class TransactionInput extends Message implements Serializable {
|
|||||||
this.parentTransaction = parentTransaction;
|
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 {
|
void parse() throws ProtocolException {
|
||||||
outpoint = new TransactionOutPoint(params, bytes, cursor);
|
outpoint = new TransactionOutPoint(params, bytes, cursor, this, parseLazy, parseRetain);
|
||||||
cursor += outpoint.getMessageSize();
|
cursor += outpoint.getMessageSize();
|
||||||
int scriptLen = (int) readVarInt();
|
int scriptLen = (int) readVarInt();
|
||||||
scriptBytes = readBytes(scriptLen);
|
scriptBytes = readBytes(scriptLen);
|
||||||
@ -82,8 +90,8 @@ public class TransactionInput extends Message implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||||
outpoint.bitcoinSerializeToStream(stream);
|
outpoint.bitcoinSerialize(stream);
|
||||||
stream.write(new VarInt(scriptBytes.length).encode());
|
stream.write(new VarInt(scriptBytes.length).encode());
|
||||||
stream.write(scriptBytes);
|
stream.write(scriptBytes);
|
||||||
Utils.uint32ToByteStreamLE(sequence, stream);
|
Utils.uint32ToByteStreamLE(sequence, stream);
|
||||||
@ -187,4 +195,14 @@ public class TransactionInput extends Message implements Serializable {
|
|||||||
outpoint.fromTx = null;
|
outpoint.fromTx = null;
|
||||||
return true;
|
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;
|
package com.google.bitcoin.core;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.Serializable;
|
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.
|
* 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;
|
private static final long serialVersionUID = -6320880638344662579L;
|
||||||
|
|
||||||
/** Hash of the transaction to which we refer. */
|
/** Hash of the transaction to which we refer. */
|
||||||
@ -54,6 +55,11 @@ public class TransactionOutPoint extends Message implements Serializable {
|
|||||||
super(params, payload, offset);
|
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
|
@Override
|
||||||
void parse() throws ProtocolException {
|
void parse() throws ProtocolException {
|
||||||
hash = readHash();
|
hash = readHash();
|
||||||
@ -61,7 +67,7 @@ public class TransactionOutPoint extends Message implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||||
stream.write(Utils.reverseBytes(hash.getBytes()));
|
stream.write(Utils.reverseBytes(hash.getBytes()));
|
||||||
Utils.uint32ToByteStreamLE(index, stream);
|
Utils.uint32ToByteStreamLE(index, stream);
|
||||||
}
|
}
|
||||||
@ -96,4 +102,14 @@ public class TransactionOutPoint extends Message implements Serializable {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "outpoint " + index + ":" + hash.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 org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigInteger;
|
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
|
* A TransactionOutput message contains a scriptPubKey that controls who is able to spend its value. It is a sub-part
|
||||||
* of the Transaction message.
|
* 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 Logger log = LoggerFactory.getLogger(TransactionOutput.class);
|
||||||
private static final long serialVersionUID = -590332479859256824L;
|
private static final long serialVersionUID = -590332479859256824L;
|
||||||
|
|
||||||
@ -58,6 +59,14 @@ public class TransactionOutput extends Message implements Serializable {
|
|||||||
availableForSpending = true;
|
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);
|
super(params);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@ -88,7 +97,7 @@ public class TransactionOutput extends Message implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bitcoinSerializeToStream( OutputStream stream) throws IOException {
|
protected void bitcoinSerializeToStream( OutputStream stream) throws IOException {
|
||||||
assert scriptBytes != null;
|
assert scriptBytes != null;
|
||||||
Utils.uint64ToByteStreamLE(getValue(), stream);
|
Utils.uint64ToByteStreamLE(getValue(), stream);
|
||||||
// TODO: Move script serialization into the Script class, where it belongs.
|
// TODO: Move script serialization into the Script class, where it belongs.
|
||||||
@ -162,4 +171,14 @@ public class TransactionOutput extends Message implements Serializable {
|
|||||||
TransactionInput getSpentBy() {
|
TransactionInput getSpentBy() {
|
||||||
return spentBy;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
@ -51,6 +51,15 @@ public class VersionMessage extends Message {
|
|||||||
super(params, msg, 0);
|
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) {
|
public VersionMessage(NetworkParameters params, int newBestHeight) {
|
||||||
super(params);
|
super(params);
|
||||||
clientVersion = NetworkParameters.PROTOCOL_VERSION;
|
clientVersion = NetworkParameters.PROTOCOL_VERSION;
|
||||||
@ -96,9 +105,9 @@ public class VersionMessage extends Message {
|
|||||||
Utils.uint32ToByteStreamLE(time >> 32, buf);
|
Utils.uint32ToByteStreamLE(time >> 32, buf);
|
||||||
try {
|
try {
|
||||||
// My address.
|
// My address.
|
||||||
myAddr.bitcoinSerializeToStream(buf);
|
myAddr.bitcoinSerialize(buf);
|
||||||
// Their address.
|
// Their address.
|
||||||
theirAddr.bitcoinSerializeToStream(buf);
|
theirAddr.bitcoinSerialize(buf);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
throw new RuntimeException(e); // Can't happen.
|
throw new RuntimeException(e); // Can't happen.
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -143,4 +152,17 @@ public class VersionMessage extends Message {
|
|||||||
return (int)bestHeight ^ clientVersion ^ (int)localServices ^ (int)time ^ subVer.hashCode() ^ myAddr.hashCode()
|
return (int)bestHeight ^ clientVersion ^ (int)localServices ^ (int)time ^ subVer.hashCode() ^ myAddr.hashCode()
|
||||||
^ theirAddr.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