Skip to content

Commit

Permalink
Modify data object serialization/deserialization to provide extension…
Browse files Browse the repository at this point in the history
… hooks

Make headerParsed, transactionParsed, headerBytesValid and transactionBytesValid protected so subclasses in the same package can access them.

Add constructor for use when a block is contained within another object (i.e. AuxPoW header)

Make headerParsed, transactionParsed, headerBytesValid and transactionBytesValid protected so subclasses in the same package can access them.

Add constructor for use when a block is contained within another object (i.e. AuxPoW header)

Add parseTransactions() method which takes in a payload offset, so block parsers can indicate that transactions do not start at byte 80.

Extract common interface from BitcoinSerializer to support alternative serializers, as well as dummy serializer for message classes which do not have their network parameters.

Thread BitcoinSerializer through messages in place of existing parseLazy and parseRetain properties.

Insert dummy serializer into message subclasses when deserialized by Java.

Replace calls to construct Block/Transaction classes from payloads, with calls to MessageSerializer, so alternative formats can be supported elegantly.

Make headerParsed, transactionParsed, headerBytesValid and transactionBytesValid protected so subclasses in the same package can access them.

Add constructor for use when a block is contained within another object (i.e. AuxPoW header)

Add parseTransactions() method which takes in a payload offset, so block
parsers can indicate that transactions do not start at byte 80.
  • Loading branch information
Ross Nicoll authored and schildbach committed Jul 28, 2015
1 parent 16a5e1d commit 1260265
Show file tree
Hide file tree
Showing 43 changed files with 630 additions and 194 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ private static void sendTransactionsToListener(StoredBlock block, NewBlockType b
try {
falsePositives.remove(tx.getHash());
if (clone)
tx = new Transaction(tx.params, tx.bitcoinSerialize());
tx = tx.params.getDefaultSerializer().makeTransaction(tx.bitcoinSerialize());
listener.receiveFromBlock(tx, block, blockType, relativityOffset++);
} catch (ScriptException e) {
// We don't want scripts we don't understand to break the block chain so just note that this tx was
Expand Down
19 changes: 8 additions & 11 deletions core/src/main/java/org/bitcoinj/core/AddressMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,28 @@ public class AddressMessage extends Message {
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException
*/
AddressMessage(NetworkParameters params, byte[] payload, int offset, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException {
super(params, payload, offset, parseLazy, parseRetain, length);
AddressMessage(NetworkParameters params, byte[] payload, int offset, MessageSerializer setSerializer, int length) throws ProtocolException {
super(params, payload, offset, setSerializer, length);
}

/**
* Contruct a new 'addr' message.
* @param params NetworkParameters object.
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
* If true and the backing byte array is invalidated due to modification of a field then
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
* @param serializer the serializer to use for this block.
* @param length The length of message if known. Usually this is provided when deserializing of the wire
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException
*/
AddressMessage(NetworkParameters params, byte[] payload, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException {
super(params, payload, 0, parseLazy, parseRetain, length);
AddressMessage(NetworkParameters params, byte[] payload, MessageSerializer serializer, int length) throws ProtocolException {
super(params, payload, 0, serializer, length);
}

AddressMessage(NetworkParameters params, byte[] payload, int offset) throws ProtocolException {
super(params, payload, offset, false, false, UNKNOWN_LENGTH);
super(params, payload, offset, params.getDefaultSerializer(), UNKNOWN_LENGTH);
}

AddressMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
super(params, payload, 0, false, false, UNKNOWN_LENGTH);
super(params, payload, 0, params.getDefaultSerializer(), UNKNOWN_LENGTH);
}

@Override
Expand All @@ -67,7 +64,7 @@ void parse() throws ProtocolException {
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, payload, cursor, protocolVersion, this, parseLazy, parseRetain);
PeerAddress addr = new PeerAddress(params, payload, cursor, protocolVersion, this, serializer);
addresses.add(addr);
cursor += addr.getMessageSize();
}
Expand Down
149 changes: 127 additions & 22 deletions core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@
* <li>Message.bitcoinSerializeToStream() needs to be properly subclassed</li>
* </ul>
*/
public class BitcoinSerializer {
public class BitcoinSerializer implements MessageSerializer {
private static final Logger log = LoggerFactory.getLogger(BitcoinSerializer.class);
private static final int COMMAND_LEN = 12;

private NetworkParameters params;
private boolean parseLazy = false;
private boolean parseRetain = false;
private final NetworkParameters params;
private final boolean parseLazy;
private final boolean parseRetain;

private static Map<Class<? extends Message>, String> names = new HashMap<Class<? extends Message>, String>();
private static final Map<Class<? extends Message>, String> names = new HashMap<Class<? extends Message>, String>();

static {
names.put(VersionMessage.class, "version");
Expand All @@ -75,12 +75,19 @@ public class BitcoinSerializer {
}

/**
* Constructs a BitcoinSerializer with the given behavior.
*
* @param params networkParams used to create Messages instances and termining packetMagic
* Constructs a partial BitcoinSerializer with the given behavior. This is
* intended for use by messages which do not understand the network they
* belong to.
*
* @param parseLazy deserialize messages in lazy mode.
* @param parseRetain retain the backing byte array of a message for fast reserialization.
* @deprecated use BitcoinSerializer(NetworkParameters, boolean, boolean) instead.
*/
public BitcoinSerializer(NetworkParameters params) {
this(params, false, false);
@Deprecated
BitcoinSerializer(boolean parseLazy, boolean parseRetain) {
this.params = null;
this.parseLazy = parseLazy;
this.parseRetain = parseRetain;
}

/**
Expand All @@ -99,6 +106,7 @@ public BitcoinSerializer(NetworkParameters params, boolean parseLazy, boolean pa
/**
* Writes message to to the output stream.
*/
@Override
public void serialize(String name, byte[] message, OutputStream out) throws IOException {
byte[] header = new byte[4 + COMMAND_LEN + 4 + 4 /* checksum */];
uint32ToByteArrayBE(params.getPacketMagic(), header, 0);
Expand All @@ -123,6 +131,7 @@ public void serialize(String name, byte[] message, OutputStream out) throws IOEx
/**
* Writes message to to the output stream.
*/
@Override
public void serialize(Message message, OutputStream out) throws IOException {
String name = names.get(message.getClass());
if (name == null) {
Expand All @@ -134,6 +143,7 @@ public void serialize(Message message, OutputStream out) throws IOException {
/**
* Reads a message from the given ByteBuffer and returns it.
*/
@Override
public Message deserialize(ByteBuffer in) throws ProtocolException, IOException {
// A Bitcoin protocol message has the following format.
//
Expand All @@ -159,6 +169,7 @@ public Message deserialize(ByteBuffer in) throws ProtocolException, IOException
* Deserializes only the header in case packet meta data is needed before decoding
* the payload. This method assumes you have already called seekPastMagicBytes()
*/
@Override
public BitcoinPacketHeader deserializeHeader(ByteBuffer in) throws ProtocolException, IOException {
return new BitcoinPacketHeader(in);
}
Expand All @@ -167,6 +178,7 @@ public BitcoinPacketHeader deserializeHeader(ByteBuffer in) throws ProtocolExcep
* Deserialize payload only. You must provide a header, typically obtained by calling
* {@link BitcoinSerializer#deserializeHeader}.
*/
@Override
public Message deserializePayload(BitcoinPacketHeader header, ByteBuffer in) throws ProtocolException, BufferUnderflowException {
byte[] payloadBytes = new byte[header.size];
in.get(payloadBytes, 0, header.size);
Expand Down Expand Up @@ -198,25 +210,22 @@ private Message makeMessage(String command, int length, byte[] payloadBytes, byt
Message message;
if (command.equals("version")) {
return new VersionMessage(params, payloadBytes);
} else if (command.equals("inv")) {
message = new InventoryMessage(params, payloadBytes, parseLazy, parseRetain, length);
} else if (command.equals("inv")) {
message = makeInventoryMessage(payloadBytes, length);
} else if (command.equals("block")) {
message = new Block(params, payloadBytes, parseLazy, parseRetain, length);
message = makeBlock(payloadBytes, length);
} else if (command.equals("merkleblock")) {
message = new FilteredBlock(params, payloadBytes);
message = makeFilteredBlock(payloadBytes);
} else if (command.equals("getdata")) {
message = new GetDataMessage(params, payloadBytes, parseLazy, parseRetain, length);
message = new GetDataMessage(params, payloadBytes, this, length);
} else if (command.equals("getblocks")) {
message = new GetBlocksMessage(params, payloadBytes);
} else if (command.equals("getheaders")) {
message = new GetHeadersMessage(params, payloadBytes);
} else if (command.equals("tx")) {
Transaction tx = new Transaction(params, payloadBytes, null, parseLazy, parseRetain, length);
if (hash != null)
tx.setHash(Sha256Hash.wrapReversed(hash));
message = tx;
message = makeTransaction(payloadBytes, 0, length, hash);
} else if (command.equals("addr")) {
message = new AddressMessage(params, payloadBytes, parseLazy, parseRetain, length);
message = makeAddressMessage(payloadBytes, length);
} else if (command.equals("ping")) {
message = new Ping(params, payloadBytes);
} else if (command.equals("pong")) {
Expand All @@ -226,9 +235,9 @@ private Message makeMessage(String command, int length, byte[] payloadBytes, byt
} else if (command.equals("headers")) {
return new HeadersMessage(params, payloadBytes);
} else if (command.equals("alert")) {
return new AlertMessage(params, payloadBytes);
return makeAlertMessage(payloadBytes);
} else if (command.equals("filterload")) {
return new BloomFilter(params, payloadBytes);
return makeBloomFilter(payloadBytes);
} else if (command.equals("notfound")) {
return new NotFoundMessage(params, payloadBytes);
} else if (command.equals("mempool")) {
Expand All @@ -246,6 +255,100 @@ private Message makeMessage(String command, int length, byte[] payloadBytes, byt
return message;
}

/**
* Get the network parameters for this serializer.
*/
public NetworkParameters getParameters() {
return params;
}

/**
* Make an address message from the payload. Extension point for alternative
* serialization format support.
*/
@Override
public AddressMessage makeAddressMessage(byte[] payloadBytes, int length) throws ProtocolException {
return new AddressMessage(params, payloadBytes, this, length);
}

/**
* Make an alert message from the payload. Extension point for alternative
* serialization format support.
*/
@Override
public Message makeAlertMessage(byte[] payloadBytes) throws ProtocolException {
return new AlertMessage(params, payloadBytes);
}

/**
* Make a block from the payload. Extension point for alternative
* serialization format support.
*/
@Override
public Block makeBlock(byte[] payloadBytes) throws ProtocolException {
return new Block(params, payloadBytes, this, payloadBytes.length);
}

/**
* Make a block from the payload. Extension point for alternative
* serialization format support.
*/
@Override
public Block makeBlock(byte[] payloadBytes, int length) throws ProtocolException {
return new Block(params, payloadBytes, this, length);
}

/**
* Make an filter message from the payload. Extension point for alternative
* serialization format support.
*/
@Override
public Message makeBloomFilter(byte[] payloadBytes) throws ProtocolException {
return new BloomFilter(params, payloadBytes);
}

/**
* Make a filtered block from the payload. Extension point for alternative
* serialization format support.
*/
@Override
public FilteredBlock makeFilteredBlock(byte[] payloadBytes) throws ProtocolException {
return new FilteredBlock(params, payloadBytes);
}

/**
* Make an inventory message from the payload. Extension point for alternative
* serialization format support.
*/
@Override
public InventoryMessage makeInventoryMessage(byte[] payloadBytes, int length) throws ProtocolException {
return new InventoryMessage(params, payloadBytes, this, length);
}

/**
* Make a transaction from the payload. Extension point for alternative
* serialization format support.
*/
@Override
public Transaction makeTransaction(byte[] payloadBytes, int offset,
int length, byte[] hash) throws ProtocolException {
Transaction tx = new Transaction(params, payloadBytes, offset, null, this, length);
if (hash != null)
tx.setHash(Sha256Hash.wrapReversed(hash));
return tx;
}

@Override
public Transaction makeTransaction(byte[] payloadBytes) throws ProtocolException {
return makeTransaction(payloadBytes, 0, payloadBytes.length, null);
}

@Override
public Transaction makeTransaction(byte[] payloadBytes, int offset) throws ProtocolException {
return makeTransaction(payloadBytes, offset, payloadBytes.length, null);
}

@Override
public void seekPastMagicBytes(ByteBuffer in) throws BufferUnderflowException {
int magicCursor = 3; // Which byte of the magic we're looking for currently.
while (true) {
Expand All @@ -270,13 +373,15 @@ public void seekPastMagicBytes(ByteBuffer in) throws BufferUnderflowException {
/**
* Whether the serializer will produce lazy parse mode Messages
*/
@Override
public boolean isParseLazyMode() {
return parseLazy;
}

/**
* Whether the serializer will produce cached mode Messages
*/
@Override
public boolean isParseRetainMode() {
return parseRetain;
}
Expand Down

0 comments on commit 1260265

Please sign in to comment.